From 625f5e286f368f6d7002f12d416b6379b063c8ea Mon Sep 17 00:00:00 2001 From: Vadim Caen Date: Thu, 3 Feb 2022 15:02:23 +0100 Subject: [PATCH] Add enableOnBackInvokedCallaback manifest attribute Add a manifest flag for applications to opt-out of the new back navigation system. Test: atest CtsWindowManagerDeviceTestCases: android.server.wm.BackNavigationLegacyTest Bug: 217709328 Change-Id: I43d09a37b126e59bdb8f20bb83cf6d99e53a1e91 --- core/api/current.txt | 1 + core/java/android/app/Activity.java | 5 +++- core/java/android/app/Dialog.java | 5 ++-- .../android/content/pm/ApplicationInfo.java | 26 +++++++++++++++++++ core/java/android/view/ViewRootImpl.java | 4 +-- .../window/WindowOnBackInvokedDispatcher.java | 26 ++++++++++++++----- core/res/res/values/attrs_manifest.xml | 8 ++++++ core/res/res/values/public.xml | 1 + data/etc/services.core.protolog.json | 6 +++++ .../shell/back/BackAnimationController.java | 2 +- .../parsing/PackageInfoWithoutStateUtils.java | 4 ++- .../server/pm/pkg/parsing/ParsingPackage.java | 2 ++ .../pm/pkg/parsing/ParsingPackageImpl.java | 12 +++++++++ .../pm/pkg/parsing/ParsingPackageRead.java | 5 ++++ .../pm/pkg/parsing/ParsingPackageUtils.java | 5 ++-- .../server/wm/BackNavigationController.java | 13 +++++++++- .../com/android/server/wm/TaskFragment.java | 4 +-- .../parsing/parcelling/AndroidPackageTest.kt | 7 ++++- .../wm/BackNavigationControllerTests.java | 4 +++ 19 files changed, 121 insertions(+), 19 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index ea3830ff2da06..005ddf26b8a87 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -631,6 +631,7 @@ package android { field public static final int elevation = 16843840; // 0x1010440 field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 + field public static final int enableOnBackInvokedCallback; field public static final int enableVrMode = 16844069; // 0x1010525 field public static final int enabled = 16842766; // 0x101000e field public static final int end = 16843996; // 0x10104dc diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 983dde3beda04..db50290dcc009 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1649,7 +1649,10 @@ public class Activity extends ContextThemeWrapper } mRestoredFromBundle = savedInstanceState != null; mCalled = true; - if (!WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + + boolean aheadOfTimeBack = WindowOnBackInvokedDispatcher + .isOnBackInvokedCallbackEnabled(this); + if (aheadOfTimeBack) { // Add onBackPressed as default back behavior. mDefaultBackCallback = new OnBackInvokedCallback() { @Override diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index aa6c1842ddeca..569fda961a4c6 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -456,7 +456,8 @@ public class Dialog implements DialogInterface, Window.Callback, */ protected void onStart() { if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true); - if (mContext != null && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + if (mContext != null + && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { // Add onBackPressed as default back behavior. mDefaultBackCallback = new OnBackInvokedCallback() { @Override @@ -703,7 +704,7 @@ public class Dialog implements DialogInterface, Window.Callback, if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) && event.isTracking() && !event.isCanceled() - && WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + && !WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { onBackPressed(); return true; } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 2528e16ce7b70..be58ba7422d0b 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -39,6 +39,7 @@ import android.util.ArraySet; import android.util.Printer; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; +import android.view.OnBackInvokedCallback; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Parcelling; @@ -801,11 +802,24 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE = 1 << 2; + + /** + * If false, {@link android.view.KeyEvent#KEYCODE_BACK} related events will be forwarded to + * the Activities, Dialogs and Views and {@link android.app.Activity#onBackPressed()}, + * {@link android.app.Dialog#onBackPressed} will be called. Otherwise, those events will be + * replaced by a call to {@link OnBackInvokedCallback#onBackInvoked()} on the focused window. + * + * @hide + * @see android.R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback + */ + public static final int PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK = 1 << 3; + /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = { PRIVATE_FLAG_EXT_PROFILEABLE, PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION, PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE, + PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoPrivateFlagsExt {} @@ -1683,6 +1697,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "localeConfigRes=0x" + Integer.toHexString(localeConfigRes)); } + pw.println(prefix + "enableOnBackInvokedCallback=" + isOnBackInvokedCallbackEnabled()); } pw.println(prefix + "createTimestamp=" + createTimestamp); if (mKnownActivityEmbeddingCerts != null) { @@ -2565,6 +2580,17 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { & ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION) != 0; } + /** + * Returns whether the application will use the {@link android.view.OnBackInvokedCallback} + * navigation system instead of the {@link android.view.KeyEvent#KEYCODE_BACK} and related + * callbacks. + * + * @hide + */ + public boolean isOnBackInvokedCallbackEnabled() { + return ((privateFlagsExt & PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK)) != 0; + } + /** * @hide */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8236fbbc3e81a..844403298cc97 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1201,7 +1201,7 @@ public final class ViewRootImpl implements ViewParent, mTmpFrames.displayFrame, mTempRect2, mTmpFrames.frame); setFrame(mTmpFrames.frame); registerBackCallbackOnWindow(); - if (WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { // For apps requesting legacy back behavior, we add a compat callback that // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views. // This way from system point of view, these apps are providing custom @@ -6507,7 +6507,7 @@ public final class ViewRootImpl implements ViewParent, if (isBack(event) && mContext != null - && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) { + && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { // Invoke the appropriate {@link OnBackInvokedCallback} if the new back // navigation should be used, and the key event is not handled by anything else. OnBackInvokedCallback topCallback = diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 62292f97ad4df..0503c406c2871 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -19,9 +19,11 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.compat.CompatChanges; +import android.content.Context; import android.os.Handler; import android.os.RemoteException; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Log; import android.view.IWindow; import android.view.IWindowSession; @@ -50,10 +52,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private IWindowSession mWindowSession; private IWindow mWindow; private static final String TAG = "WindowOnBackDispatcher"; - private static final boolean DEBUG = false; private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability"; private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties - .getInt(BACK_PREDICTABILITY_PROP, 0) > 0; + .getInt(BACK_PREDICTABILITY_PROP, 1) > 0; /** Convenience hashmap to quickly decide if a callback has been added. */ private final HashMap mAllCallbacks = new HashMap<>(); @@ -227,10 +228,23 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { * * Legacy back behavior dispatches KEYCODE_BACK instead of invoking the application registered * {@link android.view.OnBackInvokedCallback}. - * */ - public static boolean shouldUseLegacyBack() { - return !CompatChanges.isChangeEnabled(DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME) - || !IS_BACK_PREDICTABILITY_ENABLED; + public static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) { + // new back is enabled if the app targets T AND the feature flag is enabled AND the app + // does not explicitly request legacy back. + boolean targetsT = CompatChanges.isChangeEnabled(DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME); + boolean featureFlagEnabled = IS_BACK_PREDICTABILITY_ENABLED; + // If the context is null, we assume true and fallback on the two other conditions. + boolean appRequestsLegacy = + context == null || !context.getApplicationInfo().isOnBackInvokedCallbackEnabled(); + + if (DEBUG) { + Log.d(TAG, TextUtils.formatSimple("App: %s isChangeEnabled=%s featureFlagEnabled=%s " + + "onBackInvokedEnabled=%s", + context != null ? context.getApplicationInfo().packageName : "null context", + targetsT, featureFlagEnabled, !appRequestsLegacy)); + } + + return targetsT && featureFlagEnabled && !appRequestsLegacy; } } diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 6dc975b4112c5..0e0c6a34e818a 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2030,6 +2030,14 @@ --> + + +