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