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