• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 package com.android.quickstep;
17 
18 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS;
19 import static android.view.MotionEvent.ACTION_CANCEL;
20 import static android.view.MotionEvent.ACTION_DOWN;
21 import static android.view.MotionEvent.ACTION_MOVE;
22 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
23 import static android.view.MotionEvent.ACTION_POINTER_UP;
24 import static android.view.MotionEvent.ACTION_UP;
25 
26 import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
27 import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
28 import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
29 import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
30 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
31 import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
32 import static com.android.quickstep.GestureState.DEFAULT_STATE;
33 import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType;
34 import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER;
35 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER;
36 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
37 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE;
38 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
39 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
40 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
41 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
42 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
43 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
44 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
45 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
46 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;
47 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
48 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP;
49 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED;
50 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
51 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
52 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
53 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
54 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
55 
56 import android.annotation.TargetApi;
57 import android.app.PendingIntent;
58 import android.app.RemoteAction;
59 import android.app.Service;
60 import android.content.IIntentReceiver;
61 import android.content.IIntentSender;
62 import android.content.Intent;
63 import android.content.SharedPreferences;
64 import android.content.res.Configuration;
65 import android.graphics.Region;
66 import android.graphics.drawable.Icon;
67 import android.os.Build;
68 import android.os.Bundle;
69 import android.os.IBinder;
70 import android.os.Looper;
71 import android.os.SystemClock;
72 import android.os.Trace;
73 import android.util.Log;
74 import android.view.Choreographer;
75 import android.view.InputDevice;
76 import android.view.InputEvent;
77 import android.view.MotionEvent;
78 import android.view.SurfaceControl;
79 import android.view.accessibility.AccessibilityManager;
80 
81 import androidx.annotation.BinderThread;
82 import androidx.annotation.NonNull;
83 import androidx.annotation.Nullable;
84 import androidx.annotation.UiThread;
85 
86 import com.android.launcher3.BaseDraggingActivity;
87 import com.android.launcher3.DeviceProfile;
88 import com.android.launcher3.LauncherPrefs;
89 import com.android.launcher3.R;
90 import com.android.launcher3.anim.AnimatedFloat;
91 import com.android.launcher3.config.FeatureFlags;
92 import com.android.launcher3.provider.RestoreDbTask;
93 import com.android.launcher3.statemanager.StatefulActivity;
94 import com.android.launcher3.taskbar.TaskbarActivityContext;
95 import com.android.launcher3.taskbar.TaskbarManager;
96 import com.android.launcher3.testing.TestLogging;
97 import com.android.launcher3.testing.shared.ResourceUtils;
98 import com.android.launcher3.testing.shared.TestProtocol;
99 import com.android.launcher3.uioverrides.flags.FlagsFactory;
100 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
101 import com.android.launcher3.util.DisplayController;
102 import com.android.launcher3.util.LockedUserState;
103 import com.android.launcher3.util.OnboardingPrefs;
104 import com.android.launcher3.util.SafeCloseable;
105 import com.android.launcher3.util.TraceHelper;
106 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
107 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
108 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
109 import com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer;
110 import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
111 import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
112 import com.android.quickstep.inputconsumers.OverviewInputConsumer;
113 import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
114 import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer;
115 import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
116 import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
117 import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
118 import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer;
119 import com.android.quickstep.inputconsumers.TrackpadStatusBarInputConsumer;
120 import com.android.quickstep.util.ActiveGestureLog;
121 import com.android.quickstep.util.ActiveGestureLog.CompoundString;
122 import com.android.quickstep.util.AssistStateManager;
123 import com.android.quickstep.util.AssistUtils;
124 import com.android.systemui.shared.recents.IOverviewProxy;
125 import com.android.systemui.shared.recents.ISystemUiProxy;
126 import com.android.systemui.shared.system.ActivityManagerWrapper;
127 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
128 import com.android.systemui.shared.system.InputConsumerController;
129 import com.android.systemui.shared.system.InputMonitorCompat;
130 import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
131 import com.android.systemui.unfold.progress.IUnfoldAnimation;
132 import com.android.wm.shell.back.IBackAnimation;
133 import com.android.wm.shell.bubbles.IBubbles;
134 import com.android.wm.shell.desktopmode.IDesktopMode;
135 import com.android.wm.shell.draganddrop.IDragAndDrop;
136 import com.android.wm.shell.onehanded.IOneHanded;
137 import com.android.wm.shell.pip.IPip;
138 import com.android.wm.shell.recents.IRecentTasks;
139 import com.android.wm.shell.splitscreen.ISplitScreen;
140 import com.android.wm.shell.startingsurface.IStartingWindow;
141 import com.android.wm.shell.transition.IShellTransitions;
142 
143 import java.io.FileDescriptor;
144 import java.io.PrintWriter;
145 import java.lang.ref.WeakReference;
146 import java.util.function.Consumer;
147 import java.util.function.Function;
148 
149 /**
150  * Service connected by system-UI for handling touch interaction.
151  */
152 @TargetApi(Build.VERSION_CODES.R)
153 public class TouchInteractionService extends Service {
154 
155     private static final String SUBSTRING_PREFIX = "; ";
156     private static final String NEWLINE_PREFIX = "\n\t\t\t-> ";
157 
158     private static final String TAG = "TouchInteractionService";
159 
160     private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
161 
162     private final TISBinder mTISBinder = new TISBinder(this);
163 
164     /**
165      * Local IOverviewProxy implementation with some methods for local components
166      */
167     public static class TISBinder extends IOverviewProxy.Stub {
168 
169         private final WeakReference<TouchInteractionService> mTis;
170 
171         @Nullable private Runnable mOnOverviewTargetChangeListener = null;
172 
TISBinder(TouchInteractionService tis)173         private TISBinder(TouchInteractionService tis) {
174             mTis = new WeakReference<>(tis);
175         }
176 
177         @BinderThread
onInitialize(Bundle bundle)178         public void onInitialize(Bundle bundle) {
179             ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
180                     bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
181             IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
182             IBubbles bubbles = IBubbles.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_BUBBLES));
183             ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
184                     KEY_EXTRA_SHELL_SPLIT_SCREEN));
185             IOneHanded onehanded = IOneHanded.Stub.asInterface(
186                     bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
187             IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
188                     bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS));
189             IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
190                     bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW));
191             ISysuiUnlockAnimationController launcherUnlockAnimationController =
192                     ISysuiUnlockAnimationController.Stub.asInterface(
193                             bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
194             IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
195                     bundle.getBinder(KEY_EXTRA_SHELL_RECENT_TASKS));
196             IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
197                     bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
198             IDesktopMode desktopMode = IDesktopMode.Stub.asInterface(
199                     bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE));
200             IUnfoldAnimation unfoldTransition = IUnfoldAnimation.Stub.asInterface(
201                     bundle.getBinder(KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER));
202             IDragAndDrop dragAndDrop = IDragAndDrop.Stub.asInterface(
203                     bundle.getBinder(KEY_EXTRA_SHELL_DRAG_AND_DROP));
204             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
205                 SystemUiProxy.INSTANCE.get(tis).setProxy(proxy, pip,
206                         bubbles, splitscreen, onehanded, shellTransitions, startingWindow,
207                         recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode,
208                         unfoldTransition, dragAndDrop);
209                 tis.initInputMonitor("TISBinder#onInitialize()");
210                 tis.preloadOverview(true /* fromInit */);
211             }));
212             sIsInitialized = true;
213         }
214 
215         @BinderThread
216         @Override
onTaskbarToggled()217         public void onTaskbarToggled() {
218             if (!FeatureFlags.ENABLE_KEYBOARD_TASKBAR_TOGGLE.get()) return;
219             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
220                 TaskbarActivityContext activityContext =
221                         tis.mTaskbarManager.getCurrentActivityContext();
222 
223                 if (activityContext != null) {
224                     activityContext.toggleTaskbarStash();
225                 }
226             }));
227         }
228 
229         @BinderThread
onOverviewToggle()230         public void onOverviewToggle() {
231             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
232             executeForTouchInteractionService(tis -> {
233                 // If currently screen pinning, do not enter overview
234                 if (tis.mDeviceState.isScreenPinningActive()) {
235                     return;
236                 }
237                 TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
238                 tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
239             });
240         }
241 
242         @BinderThread
243         @Override
onOverviewShown(boolean triggeredFromAltTab)244         public void onOverviewShown(boolean triggeredFromAltTab) {
245             executeForTouchInteractionService(tis -> {
246                 if (triggeredFromAltTab) {
247                     TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
248                     tis.mOverviewCommandHelper.addCommand(
249                             OverviewCommandHelper.TYPE_KEYBOARD_INPUT);
250                 } else {
251                     tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW);
252                 }
253             });
254         }
255 
256         @BinderThread
257         @Override
onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)258         public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
259             executeForTouchInteractionService(tis -> {
260                 if (triggeredFromAltTab && !triggeredFromHomeKey) {
261                     // onOverviewShownFromAltTab hides the overview and ends at the target app
262                     tis.mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE);
263                 }
264             });
265         }
266 
267         @BinderThread
268         @Override
onAssistantAvailable(boolean available, boolean longPressHomeEnabled)269         public void onAssistantAvailable(boolean available, boolean longPressHomeEnabled) {
270             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
271                 tis.mDeviceState.setAssistantAvailable(available);
272                 tis.onAssistantVisibilityChanged();
273                 executeForTaskbarManager(taskbarManager -> taskbarManager
274                         .onLongPressHomeEnabled(longPressHomeEnabled));
275             }));
276         }
277 
278         @BinderThread
279         @Override
onAssistantVisibilityChanged(float visibility)280         public void onAssistantVisibilityChanged(float visibility) {
281             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
282                 tis.mDeviceState.setAssistantVisibility(visibility);
283                 tis.onAssistantVisibilityChanged();
284             }));
285         }
286 
287         /**
288          * Sent when the assistant has been invoked with the given type (defined in AssistManager)
289          * and should be shown. This method is used if SystemUiProxy#setAssistantOverridesRequested
290          * was previously called including this invocation type.
291          */
292         @Override
onAssistantOverrideInvoked(int invocationType)293         public void onAssistantOverrideInvoked(int invocationType) {
294             executeForTouchInteractionService(tis -> {
295                 if (!AssistUtils.newInstance(tis).tryStartAssistOverride(invocationType)) {
296                     Log.w(TAG, "Failed to invoke Assist override");
297                 }
298             });
299         }
300 
301         @Override
onNavigationBarSurface(SurfaceControl surface)302         public void onNavigationBarSurface(SurfaceControl surface) {
303             // TODO: implement
304         }
305 
306         @BinderThread
onSystemUiStateChanged(int stateFlags)307         public void onSystemUiStateChanged(int stateFlags) {
308             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
309                 int lastFlags = tis.mDeviceState.getSystemUiStateFlags();
310                 tis.mDeviceState.setSystemUiFlags(stateFlags);
311                 tis.onSystemUiFlagsChanged(lastFlags);
312             }));
313         }
314 
315         @BinderThread
onActiveNavBarRegionChanges(Region region)316         public void onActiveNavBarRegionChanges(Region region) {
317             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
318                     tis -> tis.mDeviceState.setDeferredGestureRegion(region)));
319         }
320 
321         @BinderThread
322         @Override
enterStageSplitFromRunningApp(boolean leftOrTop)323         public void enterStageSplitFromRunningApp(boolean leftOrTop) {
324             executeForTouchInteractionService(tis -> {
325                 StatefulActivity activity =
326                         tis.mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
327                 if (activity != null) {
328                     activity.enterStageSplitFromRunningApp(leftOrTop);
329                 }
330             });
331         }
332 
333         /**
334          * Preloads the Overview activity.
335          * <p>
336          * This method should only be used when the All Set page of the SUW is reached to safely
337          * preload the Launcher for the SUW first reveal.
338          */
preloadOverviewForSUWAllSet()339         public void preloadOverviewForSUWAllSet() {
340             executeForTouchInteractionService(tis -> tis.preloadOverview(false, true));
341         }
342 
343         @Override
onRotationProposal(int rotation, boolean isValid)344         public void onRotationProposal(int rotation, boolean isValid) {
345             executeForTaskbarManager(taskbarManager ->
346                     taskbarManager.onRotationProposal(rotation, isValid));
347         }
348 
349         @Override
disable(int displayId, int state1, int state2, boolean animate)350         public void disable(int displayId, int state1, int state2, boolean animate) {
351             executeForTaskbarManager(taskbarManager ->
352                     taskbarManager.disableNavBarElements(displayId, state1, state2, animate));
353         }
354 
355         @Override
onSystemBarAttributesChanged(int displayId, int behavior)356         public void onSystemBarAttributesChanged(int displayId, int behavior) {
357             executeForTaskbarManager(taskbarManager ->
358                     taskbarManager.onSystemBarAttributesChanged(displayId, behavior));
359         }
360 
361         @Override
onNavButtonsDarkIntensityChanged(float darkIntensity)362         public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
363             executeForTaskbarManager(taskbarManager ->
364                     taskbarManager.onNavButtonsDarkIntensityChanged(darkIntensity));
365         }
366 
executeForTouchInteractionService( @onNull Consumer<TouchInteractionService> tisConsumer)367         private void executeForTouchInteractionService(
368                 @NonNull Consumer<TouchInteractionService> tisConsumer) {
369             TouchInteractionService tis = mTis.get();
370             if (tis == null) return;
371             tisConsumer.accept(tis);
372         }
373 
executeForTaskbarManager( @onNull Consumer<TaskbarManager> taskbarManagerConsumer)374         private void executeForTaskbarManager(
375                 @NonNull Consumer<TaskbarManager> taskbarManagerConsumer) {
376             MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> {
377                 TaskbarManager taskbarManager = tis.mTaskbarManager;
378                 if (taskbarManager == null) return;
379                 taskbarManagerConsumer.accept(taskbarManager);
380             }));
381         }
382 
383         /**
384          * Returns the {@link TaskbarManager}.
385          * <p>
386          * Returns {@code null} if TouchInteractionService is not connected
387          */
388         @Nullable
getTaskbarManager()389         public TaskbarManager getTaskbarManager() {
390             TouchInteractionService tis = mTis.get();
391             if (tis == null) return null;
392             return tis.mTaskbarManager;
393         }
394 
395         /**
396          * Returns the {@link OverviewCommandHelper}.
397          * <p>
398          * Returns {@code null} if TouchInteractionService is not connected
399          */
400         @Nullable
getOverviewCommandHelper()401         public OverviewCommandHelper getOverviewCommandHelper() {
402             TouchInteractionService tis = mTis.get();
403             if (tis == null) return null;
404             return tis.mOverviewCommandHelper;
405         }
406 
407         /**
408          * Sets a proxy to bypass swipe up behavior
409          */
setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy)410         public void setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy) {
411             TouchInteractionService tis = mTis.get();
412             if (tis == null) return;
413             tis.mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null);
414         }
415 
416         /**
417          * Sets the task id where gestures should be blocked
418          */
setGestureBlockedTaskId(int taskId)419         public void setGestureBlockedTaskId(int taskId) {
420             TouchInteractionService tis = mTis.get();
421             if (tis == null) return;
422             tis.mDeviceState.setGestureBlockingTaskId(taskId);
423         }
424 
425         /** Sets a listener to be run on Overview Target updates. */
setOverviewTargetChangeListener(@ullable Runnable listener)426         public void setOverviewTargetChangeListener(@Nullable Runnable listener) {
427             mOnOverviewTargetChangeListener = listener;
428         }
429 
onOverviewTargetChange()430         protected void onOverviewTargetChange() {
431             if (mOnOverviewTargetChangeListener != null) {
432                 mOnOverviewTargetChangeListener.run();
433                 mOnOverviewTargetChangeListener = null;
434             }
435         }
436     }
437 
438     private static boolean sConnected = false;
439     private static boolean sIsInitialized = false;
440     private RotationTouchHelper mRotationTouchHelper;
441 
isConnected()442     public static boolean isConnected() {
443         return sConnected;
444     }
445 
isInitialized()446     public static boolean isInitialized() {
447         return sIsInitialized;
448     }
449 
450     private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
451             this::createLauncherSwipeHandler;
452     private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
453             this::createFallbackSwipeHandler;
454 
455     private ActivityManagerWrapper mAM;
456     private OverviewCommandHelper mOverviewCommandHelper;
457     private OverviewComponentObserver mOverviewComponentObserver;
458     private InputConsumerController mInputConsumer;
459     private RecentsAnimationDeviceState mDeviceState;
460     private TaskAnimationManager mTaskAnimationManager;
461 
462     private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
463     private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP;
464     private Choreographer mMainChoreographer;
465     private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer;
466     private GestureState mGestureState = DEFAULT_STATE;
467 
468     private InputMonitorCompat mInputMonitorCompat;
469     private InputEventReceiver mInputEventReceiver;
470 
471     private TaskbarManager mTaskbarManager;
472     private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
473 
474     @Override
onCreate()475     public void onCreate() {
476         super.onCreate();
477         // Initialize anything here that is needed in direct boot mode.
478         // Everything else should be initialized in onUserUnlocked() below.
479         mMainChoreographer = Choreographer.getInstance();
480         mAM = ActivityManagerWrapper.getInstance();
481         mDeviceState = new RecentsAnimationDeviceState(this, true);
482         mTaskbarManager = new TaskbarManager(this);
483         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
484         BootAwarePreloader.start(this);
485 
486         // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
487         LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
488         LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
489         mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
490         sConnected = true;
491     }
492 
disposeEventHandlers(String reason)493     private void disposeEventHandlers(String reason) {
494         Log.d(TAG, "disposeEventHandlers: Reason: " + reason);
495         if (mInputEventReceiver != null) {
496             mInputEventReceiver.dispose();
497             mInputEventReceiver = null;
498         }
499         if (mInputMonitorCompat != null) {
500             mInputMonitorCompat.dispose();
501             mInputMonitorCompat = null;
502         }
503     }
504 
initInputMonitor(String reason)505     private void initInputMonitor(String reason) {
506         disposeEventHandlers("Initializing input monitor due to: " + reason);
507 
508         if (mDeviceState.isButtonNavMode() && !ENABLE_TRACKPAD_GESTURE.get()) {
509             return;
510         }
511 
512         mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId());
513         mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
514                 mMainChoreographer, this::onInputEvent);
515 
516         mRotationTouchHelper.updateGestureTouchRegions();
517     }
518 
519     /**
520      * Called when the navigation mode changes, guaranteed to be after the device state has updated.
521      */
onNavigationModeChanged()522     private void onNavigationModeChanged() {
523         initInputMonitor("onNavigationModeChanged()");
524         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
525     }
526 
527     @UiThread
onUserUnlocked()528     public void onUserUnlocked() {
529         mTaskAnimationManager = new TaskAnimationManager(this);
530         mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
531         mOverviewCommandHelper = new OverviewCommandHelper(this,
532                 mOverviewComponentObserver, mTaskAnimationManager);
533         mResetGestureInputConsumer = new ResetGestureInputConsumer(
534                 mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
535         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
536         mInputConsumer.registerInputConsumer();
537         onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
538         onAssistantVisibilityChanged();
539 
540         // Initialize the task tracker
541         TopTaskTracker.INSTANCE.get(this);
542 
543         // Temporarily disable model preload
544         // new ModelPreload().start(this);
545         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
546 
547         mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
548         onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
549     }
550 
getOverviewCommandHelper()551     public OverviewCommandHelper getOverviewCommandHelper() {
552         return mOverviewCommandHelper;
553     }
554 
resetHomeBounceSeenOnQuickstepEnabledFirstTime()555     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
556         if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
557             // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
558             // mode doesn't have gestures
559             return;
560         }
561 
562         // Reset home bounce seen on quick step enabled for first time
563         SharedPreferences sharedPrefs = LauncherPrefs.getPrefs(this);
564         if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) {
565             sharedPrefs.edit()
566                     .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
567                     .putBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN, false)
568                     .apply();
569         }
570     }
571 
onOverviewTargetChange(boolean isHomeAndOverviewSame)572     private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
573         AccessibilityManager am = getSystemService(AccessibilityManager.class);
574 
575         if (isHomeAndOverviewSame) {
576             am.registerSystemAction(createAllAppsAction(), GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
577         } else {
578             am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
579         }
580 
581         StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
582                 .getCreatedActivity();
583         if (newOverviewActivity != null) {
584             mTaskbarManager.setActivity(newOverviewActivity);
585         }
586         mTISBinder.onOverviewTargetChange();
587     }
588 
createAllAppsAction()589     private RemoteAction createAllAppsAction() {
590         final Intent homeIntent = new Intent(mOverviewComponentObserver.getHomeIntent())
591                 .setAction(INTENT_ACTION_ALL_APPS_TOGGLE);
592         final PendingIntent actionPendingIntent;
593 
594         if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
595             actionPendingIntent = new PendingIntent(new IIntentSender.Stub() {
596                 @Override
597                 public void send(int code, Intent intent, String resolvedType,
598                         IBinder allowlistToken, IIntentReceiver finishedReceiver,
599                         String requiredPermission, Bundle options) {
600                     MAIN_EXECUTOR.execute(() -> mTaskbarManager.toggleAllApps(homeIntent));
601                 }
602             });
603         } else {
604             actionPendingIntent = PendingIntent.getActivity(
605                     this,
606                     GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
607                     homeIntent,
608                     PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
609         }
610 
611         return new RemoteAction(
612                 Icon.createWithResource(this, R.drawable.ic_apps),
613                 getString(R.string.all_apps_label),
614                 getString(R.string.all_apps_label),
615                 actionPendingIntent);
616     }
617 
618     @UiThread
onSystemUiFlagsChanged(int lastSysUIFlags)619     private void onSystemUiFlagsChanged(int lastSysUIFlags) {
620         if (LockedUserState.get(this).isUserUnlocked()) {
621             int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
622             SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
623             mOverviewComponentObserver.onSystemUiStateChanged();
624             mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
625 
626             int isShadeExpandedFlag =
627                     SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
628             boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0;
629             boolean isExpanded = (systemUiStateFlags & isShadeExpandedFlag) != 0;
630             if (wasExpanded != isExpanded && isExpanded) {
631                 // End live tile when expanding the notification panel for the first time from
632                 // overview.
633                 mTaskAnimationManager.endLiveTile();
634             }
635         }
636     }
637 
638     @UiThread
onAssistantVisibilityChanged()639     private void onAssistantVisibilityChanged() {
640         if (LockedUserState.get(this).isUserUnlocked()) {
641             mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
642                     mDeviceState.getAssistantVisibility());
643         }
644     }
645 
646     @Override
onDestroy()647     public void onDestroy() {
648         Log.d(TAG, "Touch service destroyed: user=" + getUserId());
649         sIsInitialized = false;
650         if (LockedUserState.get(this).isUserUnlocked()) {
651             mInputConsumer.unregisterInputConsumer();
652             mOverviewComponentObserver.onDestroy();
653         }
654         disposeEventHandlers("TouchInteractionService onDestroy()");
655         mDeviceState.destroy();
656         SystemUiProxy.INSTANCE.get(this).clearProxy();
657 
658         getSystemService(AccessibilityManager.class)
659                 .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
660 
661         mTaskbarManager.destroy();
662         sConnected = false;
663         super.onDestroy();
664     }
665 
666     @Override
onBind(Intent intent)667     public IBinder onBind(Intent intent) {
668         Log.d(TAG, "Touch service connected: user=" + getUserId());
669         return mTISBinder;
670     }
671 
onInputEvent(InputEvent ev)672     private void onInputEvent(InputEvent ev) {
673         if (!(ev instanceof MotionEvent)) {
674             Log.e(TAG, "Unknown event " + ev);
675             return;
676         }
677         MotionEvent event = (MotionEvent) ev;
678 
679         TestLogging.recordMotionEvent(
680                 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
681 
682         if (!LockedUserState.get(this).isUserUnlocked() || (mDeviceState.isButtonNavMode()
683                 && !isTrackpadMotionEvent(event))) {
684             return;
685         }
686 
687         SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
688 
689         final int action = event.getActionMasked();
690         // Note this will create a new consumer every mouse click, as after ACTION_UP from the click
691         // an ACTION_HOVER_ENTER will fire as well.
692         boolean isHoverActionWithoutConsumer =
693                 event.isHoverEvent() && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0;
694         if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
695             mRotationTouchHelper.setOrientationTransformIfNeeded(event);
696 
697             if ((!mDeviceState.isOneHandedModeActive()
698                     && mRotationTouchHelper.isInSwipeUpTouchRegion(event))
699                     || isHoverActionWithoutConsumer) {
700                 // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
701                 // onConsumerInactive and wipe the previous gesture state
702                 GestureState prevGestureState = new GestureState(mGestureState);
703                 GestureState newGestureState = createGestureState(mGestureState,
704                         getTrackpadGestureType(event));
705                 newGestureState.setSwipeUpStartTimeMs(SystemClock.uptimeMillis());
706                 mConsumer.onConsumerAboutToBeSwitched();
707                 mGestureState = newGestureState;
708                 mConsumer = newConsumer(prevGestureState, mGestureState, event);
709                 mUncheckedConsumer = mConsumer;
710             } else if (LockedUserState.get(this).isUserUnlocked()
711                     && (mDeviceState.isFullyGesturalNavMode() || isTrackpadMultiFingerSwipe(event))
712                     && mDeviceState.canTriggerAssistantAction(event)) {
713                 mGestureState = createGestureState(mGestureState,
714                         getTrackpadGestureType(event));
715                 // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
716                 // should not interrupt it. QuickSwitch assumes that interruption can only
717                 // happen if the next gesture is also quick switch.
718                 mUncheckedConsumer = tryCreateAssistantInputConsumer(mGestureState, event);
719             } else if (mDeviceState.canTriggerOneHandedAction(event)) {
720                 // Consume gesture event for triggering one handed feature.
721                 mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
722                         InputConsumer.NO_OP, mInputMonitorCompat);
723             } else {
724                 mUncheckedConsumer = InputConsumer.NO_OP;
725             }
726         } else {
727             // Other events
728             if (mUncheckedConsumer != InputConsumer.NO_OP) {
729                 // Only transform the event if we are handling it in a proper consumer
730                 mRotationTouchHelper.setOrientationTransformIfNeeded(event);
731             }
732         }
733 
734         if (mUncheckedConsumer != InputConsumer.NO_OP) {
735             switch (event.getActionMasked()) {
736                 case ACTION_DOWN:
737                     // fall through
738                 case ACTION_UP:
739                     ActiveGestureLog.INSTANCE.addLog(
740                             /* event= */ "onMotionEvent(" + (int) event.getRawX() + ", "
741                                     + (int) event.getRawY() + "): "
742                                     + MotionEvent.actionToString(event.getActionMasked()) + ", "
743                                     + MotionEvent.classificationToString(event.getClassification()),
744                             /* gestureEvent= */ event.getActionMasked() == ACTION_DOWN
745                                     ? MOTION_DOWN
746                                     : MOTION_UP);
747                     break;
748                 case ACTION_MOVE:
749                     ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
750                             + MotionEvent.actionToString(event.getActionMasked()) + ","
751                             + MotionEvent.classificationToString(event.getClassification())
752                             + ", pointerCount: " + event.getPointerCount(), MOTION_MOVE);
753                     break;
754                 default: {
755                     ActiveGestureLog.INSTANCE.addLog("onMotionEvent: "
756                             + MotionEvent.actionToString(event.getActionMasked()) + ","
757                             + MotionEvent.classificationToString(event.getClassification()));
758                 }
759             }
760         }
761 
762         boolean cancelGesture = mGestureState.getActivityInterface() != null
763                 && mGestureState.getActivityInterface().shouldCancelCurrentGesture();
764         boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture)
765                 && mConsumer != null
766                 && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
767         if (cancelGesture) {
768             event.setAction(ACTION_CANCEL);
769         }
770 
771         if (mGestureState.isTrackpadGesture() && (action == ACTION_POINTER_DOWN
772                 || action == ACTION_POINTER_UP)) {
773             // Skip ACTION_POINTER_DOWN and ACTION_POINTER_UP events from trackpad.
774         } else if (isCursorHoverEvent(event)) {
775             mUncheckedConsumer.onHoverEvent(event);
776         } else {
777             mUncheckedConsumer.onMotionEvent(event);
778         }
779 
780         if (cleanUpConsumer) {
781             reset();
782         }
783         traceToken.close();
784     }
785 
786     // Talkback generates hover events on touch, which we do not want to consume.
isCursorHoverEvent(MotionEvent event)787     private boolean isCursorHoverEvent(MotionEvent event) {
788         return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE;
789     }
790 
tryCreateAssistantInputConsumer( GestureState gestureState, MotionEvent motionEvent)791     private InputConsumer tryCreateAssistantInputConsumer(
792             GestureState gestureState, MotionEvent motionEvent) {
793         return tryCreateAssistantInputConsumer(
794                 InputConsumer.NO_OP, gestureState, motionEvent, CompoundString.NO_OP);
795     }
796 
tryCreateAssistantInputConsumer( InputConsumer base, GestureState gestureState, MotionEvent motionEvent, CompoundString reasonString)797     private InputConsumer tryCreateAssistantInputConsumer(
798             InputConsumer base,
799             GestureState gestureState,
800             MotionEvent motionEvent,
801             CompoundString reasonString) {
802         if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) {
803             reasonString.append(SUBSTRING_PREFIX)
804                     .append("is gesture-blocked task, using base input consumer");
805             return base;
806         } else {
807             reasonString.append(SUBSTRING_PREFIX).append("using AssistantInputConsumer");
808             return new AssistantInputConsumer(
809                     this, gestureState, base, mInputMonitorCompat, mDeviceState, motionEvent);
810         }
811     }
812 
createGestureState(GestureState previousGestureState, GestureState.TrackpadGestureType trackpadGestureType)813     public GestureState createGestureState(GestureState previousGestureState,
814             GestureState.TrackpadGestureType trackpadGestureType) {
815         final GestureState gestureState;
816         TopTaskTracker.CachedTaskInfo taskInfo;
817         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
818             gestureState = new GestureState(mOverviewComponentObserver,
819                     ActiveGestureLog.INSTANCE.getLogId());
820             taskInfo = previousGestureState.getRunningTask();
821             gestureState.updateRunningTask(taskInfo);
822             gestureState.updateLastStartedTaskIds(previousGestureState.getLastStartedTaskIds());
823             gestureState.updatePreviouslyAppearedTaskIds(
824                     previousGestureState.getPreviouslyAppearedTaskIds());
825         } else {
826             gestureState = new GestureState(mOverviewComponentObserver,
827                     ActiveGestureLog.INSTANCE.incrementLogId());
828             taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
829             gestureState.updateRunningTask(taskInfo);
830         }
831         gestureState.setTrackpadGestureType(trackpadGestureType);
832 
833         // Log initial state for the gesture.
834         ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=")
835                 .append(taskInfo == null ? "no running task" : taskInfo.getPackageName()));
836         ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=")
837                 .append(mDeviceState.getSystemUiStateString()));
838         return gestureState;
839     }
840 
newConsumer( GestureState previousGestureState, GestureState newGestureState, MotionEvent event)841     private InputConsumer newConsumer(
842             GestureState previousGestureState, GestureState newGestureState, MotionEvent event) {
843         AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
844         if (progressProxy != null) {
845             InputConsumer consumer = new ProgressDelegateInputConsumer(
846                     this, mTaskAnimationManager, mGestureState, mInputMonitorCompat, progressProxy);
847 
848             logInputConsumerSelectionReason(consumer, newCompoundString(
849                     "mSwipeUpProxyProvider has been set, using ProgressDelegateInputConsumer"));
850 
851             return consumer;
852         }
853 
854         boolean canStartSystemGesture =
855                 mGestureState.isTrackpadGesture() ? mDeviceState.canStartTrackpadGesture()
856                         : mDeviceState.canStartSystemGesture();
857 
858         if (!LockedUserState.get(this).isUserUnlocked()) {
859             CompoundString reasonString = newCompoundString("device locked");
860             InputConsumer consumer;
861             if (canStartSystemGesture) {
862                 // This handles apps launched in direct boot mode (e.g. dialer) as well as apps
863                 // launched while device is locked even after exiting direct boot mode (e.g. camera).
864                 consumer = createDeviceLockedInputConsumer(
865                         newGestureState, reasonString.append(SUBSTRING_PREFIX)
866                                 .append("can start system gesture"));
867             } else {
868                 consumer = getDefaultInputConsumer(
869                         reasonString.append(SUBSTRING_PREFIX)
870                                 .append("cannot start system gesture"));
871             }
872             logInputConsumerSelectionReason(consumer, reasonString);
873             return consumer;
874         }
875 
876         CompoundString reasonString;
877         InputConsumer base;
878         // When there is an existing recents animation running, bypass systemState check as this is
879         // a followup gesture and the first gesture started in a valid system state.
880         if (canStartSystemGesture || previousGestureState.isRecentsAnimationRunning()) {
881             reasonString = newCompoundString(canStartSystemGesture
882                     ? "can start system gesture" : "recents animation was running")
883                     .append(", trying to use base consumer");
884             base = newBaseConsumer(previousGestureState, newGestureState, event, reasonString);
885         } else {
886             reasonString = newCompoundString(
887                     "cannot start system gesture and recents animation was not running")
888                     .append(", trying to use default input consumer");
889             base = getDefaultInputConsumer(reasonString);
890         }
891         if (mDeviceState.isGesturalNavMode() || newGestureState.isTrackpadGesture()) {
892             handleOrientationSetup(base);
893         }
894         if (mDeviceState.isFullyGesturalNavMode() || newGestureState.isTrackpadGesture()) {
895             String reasonPrefix = "device is in gesture navigation mode or 3-button mode with a"
896                     + " trackpad gesture";
897             if (mDeviceState.canTriggerAssistantAction(event)) {
898                 reasonString.append(NEWLINE_PREFIX)
899                         .append(reasonPrefix)
900                         .append(SUBSTRING_PREFIX)
901                         .append("gesture can trigger the assistant")
902                         .append(", trying to use assistant input consumer");
903                 base = tryCreateAssistantInputConsumer(base, newGestureState, event, reasonString);
904             }
905 
906             // If Taskbar is present, we listen for swipe or cursor hover events to unstash it.
907             TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
908             if (tac != null && !(base instanceof AssistantInputConsumer)) {
909                 // Present always on large screen or on small screen w/ flag
910                 DeviceProfile dp = tac.getDeviceProfile();
911                 boolean useTaskbarConsumer = dp.isTaskbarPresent && !TaskbarManager.isPhoneMode(dp)
912                         && !tac.isInStashedLauncherState();
913                 if (canStartSystemGesture && useTaskbarConsumer) {
914                     reasonString.append(NEWLINE_PREFIX)
915                             .append(reasonPrefix)
916                             .append(SUBSTRING_PREFIX)
917                             .append("TaskbarActivityContext != null, "
918                                     + "using TaskbarUnstashInputConsumer");
919                     base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac,
920                             mOverviewCommandHelper);
921                 }
922             } else if (canStartSystemGesture && FeatureFlags.ENABLE_LONG_PRESS_NAV_HANDLE.get()
923                     && !previousGestureState.isRecentsAnimationRunning()) {
924                 reasonString.append(NEWLINE_PREFIX)
925                         .append(reasonPrefix)
926                         .append(SUBSTRING_PREFIX)
927                         .append("Long press nav handle enabled, "
928                                 + "using NavHandleLongPressInputConsumer");
929                 base = new NavHandleLongPressInputConsumer(this, base, mInputMonitorCompat);
930             }
931 
932             if (mDeviceState.isBubblesExpanded()) {
933                 reasonString = newCompoundString(reasonPrefix)
934                         .append(SUBSTRING_PREFIX)
935                         .append("bubbles expanded, trying to use default input consumer");
936                 // Bubbles can handle home gesture itself.
937                 base = getDefaultInputConsumer(reasonString);
938             }
939 
940             if (mDeviceState.isSystemUiDialogShowing()) {
941                 reasonString = newCompoundString(reasonPrefix)
942                         .append(SUBSTRING_PREFIX)
943                         .append("system dialog is showing, using SysUiOverlayInputConsumer");
944                 base = new SysUiOverlayInputConsumer(
945                         getBaseContext(), mDeviceState, mInputMonitorCompat);
946             }
947 
948             if (ENABLE_TRACKPAD_GESTURE.get() && mGestureState.isTrackpadGesture()
949                     && canStartSystemGesture && !previousGestureState.isRecentsAnimationRunning()) {
950                 reasonString = newCompoundString(reasonPrefix)
951                         .append(SUBSTRING_PREFIX)
952                         .append("Trackpad 3-finger gesture, using TrackpadStatusBarInputConsumer");
953                 base = new TrackpadStatusBarInputConsumer(getBaseContext(), base,
954                         mInputMonitorCompat);
955             }
956 
957             if (mDeviceState.isScreenPinningActive()) {
958                 reasonString = newCompoundString(reasonPrefix)
959                         .append(SUBSTRING_PREFIX)
960                         .append("screen pinning is active, using ScreenPinnedInputConsumer");
961                 // Note: we only allow accessibility to wrap this, and it replaces the previous
962                 // base input consumer (which should be NO_OP anyway since topTaskLocked == true).
963                 base = new ScreenPinnedInputConsumer(this, newGestureState);
964             }
965 
966             if (mDeviceState.canTriggerOneHandedAction(event)) {
967                 reasonString.append(NEWLINE_PREFIX)
968                         .append(reasonPrefix)
969                         .append(SUBSTRING_PREFIX)
970                         .append("gesture can trigger one handed mode")
971                         .append(", using OneHandedModeInputConsumer");
972                 base = new OneHandedModeInputConsumer(
973                         this, mDeviceState, base, mInputMonitorCompat);
974             }
975 
976             if (mDeviceState.isAccessibilityMenuAvailable()) {
977                 reasonString.append(NEWLINE_PREFIX)
978                         .append(reasonPrefix)
979                         .append(SUBSTRING_PREFIX)
980                         .append("accessibility menu is available")
981                         .append(", using AccessibilityInputConsumer");
982                 base = new AccessibilityInputConsumer(
983                         this, mDeviceState, mGestureState, base, mInputMonitorCompat);
984             }
985         } else {
986             String reasonPrefix = "device is not in gesture navigation mode";
987             if (mDeviceState.isScreenPinningActive()) {
988                 reasonString = newCompoundString(reasonPrefix)
989                         .append(SUBSTRING_PREFIX)
990                         .append("screen pinning is active, trying to use default input consumer");
991                 base = getDefaultInputConsumer(reasonString);
992             }
993 
994             if (mDeviceState.canTriggerOneHandedAction(event)) {
995                 reasonString.append(NEWLINE_PREFIX)
996                         .append(reasonPrefix)
997                         .append(SUBSTRING_PREFIX)
998                         .append("gesture can trigger one handed mode")
999                         .append(", using OneHandedModeInputConsumer");
1000                 base = new OneHandedModeInputConsumer(
1001                         this, mDeviceState, base, mInputMonitorCompat);
1002             }
1003         }
1004         logInputConsumerSelectionReason(base, reasonString);
1005         return base;
1006     }
1007 
newCompoundString(String substring)1008     private CompoundString newCompoundString(String substring) {
1009         return new CompoundString(NEWLINE_PREFIX).append(substring);
1010     }
1011 
logInputConsumerSelectionReason( InputConsumer consumer, CompoundString reasonString)1012     private void logInputConsumerSelectionReason(
1013             InputConsumer consumer, CompoundString reasonString) {
1014         ActiveGestureLog.INSTANCE.addLog(new CompoundString("setInputConsumer: ")
1015                 .append(consumer.getName())
1016                 .append(". reason(s):")
1017                 .append(reasonString));
1018         if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) {
1019             ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER);
1020         }
1021     }
1022 
handleOrientationSetup(InputConsumer baseInputConsumer)1023     private void handleOrientationSetup(InputConsumer baseInputConsumer) {
1024         baseInputConsumer.notifyOrientationSetup();
1025     }
1026 
newBaseConsumer( GestureState previousGestureState, GestureState gestureState, MotionEvent event, CompoundString reasonString)1027     private InputConsumer newBaseConsumer(
1028             GestureState previousGestureState,
1029             GestureState gestureState,
1030             MotionEvent event,
1031             CompoundString reasonString) {
1032         if (mDeviceState.isKeyguardShowingOccluded()) {
1033             // This handles apps showing over the lockscreen (e.g. camera)
1034             return createDeviceLockedInputConsumer(
1035                     gestureState,
1036                     reasonString.append(SUBSTRING_PREFIX)
1037                             .append("keyguard is showing occluded")
1038                             .append(", trying to use device locked input consumer"));
1039         }
1040 
1041         reasonString.append(SUBSTRING_PREFIX).append("keyguard is not showing occluded");
1042 
1043         // Use overview input consumer for sharesheets on top of home.
1044         boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted()
1045                 && gestureState.getRunningTask() != null
1046                 && gestureState.getRunningTask().isRootChooseActivity();
1047 
1048         // In the case where we are in an excluded, translucent overlay, ignore it and treat the
1049         // running activity as the task behind the overlay.
1050         TopTaskTracker.CachedTaskInfo otherVisibleTask = gestureState.getRunningTask() == null
1051                 ? null
1052                 : gestureState.getRunningTask().otherVisibleTaskThisIsExcludedOver();
1053         if (otherVisibleTask != null) {
1054             ActiveGestureLog.INSTANCE.addLog(new CompoundString("Changing active task to ")
1055                     .append(otherVisibleTask.getPackageName())
1056                     .append(" because the previous task running on top of this one (")
1057                     .append(gestureState.getRunningTask().getPackageName())
1058                     .append(") was excluded from recents"));
1059             gestureState.updateRunningTask(otherVisibleTask);
1060         }
1061 
1062         boolean previousGestureAnimatedToLauncher =
1063                 previousGestureState.isRunningAnimationToLauncher();
1064         // with shell-transitions, home is resumed during recents animation, so
1065         // explicitly check against recents animation too.
1066         boolean launcherResumedThroughShellTransition =
1067                 gestureState.getActivityInterface().isResumed()
1068                         && !previousGestureState.isRecentsAnimationRunning();
1069         if (gestureState.getActivityInterface().isInLiveTileMode()) {
1070             return createOverviewInputConsumer(
1071                     previousGestureState,
1072                     gestureState,
1073                     event,
1074                     forceOverviewInputConsumer,
1075                     reasonString.append(SUBSTRING_PREFIX)
1076                             .append("is in live tile mode, trying to use overview input consumer"));
1077         } else if (gestureState.getRunningTask() == null) {
1078             return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
1079                     .append("running task == null"));
1080         } else if (previousGestureAnimatedToLauncher
1081                 || launcherResumedThroughShellTransition
1082                 || forceOverviewInputConsumer) {
1083             return createOverviewInputConsumer(
1084                     previousGestureState,
1085                     gestureState,
1086                     event,
1087                     forceOverviewInputConsumer,
1088                     reasonString.append(SUBSTRING_PREFIX)
1089                             .append(previousGestureAnimatedToLauncher
1090                                     ? "previous gesture animated to launcher"
1091                                     : (launcherResumedThroughShellTransition
1092                                             ? "launcher resumed through a shell transition"
1093                                             : "forceOverviewInputConsumer == true"))
1094                             .append(", trying to use overview input consumer"));
1095         } else if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) {
1096             return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
1097                     .append("is gesture-blocked task, trying to use default input consumer"));
1098         } else {
1099             reasonString.append(SUBSTRING_PREFIX)
1100                     .append("using OtherActivityInputConsumer");
1101             return createOtherActivityInputConsumer(gestureState, event);
1102         }
1103     }
1104 
getSwipeUpHandlerFactory()1105     public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
1106         return !mOverviewComponentObserver.isHomeAndOverviewSame()
1107                 ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory;
1108     }
1109 
createOtherActivityInputConsumer(GestureState gestureState, MotionEvent event)1110     private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
1111             MotionEvent event) {
1112 
1113         final AbsSwipeUpHandler.Factory factory = getSwipeUpHandlerFactory();
1114         final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame()
1115                 || gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event);
1116         final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
1117         return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
1118                 gestureState, shouldDefer, this::onConsumerInactive,
1119                 mInputMonitorCompat, mInputEventReceiver, disableHorizontalSwipe, factory);
1120     }
1121 
createDeviceLockedInputConsumer( GestureState gestureState, CompoundString reasonString)1122     private InputConsumer createDeviceLockedInputConsumer(
1123             GestureState gestureState, CompoundString reasonString) {
1124         if ((mDeviceState.isFullyGesturalNavMode() || gestureState.isTrackpadGesture())
1125                 && gestureState.getRunningTask() != null) {
1126             reasonString.append(SUBSTRING_PREFIX)
1127                     .append("device is in gesture nav mode or 3-button mode with a trackpad gesture"
1128                             + "and running task != null")
1129                     .append(", using DeviceLockedInputConsumer");
1130             return new DeviceLockedInputConsumer(
1131                     this, mDeviceState, mTaskAnimationManager, gestureState, mInputMonitorCompat);
1132         } else {
1133             return getDefaultInputConsumer(reasonString
1134                     .append(SUBSTRING_PREFIX)
1135                     .append((mDeviceState.isFullyGesturalNavMode()
1136                                     || gestureState.isTrackpadGesture())
1137                             ? "running task == null"
1138                             : "device is not in gesture nav mode and it's not a trackpad gesture")
1139                     .append(", trying to use default input consumer"));
1140         }
1141     }
1142 
createOverviewInputConsumer( GestureState previousGestureState, GestureState gestureState, MotionEvent event, boolean forceOverviewInputConsumer, CompoundString reasonString)1143     public InputConsumer createOverviewInputConsumer(
1144             GestureState previousGestureState,
1145             GestureState gestureState,
1146             MotionEvent event,
1147             boolean forceOverviewInputConsumer,
1148             CompoundString reasonString) {
1149         StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
1150         if (activity == null) {
1151             return getDefaultInputConsumer(
1152                     reasonString.append(SUBSTRING_PREFIX)
1153                             .append("activity == null, trying to use default input consumer"));
1154         }
1155 
1156         boolean hasWindowFocus = activity.getRootView().hasWindowFocus();
1157         boolean isPreviousGestureAnimatingToLauncher =
1158                 previousGestureState.isRunningAnimationToLauncher();
1159         boolean isInLiveTileMode = gestureState.getActivityInterface().isInLiveTileMode();
1160         reasonString.append(SUBSTRING_PREFIX)
1161                 .append(hasWindowFocus
1162                         ? "activity has window focus"
1163                         : (isPreviousGestureAnimatingToLauncher
1164                                 ? "previous gesture is still animating to launcher"
1165                                 : isInLiveTileMode
1166                                         ? "device is in live mode"
1167                                         : "all overview focus conditions failed"));
1168         if (hasWindowFocus
1169                 || isPreviousGestureAnimatingToLauncher
1170                 || isInLiveTileMode) {
1171             reasonString.append(SUBSTRING_PREFIX)
1172                     .append("overview should have focus, using OverviewInputConsumer");
1173             return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
1174                     false /* startingInActivityBounds */);
1175         } else {
1176             reasonString.append(SUBSTRING_PREFIX).append(
1177                     "overview shouldn't have focus, using OverviewWithoutFocusInputConsumer");
1178             final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
1179             return new OverviewWithoutFocusInputConsumer(activity, mDeviceState, gestureState,
1180                     mInputMonitorCompat, disableHorizontalSwipe);
1181         }
1182     }
1183 
1184     /**
1185      * To be called by the consumer when it's no longer active. This can be called by any consumer
1186      * in the hierarchy at any point during the gesture (ie. if a delegate consumer starts
1187      * intercepting touches, the base consumer can try to call this).
1188      */
onConsumerInactive(InputConsumer caller)1189     private void onConsumerInactive(InputConsumer caller) {
1190         if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) {
1191             reset();
1192         }
1193     }
1194 
reset()1195     private void reset() {
1196         mConsumer = mUncheckedConsumer = getDefaultInputConsumer();
1197         mGestureState = DEFAULT_STATE;
1198         // By default, use batching of the input events, but check receiver before using in the rare
1199         // case that the monitor was disposed before the swipe settled
1200         if (mInputEventReceiver != null) {
1201             mInputEventReceiver.setBatchingEnabled(true);
1202         }
1203     }
1204 
getDefaultInputConsumer()1205     private @NonNull InputConsumer getDefaultInputConsumer() {
1206         return getDefaultInputConsumer(CompoundString.NO_OP);
1207     }
1208 
1209     /**
1210      * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
1211      */
getDefaultInputConsumer(@onNull CompoundString reasonString)1212     private @NonNull InputConsumer getDefaultInputConsumer(@NonNull CompoundString reasonString) {
1213         if (mResetGestureInputConsumer != null) {
1214             reasonString.append(SUBSTRING_PREFIX).append(
1215                     "mResetGestureInputConsumer initialized, using ResetGestureInputConsumer");
1216             return mResetGestureInputConsumer;
1217         } else {
1218             reasonString.append(SUBSTRING_PREFIX).append(
1219                     "mResetGestureInputConsumer not initialized, using no-op input consumer");
1220             // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
1221             // NO_OP until then (we never want these to be null).
1222             return InputConsumer.NO_OP;
1223         }
1224     }
1225 
preloadOverview(boolean fromInit)1226     private void preloadOverview(boolean fromInit) {
1227         Trace.beginSection("preloadOverview(fromInit=" + fromInit + ")");
1228         preloadOverview(fromInit, false);
1229         Trace.endSection();
1230     }
1231 
preloadOverview(boolean fromInit, boolean forSUWAllSet)1232     private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
1233         if (!LockedUserState.get(this).isUserUnlocked()) {
1234             return;
1235         }
1236 
1237         if (mDeviceState.isButtonNavMode() && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
1238             // Prevent the overview from being started before the real home on first boot.
1239             return;
1240         }
1241 
1242         if ((RestoreDbTask.isPending(this) && !forSUWAllSet)
1243                 || !mDeviceState.isUserSetupComplete()) {
1244             // Preloading while a restore is pending may cause launcher to start the restore
1245             // too early.
1246             return;
1247         }
1248 
1249         final BaseActivityInterface activityInterface =
1250                 mOverviewComponentObserver.getActivityInterface();
1251         final Intent overviewIntent = new Intent(
1252                 mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
1253         if (activityInterface.getCreatedActivity() != null && fromInit) {
1254             // The activity has been created before the initialization of overview service. It is
1255             // usually happens when booting or launcher is the top activity, so we should already
1256             // have the latest state.
1257             return;
1258         }
1259 
1260         // TODO(b/258022658): Remove temporary logging.
1261         Log.i(TAG, "preloadOverview: forSUWAllSet=" + forSUWAllSet
1262                 + ", isHomeAndOverviewSame=" + mOverviewComponentObserver.isHomeAndOverviewSame());
1263 
1264         mTaskAnimationManager.preloadRecentsAnimation(overviewIntent);
1265     }
1266 
1267     @Override
onConfigurationChanged(Configuration newConfig)1268     public void onConfigurationChanged(Configuration newConfig) {
1269         if (!LockedUserState.get(this).isUserUnlocked()) {
1270             return;
1271         }
1272         final BaseActivityInterface activityInterface =
1273                 mOverviewComponentObserver.getActivityInterface();
1274         final BaseDraggingActivity activity = activityInterface.getCreatedActivity();
1275         if (activity == null || activity.isStarted()) {
1276             // We only care about the existing background activity.
1277             return;
1278         }
1279         Configuration oldConfig = activity.getResources().getConfiguration();
1280         boolean isFoldUnfold = isTablet(oldConfig) != isTablet(newConfig);
1281         if (!isFoldUnfold && mOverviewComponentObserver.canHandleConfigChanges(
1282                 activity.getComponentName(),
1283                 activity.getResources().getConfiguration().diff(newConfig))) {
1284             // Since navBar gestural height are different between portrait and landscape,
1285             // can handle orientation changes and refresh navigation gestural region through
1286             // onOneHandedModeChanged()
1287             int newGesturalHeight = ResourceUtils.getNavbarSize(
1288                     ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
1289                     getApplicationContext().getResources());
1290             mDeviceState.onOneHandedModeChanged(newGesturalHeight);
1291             return;
1292         }
1293 
1294         preloadOverview(false /* fromInit */);
1295     }
1296 
isTablet(Configuration config)1297     private static boolean isTablet(Configuration config) {
1298         return config.smallestScreenWidthDp >= MIN_TABLET_WIDTH;
1299     }
1300 
1301     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs)1302     protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
1303         // Dump everything
1304         FlagsFactory.dump(pw);
1305         if (LockedUserState.get(this).isUserUnlocked()) {
1306             PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
1307         }
1308         mDeviceState.dump(pw);
1309         if (mOverviewComponentObserver != null) {
1310             mOverviewComponentObserver.dump(pw);
1311         }
1312         if (mOverviewCommandHelper != null) {
1313             mOverviewCommandHelper.dump(pw);
1314         }
1315         if (mGestureState != null) {
1316             mGestureState.dump(pw);
1317         }
1318         pw.println("Input state:");
1319         pw.println("  mInputMonitorCompat=" + mInputMonitorCompat);
1320         pw.println("  mInputEventReceiver=" + mInputEventReceiver);
1321         DisplayController.INSTANCE.get(this).dump(pw);
1322         pw.println("TouchState:");
1323         BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
1324                 : mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
1325         boolean resumed = mOverviewComponentObserver != null
1326                 && mOverviewComponentObserver.getActivityInterface().isResumed();
1327         pw.println("  createdOverviewActivity=" + createdOverviewActivity);
1328         pw.println("  resumed=" + resumed);
1329         pw.println("  mConsumer=" + mConsumer.getName());
1330         ActiveGestureLog.INSTANCE.dump("", pw);
1331         RecentsModel.INSTANCE.get(this).dump("", pw);
1332         if (createdOverviewActivity != null) {
1333             createdOverviewActivity.getDeviceProfile().dump(this, "", pw);
1334         }
1335         mTaskbarManager.dumpLogs("", pw);
1336         pw.println("AssistStateManager:");
1337         AssistStateManager.INSTANCE.get(this).dump("  ", pw);
1338     }
1339 
createLauncherSwipeHandler( GestureState gestureState, long touchTimeMs)1340     private AbsSwipeUpHandler createLauncherSwipeHandler(
1341             GestureState gestureState, long touchTimeMs) {
1342         return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
1343                 gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
1344                 mInputConsumer);
1345     }
1346 
createFallbackSwipeHandler( GestureState gestureState, long touchTimeMs)1347     private AbsSwipeUpHandler createFallbackSwipeHandler(
1348             GestureState gestureState, long touchTimeMs) {
1349         return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
1350                 gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
1351                 mInputConsumer);
1352     }
1353 }
1354