1 /*
2  * Copyright (C) 2019 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.systemui.assist;
18 
19 import android.app.ActivityManager;
20 import android.app.KeyguardManager;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.ResolveInfo;
27 
28 import androidx.annotation.Nullable;
29 
30 import com.android.systemui.BootCompleteCache;
31 import com.android.systemui.broadcast.BroadcastDispatcher;
32 import com.android.systemui.dagger.SysUISingleton;
33 import com.android.systemui.plugins.statusbar.StatusBarStateController;
34 import com.android.systemui.shared.system.ActivityManagerWrapper;
35 import com.android.systemui.shared.system.PackageManagerWrapper;
36 import com.android.systemui.shared.system.TaskStackChangeListener;
37 import com.android.systemui.shared.system.TaskStackChangeListeners;
38 import com.android.systemui.statusbar.StatusBarState;
39 import com.android.systemui.statusbar.phone.CentralSurfaces;
40 
41 import dagger.Lazy;
42 
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.Optional;
46 
47 import javax.inject.Inject;
48 
49 /** Class to monitor and report the state of the phone. */
50 @SysUISingleton
51 public final class PhoneStateMonitor {
52 
53     public static final int PHONE_STATE_AOD1 = 1;
54     public static final int PHONE_STATE_AOD2 = 2;
55     public static final int PHONE_STATE_BOUNCER = 3;
56     public static final int PHONE_STATE_UNLOCKED_LOCKSCREEN = 4;
57     public static final int PHONE_STATE_HOME = 5;
58     public static final int PHONE_STATE_OVERVIEW = 6;
59     public static final int PHONE_STATE_ALL_APPS = 7;
60     public static final int PHONE_STATE_APP_DEFAULT = 8;
61     public static final int PHONE_STATE_APP_IMMERSIVE = 9;
62     public static final int PHONE_STATE_APP_FULLSCREEN = 10;
63 
64     private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] {
65             PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED,
66             Intent.ACTION_PACKAGE_ADDED,
67             Intent.ACTION_PACKAGE_CHANGED,
68             Intent.ACTION_PACKAGE_REMOVED
69     };
70 
71     private final Context mContext;
72     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
73     private final StatusBarStateController mStatusBarStateController;
74 
75     private boolean mLauncherShowing;
76     @Nullable private ComponentName mDefaultHome;
77 
78     @Inject
PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, BootCompleteCache bootCompleteCache, StatusBarStateController statusBarStateController)79     PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher,
80             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
81             BootCompleteCache bootCompleteCache,
82             StatusBarStateController statusBarStateController) {
83         mContext = context;
84         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
85         mStatusBarStateController = statusBarStateController;
86 
87         mDefaultHome = getCurrentDefaultHome();
88         bootCompleteCache.addListener(() -> mDefaultHome = getCurrentDefaultHome());
89         IntentFilter intentFilter = new IntentFilter();
90         for (String action : DEFAULT_HOME_CHANGE_ACTIONS) {
91             intentFilter.addAction(action);
92         }
93         broadcastDispatcher.registerReceiver(new BroadcastReceiver() {
94             @Override
95             public void onReceive(Context context, Intent intent) {
96                 mDefaultHome = getCurrentDefaultHome();
97             }
98         }, intentFilter);
99         mLauncherShowing = isLauncherShowing(ActivityManagerWrapper.getInstance().getRunningTask());
100         TaskStackChangeListeners.getInstance().registerTaskStackListener(
101                 new TaskStackChangeListener() {
102                     @Override
103                     public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
104                         mLauncherShowing = isLauncherShowing(taskInfo);
105                     }
106         });
107     }
108 
getPhoneState()109     public int getPhoneState() {
110         int phoneState;
111         if (isShadeFullscreen()) {
112             phoneState = getPhoneLockscreenState();
113         } else if (mLauncherShowing) {
114             phoneState = getPhoneLauncherState();
115         } else {
116             phoneState = PHONE_STATE_APP_IMMERSIVE;
117         }
118         return phoneState;
119     }
120 
121     @Nullable
getCurrentDefaultHome()122     private static ComponentName getCurrentDefaultHome() {
123         List<ResolveInfo> homeActivities = new ArrayList<>();
124         ComponentName defaultHome =
125                 PackageManagerWrapper.getInstance().getHomeActivities(homeActivities);
126         if (defaultHome != null) {
127             return defaultHome;
128         }
129 
130         int topPriority = Integer.MIN_VALUE;
131         ComponentName topComponent = null;
132         for (ResolveInfo resolveInfo : homeActivities) {
133             if (resolveInfo.priority > topPriority) {
134                 topComponent = resolveInfo.activityInfo.getComponentName();
135                 topPriority = resolveInfo.priority;
136             } else if (resolveInfo.priority == topPriority) {
137                 topComponent = null;
138             }
139         }
140         return topComponent;
141     }
142 
getPhoneLockscreenState()143     private int getPhoneLockscreenState() {
144         if (isDozing()) {
145             return PHONE_STATE_AOD1;
146         } else if (isBouncerShowing()) {
147             return PHONE_STATE_BOUNCER;
148         } else if (isKeyguardLocked()) {
149             return PHONE_STATE_AOD2;
150         } else {
151             return PHONE_STATE_UNLOCKED_LOCKSCREEN;
152         }
153     }
154 
getPhoneLauncherState()155     private int getPhoneLauncherState() {
156         if (isLauncherInOverview()) {
157             return PHONE_STATE_OVERVIEW;
158         } else if (isLauncherInAllApps()) {
159             return PHONE_STATE_ALL_APPS;
160         } else {
161             return PHONE_STATE_HOME;
162         }
163     }
164 
isShadeFullscreen()165     private boolean isShadeFullscreen() {
166         int statusBarState = mStatusBarStateController.getState();
167         return statusBarState == StatusBarState.KEYGUARD
168                 || statusBarState == StatusBarState.SHADE_LOCKED;
169     }
170 
isDozing()171     private boolean isDozing() {
172         return mStatusBarStateController.isDozing();
173     }
174 
isLauncherShowing(@ullable ActivityManager.RunningTaskInfo runningTaskInfo)175     private boolean isLauncherShowing(@Nullable ActivityManager.RunningTaskInfo runningTaskInfo) {
176         if (runningTaskInfo == null || runningTaskInfo.topActivity == null) {
177             return false;
178         } else {
179             return runningTaskInfo.topActivity.equals(mDefaultHome);
180         }
181     }
182 
isBouncerShowing()183     private boolean isBouncerShowing() {
184         return mCentralSurfacesOptionalLazy.get()
185                 .map(CentralSurfaces::isBouncerShowing).orElse(false);
186     }
187 
isKeyguardLocked()188     private boolean isKeyguardLocked() {
189         // TODO: Move binder call off of critical path
190         KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
191         return keyguardManager != null && keyguardManager.isKeyguardLocked();
192     }
193 
isLauncherInOverview()194     private boolean isLauncherInOverview() {
195         // TODO
196         return false;
197     }
198 
isLauncherInAllApps()199     private boolean isLauncherInAllApps() {
200         // TODO
201         return false;
202     }
203 }
204