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