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.Intent.ACTION_PACKAGE_ADDED;
20 import static android.content.Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST;
21 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
22 import static android.view.MotionEvent.ACTION_CANCEL;
23 import static android.view.MotionEvent.ACTION_DOWN;
24 import static android.view.MotionEvent.ACTION_UP;
25 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
26 
27 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
28 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
29 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
30 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
31 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE;
32 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
33 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
34 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
35 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY;
36 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
38 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
39 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
40 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_TRANSITION;
41 
42 import android.annotation.FloatRange;
43 import android.app.ActivityTaskManager;
44 import android.content.BroadcastReceiver;
45 import android.content.ComponentName;
46 import android.content.Context;
47 import android.content.Intent;
48 import android.content.IntentFilter;
49 import android.content.ServiceConnection;
50 import android.content.pm.ResolveInfo;
51 import android.graphics.Region;
52 import android.hardware.input.InputManager;
53 import android.hardware.input.InputManagerGlobal;
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.Process;
61 import android.os.RemoteException;
62 import android.os.SystemClock;
63 import android.os.UserHandle;
64 import android.util.Log;
65 import android.view.InputDevice;
66 import android.view.KeyCharacterMap;
67 import android.view.KeyEvent;
68 import android.view.MotionEvent;
69 import android.view.Surface;
70 import android.view.SurfaceControl;
71 import android.view.accessibility.AccessibilityManager;
72 import android.view.inputmethod.InputMethodManager;
73 
74 import androidx.annotation.NonNull;
75 
76 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
77 import com.android.internal.annotations.VisibleForTesting;
78 import com.android.internal.app.AssistUtils;
79 import com.android.internal.app.IVoiceInteractionSessionListener;
80 import com.android.internal.logging.UiEventLogger;
81 import com.android.internal.util.ScreenshotHelper;
82 import com.android.internal.util.ScreenshotRequest;
83 import com.android.systemui.Dumpable;
84 import com.android.systemui.dagger.SysUISingleton;
85 import com.android.systemui.dagger.qualifiers.Main;
86 import com.android.systemui.dump.DumpManager;
87 import com.android.systemui.flags.FeatureFlags;
88 import com.android.systemui.flags.Flags;
89 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
90 import com.android.systemui.keyguard.WakefulnessLifecycle;
91 import com.android.systemui.model.SysUiState;
92 import com.android.systemui.navigationbar.NavigationBar;
93 import com.android.systemui.navigationbar.NavigationBarController;
94 import com.android.systemui.navigationbar.NavigationBarView;
95 import com.android.systemui.navigationbar.NavigationModeController;
96 import com.android.systemui.navigationbar.buttons.KeyButtonView;
97 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
98 import com.android.systemui.scene.domain.interactor.SceneInteractor;
99 import com.android.systemui.settings.DisplayTracker;
100 import com.android.systemui.settings.UserTracker;
101 import com.android.systemui.shade.ShadeViewController;
102 import com.android.systemui.shared.recents.IOverviewProxy;
103 import com.android.systemui.shared.recents.ISystemUiProxy;
104 import com.android.systemui.shared.system.QuickStepContract;
105 import com.android.systemui.statusbar.CommandQueue;
106 import com.android.systemui.statusbar.NotificationShadeWindowController;
107 import com.android.systemui.statusbar.phone.CentralSurfaces;
108 import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
109 import com.android.systemui.statusbar.policy.CallbackController;
110 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
111 import com.android.wm.shell.sysui.ShellInterface;
112 
113 import dagger.Lazy;
114 
115 import java.io.PrintWriter;
116 import java.util.ArrayList;
117 import java.util.List;
118 import java.util.Objects;
119 import java.util.Optional;
120 import java.util.concurrent.CountDownLatch;
121 import java.util.concurrent.Executor;
122 import java.util.function.Supplier;
123 
124 import javax.inject.Inject;
125 import javax.inject.Provider;
126 
127 /**
128  * Class to send information from overview to launcher with a binder.
129  */
130 @SysUISingleton
131 public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
132         NavigationModeController.ModeChangedListener, Dumpable {
133 
134     @VisibleForTesting
135     static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
136 
137     public static final String TAG_OPS = "OverviewProxyService";
138     private static final long BACKOFF_MILLIS = 1000;
139     private static final long DEFERRED_CALLBACK_MILLIS = 5000;
140 
141     // Max backoff caps at 5 mins
142     private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
143 
144     private final Context mContext;
145     private final FeatureFlags mFeatureFlags;
146     private final Executor mMainExecutor;
147     private final ShellInterface mShellInterface;
148     private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
149     private final Lazy<ShadeViewController> mShadeViewControllerLazy;
150     private SysUiState mSysUiState;
151     private final Handler mHandler;
152     private final Lazy<NavigationBarController> mNavBarControllerLazy;
153     private final NotificationShadeWindowController mStatusBarWinController;
154     private final Provider<SceneInteractor> mSceneInteractor;
155 
156     private final Runnable mConnectionRunnable = () ->
157             internalConnectToCurrentUser("runnable: startConnectionToCurrentUser");
158     private final ComponentName mRecentsComponentName;
159     private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
160     private final Intent mQuickStepIntent;
161     private final ScreenshotHelper mScreenshotHelper;
162     private final CommandQueue mCommandQueue;
163     private final UserTracker mUserTracker;
164     private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
165     private final Optional<UnfoldTransitionProgressForwarder> mUnfoldTransitionProgressForwarder;
166     private final UiEventLogger mUiEventLogger;
167     private final DisplayTracker mDisplayTracker;
168 
169     private Region mActiveNavBarRegion;
170     private SurfaceControl mNavigationBarSurface;
171 
172     private IOverviewProxy mOverviewProxy;
173     private int mConnectionBackoffAttempts;
174     private boolean mBound;
175     private boolean mIsEnabled;
176     private int mCurrentBoundedUserId = -1;
177     private boolean mInputFocusTransferStarted;
178     private float mInputFocusTransferStartY;
179     private long mInputFocusTransferStartMillis;
180     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
181 
182     @VisibleForTesting
183     public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
184         @Override
185         public void startScreenPinning(int taskId) {
186             verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
187                     mCentralSurfacesOptionalLazy.get().ifPresent(
188                             statusBar -> statusBar.showScreenPinningRequest(taskId,
189                                     false /* allowCancel */)));
190         }
191 
192         @Override
193         public void stopScreenPinning() {
194             verifyCallerAndClearCallingIdentityPostMain("stopScreenPinning", () -> {
195                 try {
196                     ActivityTaskManager.getService().stopSystemLockTaskMode();
197                 } catch (RemoteException e) {
198                     Log.e(TAG_OPS, "Failed to stop screen pinning");
199                 }
200             });
201         }
202 
203         // TODO: change the method signature to use (boolean inputFocusTransferStarted)
204         @Override
205         public void onStatusBarTouchEvent(MotionEvent event) {
206             verifyCallerAndClearCallingIdentity("onStatusBarTouchEvent", () -> {
207                 // TODO move this logic to message queue
208                 mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
209                     if (event.getActionMasked() == ACTION_DOWN) {
210                         mShadeViewControllerLazy.get().startExpandLatencyTracking();
211                     }
212                     mHandler.post(() -> {
213                         int action = event.getActionMasked();
214                         if (action == ACTION_DOWN) {
215                             mInputFocusTransferStarted = true;
216                             mInputFocusTransferStartY = event.getY();
217                             mInputFocusTransferStartMillis = event.getEventTime();
218 
219                             // If scene framework is enabled, set the scene container window to
220                             // visible and let the touch "slip" into that window.
221                             if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
222                                 mSceneInteractor.get().setVisible(true, "swipe down on launcher");
223                             } else {
224                                 centralSurfaces.onInputFocusTransfer(
225                                         mInputFocusTransferStarted, false /* cancel */,
226                                         0 /* velocity */);
227                             }
228                         }
229                         if (action == ACTION_UP || action == ACTION_CANCEL) {
230                             mInputFocusTransferStarted = false;
231 
232                             if (!mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
233                                 float velocity = (event.getY() - mInputFocusTransferStartY)
234                                         / (event.getEventTime() - mInputFocusTransferStartMillis);
235                                 centralSurfaces.onInputFocusTransfer(mInputFocusTransferStarted,
236                                         action == ACTION_CANCEL,
237                                         velocity);
238                             }
239                         }
240                         event.recycle();
241                     });
242                 });
243             });
244         }
245 
246         @Override
247         public void onStatusBarTrackpadEvent(MotionEvent event) {
248             verifyCallerAndClearCallingIdentityPostMain("onStatusBarTrackpadEvent", () ->
249                     mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces ->
250                             centralSurfaces.onStatusBarTrackpadEvent(event)));
251         }
252 
253         @Override
254         public void onBackPressed() {
255             verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
256                 sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
257                 sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
258             });
259         }
260 
261         @Override
262         public void onImeSwitcherPressed() {
263             // TODO(b/204901476) We're intentionally using the default display for now since
264             // Launcher/Taskbar isn't display aware.
265             mContext.getSystemService(InputMethodManager.class)
266                     .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
267                             mDisplayTracker.getDefaultDisplayId());
268             mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
269         }
270 
271         @Override
272         public void setHomeRotationEnabled(boolean enabled) {
273             verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () ->
274                     mHandler.post(() -> notifyHomeRotationEnabled(enabled)));
275         }
276 
277         @Override
278         public void notifyTaskbarStatus(boolean visible, boolean stashed) {
279             verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarStatus", () ->
280                     onTaskbarStatusUpdated(visible, stashed));
281         }
282 
283         @Override
284         public void notifyTaskbarAutohideSuspend(boolean suspend) {
285             verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarAutohideSuspend", () ->
286                     onTaskbarAutohideSuspend(suspend));
287         }
288 
289         private boolean sendEvent(int action, int code) {
290             long when = SystemClock.uptimeMillis();
291             final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
292                     0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
293                     KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
294                     InputDevice.SOURCE_KEYBOARD);
295 
296             ev.setDisplayId(mContext.getDisplay().getDisplayId());
297             return InputManagerGlobal.getInstance()
298                     .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
299         }
300 
301         @Override
302         public void onOverviewShown(boolean fromHome) {
303             verifyCallerAndClearCallingIdentityPostMain("onOverviewShown", () -> {
304                 for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
305                     mConnectionCallbacks.get(i).onOverviewShown(fromHome);
306                 }
307             });
308         }
309 
310         @Override
311         public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
312             verifyCallerAndClearCallingIdentityPostMain("onAssistantProgress", () ->
313                     notifyAssistantProgress(progress));
314         }
315 
316         @Override
317         public void onAssistantGestureCompletion(float velocity) {
318             verifyCallerAndClearCallingIdentityPostMain("onAssistantGestureCompletion", () ->
319                     notifyAssistantGestureCompletion(velocity));
320         }
321 
322         @Override
323         public void startAssistant(Bundle bundle) {
324             verifyCallerAndClearCallingIdentityPostMain("startAssistant", () ->
325                     notifyStartAssistant(bundle));
326         }
327 
328         @Override
329         public void setAssistantOverridesRequested(int[] invocationTypes) {
330             verifyCallerAndClearCallingIdentityPostMain("setAssistantOverridesRequested", () ->
331                     notifyAssistantOverrideRequested(invocationTypes));
332         }
333 
334         @Override
335         public void notifyAccessibilityButtonClicked(int displayId) {
336             verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonClicked", () ->
337                     AccessibilityManager.getInstance(mContext)
338                             .notifyAccessibilityButtonClicked(displayId));
339         }
340 
341         @Override
342         public void notifyAccessibilityButtonLongClicked() {
343             verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonLongClicked",
344                     () -> {
345                         final Intent intent =
346                                 new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
347                         final String chooserClassName = AccessibilityButtonChooserActivity
348                                 .class.getName();
349                         intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
350                         intent.addFlags(
351                                 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
352                         mContext.startActivityAsUser(intent, mUserTracker.getUserHandle());
353                     });
354         }
355 
356         @Override
357         public void notifyPrioritizedRotation(@Surface.Rotation int rotation) {
358             verifyCallerAndClearCallingIdentityPostMain("notifyPrioritizedRotation", () ->
359                     notifyPrioritizedRotationInternal(rotation));
360         }
361 
362         @Override
363         public void takeScreenshot(ScreenshotRequest request) {
364             mScreenshotHelper.takeScreenshot(request, mHandler, null);
365         }
366 
367         @Override
368         public void expandNotificationPanel() {
369             verifyCallerAndClearCallingIdentityPostMain("expandNotificationPanel",
370                     () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
371                             KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
372         }
373 
374         @Override
375         public void toggleNotificationPanel() {
376             verifyCallerAndClearCallingIdentityPostMain("toggleNotificationPanel", () ->
377                     mCommandQueue.togglePanel());
378         }
379 
380         private boolean verifyCaller(String reason) {
381             final int callerId = Binder.getCallingUserHandle().getIdentifier();
382             if (callerId != mCurrentBoundedUserId) {
383                 Log.w(TAG_OPS, "Launcher called sysui with invalid user: " + callerId + ", reason: "
384                         + reason);
385                 return false;
386             }
387             return true;
388         }
389 
390         private <T> T verifyCallerAndClearCallingIdentity(String reason, Supplier<T> supplier) {
391             if (!verifyCaller(reason)) {
392                 return null;
393             }
394             final long token = Binder.clearCallingIdentity();
395             try {
396                 return supplier.get();
397             } finally {
398                 Binder.restoreCallingIdentity(token);
399             }
400         }
401 
402         private void verifyCallerAndClearCallingIdentity(String reason, Runnable runnable) {
403             verifyCallerAndClearCallingIdentity(reason, () -> {
404                 runnable.run();
405                 return null;
406             });
407         }
408 
409         private void verifyCallerAndClearCallingIdentityPostMain(String reason, Runnable runnable) {
410             verifyCallerAndClearCallingIdentity(reason, () -> mHandler.post(runnable));
411         }
412     };
413 
414     private final Runnable mDeferredConnectionCallback = () -> {
415         Log.w(TAG_OPS, "Binder supposed established connection but actual connection to service "
416             + "timed out, trying again");
417         retryConnectionWithBackoff();
418     };
419 
420     private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
421         @Override
422         public void onReceive(Context context, Intent intent) {
423             // If adding, bind immediately
424             if (Objects.equals(intent.getAction(), ACTION_PACKAGE_ADDED)) {
425                 updateEnabledAndBinding();
426                 return;
427             }
428 
429             // ACTION_PACKAGE_CHANGED
430             String[] compsList = intent.getStringArrayExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST);
431             if (compsList == null) {
432                 return;
433             }
434 
435             // Only rebind for TouchInteractionService component from launcher
436             ResolveInfo ri = context.getPackageManager()
437                     .resolveService(new Intent(ACTION_QUICKSTEP), 0);
438             String interestingComponent = ri.serviceInfo.name;
439             for (String component : compsList) {
440                 if (interestingComponent.equals(component)) {
441                     Log.i(TAG_OPS, "Rebinding for component [" + component + "] change");
442                     updateEnabledAndBinding();
443                     return;
444                 }
445             }
446         }
447     };
448 
449     private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
450         @Override
451         public void onServiceConnected(ComponentName name, IBinder service) {
452             Log.d(TAG_OPS, "Overview proxy service connected");
453             mConnectionBackoffAttempts = 0;
454             mHandler.removeCallbacks(mDeferredConnectionCallback);
455             try {
456                 service.linkToDeath(mOverviewServiceDeathRcpt, 0);
457             } catch (RemoteException e) {
458                 // Failed to link to death (process may have died between binding and connecting),
459                 // just unbind the service for now and retry again
460                 Log.e(TAG_OPS, "Lost connection to launcher service", e);
461                 disconnectFromLauncherService("Lost connection to launcher service");
462                 retryConnectionWithBackoff();
463                 return;
464             }
465 
466             mCurrentBoundedUserId = mUserTracker.getUserId();
467             mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
468 
469             Bundle params = new Bundle();
470             params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
471             params.putBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER,
472                     mSysuiUnlockAnimationController.asBinder());
473             mUnfoldTransitionProgressForwarder.ifPresent(
474                     unfoldProgressForwarder -> params.putBinder(
475                             KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER,
476                             unfoldProgressForwarder.asBinder()));
477             // Add all the interfaces exposed by the shell
478             mShellInterface.createExternalInterfaces(params);
479 
480             try {
481                 Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy");
482                 mOverviewProxy.onInitialize(params);
483             } catch (RemoteException e) {
484                 mCurrentBoundedUserId = -1;
485                 Log.e(TAG_OPS, "Failed to call onInitialize()", e);
486             }
487             dispatchNavButtonBounds();
488             dispatchNavigationBarSurface();
489 
490             // Force-update the systemui state flags
491             updateSystemUiStateFlags();
492             notifySystemUiStateFlags(mSysUiState.getFlags());
493 
494             notifyConnectionChanged();
495             if (mLatchForOnUserChanging != null) {
496                 mLatchForOnUserChanging.countDown();
497                 mLatchForOnUserChanging = null;
498             }
499         }
500 
501         @Override
502         public void onNullBinding(ComponentName name) {
503             Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
504             mCurrentBoundedUserId = -1;
505             retryConnectionWithBackoff();
506         }
507 
508         @Override
509         public void onBindingDied(ComponentName name) {
510             Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
511             mCurrentBoundedUserId = -1;
512             retryConnectionWithBackoff();
513         }
514 
515         @Override
516         public void onServiceDisconnected(ComponentName name) {
517             Log.w(TAG_OPS, "Service disconnected");
518             // Do nothing
519             mCurrentBoundedUserId = -1;
520         }
521     };
522 
523     private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
524 
525     // This is the death handler for the binder from the launcher service
526     private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
527             = this::cleanupAfterDeath;
528 
529     private final IVoiceInteractionSessionListener mVoiceInteractionSessionListener =
530             new IVoiceInteractionSessionListener.Stub() {
531         @Override
532         public void onVoiceSessionShown() {
533             // Do nothing
534         }
535 
536         @Override
537         public void onVoiceSessionHidden() {
538             // Do nothing
539         }
540 
541         @Override
542         public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
543             mContext.getMainExecutor().execute(() ->
544                     OverviewProxyService.this.onVoiceSessionWindowVisibilityChanged(visible));
545         }
546 
547         @Override
548         public void onSetUiHints(Bundle hints) {
549             // Do nothing
550         }
551     };
552 
553     private CountDownLatch mLatchForOnUserChanging;
554     private final UserTracker.Callback mUserChangedCallback =
555             new UserTracker.Callback() {
556                 @Override
557                 public void onUserChanging(int newUser, @NonNull Context userContext,
558                         CountDownLatch latch) {
559                     mConnectionBackoffAttempts = 0;
560                     mLatchForOnUserChanging = latch;
561                     internalConnectToCurrentUser("User changed");
562                 }
563             };
564 
565     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
566     @Inject
OverviewProxyService(Context context, @Main Executor mainExecutor, CommandQueue commandQueue, ShellInterface shellInterface, Lazy<NavigationBarController> navBarControllerLazy, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy, Lazy<ShadeViewController> shadeViewControllerLazy, NavigationModeController navModeController, NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, Provider<SceneInteractor> sceneInteractor, UserTracker userTracker, WakefulnessLifecycle wakefulnessLifecycle, UiEventLogger uiEventLogger, DisplayTracker displayTracker, KeyguardUnlockAnimationController sysuiUnlockAnimationController, AssistUtils assistUtils, FeatureFlags featureFlags, DumpManager dumpManager, Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder )567     public OverviewProxyService(Context context,
568             @Main Executor mainExecutor,
569             CommandQueue commandQueue,
570             ShellInterface shellInterface,
571             Lazy<NavigationBarController> navBarControllerLazy,
572             Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
573             Lazy<ShadeViewController> shadeViewControllerLazy,
574             NavigationModeController navModeController,
575             NotificationShadeWindowController statusBarWinController,
576             SysUiState sysUiState,
577             Provider<SceneInteractor> sceneInteractor,
578             UserTracker userTracker,
579             WakefulnessLifecycle wakefulnessLifecycle,
580             UiEventLogger uiEventLogger,
581             DisplayTracker displayTracker,
582             KeyguardUnlockAnimationController sysuiUnlockAnimationController,
583             AssistUtils assistUtils,
584             FeatureFlags featureFlags,
585             DumpManager dumpManager,
586             Optional<UnfoldTransitionProgressForwarder> unfoldTransitionProgressForwarder
587     ) {
588         // b/241601880: This component shouldn't be running for a non-primary user
589         if (!Process.myUserHandle().equals(UserHandle.SYSTEM)) {
590             Log.e(TAG_OPS, "Unexpected initialization for non-primary user", new Throwable());
591         }
592 
593         mContext = context;
594         mFeatureFlags = featureFlags;
595         mMainExecutor = mainExecutor;
596         mShellInterface = shellInterface;
597         mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
598         mShadeViewControllerLazy = shadeViewControllerLazy;
599         mHandler = new Handler();
600         mNavBarControllerLazy = navBarControllerLazy;
601         mStatusBarWinController = statusBarWinController;
602         mSceneInteractor = sceneInteractor;
603         mUserTracker = userTracker;
604         mConnectionBackoffAttempts = 0;
605         mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
606                 com.android.internal.R.string.config_recentsComponentName));
607         mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
608                 .setPackage(mRecentsComponentName.getPackageName());
609         mSysUiState = sysUiState;
610         mSysUiState.addCallback(this::notifySystemUiStateFlags);
611         mUiEventLogger = uiEventLogger;
612         mDisplayTracker = displayTracker;
613         mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
614         mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
615 
616         dumpManager.registerDumpable(getClass().getSimpleName(), this);
617 
618         // Listen for nav bar mode changes
619         mNavBarMode = navModeController.addListener(this);
620 
621         // Listen for launcher package changes
622         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
623         filter.addDataScheme("package");
624         filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
625                 PatternMatcher.PATTERN_LITERAL);
626         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
627         mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
628 
629         // Listen for status bar state changes
630         statusBarWinController.registerCallback(mStatusBarWindowCallback);
631         mScreenshotHelper = new ScreenshotHelper(context);
632 
633         commandQueue.addCallback(new CommandQueue.Callbacks() {
634 
635             // Listen for tracing state changes
636             @Override
637             public void onTracingStateChanged(boolean enabled) {
638                 mSysUiState.setFlag(SYSUI_STATE_TRACING_ENABLED, enabled)
639                         .commitUpdate(mContext.getDisplayId());
640             }
641 
642             @Override
643             public void enterStageSplitFromRunningApp(boolean leftOrTop) {
644                 if (mOverviewProxy != null) {
645                     try {
646                         mOverviewProxy.enterStageSplitFromRunningApp(leftOrTop);
647                     } catch (RemoteException e) {
648                         Log.w(TAG_OPS, "Unable to enter stage split from the current running app");
649                     }
650                 }
651             }
652         });
653         mCommandQueue = commandQueue;
654 
655         // Listen for user setup
656         mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
657 
658         wakefulnessLifecycle.addObserver(mWakefulnessLifecycleObserver);
659         // Connect to the service
660         updateEnabledAndBinding();
661 
662         // Listen for assistant changes
663         assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
664     }
665 
onVoiceSessionWindowVisibilityChanged(boolean visible)666     public void onVoiceSessionWindowVisibilityChanged(boolean visible) {
667         mSysUiState.setFlag(SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING, visible)
668                 .commitUpdate(mContext.getDisplayId());
669     }
670 
671     /**
672      * Called when the navigation bar surface is created or changed
673      */
onNavigationBarSurfaceChanged(SurfaceControl navbarSurface)674     public void onNavigationBarSurfaceChanged(SurfaceControl navbarSurface) {
675         mNavigationBarSurface = navbarSurface;
676         dispatchNavigationBarSurface();
677     }
678 
dispatchNavigationBarSurface()679     private void dispatchNavigationBarSurface() {
680         try {
681             if (mOverviewProxy != null) {
682                 // Catch all for cases where the surface is no longer valid
683                 if (mNavigationBarSurface != null && !mNavigationBarSurface.isValid()) {
684                     mNavigationBarSurface = null;
685                 }
686                 mOverviewProxy.onNavigationBarSurface(mNavigationBarSurface);
687             }
688         } catch (RemoteException e) {
689             Log.e(TAG_OPS, "Failed to notify back action", e);
690         }
691     }
692 
updateEnabledAndBinding()693     private void updateEnabledAndBinding() {
694         updateEnabledState();
695         startConnectionToCurrentUser();
696     }
697 
updateSystemUiStateFlags()698     private void updateSystemUiStateFlags() {
699         final NavigationBar navBarFragment =
700                 mNavBarControllerLazy.get().getDefaultNavigationBar();
701         final NavigationBarView navBarView =
702                 mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
703         if (SysUiState.DEBUG) {
704             Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
705                     + " navBarView=" + navBarView
706                     + " shadeViewController=" + mShadeViewControllerLazy.get());
707         }
708 
709         if (navBarFragment != null) {
710             navBarFragment.updateSystemUiStateFlags();
711         }
712         if (navBarView != null) {
713             navBarView.updateDisabledSystemUiStateFlags(mSysUiState);
714         }
715         mShadeViewControllerLazy.get().updateSystemUiStateFlags();
716         if (mStatusBarWinController != null) {
717             mStatusBarWinController.notifyStateChangedCallbacks();
718         }
719     }
720 
notifySystemUiStateFlags(int flags)721     private void notifySystemUiStateFlags(int flags) {
722         if (SysUiState.DEBUG) {
723             Log.d(TAG_OPS, "Notifying sysui state change to overview service: proxy="
724                     + mOverviewProxy + " flags=" + flags);
725         }
726         try {
727             if (mOverviewProxy != null) {
728                 mOverviewProxy.onSystemUiStateChanged(flags);
729             }
730         } catch (RemoteException e) {
731             Log.e(TAG_OPS, "Failed to notify sysui state change", e);
732         }
733     }
734 
onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean keyguardGoingAway, boolean bouncerShowing, boolean isDozing, boolean panelExpanded, boolean isDreaming)735     private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
736             boolean keyguardGoingAway, boolean bouncerShowing, boolean isDozing,
737             boolean panelExpanded, boolean isDreaming) {
738         mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
739                         keyguardShowing && !keyguardOccluded)
740                 .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
741                         keyguardShowing && keyguardOccluded)
742                 .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY,
743                         keyguardGoingAway)
744                 .setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
745                 .setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing)
746                 .setFlag(SYSUI_STATE_DEVICE_DREAMING, isDreaming)
747                 .commitUpdate(mContext.getDisplayId());
748     }
749 
750     /**
751      * Sets the navbar region which can receive touch inputs
752      */
onActiveNavBarRegionChanges(Region activeRegion)753     public void onActiveNavBarRegionChanges(Region activeRegion) {
754         mActiveNavBarRegion = activeRegion;
755         dispatchNavButtonBounds();
756     }
757 
dispatchNavButtonBounds()758     private void dispatchNavButtonBounds() {
759         if (mOverviewProxy != null && mActiveNavBarRegion != null) {
760             try {
761                 mOverviewProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion);
762             } catch (RemoteException e) {
763                 Log.e(TAG_OPS, "Failed to call onActiveNavBarRegionChanges()", e);
764             }
765         }
766     }
767 
cleanupAfterDeath()768     public void cleanupAfterDeath() {
769         if (mInputFocusTransferStarted) {
770             mHandler.post(() -> {
771                 mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
772                     mInputFocusTransferStarted = false;
773                     centralSurfaces.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
774                 });
775             });
776         }
777         startConnectionToCurrentUser();
778     }
779 
startConnectionToCurrentUser()780     public void startConnectionToCurrentUser() {
781         Log.v(TAG_OPS, "startConnectionToCurrentUser: connection is restarted");
782         if (mHandler.getLooper() != Looper.myLooper()) {
783             mHandler.post(mConnectionRunnable);
784         } else {
785             internalConnectToCurrentUser("startConnectionToCurrentUser");
786         }
787     }
788 
internalConnectToCurrentUser(String reason)789     private void internalConnectToCurrentUser(String reason) {
790         disconnectFromLauncherService(reason);
791 
792         // If user has not setup yet or already connected, do not try to connect
793         if (!isEnabled()) {
794             Log.v(TAG_OPS, "Cannot attempt connection, is enabled " + isEnabled());
795             return;
796         }
797         mHandler.removeCallbacks(mConnectionRunnable);
798         try {
799             mBound = mContext.bindServiceAsUser(mQuickStepIntent,
800                     mOverviewServiceConnection,
801                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
802                     UserHandle.of(mUserTracker.getUserId()));
803         } catch (SecurityException e) {
804             Log.e(TAG_OPS, "Unable to bind because of security error", e);
805         }
806         if (mBound) {
807             // Ensure that connection has been established even if it thinks it is bound
808             mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
809         } else {
810             // Retry after exponential backoff timeout
811             retryConnectionWithBackoff();
812         }
813     }
814 
retryConnectionWithBackoff()815     private void retryConnectionWithBackoff() {
816         if (mHandler.hasCallbacks(mConnectionRunnable)) {
817             return;
818         }
819         final long timeoutMs = (long) Math.min(
820                 Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts), MAX_BACKOFF_MILLIS);
821         mHandler.postDelayed(mConnectionRunnable, timeoutMs);
822         mConnectionBackoffAttempts++;
823         Log.w(TAG_OPS, "Failed to connect on attempt " + mConnectionBackoffAttempts
824                 + " will try again in " + timeoutMs + "ms");
825     }
826 
827     @Override
addCallback(@onNull OverviewProxyListener listener)828     public void addCallback(@NonNull OverviewProxyListener listener) {
829         if (!mConnectionCallbacks.contains(listener)) {
830             mConnectionCallbacks.add(listener);
831         }
832         listener.onConnectionChanged(mOverviewProxy != null);
833     }
834 
835     @Override
removeCallback(@onNull OverviewProxyListener listener)836     public void removeCallback(@NonNull OverviewProxyListener listener) {
837         mConnectionCallbacks.remove(listener);
838     }
839 
shouldShowSwipeUpUI()840     public boolean shouldShowSwipeUpUI() {
841         return isEnabled() && !QuickStepContract.isLegacyMode(mNavBarMode);
842     }
843 
isEnabled()844     public boolean isEnabled() {
845         return mIsEnabled;
846     }
847 
getProxy()848     public IOverviewProxy getProxy() {
849         return mOverviewProxy;
850     }
851 
disconnectFromLauncherService(String disconnectReason)852     private void disconnectFromLauncherService(String disconnectReason) {
853         Log.d(TAG_OPS, "disconnectFromLauncherService bound?: " + mBound +
854                 " currentProxy: " + mOverviewProxy + " disconnectReason: " + disconnectReason,
855                 new Throwable());
856         if (mBound) {
857             // Always unbind the service (ie. if called through onNullBinding or onBindingDied)
858             mContext.unbindService(mOverviewServiceConnection);
859             mBound = false;
860         }
861 
862         if (mOverviewProxy != null) {
863             mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
864             mOverviewProxy = null;
865             notifyConnectionChanged();
866         }
867     }
868 
notifyHomeRotationEnabled(boolean enabled)869     private void notifyHomeRotationEnabled(boolean enabled) {
870         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
871             mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled);
872         }
873     }
874 
onTaskbarStatusUpdated(boolean visible, boolean stashed)875     private void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
876         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
877             mConnectionCallbacks.get(i).onTaskbarStatusUpdated(visible, stashed);
878         }
879     }
880 
onTaskbarAutohideSuspend(boolean suspend)881     private void onTaskbarAutohideSuspend(boolean suspend) {
882         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
883             mConnectionCallbacks.get(i).onTaskbarAutohideSuspend(suspend);
884         }
885     }
886 
notifyConnectionChanged()887     private void notifyConnectionChanged() {
888         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
889             mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
890         }
891     }
892 
notifyPrioritizedRotationInternal(@urface.Rotation int rotation)893     private void notifyPrioritizedRotationInternal(@Surface.Rotation int rotation) {
894         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
895             mConnectionCallbacks.get(i).onPrioritizedRotation(rotation);
896         }
897     }
898 
notifyAssistantProgress(@loatRangefrom = 0.0, to = 1.0) float progress)899     private void notifyAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
900         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
901             mConnectionCallbacks.get(i).onAssistantProgress(progress);
902         }
903     }
904 
notifyAssistantGestureCompletion(float velocity)905     private void notifyAssistantGestureCompletion(float velocity) {
906         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
907             mConnectionCallbacks.get(i).onAssistantGestureCompletion(velocity);
908         }
909     }
910 
notifyStartAssistant(Bundle bundle)911     private void notifyStartAssistant(Bundle bundle) {
912         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
913             mConnectionCallbacks.get(i).startAssistant(bundle);
914         }
915     }
916 
notifyAssistantOverrideRequested(int[] invocationTypes)917     private void notifyAssistantOverrideRequested(int[] invocationTypes) {
918         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
919             mConnectionCallbacks.get(i).setAssistantOverridesRequested(invocationTypes);
920         }
921     }
922 
notifyAssistantVisibilityChanged(float visibility)923     public void notifyAssistantVisibilityChanged(float visibility) {
924         try {
925             if (mOverviewProxy != null) {
926                 mOverviewProxy.onAssistantVisibilityChanged(visibility);
927             } else {
928                 Log.e(TAG_OPS, "Failed to get overview proxy for assistant visibility.");
929             }
930         } catch (RemoteException e) {
931             Log.e(TAG_OPS, "Failed to call notifyAssistantVisibilityChanged()", e);
932         }
933     }
934 
935     private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
936             new WakefulnessLifecycle.Observer() {
937                 @Override
938                 public void onStartedWakingUp() {
939                     mSysUiState
940                             .setFlag(SYSUI_STATE_AWAKE, true)
941                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
942                             .commitUpdate(mContext.getDisplayId());
943                 }
944 
945                 @Override
946                 public void onFinishedWakingUp() {
947                     mSysUiState
948                             .setFlag(SYSUI_STATE_AWAKE, true)
949                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
950                             .commitUpdate(mContext.getDisplayId());
951                 }
952 
953                 @Override
954                 public void onStartedGoingToSleep() {
955                     mSysUiState
956                             .setFlag(SYSUI_STATE_AWAKE, false)
957                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, true)
958                             .commitUpdate(mContext.getDisplayId());
959                 }
960 
961                 @Override
962                 public void onFinishedGoingToSleep() {
963                     mSysUiState
964                             .setFlag(SYSUI_STATE_AWAKE, false)
965                             .setFlag(SYSUI_STATE_WAKEFULNESS_TRANSITION, false)
966                             .commitUpdate(mContext.getDisplayId());
967                 }
968             };
969 
notifyToggleRecentApps()970     void notifyToggleRecentApps() {
971         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
972             mConnectionCallbacks.get(i).onToggleRecentApps();
973         }
974     }
975 
disable(int displayId, int state1, int state2, boolean animate)976     public void disable(int displayId, int state1, int state2, boolean animate) {
977         try {
978             if (mOverviewProxy != null) {
979                 mOverviewProxy.disable(displayId, state1, state2, animate);
980             } else {
981                 Log.e(TAG_OPS, "Failed to get overview proxy for disable flags.");
982             }
983         } catch (RemoteException e) {
984             Log.e(TAG_OPS, "Failed to call disable()", e);
985         }
986     }
987 
onRotationProposal(int rotation, boolean isValid)988     public void onRotationProposal(int rotation, boolean isValid) {
989         try {
990             if (mOverviewProxy != null) {
991                 mOverviewProxy.onRotationProposal(rotation, isValid);
992             } else {
993                 Log.e(TAG_OPS, "Failed to get overview proxy for proposing rotation.");
994             }
995         } catch (RemoteException e) {
996             Log.e(TAG_OPS, "Failed to call onRotationProposal()", e);
997         }
998     }
999 
onSystemBarAttributesChanged(int displayId, int behavior)1000     public void onSystemBarAttributesChanged(int displayId, int behavior) {
1001         try {
1002             if (mOverviewProxy != null) {
1003                 mOverviewProxy.onSystemBarAttributesChanged(displayId, behavior);
1004             } else {
1005                 Log.e(TAG_OPS, "Failed to get overview proxy for system bar attr change.");
1006             }
1007         } catch (RemoteException e) {
1008             Log.e(TAG_OPS, "Failed to call onSystemBarAttributesChanged()", e);
1009         }
1010     }
1011 
onNavButtonsDarkIntensityChanged(float darkIntensity)1012     public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
1013         try {
1014             if (mOverviewProxy != null) {
1015                 mOverviewProxy.onNavButtonsDarkIntensityChanged(darkIntensity);
1016             } else {
1017                 Log.e(TAG_OPS, "Failed to get overview proxy to update nav buttons dark intensity");
1018             }
1019         } catch (RemoteException e) {
1020             Log.e(TAG_OPS, "Failed to call onNavButtonsDarkIntensityChanged()", e);
1021         }
1022     }
1023 
updateEnabledState()1024     private void updateEnabledState() {
1025         final int currentUser = mUserTracker.getUserId();
1026         mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
1027                 MATCH_SYSTEM_ONLY, currentUser) != null;
1028     }
1029 
1030     @Override
onNavigationModeChanged(int mode)1031     public void onNavigationModeChanged(int mode) {
1032         mNavBarMode = mode;
1033     }
1034 
1035     @Override
dump(PrintWriter pw, String[] args)1036     public void dump(PrintWriter pw, String[] args) {
1037         pw.println(TAG_OPS + " state:");
1038         pw.print("  isConnected="); pw.println(mOverviewProxy != null);
1039         pw.print("  mIsEnabled="); pw.println(isEnabled());
1040         pw.print("  mRecentsComponentName="); pw.println(mRecentsComponentName);
1041         pw.print("  mQuickStepIntent="); pw.println(mQuickStepIntent);
1042         pw.print("  mBound="); pw.println(mBound);
1043         pw.print("  mCurrentBoundedUserId="); pw.println(mCurrentBoundedUserId);
1044         pw.print("  mConnectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
1045         pw.print("  mInputFocusTransferStarted="); pw.println(mInputFocusTransferStarted);
1046         pw.print("  mInputFocusTransferStartY="); pw.println(mInputFocusTransferStartY);
1047         pw.print("  mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis);
1048         pw.print("  mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
1049         pw.print("  mNavigationBarSurface="); pw.println(mNavigationBarSurface);
1050         pw.print("  mNavBarMode="); pw.println(mNavBarMode);
1051         mSysUiState.dump(pw, args);
1052     }
1053 
1054     public interface OverviewProxyListener {
onConnectionChanged(boolean isConnected)1055         default void onConnectionChanged(boolean isConnected) {}
onPrioritizedRotation(@urface.Rotation int rotation)1056         default void onPrioritizedRotation(@Surface.Rotation int rotation) {}
onOverviewShown(boolean fromHome)1057         default void onOverviewShown(boolean fromHome) {}
1058         /** Notify the recents app (overview) is started by 3-button navigation. */
onToggleRecentApps()1059         default void onToggleRecentApps() {}
onHomeRotationEnabled(boolean enabled)1060         default void onHomeRotationEnabled(boolean enabled) {}
onTaskbarStatusUpdated(boolean visible, boolean stashed)1061         default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
onTaskbarAutohideSuspend(boolean suspend)1062         default void onTaskbarAutohideSuspend(boolean suspend) {}
onAssistantProgress(@loatRangefrom = 0.0, to = 1.0) float progress)1063         default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
onAssistantGestureCompletion(float velocity)1064         default void onAssistantGestureCompletion(float velocity) {}
startAssistant(Bundle bundle)1065         default void startAssistant(Bundle bundle) {}
setAssistantOverridesRequested(int[] invocationTypes)1066         default void setAssistantOverridesRequested(int[] invocationTypes) {}
1067     }
1068 
1069     /**
1070      * Shuts down this service at the end of a testcase.
1071      * <p>
1072      * The in-production service is never shuts down, and it was not designed with testing in mind.
1073      * This unregisters the mechanisms by which the service will be revived after a testcase.
1074      * <p>
1075      * NOTE: This is a stop-gap introduced when first added some tests to this class. It should
1076      * probably be replaced by proper lifecycle management on this class.
1077      */
1078     @VisibleForTesting()
shutdownForTest()1079     void shutdownForTest() {
1080         mContext.unregisterReceiver(mLauncherStateChangedReceiver);
1081         mIsEnabled = false;
1082         mHandler.removeCallbacks(mConnectionRunnable);
1083         disconnectFromLauncherService("Shutdown for test");
1084     }
1085 }
1086