• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.systemui.navigationbar.gestural;
17 
18 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
19 
20 import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
21 
22 import android.annotation.NonNull;
23 import android.app.ActivityManager;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.res.Configuration;
29 import android.content.res.Resources;
30 import android.graphics.PixelFormat;
31 import android.graphics.Point;
32 import android.graphics.PointF;
33 import android.graphics.Rect;
34 import android.graphics.Region;
35 import android.hardware.input.InputManager;
36 import android.os.Looper;
37 import android.os.RemoteException;
38 import android.os.SystemClock;
39 import android.os.SystemProperties;
40 import android.os.Trace;
41 import android.provider.DeviceConfig;
42 import android.util.DisplayMetrics;
43 import android.util.Log;
44 import android.util.TypedValue;
45 import android.view.Choreographer;
46 import android.view.ISystemGestureExclusionListener;
47 import android.view.IWindowManager;
48 import android.view.InputDevice;
49 import android.view.InputEvent;
50 import android.view.InputMonitor;
51 import android.view.KeyCharacterMap;
52 import android.view.KeyEvent;
53 import android.view.MotionEvent;
54 import android.view.Surface;
55 import android.view.ViewConfiguration;
56 import android.view.WindowManager;
57 import android.window.BackEvent;
58 
59 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
60 import com.android.internal.policy.GestureNavigationSettingsObserver;
61 import com.android.systemui.R;
62 import com.android.systemui.dagger.qualifiers.Background;
63 import com.android.systemui.dagger.qualifiers.Main;
64 import com.android.systemui.flags.FeatureFlags;
65 import com.android.systemui.flags.Flags;
66 import com.android.systemui.model.SysUiState;
67 import com.android.systemui.navigationbar.NavigationBarView;
68 import com.android.systemui.navigationbar.NavigationModeController;
69 import com.android.systemui.plugins.FalsingManager;
70 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
71 import com.android.systemui.plugins.PluginListener;
72 import com.android.systemui.plugins.PluginManager;
73 import com.android.systemui.recents.OverviewProxyService;
74 import com.android.systemui.settings.UserTracker;
75 import com.android.systemui.shared.system.ActivityManagerWrapper;
76 import com.android.systemui.shared.system.InputChannelCompat;
77 import com.android.systemui.shared.system.QuickStepContract;
78 import com.android.systemui.shared.system.SysUiStatsLog;
79 import com.android.systemui.shared.system.TaskStackChangeListener;
80 import com.android.systemui.shared.system.TaskStackChangeListeners;
81 import com.android.systemui.shared.tracing.ProtoTraceable;
82 import com.android.systemui.tracing.ProtoTracer;
83 import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
84 import com.android.systemui.tracing.nano.SystemUiTraceProto;
85 import com.android.systemui.util.Assert;
86 import com.android.wm.shell.back.BackAnimation;
87 import com.android.wm.shell.pip.Pip;
88 
89 import java.io.PrintWriter;
90 import java.util.ArrayDeque;
91 import java.util.ArrayList;
92 import java.util.List;
93 import java.util.Map;
94 import java.util.Optional;
95 import java.util.concurrent.Executor;
96 import java.util.function.Consumer;
97 
98 import javax.inject.Inject;
99 import javax.inject.Provider;
100 
101 /**
102  * Utility class to handle edge swipes for back gesture
103  */
104 public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin>,
105         ProtoTraceable<SystemUiTraceProto> {
106 
107     private static final String TAG = "EdgeBackGestureHandler";
108     private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
109             "gestures.back_timeout", 250);
110 
111     private static final int MAX_NUM_LOGGED_PREDICTIONS = 10;
112     private static final int MAX_NUM_LOGGED_GESTURES = 10;
113 
114     static final boolean DEBUG_MISSING_GESTURE = false;
115     public static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
116 
117     private ISystemGestureExclusionListener mGestureExclusionListener =
118             new ISystemGestureExclusionListener.Stub() {
119                 @Override
120                 public void onSystemGestureExclusionChanged(int displayId,
121                         Region systemGestureExclusion, Region unrestrictedOrNull) {
122                     if (displayId == mDisplayId) {
123                         mMainExecutor.execute(() -> {
124                             mExcludeRegion.set(systemGestureExclusion);
125                             mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null
126                                     ? unrestrictedOrNull : systemGestureExclusion);
127                         });
128                     }
129                 }
130             };
131 
132     private OverviewProxyService.OverviewProxyListener mQuickSwitchListener =
133             new OverviewProxyService.OverviewProxyListener() {
134                 @Override
135                 public void onPrioritizedRotation(@Surface.Rotation int rotation) {
136                     mStartingQuickstepRotation = rotation;
137                     updateDisabledForQuickstep(mLastReportedConfig);
138                 }
139             };
140 
141     private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
142         @Override
143         public void onTaskStackChanged() {
144             mGestureBlockingActivityRunning = isGestureBlockingActivityRunning();
145         }
146         @Override
147         public void onTaskCreated(int taskId, ComponentName componentName) {
148             if (componentName != null) {
149                 mPackageName = componentName.getPackageName();
150             } else {
151                 mPackageName = "_UNKNOWN";
152             }
153         }
154     };
155 
156     private DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
157             new DeviceConfig.OnPropertiesChangedListener() {
158                 @Override
159                 public void onPropertiesChanged(DeviceConfig.Properties properties) {
160                     if (DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())
161                             && (properties.getKeyset().contains(
162                                     SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD)
163                             || properties.getKeyset().contains(
164                                     SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL)
165                             || properties.getKeyset().contains(
166                                     SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_NAME))) {
167                         updateMLModelState();
168                     }
169                 }
170             };
171 
172 
173     private final Context mContext;
174     private final UserTracker mUserTracker;
175     private final OverviewProxyService mOverviewProxyService;
176     private final SysUiState mSysUiState;
177     private Runnable mStateChangeCallback;
178 
179     private final PluginManager mPluginManager;
180     private final ProtoTracer mProtoTracer;
181     private final NavigationModeController mNavigationModeController;
182     private final BackPanelController.Factory mBackPanelControllerFactory;
183     private final ViewConfiguration mViewConfiguration;
184     private final WindowManager mWindowManager;
185     private final IWindowManager mWindowManagerService;
186     private final Optional<Pip> mPipOptional;
187     private final FalsingManager mFalsingManager;
188     private final Configuration mLastReportedConfig = new Configuration();
189     // Activities which should not trigger Back gesture.
190     private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>();
191 
192     private final Point mDisplaySize = new Point();
193     private final int mDisplayId;
194 
195     private final Executor mMainExecutor;
196     private final Executor mBackgroundExecutor;
197 
198     private final Rect mPipExcludedBounds = new Rect();
199     private final Rect mNavBarOverlayExcludedBounds = new Rect();
200     private final Region mExcludeRegion = new Region();
201     private final Region mUnrestrictedExcludeRegion = new Region();
202     private final Provider<NavigationBarEdgePanel> mNavBarEdgePanelProvider;
203     private final Provider<BackGestureTfClassifierProvider>
204             mBackGestureTfClassifierProviderProvider;
205     private final FeatureFlags mFeatureFlags;
206 
207     // The left side edge width where touch down is allowed
208     private int mEdgeWidthLeft;
209     // The right side edge width where touch down is allowed
210     private int mEdgeWidthRight;
211     // The bottom gesture area height
212     private float mBottomGestureHeight;
213     // The slop to distinguish between horizontal and vertical motion
214     private float mTouchSlop;
215     // The threshold for triggering back
216     private float mBackSwipeTriggerThreshold;
217     // The threshold for back swipe full progress.
218     private float mBackSwipeProgressThreshold;
219     // Duration after which we consider the event as longpress.
220     private final int mLongPressTimeout;
221     private int mStartingQuickstepRotation = -1;
222     // We temporarily disable back gesture when user is quickswitching
223     // between apps of different orientations
224     private boolean mDisabledForQuickstep;
225     // This gets updated when the value of PipTransitionState#isInPip changes.
226     private boolean mIsInPip;
227 
228     private final PointF mDownPoint = new PointF();
229     private final PointF mEndPoint = new PointF();
230     private boolean mThresholdCrossed = false;
231     private boolean mAllowGesture = false;
232     private boolean mLogGesture = false;
233     private boolean mInRejectedExclusion = false;
234     private boolean mIsOnLeftEdge;
235 
236     private boolean mIsAttached;
237     private boolean mIsGesturalModeEnabled;
238     private boolean mIsEnabled;
239     private boolean mIsNavBarShownTransiently;
240     private boolean mIsBackGestureAllowed;
241     private boolean mGestureBlockingActivityRunning;
242     private boolean mIsNewBackAffordanceEnabled;
243 
244     private InputMonitor mInputMonitor;
245     private InputChannelCompat.InputEventReceiver mInputEventReceiver;
246 
247     private NavigationEdgeBackPlugin mEdgeBackPlugin;
248     private BackAnimation mBackAnimation;
249     private int mLeftInset;
250     private int mRightInset;
251     private int mSysUiFlags;
252 
253     // For Tf-Lite model.
254     private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
255     private Map<String, Integer> mVocab;
256     private boolean mUseMLModel;
257     private boolean mMLModelIsLoading;
258     // minimum width below which we do not run the model
259     private int mMLEnableWidth;
260     private float mMLModelThreshold;
261     private String mPackageName;
262     private float mMLResults;
263 
264     // For debugging
265     private LogArray mPredictionLog = new LogArray(MAX_NUM_LOGGED_PREDICTIONS);
266     private LogArray mGestureLogInsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
267     private LogArray mGestureLogOutsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
268 
269     private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
270 
271     private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
272             new NavigationEdgeBackPlugin.BackCallback() {
273                 @Override
274                 public void triggerBack() {
275                     // Notify FalsingManager that an intentional gesture has occurred.
276                     mFalsingManager.isFalseTouch(BACK_GESTURE);
277                     // Only inject back keycodes when ahead-of-time back dispatching is disabled.
278                     if (mBackAnimation == null) {
279                         boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
280                         boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
281                         if (DEBUG_MISSING_GESTURE) {
282                             Log.d(DEBUG_MISSING_GESTURE_TAG, "Triggered back: down="
283                                     + sendDown + ", up=" + sendUp);
284                         }
285                     } else {
286                         mBackAnimation.setTriggerBack(true);
287                     }
288 
289                     logGesture(mInRejectedExclusion
290                             ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED
291                             : SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED);
292                 }
293 
294                 @Override
295                 public void cancelBack() {
296                     if (mBackAnimation != null) {
297                         mBackAnimation.setTriggerBack(false);
298                     }
299                     logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE);
300                 }
301 
302                 @Override
303                 public void setTriggerBack(boolean triggerBack) {
304                     if (mBackAnimation != null) {
305                         mBackAnimation.setTriggerBack(triggerBack);
306                     }
307                 }
308             };
309 
310     private final SysUiState.SysUiStateCallback mSysUiStateCallback =
311             new SysUiState.SysUiStateCallback() {
312         @Override
313         public void onSystemUiStateChanged(int sysUiFlags) {
314             mSysUiFlags = sysUiFlags;
315         }
316     };
317 
318     private final Consumer<Boolean> mOnIsInPipStateChangedListener =
319             (isInPip) -> mIsInPip = isInPip;
320 
321     private final UserTracker.Callback mUserChangedCallback =
322             new UserTracker.Callback() {
323                 @Override
324                 public void onUserChanged(int newUser, @NonNull Context userContext) {
325                     updateIsEnabled();
326                     updateCurrentUserResources();
327                 }
328             };
329 
EdgeBackGestureHandler( Context context, OverviewProxyService overviewProxyService, SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, @Background Executor backgroundExecutor, UserTracker userTracker, ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, WindowManager windowManager, IWindowManager windowManagerService, Optional<Pip> pipOptional, FalsingManager falsingManager, Provider<NavigationBarEdgePanel> navigationBarEdgePanelProvider, Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider, FeatureFlags featureFlags)330     EdgeBackGestureHandler(
331             Context context,
332             OverviewProxyService overviewProxyService,
333             SysUiState sysUiState,
334             PluginManager pluginManager,
335             @Main Executor executor,
336             @Background Executor backgroundExecutor,
337             UserTracker userTracker,
338             ProtoTracer protoTracer,
339             NavigationModeController navigationModeController,
340             BackPanelController.Factory backPanelControllerFactory,
341             ViewConfiguration viewConfiguration,
342             WindowManager windowManager,
343             IWindowManager windowManagerService,
344             Optional<Pip> pipOptional,
345             FalsingManager falsingManager,
346             Provider<NavigationBarEdgePanel> navigationBarEdgePanelProvider,
347             Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider,
348             FeatureFlags featureFlags) {
349         mContext = context;
350         mDisplayId = context.getDisplayId();
351         mMainExecutor = executor;
352         mBackgroundExecutor = backgroundExecutor;
353         mUserTracker = userTracker;
354         mOverviewProxyService = overviewProxyService;
355         mSysUiState = sysUiState;
356         mPluginManager = pluginManager;
357         mProtoTracer = protoTracer;
358         mNavigationModeController = navigationModeController;
359         mBackPanelControllerFactory = backPanelControllerFactory;
360         mViewConfiguration = viewConfiguration;
361         mWindowManager = windowManager;
362         mWindowManagerService = windowManagerService;
363         mPipOptional = pipOptional;
364         mFalsingManager = falsingManager;
365         mNavBarEdgePanelProvider = navigationBarEdgePanelProvider;
366         mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
367         mFeatureFlags = featureFlags;
368         mLastReportedConfig.setTo(mContext.getResources().getConfiguration());
369         ComponentName recentsComponentName = ComponentName.unflattenFromString(
370                 context.getString(com.android.internal.R.string.config_recentsComponentName));
371         if (recentsComponentName != null) {
372             String recentsPackageName = recentsComponentName.getPackageName();
373             PackageManager manager = context.getPackageManager();
374             try {
375                 Resources resources = manager.getResourcesForApplication(
376                         manager.getApplicationInfo(recentsPackageName,
377                                 PackageManager.MATCH_UNINSTALLED_PACKAGES
378                                         | PackageManager.MATCH_DISABLED_COMPONENTS
379                                         | PackageManager.GET_SHARED_LIBRARY_FILES));
380                 int resId = resources.getIdentifier(
381                         "gesture_blocking_activities", "array", recentsPackageName);
382 
383                 if (resId == 0) {
384                     Log.e(TAG, "No resource found for gesture-blocking activities");
385                 } else {
386                     String[] gestureBlockingActivities = resources.getStringArray(resId);
387                     for (String gestureBlockingActivity : gestureBlockingActivities) {
388                         mGestureBlockingActivities.add(
389                                 ComponentName.unflattenFromString(gestureBlockingActivity));
390                     }
391                 }
392             } catch (NameNotFoundException e) {
393                 Log.e(TAG, "Failed to add gesture blocking activities", e);
394             }
395         }
396         mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
397                 ViewConfiguration.getLongPressTimeout());
398 
399         mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
400                 mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
401 
402         updateCurrentUserResources();
403     }
404 
setStateChangeCallback(Runnable callback)405     public void setStateChangeCallback(Runnable callback) {
406         mStateChangeCallback = callback;
407     }
408 
updateCurrentUserResources()409     public void updateCurrentUserResources() {
410         Resources res = mNavigationModeController.getCurrentUserContext().getResources();
411         mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
412         mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
413         mIsBackGestureAllowed =
414                 !mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
415 
416         final DisplayMetrics dm = res.getDisplayMetrics();
417         final float defaultGestureHeight = res.getDimension(
418                 com.android.internal.R.dimen.navigation_bar_gesture_height) / dm.density;
419         final float gestureHeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
420                 SystemUiDeviceConfigFlags.BACK_GESTURE_BOTTOM_HEIGHT,
421                 defaultGestureHeight);
422         mBottomGestureHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gestureHeight,
423                 dm);
424 
425         // Set the minimum bounds to activate ML to 12dp or the minimum of configured values
426         mMLEnableWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12.0f, dm);
427         if (mMLEnableWidth > mEdgeWidthRight) mMLEnableWidth = mEdgeWidthRight;
428         if (mMLEnableWidth > mEdgeWidthLeft) mMLEnableWidth = mEdgeWidthLeft;
429 
430         // Reduce the default touch slop to ensure that we can intercept the gesture
431         // before the app starts to react to it.
432         // TODO(b/130352502) Tune this value and extract into a constant
433         final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
434                         SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f);
435         mTouchSlop = mViewConfiguration.getScaledTouchSlop() * backGestureSlop;
436         mBackSwipeTriggerThreshold = res.getDimension(
437                 R.dimen.navigation_edge_action_drag_threshold);
438         mBackSwipeProgressThreshold = res.getDimension(
439                 R.dimen.navigation_edge_action_progress_threshold);
440         updateBackAnimationThresholds();
441     }
442 
updateNavigationBarOverlayExcludeRegion(Rect exclude)443     public void updateNavigationBarOverlayExcludeRegion(Rect exclude) {
444         mNavBarOverlayExcludedBounds.set(exclude);
445     }
446 
onNavigationSettingsChanged()447     private void onNavigationSettingsChanged() {
448         boolean wasBackAllowed = isHandlingGestures();
449         updateCurrentUserResources();
450         if (mStateChangeCallback != null && wasBackAllowed != isHandlingGestures()) {
451             mStateChangeCallback.run();
452         }
453     }
454 
455     /**
456      * Called when the nav/task bar is attached.
457      */
onNavBarAttached()458     public void onNavBarAttached() {
459         mIsAttached = true;
460         mProtoTracer.add(this);
461         mOverviewProxyService.addCallback(mQuickSwitchListener);
462         mSysUiState.addCallback(mSysUiStateCallback);
463         updateIsEnabled();
464         mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
465     }
466 
467     /**
468      * Called when the nav/task bar is detached.
469      */
onNavBarDetached()470     public void onNavBarDetached() {
471         mIsAttached = false;
472         mProtoTracer.remove(this);
473         mOverviewProxyService.removeCallback(mQuickSwitchListener);
474         mSysUiState.removeCallback(mSysUiStateCallback);
475         updateIsEnabled();
476         mUserTracker.removeCallback(mUserChangedCallback);
477     }
478 
479     /**
480      * @see NavigationModeController.ModeChangedListener#onNavigationModeChanged
481      */
onNavigationModeChanged(int mode)482     public void onNavigationModeChanged(int mode) {
483         mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode);
484         updateIsEnabled();
485         updateCurrentUserResources();
486     }
487 
onNavBarTransientStateChanged(boolean isTransient)488     public void onNavBarTransientStateChanged(boolean isTransient) {
489         mIsNavBarShownTransiently = isTransient;
490     }
491 
disposeInputChannel()492     private void disposeInputChannel() {
493         if (mInputEventReceiver != null) {
494             mInputEventReceiver.dispose();
495             mInputEventReceiver = null;
496         }
497         if (mInputMonitor != null) {
498             mInputMonitor.dispose();
499             mInputMonitor = null;
500         }
501     }
502 
updateIsEnabled()503     private void updateIsEnabled() {
504         try {
505             Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");
506             updateIsEnabledTraced();
507         } finally {
508             Trace.endSection();
509         }
510     }
511 
updateIsEnabledTraced()512     private void updateIsEnabledTraced() {
513         boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;
514         if (isEnabled == mIsEnabled) {
515             return;
516         }
517         mIsEnabled = isEnabled;
518         disposeInputChannel();
519 
520         if (mEdgeBackPlugin != null) {
521             mEdgeBackPlugin.onDestroy();
522             mEdgeBackPlugin = null;
523         }
524 
525         if (!mIsEnabled) {
526             mGestureNavigationSettingsObserver.unregister();
527             if (DEBUG_MISSING_GESTURE) {
528                 Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener");
529             }
530             mPluginManager.removePluginListener(this);
531             TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
532             DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
533             mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null));
534 
535             try {
536                 mWindowManagerService.unregisterSystemGestureExclusionListener(
537                         mGestureExclusionListener, mDisplayId);
538             } catch (RemoteException | IllegalArgumentException e) {
539                 Log.e(TAG, "Failed to unregister window manager callbacks", e);
540             }
541 
542         } else {
543             mGestureNavigationSettingsObserver.register();
544             updateDisplaySize();
545             if (DEBUG_MISSING_GESTURE) {
546                 Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener");
547             }
548             TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
549             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
550                     mMainExecutor::execute, mOnPropertiesChangedListener);
551             mPipOptional.ifPresent(
552                     pip -> pip.setOnIsInPipStateChangedListener(mOnIsInPipStateChangedListener));
553 
554             try {
555                 mWindowManagerService.registerSystemGestureExclusionListener(
556                         mGestureExclusionListener, mDisplayId);
557             } catch (RemoteException | IllegalArgumentException e) {
558                 Log.e(TAG, "Failed to register window manager callbacks", e);
559             }
560 
561             // Register input event receiver
562             mInputMonitor = InputManager.getInstance().monitorGestureInput(
563                     "edge-swipe", mDisplayId);
564             mInputEventReceiver = new InputChannelCompat.InputEventReceiver(
565                     mInputMonitor.getInputChannel(), Looper.getMainLooper(),
566                     Choreographer.getInstance(), this::onInputEvent);
567 
568             // Add a nav bar panel window
569             mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE);
570             resetEdgeBackPlugin();
571             mPluginManager.addPluginListener(
572                     this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
573         }
574         // Update the ML model resources.
575         updateMLModelState();
576     }
577 
578     @Override
onPluginConnected(NavigationEdgeBackPlugin plugin, Context context)579     public void onPluginConnected(NavigationEdgeBackPlugin plugin, Context context) {
580         setEdgeBackPlugin(plugin);
581     }
582 
583     @Override
onPluginDisconnected(NavigationEdgeBackPlugin plugin)584     public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
585         resetEdgeBackPlugin();
586     }
587 
resetEdgeBackPlugin()588     private void resetEdgeBackPlugin() {
589         if (mIsNewBackAffordanceEnabled) {
590             setEdgeBackPlugin(
591                     mBackPanelControllerFactory.create(mContext));
592         } else {
593             setEdgeBackPlugin(mNavBarEdgePanelProvider.get());
594         }
595     }
596 
setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin)597     private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
598         try {
599             Trace.beginSection("setEdgeBackPlugin");
600             if (mEdgeBackPlugin != null) {
601                 mEdgeBackPlugin.onDestroy();
602             }
603             mEdgeBackPlugin = edgeBackPlugin;
604             mEdgeBackPlugin.setBackCallback(mBackCallback);
605             mEdgeBackPlugin.setLayoutParams(createLayoutParams());
606             updateDisplaySize();
607         } finally {
608             Trace.endSection();
609         }
610     }
611 
isHandlingGestures()612     public boolean isHandlingGestures() {
613         return mIsEnabled && mIsBackGestureAllowed;
614     }
615 
616     /**
617      * Update the PiP bounds, used for exclusion calculation.
618      */
setPipStashExclusionBounds(Rect bounds)619     public void setPipStashExclusionBounds(Rect bounds) {
620         mPipExcludedBounds.set(bounds);
621     }
622 
createLayoutParams()623     private WindowManager.LayoutParams createLayoutParams() {
624         Resources resources = mContext.getResources();
625         WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
626                 resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
627                 resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
628                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
629                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
630                         | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
631                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
632                 PixelFormat.TRANSLUCENT);
633         layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
634         layoutParams.windowAnimations = 0;
635         layoutParams.privateFlags |=
636                 (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
637                 | PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION);
638         layoutParams.setTitle(TAG + mContext.getDisplayId());
639         layoutParams.setFitInsetsTypes(0 /* types */);
640         layoutParams.setTrustedOverlay();
641         return layoutParams;
642     }
643 
onInputEvent(InputEvent ev)644     private void onInputEvent(InputEvent ev) {
645         if (!(ev instanceof MotionEvent)) return;
646         MotionEvent event = (MotionEvent) ev;
647         onMotionEvent(event);
648     }
649 
updateMLModelState()650     private void updateMLModelState() {
651         boolean newState =
652                 mIsGesturalModeEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
653                         SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false);
654 
655         if (newState == mUseMLModel) {
656             return;
657         }
658 
659         mUseMLModel = newState;
660 
661         if (mUseMLModel) {
662             Assert.isMainThread();
663             if (mMLModelIsLoading) {
664                 Log.d(TAG, "Model tried to load while already loading.");
665                 return;
666             }
667             mMLModelIsLoading = true;
668             mBackgroundExecutor.execute(() -> loadMLModel());
669         } else if (mBackGestureTfClassifierProvider != null) {
670             mBackGestureTfClassifierProvider.release();
671             mBackGestureTfClassifierProvider = null;
672             mVocab = null;
673         }
674     }
675 
loadMLModel()676     private void loadMLModel() {
677         BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProviderProvider.get();
678         float threshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
679                 SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
680         Map<String, Integer> vocab = null;
681         if (provider != null && !provider.isActive()) {
682             provider.release();
683             provider = null;
684             Log.w(TAG, "Cannot load model because it isn't active");
685         }
686         if (provider != null) {
687             Trace.beginSection("EdgeBackGestureHandler#loadVocab");
688             vocab = provider.loadVocab(mContext.getAssets());
689             Trace.endSection();
690         }
691         BackGestureTfClassifierProvider finalProvider = provider;
692         Map<String, Integer> finalVocab = vocab;
693         mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
694     }
695 
onMLModelLoadFinished(BackGestureTfClassifierProvider provider, Map<String, Integer> vocab, float threshold)696     private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
697             Map<String, Integer> vocab, float threshold) {
698         Assert.isMainThread();
699         mMLModelIsLoading = false;
700         if (!mUseMLModel) {
701             // This can happen if the user disables Gesture Nav while the model is loading.
702             if (provider != null) {
703                 provider.release();
704             }
705             Log.d(TAG, "Model finished loading but isn't needed.");
706             return;
707         }
708         mBackGestureTfClassifierProvider = provider;
709         mVocab = vocab;
710         mMLModelThreshold = threshold;
711     }
712 
getBackGesturePredictionsCategory(int x, int y, int app)713     private int getBackGesturePredictionsCategory(int x, int y, int app) {
714         BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProvider;
715         if (provider == null || app == -1) {
716             return -1;
717         }
718         int distanceFromEdge;
719         int location;
720         if (x <= mDisplaySize.x / 2.0) {
721             location = 1;  // left
722             distanceFromEdge = x;
723         } else {
724             location = 2;  // right
725             distanceFromEdge = mDisplaySize.x - x;
726         }
727 
728         Object[] featuresVector = {
729             new long[]{(long) mDisplaySize.x},
730             new long[]{(long) distanceFromEdge},
731             new long[]{(long) location},
732             new long[]{(long) app},
733             new long[]{(long) y},
734         };
735 
736         mMLResults = provider.predict(featuresVector);
737         if (mMLResults == -1) {
738             return -1;
739         }
740         return mMLResults >= mMLModelThreshold ? 1 : 0;
741     }
742 
isWithinInsets(int x, int y)743     private boolean isWithinInsets(int x, int y) {
744         // Disallow if we are in the bottom gesture area
745         if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
746             return false;
747         }
748         // If the point is way too far (twice the margin), it is
749         // not interesting to us for logging purposes, nor we
750         // should process it.  Simply return false and keep
751         // mLogGesture = false.
752         if (x > 2 * (mEdgeWidthLeft + mLeftInset)
753                 && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
754             return false;
755         }
756         return true;
757     }
758 
isWithinTouchRegion(int x, int y)759     private boolean isWithinTouchRegion(int x, int y) {
760         // If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back
761         // gesture
762         final boolean isInsidePip = mIsInPip && mPipExcludedBounds.contains(x, y);
763         if (isInsidePip || mNavBarOverlayExcludedBounds.contains(x, y)) {
764             return false;
765         }
766 
767         int app = -1;
768         if (mVocab != null) {
769             app = mVocab.getOrDefault(mPackageName, -1);
770         }
771 
772         // Denotes whether we should proceed with the gesture. Even if it is false, we may want to
773         // log it assuming it is not invalid due to exclusion.
774         boolean withinRange = x < mEdgeWidthLeft + mLeftInset
775                 || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
776         if (withinRange) {
777             int results = -1;
778 
779             // Check if we are within the tightest bounds beyond which we would not need to run the
780             // ML model
781             boolean withinMinRange = x < mMLEnableWidth + mLeftInset
782                     || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset);
783             if (!withinMinRange && mUseMLModel && !mMLModelIsLoading
784                     && (results = getBackGesturePredictionsCategory(x, y, app)) != -1) {
785                 withinRange = (results == 1);
786             }
787         }
788 
789         // For debugging purposes
790         mPredictionLog.log(String.format("Prediction [%d,%d,%d,%d,%f,%d]",
791                 System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0));
792 
793         // Always allow if the user is in a transient sticky immersive state
794         if (mIsNavBarShownTransiently) {
795             mLogGesture = true;
796             return withinRange;
797         }
798 
799         if (mExcludeRegion.contains(x, y)) {
800             if (withinRange) {
801                 // We don't have the end point for logging purposes.
802                 mEndPoint.x = -1;
803                 mEndPoint.y = -1;
804                 mLogGesture = true;
805                 logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED);
806             }
807             return false;
808         }
809 
810         mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y);
811         mLogGesture = true;
812         return withinRange;
813     }
814 
cancelGesture(MotionEvent ev)815     private void cancelGesture(MotionEvent ev) {
816         // Send action cancel to reset all the touch events
817         mAllowGesture = false;
818         mLogGesture = false;
819         mInRejectedExclusion = false;
820         MotionEvent cancelEv = MotionEvent.obtain(ev);
821         cancelEv.setAction(MotionEvent.ACTION_CANCEL);
822         mEdgeBackPlugin.onMotionEvent(cancelEv);
823         dispatchToBackAnimation(cancelEv);
824         cancelEv.recycle();
825     }
826 
logGesture(int backType)827     private void logGesture(int backType) {
828         if (!mLogGesture) {
829             return;
830         }
831         mLogGesture = false;
832         String logPackageName = "";
833         Map<String, Integer> vocab = mVocab;
834         // Due to privacy, only top 100 most used apps by all users can be logged.
835         if (mUseMLModel && vocab != null && vocab.containsKey(mPackageName)
836                 && vocab.get(mPackageName) < 100) {
837             logPackageName = mPackageName;
838         }
839         SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backType,
840                 (int) mDownPoint.y, mIsOnLeftEdge
841                         ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT
842                         : SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT,
843                 (int) mDownPoint.x, (int) mDownPoint.y,
844                 (int) mEndPoint.x, (int) mEndPoint.y,
845                 mEdgeWidthLeft + mLeftInset,
846                 mDisplaySize.x - (mEdgeWidthRight + mRightInset),
847                 mUseMLModel ? mMLResults : -2, logPackageName);
848     }
849 
onMotionEvent(MotionEvent ev)850     private void onMotionEvent(MotionEvent ev) {
851         int action = ev.getActionMasked();
852         if (action == MotionEvent.ACTION_DOWN) {
853             if (DEBUG_MISSING_GESTURE) {
854                 Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev);
855             }
856 
857             // Verify if this is in within the touch region and we aren't in immersive mode, and
858             // either the bouncer is showing or the notification panel is hidden
859             mInputEventReceiver.setBatchingEnabled(false);
860             mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
861             mMLResults = 0;
862             mLogGesture = false;
863             mInRejectedExclusion = false;
864             boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
865             mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets
866                     && !mGestureBlockingActivityRunning
867                     && !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
868                     && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
869             if (mAllowGesture) {
870                 mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
871                 mEdgeBackPlugin.onMotionEvent(ev);
872                 dispatchToBackAnimation(ev);
873             }
874             if (mLogGesture) {
875                 mDownPoint.set(ev.getX(), ev.getY());
876                 mEndPoint.set(-1, -1);
877                 mThresholdCrossed = false;
878             }
879 
880             // For debugging purposes, only log edge points
881             (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format(
882                     "Gesture [%d,alw=%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
883                     System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge,
884                     mIsBackGestureAllowed,
885                     QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize,
886                     mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
887         } else if (mAllowGesture || mLogGesture) {
888             if (!mThresholdCrossed) {
889                 mEndPoint.x = (int) ev.getX();
890                 mEndPoint.y = (int) ev.getY();
891                 if (action == MotionEvent.ACTION_POINTER_DOWN) {
892                     if (mAllowGesture) {
893                         logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH);
894                         if (DEBUG_MISSING_GESTURE) {
895                             Log.d(DEBUG_MISSING_GESTURE_TAG, "Cancel back: multitouch");
896                         }
897                         // We do not support multi touch for back gesture
898                         cancelGesture(ev);
899                     }
900                     mLogGesture = false;
901                     return;
902                 } else if (action == MotionEvent.ACTION_MOVE) {
903                     if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
904                         if (mAllowGesture) {
905                             logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS);
906                             cancelGesture(ev);
907                             if (DEBUG_MISSING_GESTURE) {
908                                 Log.d(DEBUG_MISSING_GESTURE_TAG, "Cancel back [longpress]: "
909                                         + ev.getEventTime()
910                                         + "  " + ev.getDownTime()
911                                         + "  " + mLongPressTimeout);
912                             }
913                         }
914                         mLogGesture = false;
915                         return;
916                     }
917                     float dx = Math.abs(ev.getX() - mDownPoint.x);
918                     float dy = Math.abs(ev.getY() - mDownPoint.y);
919                     if (dy > dx && dy > mTouchSlop) {
920                         if (mAllowGesture) {
921                             logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_VERTICAL_MOVE);
922                             cancelGesture(ev);
923                             if (DEBUG_MISSING_GESTURE) {
924                                 Log.d(DEBUG_MISSING_GESTURE_TAG, "Cancel back [vertical move]: "
925                                         + dy + "  " + dx + "  " + mTouchSlop);
926                             }
927                         }
928                         mLogGesture = false;
929                         return;
930                     } else if (dx > dy && dx > mTouchSlop) {
931                         if (mAllowGesture) {
932                             mThresholdCrossed = true;
933                             // Capture inputs
934                             mInputMonitor.pilferPointers();
935                             if (mBackAnimation != null) {
936                                 // Notify FalsingManager that an intentional gesture has occurred.
937                                 mFalsingManager.isFalseTouch(BACK_GESTURE);
938                             }
939                             mInputEventReceiver.setBatchingEnabled(true);
940                         } else {
941                             logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE);
942                         }
943                     }
944                 }
945             }
946 
947             if (mAllowGesture) {
948                 // forward touch
949                 mEdgeBackPlugin.onMotionEvent(ev);
950                 dispatchToBackAnimation(ev);
951             }
952         }
953 
954         mProtoTracer.scheduleFrameUpdate();
955     }
956 
dispatchToBackAnimation(MotionEvent event)957     private void dispatchToBackAnimation(MotionEvent event) {
958         if (mBackAnimation != null) {
959             mBackAnimation.onBackMotion(
960                     event.getX(),
961                     event.getY(),
962                     event.getActionMasked(),
963                     mIsOnLeftEdge ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
964         }
965     }
966 
updateDisabledForQuickstep(Configuration newConfig)967     private void updateDisabledForQuickstep(Configuration newConfig) {
968         int rotation = newConfig.windowConfiguration.getRotation();
969         mDisabledForQuickstep = mStartingQuickstepRotation > -1 &&
970                 mStartingQuickstepRotation != rotation;
971     }
972 
onConfigurationChanged(@onNull Configuration newConfig)973     public void onConfigurationChanged(@NonNull Configuration newConfig) {
974         if (mStartingQuickstepRotation > -1) {
975             updateDisabledForQuickstep(newConfig);
976         }
977 
978         // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
979         Log.i(DEBUG_MISSING_GESTURE_TAG, "Config changed: newConfig=" + newConfig
980                 + " lastReportedConfig=" + mLastReportedConfig);
981         mLastReportedConfig.updateFrom(newConfig);
982         updateDisplaySize();
983     }
984 
updateDisplaySize()985     private void updateDisplaySize() {
986         Rect bounds = mLastReportedConfig.windowConfiguration.getMaxBounds();
987         mDisplaySize.set(bounds.width(), bounds.height());
988         if (DEBUG_MISSING_GESTURE) {
989             Log.d(DEBUG_MISSING_GESTURE_TAG, "Update display size: mDisplaySize=" + mDisplaySize);
990         }
991         if (mEdgeBackPlugin != null) {
992             mEdgeBackPlugin.setDisplaySize(mDisplaySize);
993         }
994         updateBackAnimationThresholds();
995     }
996 
updateBackAnimationThresholds()997     private void updateBackAnimationThresholds() {
998         if (mBackAnimation == null) {
999             return;
1000         }
1001         mBackAnimation.setSwipeThresholds(
1002                 mBackSwipeTriggerThreshold,
1003                 Math.min(mDisplaySize.x, mBackSwipeProgressThreshold));
1004     }
1005 
sendEvent(int action, int code)1006     private boolean sendEvent(int action, int code) {
1007         long when = SystemClock.uptimeMillis();
1008         final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
1009                 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
1010                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
1011                 InputDevice.SOURCE_KEYBOARD);
1012 
1013         ev.setDisplayId(mContext.getDisplay().getDisplayId());
1014         return InputManager.getInstance()
1015                 .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
1016     }
1017 
setInsets(int leftInset, int rightInset)1018     public void setInsets(int leftInset, int rightInset) {
1019         mLeftInset = leftInset;
1020         mRightInset = rightInset;
1021         if (mEdgeBackPlugin != null) {
1022             mEdgeBackPlugin.setInsets(leftInset, rightInset);
1023         }
1024     }
1025 
dump(PrintWriter pw)1026     public void dump(PrintWriter pw) {
1027         pw.println("EdgeBackGestureHandler:");
1028         pw.println("  mIsEnabled=" + mIsEnabled);
1029         pw.println("  mIsAttached=" + mIsAttached);
1030         pw.println("  mIsBackGestureAllowed=" + mIsBackGestureAllowed);
1031         pw.println("  mIsGesturalModeEnabled=" + mIsGesturalModeEnabled);
1032         pw.println("  mIsNavBarShownTransiently=" + mIsNavBarShownTransiently);
1033         pw.println("  mGestureBlockingActivityRunning=" + mGestureBlockingActivityRunning);
1034         pw.println("  mAllowGesture=" + mAllowGesture);
1035         pw.println("  mUseMLModel=" + mUseMLModel);
1036         pw.println("  mDisabledForQuickstep=" + mDisabledForQuickstep);
1037         pw.println("  mStartingQuickstepRotation=" + mStartingQuickstepRotation);
1038         pw.println("  mInRejectedExclusion=" + mInRejectedExclusion);
1039         pw.println("  mExcludeRegion=" + mExcludeRegion);
1040         pw.println("  mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
1041         pw.println("  mIsInPip=" + mIsInPip);
1042         pw.println("  mPipExcludedBounds=" + mPipExcludedBounds);
1043         pw.println("  mNavBarOverlayExcludedBounds=" + mNavBarOverlayExcludedBounds);
1044         pw.println("  mEdgeWidthLeft=" + mEdgeWidthLeft);
1045         pw.println("  mEdgeWidthRight=" + mEdgeWidthRight);
1046         pw.println("  mLeftInset=" + mLeftInset);
1047         pw.println("  mRightInset=" + mRightInset);
1048         pw.println("  mMLEnableWidth=" + mMLEnableWidth);
1049         pw.println("  mMLModelThreshold=" + mMLModelThreshold);
1050         pw.println("  mTouchSlop=" + mTouchSlop);
1051         pw.println("  mBottomGestureHeight=" + mBottomGestureHeight);
1052         pw.println("  mPredictionLog=" + String.join("\n", mPredictionLog));
1053         pw.println("  mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets));
1054         pw.println("  mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets));
1055         pw.println("  mEdgeBackPlugin=" + mEdgeBackPlugin);
1056         if (mEdgeBackPlugin != null) {
1057             mEdgeBackPlugin.dump(pw);
1058         }
1059     }
1060 
isGestureBlockingActivityRunning()1061     private boolean isGestureBlockingActivityRunning() {
1062         ActivityManager.RunningTaskInfo runningTask =
1063                 ActivityManagerWrapper.getInstance().getRunningTask();
1064         ComponentName topActivity = runningTask == null ? null : runningTask.topActivity;
1065         if (topActivity != null) {
1066             mPackageName = topActivity.getPackageName();
1067         } else {
1068             mPackageName = "_UNKNOWN";
1069         }
1070         return topActivity != null && mGestureBlockingActivities.contains(topActivity);
1071     }
1072 
1073     @Override
writeToProto(SystemUiTraceProto proto)1074     public void writeToProto(SystemUiTraceProto proto) {
1075         if (proto.edgeBackGestureHandler == null) {
1076             proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto();
1077         }
1078         proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
1079     }
1080 
setBackAnimation(BackAnimation backAnimation)1081     public void setBackAnimation(BackAnimation backAnimation) {
1082         mBackAnimation = backAnimation;
1083         updateBackAnimationThresholds();
1084     }
1085 
1086     /**
1087      * Injectable instance to create a new EdgeBackGestureHandler.
1088      *
1089      * Necessary because we don't have good handling of per-display contexts at the moment. With
1090      * this, you can pass in a specific context that knows what display it is in.
1091      */
1092     public static class Factory {
1093         private final OverviewProxyService mOverviewProxyService;
1094         private final SysUiState mSysUiState;
1095         private final PluginManager mPluginManager;
1096         private final Executor mExecutor;
1097         private final Executor mBackgroundExecutor;
1098         private final UserTracker mUserTracker;
1099         private final ProtoTracer mProtoTracer;
1100         private final NavigationModeController mNavigationModeController;
1101         private final BackPanelController.Factory mBackPanelControllerFactory;
1102         private final ViewConfiguration mViewConfiguration;
1103         private final WindowManager mWindowManager;
1104         private final IWindowManager mWindowManagerService;
1105         private final Optional<Pip> mPipOptional;
1106         private final FalsingManager mFalsingManager;
1107         private final Provider<NavigationBarEdgePanel> mNavBarEdgePanelProvider;
1108         private final Provider<BackGestureTfClassifierProvider>
1109                 mBackGestureTfClassifierProviderProvider;
1110         private final FeatureFlags mFeatureFlags;
1111 
1112         @Inject
Factory(OverviewProxyService overviewProxyService, SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, @Background Executor backgroundExecutor, UserTracker userTracker, ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, WindowManager windowManager, IWindowManager windowManagerService, Optional<Pip> pipOptional, FalsingManager falsingManager, Provider<NavigationBarEdgePanel> navBarEdgePanelProvider, Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider, FeatureFlags featureFlags)1113         public Factory(OverviewProxyService overviewProxyService,
1114                        SysUiState sysUiState,
1115                        PluginManager pluginManager,
1116                        @Main Executor executor,
1117                        @Background Executor backgroundExecutor,
1118                        UserTracker userTracker,
1119                        ProtoTracer protoTracer,
1120                        NavigationModeController navigationModeController,
1121                        BackPanelController.Factory backPanelControllerFactory,
1122                        ViewConfiguration viewConfiguration,
1123                        WindowManager windowManager,
1124                        IWindowManager windowManagerService,
1125                        Optional<Pip> pipOptional,
1126                        FalsingManager falsingManager,
1127                        Provider<NavigationBarEdgePanel> navBarEdgePanelProvider,
1128                        Provider<BackGestureTfClassifierProvider>
1129                                backGestureTfClassifierProviderProvider,
1130                        FeatureFlags featureFlags) {
1131             mOverviewProxyService = overviewProxyService;
1132             mSysUiState = sysUiState;
1133             mPluginManager = pluginManager;
1134             mExecutor = executor;
1135             mBackgroundExecutor = backgroundExecutor;
1136             mUserTracker = userTracker;
1137             mProtoTracer = protoTracer;
1138             mNavigationModeController = navigationModeController;
1139             mBackPanelControllerFactory = backPanelControllerFactory;
1140             mViewConfiguration = viewConfiguration;
1141             mWindowManager = windowManager;
1142             mWindowManagerService = windowManagerService;
1143             mPipOptional = pipOptional;
1144             mFalsingManager = falsingManager;
1145             mNavBarEdgePanelProvider = navBarEdgePanelProvider;
1146             mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
1147             mFeatureFlags = featureFlags;
1148         }
1149 
1150         /** Construct a {@link EdgeBackGestureHandler}. */
create(Context context)1151         public EdgeBackGestureHandler create(Context context) {
1152             return new EdgeBackGestureHandler(
1153                     context,
1154                     mOverviewProxyService,
1155                     mSysUiState,
1156                     mPluginManager,
1157                     mExecutor,
1158                     mBackgroundExecutor,
1159                     mUserTracker,
1160                     mProtoTracer,
1161                     mNavigationModeController,
1162                     mBackPanelControllerFactory,
1163                     mViewConfiguration,
1164                     mWindowManager,
1165                     mWindowManagerService,
1166                     mPipOptional,
1167                     mFalsingManager,
1168                     mNavBarEdgePanelProvider,
1169                     mBackGestureTfClassifierProviderProvider,
1170                     mFeatureFlags);
1171         }
1172     }
1173 
1174     private static class LogArray extends ArrayDeque<String> {
1175         private final int mLength;
1176 
LogArray(int length)1177         LogArray(int length) {
1178             mLength = length;
1179         }
1180 
log(String message)1181         void log(String message) {
1182             if (size() >= mLength) {
1183                 removeFirst();
1184             }
1185             addLast(message);
1186             if (DEBUG_MISSING_GESTURE) {
1187                 Log.d(DEBUG_MISSING_GESTURE_TAG, message);
1188             }
1189         }
1190     }
1191 }
1192