• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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