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