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