1 /* 2 * Copyright (C) 2017 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.recents; 18 19 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; 20 import static android.view.MotionEvent.ACTION_CANCEL; 21 import static android.view.MotionEvent.ACTION_DOWN; 22 import static android.view.MotionEvent.ACTION_UP; 23 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW; 24 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; 25 26 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; 27 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; 28 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; 29 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; 30 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN; 31 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW; 32 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER; 33 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; 34 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; 35 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; 36 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; 37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; 38 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; 39 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; 40 41 import android.annotation.FloatRange; 42 import android.app.ActivityTaskManager; 43 import android.content.BroadcastReceiver; 44 import android.content.ComponentName; 45 import android.content.Context; 46 import android.content.Intent; 47 import android.content.IntentFilter; 48 import android.content.ServiceConnection; 49 import android.graphics.Bitmap; 50 import android.graphics.Insets; 51 import android.graphics.Rect; 52 import android.graphics.Region; 53 import android.hardware.input.InputManager; 54 import android.os.Binder; 55 import android.os.Bundle; 56 import android.os.Handler; 57 import android.os.IBinder; 58 import android.os.Looper; 59 import android.os.PatternMatcher; 60 import android.os.RemoteException; 61 import android.os.SystemClock; 62 import android.os.UserHandle; 63 import android.util.Log; 64 import android.view.InputDevice; 65 import android.view.KeyCharacterMap; 66 import android.view.KeyEvent; 67 import android.view.MotionEvent; 68 import android.view.Surface; 69 import android.view.accessibility.AccessibilityManager; 70 71 import androidx.annotation.NonNull; 72 73 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity; 74 import com.android.internal.annotations.VisibleForTesting; 75 import com.android.internal.policy.ScreenDecorationsUtils; 76 import com.android.internal.util.ScreenshotHelper; 77 import com.android.systemui.Dumpable; 78 import com.android.systemui.broadcast.BroadcastDispatcher; 79 import com.android.systemui.dagger.SysUISingleton; 80 import com.android.systemui.model.SysUiState; 81 import com.android.systemui.navigationbar.NavigationBar; 82 import com.android.systemui.navigationbar.NavigationBarController; 83 import com.android.systemui.navigationbar.NavigationBarView; 84 import com.android.systemui.navigationbar.NavigationModeController; 85 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; 86 import com.android.systemui.settings.CurrentUserTracker; 87 import com.android.systemui.shared.recents.IOverviewProxy; 88 import com.android.systemui.shared.recents.ISystemUiProxy; 89 import com.android.systemui.shared.recents.model.Task; 90 import com.android.systemui.shared.system.ActivityManagerWrapper; 91 import com.android.systemui.shared.system.QuickStepContract; 92 import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; 93 import com.android.systemui.statusbar.CommandQueue; 94 import com.android.systemui.statusbar.NotificationShadeWindowController; 95 import com.android.systemui.statusbar.phone.StatusBar; 96 import com.android.systemui.statusbar.phone.StatusBarWindowCallback; 97 import com.android.systemui.statusbar.policy.CallbackController; 98 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; 99 import com.android.wm.shell.onehanded.OneHanded; 100 import com.android.wm.shell.pip.Pip; 101 import com.android.wm.shell.pip.PipAnimationController; 102 import com.android.wm.shell.splitscreen.SplitScreen; 103 import com.android.wm.shell.startingsurface.StartingSurface; 104 import com.android.wm.shell.transition.ShellTransitions; 105 106 import java.io.FileDescriptor; 107 import java.io.PrintWriter; 108 import java.util.ArrayList; 109 import java.util.List; 110 import java.util.Optional; 111 import java.util.function.BiConsumer; 112 113 import javax.inject.Inject; 114 115 import dagger.Lazy; 116 117 118 /** 119 * Class to send information from overview to launcher with a binder. 120 */ 121 @SysUISingleton 122 public class OverviewProxyService extends CurrentUserTracker implements 123 CallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener, 124 Dumpable { 125 126 private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; 127 128 public static final String TAG_OPS = "OverviewProxyService"; 129 private static final long BACKOFF_MILLIS = 1000; 130 private static final long DEFERRED_CALLBACK_MILLIS = 5000; 131 132 // Max backoff caps at 5 mins 133 private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000; 134 135 private final Context mContext; 136 private final Optional<Pip> mPipOptional; 137 private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy; 138 private final Optional<LegacySplitScreen> mLegacySplitScreenOptional; 139 private final Optional<SplitScreen> mSplitScreenOptional; 140 private SysUiState mSysUiState; 141 private final Handler mHandler; 142 private final Lazy<NavigationBarController> mNavBarControllerLazy; 143 private final NotificationShadeWindowController mStatusBarWinController; 144 private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser; 145 private final ComponentName mRecentsComponentName; 146 private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>(); 147 private final Intent mQuickStepIntent; 148 private final ScreenshotHelper mScreenshotHelper; 149 private final Optional<OneHanded> mOneHandedOptional; 150 private final CommandQueue mCommandQueue; 151 private final ShellTransitions mShellTransitions; 152 private final Optional<StartingSurface> mStartingSurface; 153 private final SmartspaceTransitionController mSmartspaceTransitionController; 154 155 private Region mActiveNavBarRegion; 156 157 private IOverviewProxy mOverviewProxy; 158 private int mConnectionBackoffAttempts; 159 private boolean mBound; 160 private boolean mIsEnabled; 161 private int mCurrentBoundedUserId = -1; 162 private float mNavBarButtonAlpha; 163 private boolean mInputFocusTransferStarted; 164 private float mInputFocusTransferStartY; 165 private long mInputFocusTransferStartMillis; 166 private float mWindowCornerRadius; 167 private boolean mSupportsRoundedCornersOnWindows; 168 private int mNavBarMode = NAV_BAR_MODE_3BUTTON; 169 170 @VisibleForTesting 171 public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() { 172 @Override 173 public void startScreenPinning(int taskId) { 174 if (!verifyCaller("startScreenPinning")) { 175 return; 176 } 177 final long token = Binder.clearCallingIdentity(); 178 try { 179 mHandler.post(() -> { 180 mStatusBarOptionalLazy.ifPresent( 181 statusBarLazy -> statusBarLazy.get().showScreenPinningRequest(taskId, 182 false /* allowCancel */)); 183 }); 184 } finally { 185 Binder.restoreCallingIdentity(token); 186 } 187 } 188 189 @Override 190 public void stopScreenPinning() { 191 if (!verifyCaller("stopScreenPinning")) { 192 return; 193 } 194 final long token = Binder.clearCallingIdentity(); 195 try { 196 mHandler.post(() -> { 197 try { 198 ActivityTaskManager.getService().stopSystemLockTaskMode(); 199 } catch (RemoteException e) { 200 Log.e(TAG_OPS, "Failed to stop screen pinning"); 201 } 202 }); 203 } finally { 204 Binder.restoreCallingIdentity(token); 205 } 206 } 207 208 // TODO: change the method signature to use (boolean inputFocusTransferStarted) 209 @Override 210 public void onStatusBarMotionEvent(MotionEvent event) { 211 if (!verifyCaller("onStatusBarMotionEvent")) { 212 return; 213 } 214 final long token = Binder.clearCallingIdentity(); 215 try { 216 // TODO move this logic to message queue 217 mStatusBarOptionalLazy.ifPresent(statusBarLazy -> { 218 StatusBar statusBar = statusBarLazy.get(); 219 if (event.getActionMasked() == ACTION_DOWN) { 220 statusBar.getPanelController().startExpandLatencyTracking(); 221 } 222 mHandler.post(()-> { 223 int action = event.getActionMasked(); 224 if (action == ACTION_DOWN) { 225 mInputFocusTransferStarted = true; 226 mInputFocusTransferStartY = event.getY(); 227 mInputFocusTransferStartMillis = event.getEventTime(); 228 statusBar.onInputFocusTransfer( 229 mInputFocusTransferStarted, false /* cancel */, 230 0 /* velocity */); 231 } 232 if (action == ACTION_UP || action == ACTION_CANCEL) { 233 mInputFocusTransferStarted = false; 234 statusBar.onInputFocusTransfer(mInputFocusTransferStarted, 235 action == ACTION_CANCEL, 236 (event.getY() - mInputFocusTransferStartY) 237 / (event.getEventTime() - mInputFocusTransferStartMillis)); 238 } 239 event.recycle(); 240 }); 241 }); 242 } finally { 243 Binder.restoreCallingIdentity(token); 244 } 245 } 246 247 @Override 248 public void onBackPressed() throws RemoteException { 249 if (!verifyCaller("onBackPressed")) { 250 return; 251 } 252 final long token = Binder.clearCallingIdentity(); 253 try { 254 mHandler.post(() -> { 255 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); 256 sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK); 257 258 notifyBackAction(true, -1, -1, true, false); 259 }); 260 } finally { 261 Binder.restoreCallingIdentity(token); 262 } 263 } 264 265 @Override 266 public void setHomeRotationEnabled(boolean enabled) { 267 if (!verifyCaller("setHomeRotationEnabled")) { 268 return; 269 } 270 final long token = Binder.clearCallingIdentity(); 271 try { 272 mHandler.post(() -> { 273 mHandler.post(() -> notifyHomeRotationEnabled(enabled)); 274 }); 275 } finally { 276 Binder.restoreCallingIdentity(token); 277 } 278 } 279 280 private boolean sendEvent(int action, int code) { 281 long when = SystemClock.uptimeMillis(); 282 final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */, 283 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, 284 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, 285 InputDevice.SOURCE_KEYBOARD); 286 287 ev.setDisplayId(mContext.getDisplay().getDisplayId()); 288 return InputManager.getInstance() 289 .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); 290 } 291 292 @Override 293 public void onOverviewShown(boolean fromHome) { 294 if (!verifyCaller("onOverviewShown")) { 295 return; 296 } 297 final long token = Binder.clearCallingIdentity(); 298 try { 299 mHandler.post(() -> { 300 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 301 mConnectionCallbacks.get(i).onOverviewShown(fromHome); 302 } 303 }); 304 } finally { 305 Binder.restoreCallingIdentity(token); 306 } 307 } 308 309 @Override 310 public Rect getNonMinimizedSplitScreenSecondaryBounds() { 311 if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) { 312 return null; 313 } 314 final long token = Binder.clearCallingIdentity(); 315 try { 316 return mLegacySplitScreenOptional.map(splitScreen -> 317 splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds()) 318 .orElse(null); 319 } finally { 320 Binder.restoreCallingIdentity(token); 321 } 322 } 323 324 @Override 325 public void setNavBarButtonAlpha(float alpha, boolean animate) { 326 if (!verifyCaller("setNavBarButtonAlpha")) { 327 return; 328 } 329 final long token = Binder.clearCallingIdentity(); 330 try { 331 mNavBarButtonAlpha = alpha; 332 mHandler.post(() -> notifyNavBarButtonAlphaChanged(alpha, animate)); 333 } finally { 334 Binder.restoreCallingIdentity(token); 335 } 336 } 337 338 @Override 339 public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) { 340 if (!verifyCaller("onAssistantProgress")) { 341 return; 342 } 343 final long token = Binder.clearCallingIdentity(); 344 try { 345 mHandler.post(() -> notifyAssistantProgress(progress)); 346 } finally { 347 Binder.restoreCallingIdentity(token); 348 } 349 } 350 351 @Override 352 public void onAssistantGestureCompletion(float velocity) { 353 if (!verifyCaller("onAssistantGestureCompletion")) { 354 return; 355 } 356 final long token = Binder.clearCallingIdentity(); 357 try { 358 mHandler.post(() -> notifyAssistantGestureCompletion(velocity)); 359 } finally { 360 Binder.restoreCallingIdentity(token); 361 } 362 } 363 364 @Override 365 public void startAssistant(Bundle bundle) { 366 if (!verifyCaller("startAssistant")) { 367 return; 368 } 369 final long token = Binder.clearCallingIdentity(); 370 try { 371 mHandler.post(() -> notifyStartAssistant(bundle)); 372 } finally { 373 Binder.restoreCallingIdentity(token); 374 } 375 } 376 377 @Override 378 public void notifyAccessibilityButtonClicked(int displayId) { 379 if (!verifyCaller("notifyAccessibilityButtonClicked")) { 380 return; 381 } 382 final long token = Binder.clearCallingIdentity(); 383 try { 384 AccessibilityManager.getInstance(mContext) 385 .notifyAccessibilityButtonClicked(displayId); 386 } finally { 387 Binder.restoreCallingIdentity(token); 388 } 389 } 390 391 @Override 392 public void notifyAccessibilityButtonLongClicked() { 393 if (!verifyCaller("notifyAccessibilityButtonLongClicked")) { 394 return; 395 } 396 final long token = Binder.clearCallingIdentity(); 397 try { 398 final Intent intent = 399 new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON); 400 final String chooserClassName = AccessibilityButtonChooserActivity.class.getName(); 401 intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName); 402 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 403 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 404 } finally { 405 Binder.restoreCallingIdentity(token); 406 } 407 } 408 409 @Override 410 public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen, 411 Insets visibleInsets, int taskId) { 412 // Deprecated 413 } 414 415 @Override 416 public void setSplitScreenMinimized(boolean minimized) { 417 mLegacySplitScreenOptional.ifPresent( 418 splitScreen -> splitScreen.setMinimized(minimized)); 419 } 420 421 @Override 422 public void notifySwipeToHomeFinished() { 423 if (!verifyCaller("notifySwipeToHomeFinished")) { 424 return; 425 } 426 final long token = Binder.clearCallingIdentity(); 427 try { 428 mPipOptional.ifPresent( 429 pip -> pip.setPinnedStackAnimationType( 430 PipAnimationController.ANIM_TYPE_ALPHA)); 431 } finally { 432 Binder.restoreCallingIdentity(token); 433 } 434 } 435 436 @Override 437 public void notifySwipeUpGestureStarted() { 438 if (!verifyCaller("notifySwipeUpGestureStarted")) { 439 return; 440 } 441 final long token = Binder.clearCallingIdentity(); 442 try { 443 mHandler.post(() -> notifySwipeUpGestureStartedInternal()); 444 } finally { 445 Binder.restoreCallingIdentity(token); 446 } 447 } 448 449 @Override 450 public void notifyPrioritizedRotation(@Surface.Rotation int rotation) { 451 if (!verifyCaller("notifyPrioritizedRotation")) { 452 return; 453 } 454 final long token = Binder.clearCallingIdentity(); 455 try { 456 mHandler.post(() -> notifyPrioritizedRotationInternal(rotation)); 457 } finally { 458 Binder.restoreCallingIdentity(token); 459 } 460 } 461 462 @Override 463 public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen, 464 Insets visibleInsets, Task.TaskKey task) { 465 mScreenshotHelper.provideScreenshot( 466 screenImageBundle, 467 locationInScreen, 468 visibleInsets, 469 task.id, 470 task.userId, 471 task.sourceComponent, 472 SCREENSHOT_OVERVIEW, 473 mHandler, 474 null); 475 } 476 477 @Override 478 public void expandNotificationPanel() { 479 if (!verifyCaller("expandNotificationPanel")) { 480 return; 481 } 482 final long token = Binder.clearCallingIdentity(); 483 try { 484 mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN); 485 } finally { 486 Binder.restoreCallingIdentity(token); 487 } 488 } 489 490 private boolean verifyCaller(String reason) { 491 final int callerId = Binder.getCallingUserHandle().getIdentifier(); 492 if (callerId != mCurrentBoundedUserId) { 493 Log.w(TAG_OPS, "Launcher called sysui with invalid user: " + callerId + ", reason: " 494 + reason); 495 return false; 496 } 497 return true; 498 } 499 }; 500 501 private final Runnable mDeferredConnectionCallback = () -> { 502 Log.w(TAG_OPS, "Binder supposed established connection but actual connection to service " 503 + "timed out, trying again"); 504 retryConnectionWithBackoff(); 505 }; 506 507 private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() { 508 @Override 509 public void onReceive(Context context, Intent intent) { 510 updateEnabledState(); 511 512 // Reconnect immediately, instead of waiting for resume to arrive. 513 startConnectionToCurrentUser(); 514 } 515 }; 516 517 private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() { 518 @Override 519 public void onServiceConnected(ComponentName name, IBinder service) { 520 if (SysUiState.DEBUG) { 521 Log.d(TAG_OPS, "Overview proxy service connected"); 522 } 523 mConnectionBackoffAttempts = 0; 524 mHandler.removeCallbacks(mDeferredConnectionCallback); 525 try { 526 service.linkToDeath(mOverviewServiceDeathRcpt, 0); 527 } catch (RemoteException e) { 528 // Failed to link to death (process may have died between binding and connecting), 529 // just unbind the service for now and retry again 530 Log.e(TAG_OPS, "Lost connection to launcher service", e); 531 disconnectFromLauncherService(); 532 retryConnectionWithBackoff(); 533 return; 534 } 535 536 mCurrentBoundedUserId = getCurrentUserId(); 537 mOverviewProxy = IOverviewProxy.Stub.asInterface(service); 538 539 Bundle params = new Bundle(); 540 params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); 541 params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); 542 params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows); 543 544 mPipOptional.ifPresent((pip) -> params.putBinder( 545 KEY_EXTRA_SHELL_PIP, 546 pip.createExternalInterface().asBinder())); 547 mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder( 548 KEY_EXTRA_SHELL_SPLIT_SCREEN, 549 splitscreen.createExternalInterface().asBinder())); 550 mOneHandedOptional.ifPresent((onehanded) -> params.putBinder( 551 KEY_EXTRA_SHELL_ONE_HANDED, 552 onehanded.createExternalInterface().asBinder())); 553 params.putBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS, 554 mShellTransitions.createExternalInterface().asBinder()); 555 mStartingSurface.ifPresent((startingwindow) -> params.putBinder( 556 KEY_EXTRA_SHELL_STARTING_WINDOW, 557 startingwindow.createExternalInterface().asBinder())); 558 params.putBinder( 559 KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER, 560 mSmartspaceTransitionController.createExternalInterface().asBinder()); 561 562 try { 563 mOverviewProxy.onInitialize(params); 564 } catch (RemoteException e) { 565 mCurrentBoundedUserId = -1; 566 Log.e(TAG_OPS, "Failed to call onInitialize()", e); 567 } 568 dispatchNavButtonBounds(); 569 570 // Force-update the systemui state flags 571 updateSystemUiStateFlags(); 572 notifySystemUiStateFlags(mSysUiState.getFlags()); 573 574 notifyConnectionChanged(); 575 } 576 577 @Override 578 public void onNullBinding(ComponentName name) { 579 Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting"); 580 mCurrentBoundedUserId = -1; 581 retryConnectionWithBackoff(); 582 } 583 584 @Override 585 public void onBindingDied(ComponentName name) { 586 Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting"); 587 mCurrentBoundedUserId = -1; 588 retryConnectionWithBackoff(); 589 } 590 591 @Override 592 public void onServiceDisconnected(ComponentName name) { 593 Log.w(TAG_OPS, "Service disconnected"); 594 // Do nothing 595 mCurrentBoundedUserId = -1; 596 } 597 }; 598 599 private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged; 600 private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener = 601 this::notifySplitScreenBoundsChanged; 602 603 // This is the death handler for the binder from the launcher service 604 private final IBinder.DeathRecipient mOverviewServiceDeathRcpt 605 = this::cleanupAfterDeath; 606 607 @SuppressWarnings("OptionalUsedAsFieldOrParameterType") 608 @Inject OverviewProxyService(Context context, CommandQueue commandQueue, Lazy<NavigationBarController> navBarControllerLazy, NavigationModeController navModeController, NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, Optional<Pip> pipOptional, Optional<LegacySplitScreen> legacySplitScreenOptional, Optional<SplitScreen> splitScreenOptional, Optional<Lazy<StatusBar>> statusBarOptionalLazy, Optional<OneHanded> oneHandedOptional, BroadcastDispatcher broadcastDispatcher, ShellTransitions shellTransitions, Optional<StartingSurface> startingSurface, SmartspaceTransitionController smartspaceTransitionController)609 public OverviewProxyService(Context context, CommandQueue commandQueue, 610 Lazy<NavigationBarController> navBarControllerLazy, 611 NavigationModeController navModeController, 612 NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, 613 Optional<Pip> pipOptional, 614 Optional<LegacySplitScreen> legacySplitScreenOptional, 615 Optional<SplitScreen> splitScreenOptional, 616 Optional<Lazy<StatusBar>> statusBarOptionalLazy, 617 Optional<OneHanded> oneHandedOptional, 618 BroadcastDispatcher broadcastDispatcher, 619 ShellTransitions shellTransitions, 620 Optional<StartingSurface> startingSurface, 621 SmartspaceTransitionController smartspaceTransitionController) { 622 super(broadcastDispatcher); 623 mContext = context; 624 mPipOptional = pipOptional; 625 mStatusBarOptionalLazy = statusBarOptionalLazy; 626 mHandler = new Handler(); 627 mNavBarControllerLazy = navBarControllerLazy; 628 mStatusBarWinController = statusBarWinController; 629 mConnectionBackoffAttempts = 0; 630 mRecentsComponentName = ComponentName.unflattenFromString(context.getString( 631 com.android.internal.R.string.config_recentsComponentName)); 632 mQuickStepIntent = new Intent(ACTION_QUICKSTEP) 633 .setPackage(mRecentsComponentName.getPackageName()); 634 mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()); 635 mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils 636 .supportsRoundedCornersOnWindows(mContext.getResources()); 637 mSysUiState = sysUiState; 638 mSysUiState.addCallback(this::notifySystemUiStateFlags); 639 mOneHandedOptional = oneHandedOptional; 640 mShellTransitions = shellTransitions; 641 642 // Assumes device always starts with back button until launcher tells it that it does not 643 mNavBarButtonAlpha = 1.0f; 644 645 // Listen for nav bar mode changes 646 mNavBarMode = navModeController.addListener(this); 647 648 // Listen for launcher package changes 649 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 650 filter.addDataScheme("package"); 651 filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(), 652 PatternMatcher.PATTERN_LITERAL); 653 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 654 mContext.registerReceiver(mLauncherStateChangedReceiver, filter); 655 656 // Listen for status bar state changes 657 statusBarWinController.registerCallback(mStatusBarWindowCallback); 658 mScreenshotHelper = new ScreenshotHelper(context); 659 660 // Listen for tracing state changes 661 commandQueue.addCallback(new CommandQueue.Callbacks() { 662 @Override 663 public void onTracingStateChanged(boolean enabled) { 664 mSysUiState.setFlag(SYSUI_STATE_TRACING_ENABLED, enabled) 665 .commitUpdate(mContext.getDisplayId()); 666 } 667 }); 668 mCommandQueue = commandQueue; 669 670 mSplitScreenOptional = splitScreenOptional; 671 legacySplitScreenOptional.ifPresent(splitScreen -> 672 splitScreen.registerBoundsChangeListener(mSplitScreenBoundsChangeListener)); 673 mLegacySplitScreenOptional = legacySplitScreenOptional; 674 675 // Listen for user setup 676 startTracking(); 677 678 // Connect to the service 679 updateEnabledState(); 680 startConnectionToCurrentUser(); 681 mStartingSurface = startingSurface; 682 mSmartspaceTransitionController = smartspaceTransitionController; 683 } 684 685 @Override onUserSwitched(int newUserId)686 public void onUserSwitched(int newUserId) { 687 mConnectionBackoffAttempts = 0; 688 internalConnectToCurrentUser(); 689 } 690 notifyBackAction(boolean completed, int downX, int downY, boolean isButton, boolean gestureSwipeLeft)691 public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton, 692 boolean gestureSwipeLeft) { 693 try { 694 if (mOverviewProxy != null) { 695 mOverviewProxy.onBackAction(completed, downX, downY, isButton, gestureSwipeLeft); 696 } 697 } catch (RemoteException e) { 698 Log.e(TAG_OPS, "Failed to notify back action", e); 699 } 700 } 701 updateSystemUiStateFlags()702 private void updateSystemUiStateFlags() { 703 final NavigationBar navBarFragment = 704 mNavBarControllerLazy.get().getDefaultNavigationBar(); 705 final NavigationBarView navBarView = 706 mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId()); 707 if (SysUiState.DEBUG) { 708 Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment 709 + " navBarView=" + navBarView); 710 } 711 712 if (navBarFragment != null) { 713 navBarFragment.updateSystemUiStateFlags(-1); 714 } 715 if (navBarView != null) { 716 navBarView.updatePanelSystemUiStateFlags(); 717 navBarView.updateDisabledSystemUiStateFlags(); 718 } 719 if (mStatusBarWinController != null) { 720 mStatusBarWinController.notifyStateChangedCallbacks(); 721 } 722 } 723 notifySystemUiStateFlags(int flags)724 private void notifySystemUiStateFlags(int flags) { 725 if (SysUiState.DEBUG) { 726 Log.d(TAG_OPS, "Notifying sysui state change to overview service: proxy=" 727 + mOverviewProxy + " flags=" + flags); 728 } 729 try { 730 if (mOverviewProxy != null) { 731 mOverviewProxy.onSystemUiStateChanged(flags); 732 } 733 } catch (RemoteException e) { 734 Log.e(TAG_OPS, "Failed to notify sysui state change", e); 735 } 736 } 737 onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing)738 private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded, 739 boolean bouncerShowing) { 740 mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING, 741 keyguardShowing && !keyguardOccluded) 742 .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED, 743 keyguardShowing && keyguardOccluded) 744 .setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing) 745 .commitUpdate(mContext.getDisplayId()); 746 } 747 748 /** 749 * Sets the navbar region which can receive touch inputs 750 */ onActiveNavBarRegionChanges(Region activeRegion)751 public void onActiveNavBarRegionChanges(Region activeRegion) { 752 mActiveNavBarRegion = activeRegion; 753 dispatchNavButtonBounds(); 754 } 755 dispatchNavButtonBounds()756 private void dispatchNavButtonBounds() { 757 if (mOverviewProxy != null && mActiveNavBarRegion != null) { 758 try { 759 mOverviewProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion); 760 } catch (RemoteException e) { 761 Log.e(TAG_OPS, "Failed to call onActiveNavBarRegionChanges()", e); 762 } 763 } 764 } 765 cleanupAfterDeath()766 public void cleanupAfterDeath() { 767 if (mInputFocusTransferStarted) { 768 mHandler.post(() -> { 769 mStatusBarOptionalLazy.ifPresent(statusBarLazy -> { 770 mInputFocusTransferStarted = false; 771 statusBarLazy.get().onInputFocusTransfer(false, true /* cancel */, 772 0 /* velocity */); 773 }); 774 }); 775 } 776 startConnectionToCurrentUser(); 777 778 // Clean up the minimized state if launcher dies 779 mLegacySplitScreenOptional.ifPresent( 780 splitScreen -> splitScreen.setMinimized(false)); 781 } 782 startConnectionToCurrentUser()783 public void startConnectionToCurrentUser() { 784 if (mHandler.getLooper() != Looper.myLooper()) { 785 mHandler.post(mConnectionRunnable); 786 } else { 787 internalConnectToCurrentUser(); 788 } 789 } 790 internalConnectToCurrentUser()791 private void internalConnectToCurrentUser() { 792 disconnectFromLauncherService(); 793 794 // If user has not setup yet or already connected, do not try to connect 795 if (!isEnabled()) { 796 Log.v(TAG_OPS, "Cannot attempt connection, is enabled " + isEnabled()); 797 return; 798 } 799 mHandler.removeCallbacks(mConnectionRunnable); 800 Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP) 801 .setPackage(mRecentsComponentName.getPackageName()); 802 try { 803 mBound = mContext.bindServiceAsUser(launcherServiceIntent, 804 mOverviewServiceConnection, 805 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, 806 UserHandle.of(getCurrentUserId())); 807 } catch (SecurityException e) { 808 Log.e(TAG_OPS, "Unable to bind because of security error", e); 809 } 810 if (mBound) { 811 // Ensure that connection has been established even if it thinks it is bound 812 mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS); 813 } else { 814 // Retry after exponential backoff timeout 815 retryConnectionWithBackoff(); 816 } 817 } 818 retryConnectionWithBackoff()819 private void retryConnectionWithBackoff() { 820 if (mHandler.hasCallbacks(mConnectionRunnable)) { 821 return; 822 } 823 final long timeoutMs = (long) Math.min( 824 Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts), MAX_BACKOFF_MILLIS); 825 mHandler.postDelayed(mConnectionRunnable, timeoutMs); 826 mConnectionBackoffAttempts++; 827 Log.w(TAG_OPS, "Failed to connect on attempt " + mConnectionBackoffAttempts 828 + " will try again in " + timeoutMs + "ms"); 829 } 830 831 @Override addCallback(@onNull OverviewProxyListener listener)832 public void addCallback(@NonNull OverviewProxyListener listener) { 833 if (!mConnectionCallbacks.contains(listener)) { 834 mConnectionCallbacks.add(listener); 835 } 836 listener.onConnectionChanged(mOverviewProxy != null); 837 listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false); 838 } 839 840 @Override removeCallback(@onNull OverviewProxyListener listener)841 public void removeCallback(@NonNull OverviewProxyListener listener) { 842 mConnectionCallbacks.remove(listener); 843 } 844 shouldShowSwipeUpUI()845 public boolean shouldShowSwipeUpUI() { 846 return isEnabled() && !QuickStepContract.isLegacyMode(mNavBarMode); 847 } 848 isEnabled()849 public boolean isEnabled() { 850 return mIsEnabled; 851 } 852 getProxy()853 public IOverviewProxy getProxy() { 854 return mOverviewProxy; 855 } 856 disconnectFromLauncherService()857 private void disconnectFromLauncherService() { 858 if (mBound) { 859 // Always unbind the service (ie. if called through onNullBinding or onBindingDied) 860 mContext.unbindService(mOverviewServiceConnection); 861 mBound = false; 862 } 863 864 if (mOverviewProxy != null) { 865 mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0); 866 mOverviewProxy = null; 867 notifyNavBarButtonAlphaChanged(1f, false /* animate */); 868 notifyConnectionChanged(); 869 } 870 } 871 notifyNavBarButtonAlphaChanged(float alpha, boolean animate)872 private void notifyNavBarButtonAlphaChanged(float alpha, boolean animate) { 873 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 874 mConnectionCallbacks.get(i).onNavBarButtonAlphaChanged(alpha, animate); 875 } 876 } 877 notifyHomeRotationEnabled(boolean enabled)878 private void notifyHomeRotationEnabled(boolean enabled) { 879 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 880 mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled); 881 } 882 } 883 notifyConnectionChanged()884 private void notifyConnectionChanged() { 885 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 886 mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null); 887 } 888 } 889 notifyQuickStepStarted()890 public void notifyQuickStepStarted() { 891 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 892 mConnectionCallbacks.get(i).onQuickStepStarted(); 893 } 894 } 895 notifyPrioritizedRotationInternal(@urface.Rotation int rotation)896 private void notifyPrioritizedRotationInternal(@Surface.Rotation int rotation) { 897 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 898 mConnectionCallbacks.get(i).onPrioritizedRotation(rotation); 899 } 900 } 901 notifyQuickScrubStarted()902 public void notifyQuickScrubStarted() { 903 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 904 mConnectionCallbacks.get(i).onQuickScrubStarted(); 905 } 906 } 907 notifyAssistantProgress(@loatRangefrom = 0.0, to = 1.0) float progress)908 private void notifyAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) { 909 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 910 mConnectionCallbacks.get(i).onAssistantProgress(progress); 911 } 912 } 913 notifyAssistantGestureCompletion(float velocity)914 private void notifyAssistantGestureCompletion(float velocity) { 915 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 916 mConnectionCallbacks.get(i).onAssistantGestureCompletion(velocity); 917 } 918 } 919 notifyStartAssistant(Bundle bundle)920 private void notifyStartAssistant(Bundle bundle) { 921 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 922 mConnectionCallbacks.get(i).startAssistant(bundle); 923 } 924 } 925 notifySwipeUpGestureStartedInternal()926 private void notifySwipeUpGestureStartedInternal() { 927 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 928 mConnectionCallbacks.get(i).onSwipeUpGestureStarted(); 929 } 930 } 931 notifyAssistantVisibilityChanged(float visibility)932 public void notifyAssistantVisibilityChanged(float visibility) { 933 try { 934 if (mOverviewProxy != null) { 935 mOverviewProxy.onAssistantVisibilityChanged(visibility); 936 } else { 937 Log.e(TAG_OPS, "Failed to get overview proxy for assistant visibility."); 938 } 939 } catch (RemoteException e) { 940 Log.e(TAG_OPS, "Failed to call notifyAssistantVisibilityChanged()", e); 941 } 942 } 943 944 /** 945 * Notifies the Launcher of split screen size changes 946 * 947 * @param secondaryWindowBounds Bounds of the secondary window including the insets 948 * @param secondaryWindowInsets stable insets received by the secondary window 949 */ notifySplitScreenBoundsChanged( Rect secondaryWindowBounds, Rect secondaryWindowInsets)950 public void notifySplitScreenBoundsChanged( 951 Rect secondaryWindowBounds, Rect secondaryWindowInsets) { 952 try { 953 if (mOverviewProxy != null) { 954 mOverviewProxy.onSplitScreenSecondaryBoundsChanged( 955 secondaryWindowBounds, secondaryWindowInsets); 956 } else { 957 Log.e(TAG_OPS, "Failed to get overview proxy for split screen bounds."); 958 } 959 } catch (RemoteException e) { 960 Log.e(TAG_OPS, "Failed to call onSplitScreenSecondaryBoundsChanged()", e); 961 } 962 } 963 notifyToggleRecentApps()964 void notifyToggleRecentApps() { 965 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { 966 mConnectionCallbacks.get(i).onToggleRecentApps(); 967 } 968 } 969 notifyImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)970 public void notifyImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, 971 boolean showImeSwitcher) { 972 try { 973 if (mOverviewProxy != null) { 974 mOverviewProxy.onImeWindowStatusChanged(displayId, token, vis, backDisposition, 975 showImeSwitcher); 976 } else { 977 Log.e(TAG_OPS, "Failed to get overview proxy for setting IME status."); 978 } 979 } catch (RemoteException e) { 980 Log.e(TAG_OPS, "Failed to call notifyImeWindowStatus()", e); 981 } 982 983 } 984 updateEnabledState()985 private void updateEnabledState() { 986 final int currentUser = ActivityManagerWrapper.getInstance().getCurrentUserId(); 987 mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent, 988 MATCH_SYSTEM_ONLY, currentUser) != null; 989 } 990 991 @Override onNavigationModeChanged(int mode)992 public void onNavigationModeChanged(int mode) { 993 mNavBarMode = mode; 994 } 995 996 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)997 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 998 pw.println(TAG_OPS + " state:"); 999 pw.print(" isConnected="); pw.println(mOverviewProxy != null); 1000 pw.print(" mIsEnabled="); pw.println(isEnabled()); 1001 pw.print(" mRecentsComponentName="); pw.println(mRecentsComponentName); 1002 pw.print(" mQuickStepIntent="); pw.println(mQuickStepIntent); 1003 pw.print(" mBound="); pw.println(mBound); 1004 pw.print(" mCurrentBoundedUserId="); pw.println(mCurrentBoundedUserId); 1005 pw.print(" mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts); 1006 pw.print(" mInputFocusTransferStarted="); pw.println(mInputFocusTransferStarted); 1007 pw.print(" mInputFocusTransferStartY="); pw.println(mInputFocusTransferStartY); 1008 pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis); 1009 pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius); 1010 pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows); 1011 pw.print(" mNavBarButtonAlpha="); pw.println(mNavBarButtonAlpha); 1012 pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion); 1013 pw.print(" mNavBarMode="); pw.println(mNavBarMode); 1014 mSysUiState.dump(fd, pw, args); 1015 } 1016 1017 public interface OverviewProxyListener { onConnectionChanged(boolean isConnected)1018 default void onConnectionChanged(boolean isConnected) {} onQuickStepStarted()1019 default void onQuickStepStarted() {} onSwipeUpGestureStarted()1020 default void onSwipeUpGestureStarted() {} onPrioritizedRotation(@urface.Rotation int rotation)1021 default void onPrioritizedRotation(@Surface.Rotation int rotation) {} onOverviewShown(boolean fromHome)1022 default void onOverviewShown(boolean fromHome) {} onQuickScrubStarted()1023 default void onQuickScrubStarted() {} 1024 /** Notify the recents app (overview) is started by 3-button navigation. */ onToggleRecentApps()1025 default void onToggleRecentApps() {} 1026 /** Notify changes in the nav bar button alpha */ onNavBarButtonAlphaChanged(float alpha, boolean animate)1027 default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {} onHomeRotationEnabled(boolean enabled)1028 default void onHomeRotationEnabled(boolean enabled) {} onSystemUiStateChanged(int sysuiStateFlags)1029 default void onSystemUiStateChanged(int sysuiStateFlags) {} onAssistantProgress(@loatRangefrom = 0.0, to = 1.0) float progress)1030 default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {} onAssistantGestureCompletion(float velocity)1031 default void onAssistantGestureCompletion(float velocity) {} startAssistant(Bundle bundle)1032 default void startAssistant(Bundle bundle) {} onImeWindowStatusChanged(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher)1033 default void onImeWindowStatusChanged(int displayId, IBinder token, int vis, 1034 int backDisposition, boolean showImeSwitcher) {} 1035 } 1036 } 1037