• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.wmshell;
18 
19 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
20 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
21 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
22 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
23 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
24 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
25 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
26 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
27 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
28 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
29 
30 import android.content.Context;
31 import android.content.pm.UserInfo;
32 import android.content.res.Configuration;
33 import android.graphics.Rect;
34 import android.inputmethodservice.InputMethodService;
35 import android.os.IBinder;
36 import android.view.KeyEvent;
37 
38 import androidx.annotation.NonNull;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 import com.android.keyguard.KeyguardUpdateMonitor;
42 import com.android.keyguard.KeyguardUpdateMonitorCallback;
43 import com.android.systemui.CoreStartable;
44 import com.android.systemui.dagger.SysUISingleton;
45 import com.android.systemui.dagger.WMComponent;
46 import com.android.systemui.dagger.qualifiers.Main;
47 import com.android.systemui.keyguard.ScreenLifecycle;
48 import com.android.systemui.keyguard.WakefulnessLifecycle;
49 import com.android.systemui.model.SysUiState;
50 import com.android.systemui.notetask.NoteTaskInitializer;
51 import com.android.systemui.settings.DisplayTracker;
52 import com.android.systemui.settings.UserTracker;
53 import com.android.systemui.shared.tracing.ProtoTraceable;
54 import com.android.systemui.statusbar.CommandQueue;
55 import com.android.systemui.statusbar.policy.ConfigurationController;
56 import com.android.systemui.statusbar.policy.KeyguardStateController;
57 import com.android.systemui.tracing.ProtoTracer;
58 import com.android.systemui.tracing.nano.SystemUiTraceProto;
59 import com.android.wm.shell.desktopmode.DesktopMode;
60 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
61 import com.android.wm.shell.nano.WmShellTraceProto;
62 import com.android.wm.shell.onehanded.OneHanded;
63 import com.android.wm.shell.onehanded.OneHandedEventCallback;
64 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
65 import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
66 import com.android.wm.shell.pip.Pip;
67 import com.android.wm.shell.splitscreen.SplitScreen;
68 import com.android.wm.shell.sysui.ShellInterface;
69 
70 import java.io.PrintWriter;
71 import java.util.List;
72 import java.util.Optional;
73 import java.util.concurrent.Executor;
74 
75 import javax.inject.Inject;
76 
77 /**
78  * A SystemUI service that starts with the SystemUI application and sets up any bindings between
79  * Shell and SysUI components.  This service starts happens after the {@link WMComponent} has
80  * already been initialized and may only reference Shell components that are explicitly exported to
81  * SystemUI (see {@link WMComponent}.
82  *
83  * eg. SysUI application starts
84  *     -> SystemUIFactory is initialized
85  *       -> WMComponent is created
86  *         -> WMShellBaseModule dependencies are injected
87  *         -> WMShellModule (form-factory specific) dependencies are injected
88  *       -> SysUIComponent is created
89  *         -> WMComponents are explicitly provided to SysUIComponent for injection into SysUI code
90  *     -> SysUI services are started
91  *       -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces
92  */
93 @SysUISingleton
94 public final class WMShell implements
95         CoreStartable,
96         CommandQueue.Callbacks,
97         ProtoTraceable<SystemUiTraceProto> {
98     private static final String TAG = WMShell.class.getName();
99     private static final int INVALID_SYSUI_STATE_MASK =
100             SYSUI_STATE_DIALOG_SHOWING
101                     | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
102                     | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
103                     | SYSUI_STATE_BOUNCER_SHOWING
104                     | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
105                     | SYSUI_STATE_BUBBLES_EXPANDED
106                     | SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED
107                     | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
108 
109     private final Context mContext;
110     // Shell interfaces
111     private final ShellInterface mShell;
112     private final Optional<Pip> mPipOptional;
113     private final Optional<SplitScreen> mSplitScreenOptional;
114     private final Optional<OneHanded> mOneHandedOptional;
115     private final Optional<DesktopMode> mDesktopModeOptional;
116 
117     private final CommandQueue mCommandQueue;
118     private final ConfigurationController mConfigurationController;
119     private final KeyguardStateController mKeyguardStateController;
120     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
121     private final ScreenLifecycle mScreenLifecycle;
122     private final SysUiState mSysUiState;
123     private final WakefulnessLifecycle mWakefulnessLifecycle;
124     private final ProtoTracer mProtoTracer;
125     private final UserTracker mUserTracker;
126     private final DisplayTracker mDisplayTracker;
127     private final NoteTaskInitializer mNoteTaskInitializer;
128     private final Executor mSysUiMainExecutor;
129 
130     // Listeners and callbacks. Note that we prefer member variable over anonymous class here to
131     // avoid the situation that some implementations, like KeyguardUpdateMonitor, use WeakReference
132     // internally and anonymous class could be released after registration.
133     private final ConfigurationController.ConfigurationListener mConfigurationListener =
134             new ConfigurationController.ConfigurationListener() {
135                 @Override
136                 public void onConfigChanged(Configuration newConfig) {
137                     mShell.onConfigurationChanged(newConfig);
138                 }
139             };
140     private final KeyguardStateController.Callback mKeyguardStateCallback =
141             new KeyguardStateController.Callback() {
142                 @Override
143                 public void onKeyguardShowingChanged() {
144                     mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
145                             mKeyguardStateController.isOccluded(),
146                             mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
147                 }
148             };
149     private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
150             new KeyguardUpdateMonitorCallback() {
151                 @Override
152                 public void onKeyguardDismissAnimationFinished() {
153                     mShell.onKeyguardDismissAnimationFinished();
154                 }
155             };
156     private final UserTracker.Callback mUserChangedCallback =
157             new UserTracker.Callback() {
158                 @Override
159                 public void onUserChanged(int newUser, @NonNull Context userContext) {
160                     mShell.onUserChanged(newUser, userContext);
161                 }
162 
163                 @Override
164                 public void onProfilesChanged(@NonNull List<UserInfo> profiles) {
165                     mShell.onUserProfilesChanged(profiles);
166                 }
167             };
168 
169     private boolean mIsSysUiStateValid;
170     private WakefulnessLifecycle.Observer mWakefulnessObserver;
171 
172     @Inject
WMShell( Context context, ShellInterface shell, Optional<Pip> pipOptional, Optional<SplitScreen> splitScreenOptional, Optional<OneHanded> oneHandedOptional, Optional<DesktopMode> desktopMode, CommandQueue commandQueue, ConfigurationController configurationController, KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, ScreenLifecycle screenLifecycle, SysUiState sysUiState, ProtoTracer protoTracer, WakefulnessLifecycle wakefulnessLifecycle, UserTracker userTracker, DisplayTracker displayTracker, NoteTaskInitializer noteTaskInitializer, @Main Executor sysUiMainExecutor)173     public WMShell(
174             Context context,
175             ShellInterface shell,
176             Optional<Pip> pipOptional,
177             Optional<SplitScreen> splitScreenOptional,
178             Optional<OneHanded> oneHandedOptional,
179             Optional<DesktopMode> desktopMode,
180             CommandQueue commandQueue,
181             ConfigurationController configurationController,
182             KeyguardStateController keyguardStateController,
183             KeyguardUpdateMonitor keyguardUpdateMonitor,
184             ScreenLifecycle screenLifecycle,
185             SysUiState sysUiState,
186             ProtoTracer protoTracer,
187             WakefulnessLifecycle wakefulnessLifecycle,
188             UserTracker userTracker,
189             DisplayTracker displayTracker,
190             NoteTaskInitializer noteTaskInitializer,
191             @Main Executor sysUiMainExecutor) {
192         mContext = context;
193         mShell = shell;
194         mCommandQueue = commandQueue;
195         mConfigurationController = configurationController;
196         mKeyguardStateController = keyguardStateController;
197         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
198         mScreenLifecycle = screenLifecycle;
199         mSysUiState = sysUiState;
200         mPipOptional = pipOptional;
201         mSplitScreenOptional = splitScreenOptional;
202         mOneHandedOptional = oneHandedOptional;
203         mDesktopModeOptional = desktopMode;
204         mWakefulnessLifecycle = wakefulnessLifecycle;
205         mProtoTracer = protoTracer;
206         mUserTracker = userTracker;
207         mDisplayTracker = displayTracker;
208         mNoteTaskInitializer = noteTaskInitializer;
209         mSysUiMainExecutor = sysUiMainExecutor;
210     }
211 
212     @Override
start()213     public void start() {
214         // Notify with the initial configuration and subscribe for new config changes
215         mShell.onConfigurationChanged(mContext.getResources().getConfiguration());
216         mConfigurationController.addCallback(mConfigurationListener);
217 
218         // Subscribe to keyguard changes
219         mKeyguardStateController.addCallback(mKeyguardStateCallback);
220         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
221 
222         // Subscribe to user changes
223         mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor());
224 
225         mProtoTracer.add(this);
226         mCommandQueue.addCallback(this);
227         mPipOptional.ifPresent(this::initPip);
228         mSplitScreenOptional.ifPresent(this::initSplitScreen);
229         mOneHandedOptional.ifPresent(this::initOneHanded);
230         mDesktopModeOptional.ifPresent(this::initDesktopMode);
231 
232         mNoteTaskInitializer.initialize();
233     }
234 
235     @VisibleForTesting
initPip(Pip pip)236     void initPip(Pip pip) {
237         mCommandQueue.addCallback(new CommandQueue.Callbacks() {
238             @Override
239             public void showPictureInPictureMenu() {
240                 pip.showPictureInPictureMenu();
241             }
242         });
243 
244         mSysUiState.addCallback(sysUiStateFlag -> {
245             mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0;
246             pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag);
247         });
248     }
249 
250     @VisibleForTesting
initSplitScreen(SplitScreen splitScreen)251     void initSplitScreen(SplitScreen splitScreen) {
252         mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
253             @Override
254             public void onFinishedWakingUp() {
255                 splitScreen.onFinishedWakingUp();
256             }
257         });
258         mCommandQueue.addCallback(new CommandQueue.Callbacks() {
259             @Override
260             public void goToFullscreenFromSplit() {
261                 splitScreen.goToFullscreenFromSplit();
262             }
263         });
264     }
265 
266     @VisibleForTesting
initOneHanded(OneHanded oneHanded)267     void initOneHanded(OneHanded oneHanded) {
268         oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() {
269             @Override
270             public void onStartTransition(boolean isEntering) {
271                 mSysUiMainExecutor.execute(() -> {
272                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
273                             true).commitUpdate(mDisplayTracker.getDefaultDisplayId());
274                 });
275             }
276 
277             @Override
278             public void onStartFinished(Rect bounds) {
279                 mSysUiMainExecutor.execute(() -> {
280                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
281                             true).commitUpdate(mDisplayTracker.getDefaultDisplayId());
282                 });
283             }
284 
285             @Override
286             public void onStopFinished(Rect bounds) {
287                 mSysUiMainExecutor.execute(() -> {
288                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
289                             false).commitUpdate(mDisplayTracker.getDefaultDisplayId());
290                 });
291             }
292         });
293 
294         oneHanded.registerEventCallback(new OneHandedEventCallback() {
295             @Override
296             public void notifyExpandNotification() {
297                 mSysUiMainExecutor.execute(
298                         () -> mCommandQueue.handleSystemKey(
299                                 KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
300             }
301         });
302 
303         mWakefulnessObserver =
304                 new WakefulnessLifecycle.Observer() {
305                     @Override
306                     public void onFinishedWakingUp() {
307                         // Reset locked for the case keyguard not shown.
308                         oneHanded.setLockedDisabled(false /* locked */, false /* enabled */);
309                     }
310 
311                     @Override
312                     public void onStartedGoingToSleep() {
313                         oneHanded.stopOneHanded();
314                         // When user press power button going to sleep, temperory lock OHM disabled
315                         // to avoid mis-trigger.
316                         oneHanded.setLockedDisabled(true /* locked */, false /* enabled */);
317                     }
318                 };
319         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
320 
321         mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() {
322             @Override
323             public void onScreenTurningOff() {
324                 oneHanded.stopOneHanded(
325                         OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT);
326             }
327         });
328 
329         mCommandQueue.addCallback(new CommandQueue.Callbacks() {
330             @Override
331             public void onCameraLaunchGestureDetected(int source) {
332                 oneHanded.stopOneHanded();
333             }
334 
335             @Override
336             public void setImeWindowStatus(int displayId, IBinder token, int vis,
337                     int backDisposition, boolean showImeSwitcher) {
338                 if (displayId == mDisplayTracker.getDefaultDisplayId()
339                         && (vis & InputMethodService.IME_VISIBLE) != 0) {
340                     oneHanded.stopOneHanded(
341                             OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT);
342                 }
343             }
344         });
345     }
346 
initDesktopMode(DesktopMode desktopMode)347     void initDesktopMode(DesktopMode desktopMode) {
348         desktopMode.addListener(new DesktopModeTaskRepository.VisibleTasksListener() {
349             @Override
350             public void onVisibilityChanged(boolean hasFreeformTasks) {
351                 mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, hasFreeformTasks)
352                         .commitUpdate(mDisplayTracker.getDefaultDisplayId());
353             }
354         }, mSysUiMainExecutor);
355     }
356 
357     @Override
writeToProto(SystemUiTraceProto proto)358     public void writeToProto(SystemUiTraceProto proto) {
359         if (proto.wmShell == null) {
360             proto.wmShell = new WmShellTraceProto();
361         }
362         // Dump to WMShell proto here
363         // TODO: Figure out how we want to synchronize while dumping to proto
364     }
365 
366     @Override
dump(PrintWriter pw, String[] args)367     public void dump(PrintWriter pw, String[] args) {
368         // Handle commands if provided
369         if (mShell.handleCommand(args, pw)) {
370             return;
371         }
372         // Dump WMShell stuff here if no commands were handled
373         mShell.dump(pw);
374     }
375 }
376