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