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 android.view.Display.DEFAULT_DISPLAY;
20 
21 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
22 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
23 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
24 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
25 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
26 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
27 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
28 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
29 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
30 
31 import android.content.Context;
32 import android.content.res.Configuration;
33 import android.graphics.Rect;
34 import android.graphics.drawable.Drawable;
35 import android.inputmethodservice.InputMethodService;
36 import android.os.IBinder;
37 import android.os.ParcelFileDescriptor;
38 import android.view.KeyEvent;
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.Dependency;
44 import com.android.systemui.SystemUI;
45 import com.android.systemui.dagger.SysUISingleton;
46 import com.android.systemui.dagger.WMComponent;
47 import com.android.systemui.dagger.qualifiers.Main;
48 import com.android.systemui.keyguard.ScreenLifecycle;
49 import com.android.systemui.keyguard.WakefulnessLifecycle;
50 import com.android.systemui.model.SysUiState;
51 import com.android.systemui.navigationbar.NavigationModeController;
52 import com.android.systemui.shared.tracing.ProtoTraceable;
53 import com.android.systemui.statusbar.CommandQueue;
54 import com.android.systemui.statusbar.policy.ConfigurationController;
55 import com.android.systemui.statusbar.policy.UserInfoController;
56 import com.android.systemui.tracing.ProtoTracer;
57 import com.android.systemui.tracing.nano.SystemUiTraceProto;
58 import com.android.wm.shell.ShellCommandHandler;
59 import com.android.wm.shell.compatui.CompatUI;
60 import com.android.wm.shell.draganddrop.DragAndDrop;
61 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
62 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
63 import com.android.wm.shell.nano.WmShellTraceProto;
64 import com.android.wm.shell.onehanded.OneHanded;
65 import com.android.wm.shell.onehanded.OneHandedEventCallback;
66 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
67 import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
68 import com.android.wm.shell.pip.Pip;
69 import com.android.wm.shell.protolog.ShellProtoLogImpl;
70 import com.android.wm.shell.splitscreen.SplitScreen;
71 
72 import java.io.FileDescriptor;
73 import java.io.PrintWriter;
74 import java.util.Arrays;
75 import java.util.Optional;
76 import java.util.concurrent.Executor;
77 
78 import javax.inject.Inject;
79 
80 /**
81  * A SystemUI service that starts with the SystemUI application and sets up any bindings between
82  * Shell and SysUI components.  This service starts happens after the {@link WMComponent} has
83  * already been initialized and may only reference Shell components that are explicitly exported to
84  * SystemUI (see {@link WMComponent}.
85  *
86  * eg. SysUI application starts
87  *     -> SystemUIFactory is initialized
88  *       -> WMComponent is created
89  *         -> WMShellBaseModule dependencies are injected
90  *         -> WMShellModule (form-factory specific) dependencies are injected
91  *       -> SysUIComponent is created
92  *         -> WMComponents are explicitly provided to SysUIComponent for injection into SysUI code
93  *     -> SysUI services are started
94  *       -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces
95  */
96 @SysUISingleton
97 public final class WMShell extends SystemUI
98         implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
99     private static final String TAG = WMShell.class.getName();
100     private static final int INVALID_SYSUI_STATE_MASK =
101             SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
102                     | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
103                     | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
104                     | SYSUI_STATE_BOUNCER_SHOWING
105                     | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
106                     | SYSUI_STATE_BUBBLES_EXPANDED
107                     | SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED
108                     | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
109 
110     // Shell interfaces
111     private final Optional<Pip> mPipOptional;
112     private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
113     private final Optional<SplitScreen> mSplitScreenOptional;
114     private final Optional<OneHanded> mOneHandedOptional;
115     private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
116     private final Optional<ShellCommandHandler> mShellCommandHandler;
117     private final Optional<CompatUI> mCompatUIOptional;
118     private final Optional<DragAndDrop> mDragAndDropOptional;
119 
120     private final CommandQueue mCommandQueue;
121     private final ConfigurationController mConfigurationController;
122     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
123     private final NavigationModeController mNavigationModeController;
124     private final ScreenLifecycle mScreenLifecycle;
125     private final SysUiState mSysUiState;
126     private final WakefulnessLifecycle mWakefulnessLifecycle;
127     private final ProtoTracer mProtoTracer;
128     private final Executor mSysUiMainExecutor;
129 
130     private boolean mIsSysUiStateValid;
131     private KeyguardUpdateMonitorCallback mLegacySplitScreenKeyguardCallback;
132     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
133     private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
134     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
135     private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback;
136     private WakefulnessLifecycle.Observer mWakefulnessObserver;
137 
138     @Inject
WMShell(Context context, Optional<Pip> pipOptional, Optional<LegacySplitScreen> legacySplitScreenOptional, Optional<SplitScreen> splitScreenOptional, Optional<OneHanded> oneHandedOptional, Optional<HideDisplayCutout> hideDisplayCutoutOptional, Optional<ShellCommandHandler> shellCommandHandler, Optional<CompatUI> sizeCompatUIOptional, Optional<DragAndDrop> dragAndDropOptional, CommandQueue commandQueue, ConfigurationController configurationController, KeyguardUpdateMonitor keyguardUpdateMonitor, NavigationModeController navigationModeController, ScreenLifecycle screenLifecycle, SysUiState sysUiState, ProtoTracer protoTracer, WakefulnessLifecycle wakefulnessLifecycle, @Main Executor sysUiMainExecutor)139     public WMShell(Context context,
140             Optional<Pip> pipOptional,
141             Optional<LegacySplitScreen> legacySplitScreenOptional,
142             Optional<SplitScreen> splitScreenOptional,
143             Optional<OneHanded> oneHandedOptional,
144             Optional<HideDisplayCutout> hideDisplayCutoutOptional,
145             Optional<ShellCommandHandler> shellCommandHandler,
146             Optional<CompatUI> sizeCompatUIOptional,
147             Optional<DragAndDrop> dragAndDropOptional,
148             CommandQueue commandQueue,
149             ConfigurationController configurationController,
150             KeyguardUpdateMonitor keyguardUpdateMonitor,
151             NavigationModeController navigationModeController,
152             ScreenLifecycle screenLifecycle,
153             SysUiState sysUiState,
154             ProtoTracer protoTracer,
155             WakefulnessLifecycle wakefulnessLifecycle,
156             @Main Executor sysUiMainExecutor) {
157         super(context);
158         mCommandQueue = commandQueue;
159         mConfigurationController = configurationController;
160         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
161         mNavigationModeController = navigationModeController;
162         mScreenLifecycle = screenLifecycle;
163         mSysUiState = sysUiState;
164         mPipOptional = pipOptional;
165         mLegacySplitScreenOptional = legacySplitScreenOptional;
166         mSplitScreenOptional = splitScreenOptional;
167         mOneHandedOptional = oneHandedOptional;
168         mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
169         mWakefulnessLifecycle = wakefulnessLifecycle;
170         mProtoTracer = protoTracer;
171         mShellCommandHandler = shellCommandHandler;
172         mCompatUIOptional = sizeCompatUIOptional;
173         mDragAndDropOptional = dragAndDropOptional;
174         mSysUiMainExecutor = sysUiMainExecutor;
175     }
176 
177     @Override
start()178     public void start() {
179         // TODO: Consider piping config change and other common calls to a shell component to
180         //  delegate internally
181         mProtoTracer.add(this);
182         mCommandQueue.addCallback(this);
183         mPipOptional.ifPresent(this::initPip);
184         mLegacySplitScreenOptional.ifPresent(this::initLegacySplitScreen);
185         mSplitScreenOptional.ifPresent(this::initSplitScreen);
186         mOneHandedOptional.ifPresent(this::initOneHanded);
187         mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
188         mCompatUIOptional.ifPresent(this::initCompatUi);
189         mDragAndDropOptional.ifPresent(this::initDragAndDrop);
190     }
191 
192     @VisibleForTesting
initPip(Pip pip)193     void initPip(Pip pip) {
194         mCommandQueue.addCallback(new CommandQueue.Callbacks() {
195             @Override
196             public void showPictureInPictureMenu() {
197                 pip.showPictureInPictureMenu();
198             }
199         });
200 
201         mPipKeyguardCallback = new KeyguardUpdateMonitorCallback() {
202             @Override
203             public void onKeyguardVisibilityChanged(boolean showing) {
204                 if (showing) {
205                     pip.hidePipMenu(null, null);
206                 }
207             }
208         };
209         mKeyguardUpdateMonitor.registerCallback(mPipKeyguardCallback);
210 
211         mSysUiState.addCallback(sysUiStateFlag -> {
212             mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0;
213             pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag);
214         });
215 
216         mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
217             @Override
218             public void onConfigChanged(Configuration newConfig) {
219                 pip.onConfigurationChanged(newConfig);
220             }
221 
222             @Override
223             public void onDensityOrFontScaleChanged() {
224                 pip.onDensityOrFontScaleChanged();
225             }
226 
227             @Override
228             public void onThemeChanged() {
229                 pip.onOverlayChanged();
230             }
231         });
232 
233         // The media session listener needs to be re-registered when switching users
234         UserInfoController userInfoController = Dependency.get(UserInfoController.class);
235         userInfoController.addCallback((String name, Drawable picture, String userAccount) ->
236                 pip.registerSessionListenerForCurrentUser());
237     }
238 
239     @VisibleForTesting
initLegacySplitScreen(LegacySplitScreen legacySplitScreen)240     void initLegacySplitScreen(LegacySplitScreen legacySplitScreen) {
241         mLegacySplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
242             @Override
243             public void onKeyguardVisibilityChanged(boolean showing) {
244                 // Hide the divider when keyguard is showing. Even though keyguard/statusbar is
245                 // above everything, it is actually transparent except for notifications, so
246                 // we still need to hide any surfaces that are below it.
247                 // TODO(b/148906453): Figure out keyguard dismiss animation for divider view.
248                 legacySplitScreen.onKeyguardVisibilityChanged(showing);
249             }
250         };
251         mKeyguardUpdateMonitor.registerCallback(mLegacySplitScreenKeyguardCallback);
252     }
253 
254     @VisibleForTesting
initSplitScreen(SplitScreen splitScreen)255     void initSplitScreen(SplitScreen splitScreen) {
256         mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
257             @Override
258             public void onKeyguardVisibilityChanged(boolean showing) {
259                 splitScreen.onKeyguardVisibilityChanged(showing);
260             }
261 
262             @Override
263             public void onKeyguardOccludedChanged(boolean occluded) {
264                 splitScreen.onKeyguardOccludedChanged(occluded);
265             }
266         };
267         mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
268 
269         mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
270             @Override
271             public void onFinishedWakingUp() {
272                 splitScreen.onFinishedWakingUp();
273             }
274 
275             @Override
276             public void onFinishedGoingToSleep() {
277                 splitScreen.onFinishedGoingToSleep();
278             }
279         });
280     }
281 
282     @VisibleForTesting
initOneHanded(OneHanded oneHanded)283     void initOneHanded(OneHanded oneHanded) {
284         oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() {
285             @Override
286             public void onStartTransition(boolean isEntering) {
287                 mSysUiMainExecutor.execute(() -> {
288                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
289                             true).commitUpdate(DEFAULT_DISPLAY);
290                 });
291             }
292 
293             @Override
294             public void onStartFinished(Rect bounds) {
295                 mSysUiMainExecutor.execute(() -> {
296                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
297                             true).commitUpdate(DEFAULT_DISPLAY);
298                 });
299             }
300 
301             @Override
302             public void onStopFinished(Rect bounds) {
303                 mSysUiMainExecutor.execute(() -> {
304                     mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
305                             false).commitUpdate(DEFAULT_DISPLAY);
306                 });
307             }
308         });
309 
310         oneHanded.registerEventCallback(new OneHandedEventCallback() {
311             @Override
312             public void notifyExpandNotification() {
313                 mSysUiMainExecutor.execute(
314                         () -> mCommandQueue.handleSystemKey(
315                                 KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
316             }
317         });
318 
319         mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() {
320             @Override
321             public void onKeyguardVisibilityChanged(boolean showing) {
322                 oneHanded.onKeyguardVisibilityChanged(showing);
323                 oneHanded.stopOneHanded();
324             }
325 
326             @Override
327             public void onUserSwitchComplete(int userId) {
328                 oneHanded.onUserSwitch(userId);
329             }
330         };
331         mKeyguardUpdateMonitor.registerCallback(mOneHandedKeyguardCallback);
332 
333         mWakefulnessObserver =
334                 new WakefulnessLifecycle.Observer() {
335                     @Override
336                     public void onFinishedWakingUp() {
337                         // Reset locked for the case keyguard not shown.
338                         oneHanded.setLockedDisabled(false /* locked */, false /* enabled */);
339                     }
340 
341                     @Override
342                     public void onStartedGoingToSleep() {
343                         oneHanded.stopOneHanded();
344                         // When user press power button going to sleep, temperory lock OHM disabled
345                         // to avoid mis-trigger.
346                         oneHanded.setLockedDisabled(true /* locked */, false /* enabled */);
347                     }
348                 };
349         mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
350 
351         mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() {
352             @Override
353             public void onScreenTurningOff() {
354                 oneHanded.stopOneHanded(
355                         OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_SCREEN_OFF_OUT);
356             }
357         });
358 
359         mCommandQueue.addCallback(new CommandQueue.Callbacks() {
360             @Override
361             public void onCameraLaunchGestureDetected(int source) {
362                 oneHanded.stopOneHanded();
363             }
364 
365             @Override
366             public void setImeWindowStatus(int displayId, IBinder token, int vis,
367                     int backDisposition, boolean showImeSwitcher) {
368                 if (displayId == DEFAULT_DISPLAY && (vis & InputMethodService.IME_VISIBLE) != 0) {
369                     oneHanded.stopOneHanded(
370                             OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_POP_IME_OUT);
371                 }
372             }
373         });
374 
375         mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
376             @Override
377             public void onConfigChanged(Configuration newConfig) {
378                 oneHanded.onConfigChanged(newConfig);
379             }
380         });
381     }
382 
383     @VisibleForTesting
initHideDisplayCutout(HideDisplayCutout hideDisplayCutout)384     void initHideDisplayCutout(HideDisplayCutout hideDisplayCutout) {
385         mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
386             @Override
387             public void onConfigChanged(Configuration newConfig) {
388                 hideDisplayCutout.onConfigurationChanged(newConfig);
389             }
390         });
391     }
392 
393     @VisibleForTesting
initCompatUi(CompatUI sizeCompatUI)394     void initCompatUi(CompatUI sizeCompatUI) {
395         mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
396             @Override
397             public void onKeyguardOccludedChanged(boolean occluded) {
398                 sizeCompatUI.onKeyguardOccludedChanged(occluded);
399             }
400         };
401         mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback);
402     }
403 
initDragAndDrop(DragAndDrop dragAndDrop)404     void initDragAndDrop(DragAndDrop dragAndDrop) {
405         mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
406             @Override
407             public void onConfigChanged(Configuration newConfig) {
408                 dragAndDrop.onConfigChanged(newConfig);
409             }
410 
411             @Override
412             public void onThemeChanged() {
413                 dragAndDrop.onThemeChanged();
414             }
415         });
416     }
417 
418     @Override
writeToProto(SystemUiTraceProto proto)419     public void writeToProto(SystemUiTraceProto proto) {
420         if (proto.wmShell == null) {
421             proto.wmShell = new WmShellTraceProto();
422         }
423         // Dump to WMShell proto here
424         // TODO: Figure out how we want to synchronize while dumping to proto
425     }
426 
427     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)428     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
429         // Handle commands if provided
430         if (mShellCommandHandler.isPresent()
431                 && mShellCommandHandler.get().handleCommand(args, pw)) {
432             return;
433         }
434         // Handle logging commands if provided
435         if (handleLoggingCommand(args, pw)) {
436             return;
437         }
438         // Dump WMShell stuff here if no commands were handled
439         mShellCommandHandler.ifPresent(
440                 shellCommandHandler -> shellCommandHandler.dump(pw));
441     }
442 
443     @Override
handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd)444     public void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {
445         PrintWriter pw = new PrintWriter(new ParcelFileDescriptor.AutoCloseOutputStream(outFd));
446         handleLoggingCommand(args, pw);
447         pw.flush();
448         pw.close();
449     }
450 
handleLoggingCommand(String[] args, PrintWriter pw)451     private boolean handleLoggingCommand(String[] args, PrintWriter pw) {
452         ShellProtoLogImpl protoLogImpl = ShellProtoLogImpl.getSingleInstance();
453         for (int i = 0; i < args.length; i++) {
454             switch (args[i]) {
455                 case "enable-text": {
456                     String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
457                     int result = protoLogImpl.startTextLogging(groups, pw);
458                     if (result == 0) {
459                         pw.println("Starting logging on groups: " + Arrays.toString(groups));
460                     }
461                     return true;
462                 }
463                 case "disable-text": {
464                     String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
465                     int result = protoLogImpl.stopTextLogging(groups, pw);
466                     if (result == 0) {
467                         pw.println("Stopping logging on groups: " + Arrays.toString(groups));
468                     }
469                     return true;
470                 }
471             }
472         }
473         return false;
474     }
475 }
476