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