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