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