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