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