• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.settings;
18 
19 import static android.car.settings.CarSettings.Global.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE;
20 
21 import android.app.Activity;
22 import android.car.userlib.UserHelper;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.pm.ResolveInfo;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.Message;
31 import android.os.PowerManager;
32 import android.os.SystemClock;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.provider.Settings;
36 import android.view.Gravity;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.WindowManager.LayoutParams;
40 import android.view.animation.AnimationUtils;
41 import android.widget.LinearLayout;
42 import android.widget.TextView;
43 
44 import com.android.car.settings.common.Logger;
45 
46 import java.util.Objects;
47 
48 /**
49  * Copied over from phone settings. This covers the fallback case where no launcher is available.
50  */
51 public class FallbackHome extends Activity {
52     private static final Logger LOG = new Logger(FallbackHome.class);
53     private static final int PROGRESS_TIMEOUT = 2000;
54 
55     private boolean mProvisioned;
56 
57     private boolean mFinished;
58 
59     private final Runnable mProgressTimeoutRunnable = () -> {
60         View v = getLayoutInflater().inflate(
61                 R.layout.fallback_home_finishing_boot, /* root= */ null);
62         setContentView(v);
63         v.setAlpha(0f);
64         v.animate()
65                 .alpha(1f)
66                 .setDuration(500)
67                 .setInterpolator(AnimationUtils.loadInterpolator(
68                         this, android.R.interpolator.fast_out_slow_in))
69                 .start();
70         getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
71     };
72 
73     @Override
onCreate(Bundle savedInstanceState)74     protected void onCreate(Bundle savedInstanceState) {
75         super.onCreate(savedInstanceState);
76 
77         // Set ourselves totally black before the device is provisioned
78         mProvisioned = Settings.Global.getInt(getContentResolver(),
79                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
80         int flags;
81         boolean showInfo = false;
82         if (!mProvisioned) {
83             setTheme(R.style.FallbackHome_SetupWizard);
84             flags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
85                     | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
86         } else {
87             flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
88                     | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
89             showInfo = "true".equals(Settings.Global.getString(getContentResolver(),
90                     ENABLE_USER_SWITCH_DEVELOPER_MESSAGE));
91         }
92 
93         if (showInfo) {
94             // Display some info about the current user, which is useful to debug / track user
95             // switching delays.
96             // NOTE: we're manually creating the view (instead of inflating it from XML) to
97             // minimize the performance impact.
98             TextView view = new TextView(this);
99             view.setText("FallbackHome for user " + getUserId() + ".\n\n"
100                     + "This activity is displayed while the user is starting, \n"
101                     + "and it will be replaced by the proper Home \n"
102                     + "(once the user is unlocked).\n\n"
103                     + "NOTE: this message is only shown on debuggable builds");
104             view.setGravity(Gravity.CENTER);
105             view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
106                     ViewGroup.LayoutParams.WRAP_CONTENT));
107 
108             LinearLayout parent = new LinearLayout(this);
109             parent.setOrientation(LinearLayout.VERTICAL);
110             parent.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
111                     ViewGroup.LayoutParams.MATCH_PARENT));
112             parent.addView(view);
113 
114             setContentView(parent);
115         }
116 
117         getWindow().getDecorView().setSystemUiVisibility(flags);
118 
119         registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
120         maybeFinish();
121     }
122 
123     @Override
onResume()124     protected void onResume() {
125         super.onResume();
126         LOG.d("onResume() for user " + getUserId() + ". Provisioned: " + mProvisioned);
127         if (mProvisioned) {
128             mHandler.postDelayed(mProgressTimeoutRunnable, PROGRESS_TIMEOUT);
129         }
130     }
131 
132     @Override
onPause()133     protected void onPause() {
134         super.onPause();
135         mHandler.removeCallbacks(mProgressTimeoutRunnable);
136     }
137 
onDestroy()138     protected void onDestroy() {
139         super.onDestroy();
140         unregisterReceiver(mReceiver);
141         if (!mFinished) {
142             LOG.d("User " + getUserId() + " FallbackHome is finished");
143             finishFallbackHome();
144         }
145     }
146 
147     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
148         @Override
149         public void onReceive(Context context, Intent intent) {
150             maybeFinish();
151         }
152     };
153 
maybeFinish()154     private void maybeFinish() {
155         if (getSystemService(UserManager.class).isUserUnlocked()) {
156             final Intent homeIntent = new Intent(Intent.ACTION_MAIN)
157                     .addCategory(Intent.CATEGORY_HOME);
158             final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0);
159             if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) {
160                 if (UserManager.isSplitSystemUser()
161                         && UserHandle.myUserId() == UserHandle.USER_SYSTEM) {
162                     // This avoids the situation where the system user has no home activity after
163                     // SUW and this activity continues to throw out warnings. See b/28870689.
164                     return;
165                 }
166                 LOG.d("User " + getUserId() + " unlocked but no home; let's hope someone enables "
167                         + "one soon?");
168                 mHandler.sendEmptyMessageDelayed(0, 500);
169             } else {
170                 String homePackageName = homeInfo.activityInfo.packageName;
171                 if (UserHelper.isHeadlessSystemUser(getUserId())) {
172                     // This is the transient state in HeadlessSystemMode to boot for user 10+.
173                     LOG.d("User 0 unlocked, but will not launch real home: " + homePackageName);
174                     return;
175                 }
176                 LOG.d("User " + getUserId() + " unlocked and real home (" + homePackageName
177                         + ") found; let's go!");
178                 finishFallbackHome();
179             }
180         }
181     }
182 
finishFallbackHome()183     private void finishFallbackHome() {
184         getSystemService(PowerManager.class).userActivity(SystemClock.uptimeMillis(), false);
185         finishAndRemoveTask();
186         mFinished = true;
187     }
188 
189     private Handler mHandler = new Handler() {
190         @Override
191         public void handleMessage(Message msg) {
192             maybeFinish();
193         }
194     };
195 }
196