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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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<OnBackInvokedCallback, Integer> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2030,6 +2030,14 @@
|
||||
-->
|
||||
<attr name="resetEnabledSettingsOnAppDataCleared" format="boolean" />
|
||||
<attr name="knownActivityEmbeddingCerts" />
|
||||
|
||||
<!-- If false, {@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK} and
|
||||
{@link android.app.Activity#onBackPressed Activity.onBackPressed()}
|
||||
and related event will be forwarded to the Activities and View, otherwise those events
|
||||
will be replaced by a call to
|
||||
{@link android.view.OnBackInvokedCallback#onBackInvoked
|
||||
OnBackInvokedCallback.onBackInvoked()} on the focused window. -->
|
||||
<attr name="enableOnBackInvokedCallback" format="boolean"/>
|
||||
</declare-styleable>
|
||||
|
||||
<!-- An attribution is a logical part of an app and is identified by a tag.
|
||||
|
||||
@@ -3279,6 +3279,7 @@
|
||||
<public name="allowUntrustedActivityEmbedding" />
|
||||
<public name="knownActivityEmbeddingCerts" />
|
||||
<public name="intro" />
|
||||
<public name="enableOnBackInvokedCallback" />
|
||||
</staging-public-group>
|
||||
|
||||
<staging-public-group type="id" first-id="0x01de0000">
|
||||
|
||||
@@ -2653,6 +2653,12 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"664667685": {
|
||||
"message": "Activity %s: enableOnBackInvokedCallback=false. Returning null BackNavigationInfo.",
|
||||
"level": "DEBUG",
|
||||
"group": "WM_DEBUG_BACK_PREVIEW",
|
||||
"at": "com\/android\/server\/wm\/BackNavigationController.java"
|
||||
},
|
||||
"665256544": {
|
||||
"message": "All windows drawn!",
|
||||
"level": "DEBUG",
|
||||
|
||||
@@ -54,7 +54,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
|
||||
private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
|
||||
public static final boolean IS_ENABLED = SystemProperties
|
||||
.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
|
||||
.getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
|
||||
private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
|
||||
"persist.debug.back_predictability_progress_threshold";
|
||||
private static final int PROGRESS_THRESHOLD = SystemProperties
|
||||
|
||||
@@ -887,7 +887,9 @@ public class PackageInfoWithoutStateUtils {
|
||||
| flag(pkg.hasRequestForegroundServiceExemption(),
|
||||
ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION)
|
||||
| flag(pkg.areAttributionsUserVisible(),
|
||||
ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE);
|
||||
ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE)
|
||||
| flag(pkg.isOnBackInvokedCallbackEnabled(),
|
||||
ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK);
|
||||
// @formatter:on
|
||||
return privateFlagsExt;
|
||||
}
|
||||
|
||||
@@ -387,6 +387,8 @@ public interface ParsingPackage extends ParsingPackageRead {
|
||||
*/
|
||||
ParsingPackage setKnownActivityEmbeddingCerts(Set<String> knownActivityEmbeddingCerts);
|
||||
|
||||
ParsingPackage setOnBackInvokedCallbackEnabled(boolean enableOnBackInvokedCallback);
|
||||
|
||||
// TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
|
||||
// for moving to the next step
|
||||
@CallSuper
|
||||
|
||||
@@ -548,6 +548,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48;
|
||||
private static final long SDK_LIBRARY = 1L << 49;
|
||||
private static final long INHERIT_KEYSTORE_KEYS = 1L << 50;
|
||||
private static final long ENABLE_ON_BACK_INVOKED_CALLBACK = 1L << 51;
|
||||
}
|
||||
|
||||
private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
|
||||
@@ -2396,6 +2397,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
return getBoolean(Booleans.INHERIT_KEYSTORE_KEYS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnBackInvokedCallbackEnabled() {
|
||||
return getBoolean(Booleans.ENABLE_ON_BACK_INVOKED_CALLBACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParsingPackageImpl setBaseRevisionCode(int value) {
|
||||
baseRevisionCode = value;
|
||||
@@ -2995,4 +3001,10 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
mKnownActivityEmbeddingCerts = knownEmbeddingCerts;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParsingPackage setOnBackInvokedCallbackEnabled(boolean value) {
|
||||
setBoolean(Booleans.ENABLE_ON_BACK_INVOKED_CALLBACK, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,4 +355,9 @@ public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutSt
|
||||
* @see R.styleable#AndroidManifest_inheritKeyStoreKeys
|
||||
*/
|
||||
boolean shouldInheritKeyStoreKeys();
|
||||
|
||||
/**
|
||||
* @see R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback
|
||||
*/
|
||||
boolean isOnBackInvokedCallbackEnabled();
|
||||
}
|
||||
|
||||
@@ -2202,8 +2202,9 @@ public class ParsingPackageUtils {
|
||||
.setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
|
||||
.setAttributionsAreUserVisible(bool(false, R.styleable.AndroidManifestApplication_attributionsAreUserVisible, sa))
|
||||
.setResetEnabledSettingsOnAppDataCleared(bool(false,
|
||||
R.styleable.AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared,
|
||||
sa))
|
||||
R.styleable.AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared,
|
||||
sa))
|
||||
.setOnBackInvokedCallbackEnabled(bool(false, R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback, sa))
|
||||
// targetSdkVersion gated
|
||||
.setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
|
||||
.setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
|
||||
|
||||
@@ -53,7 +53,11 @@ class BackNavigationController {
|
||||
* Returns true if the back predictability feature is enabled
|
||||
*/
|
||||
static boolean isEnabled() {
|
||||
return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
|
||||
return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
|
||||
}
|
||||
|
||||
static boolean isScreenshotEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,6 +105,13 @@ class BackNavigationController {
|
||||
|
||||
synchronized (task.mWmService.mGlobalLock) {
|
||||
activityRecord = task.topRunningActivity();
|
||||
|
||||
if(!activityRecord.info.applicationInfo.isOnBackInvokedCallbackEnabled()) {
|
||||
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Activity %s: enableOnBackInvokedCallback=false."
|
||||
+ " Returning null BackNavigationInfo.", activityRecord.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
removedWindowContainer = activityRecord;
|
||||
taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
|
||||
WindowState window = task.getWindow(WindowState::isFocused);
|
||||
|
||||
@@ -1695,7 +1695,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
if (isAddingActivity && task != null) {
|
||||
|
||||
// TODO(b/207481538): temporary per-activity screenshoting
|
||||
if (r != null && BackNavigationController.isEnabled()) {
|
||||
if (r != null && BackNavigationController.isScreenshotEnabled()) {
|
||||
ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
|
||||
r.mActivityComponent.flattenToString());
|
||||
Rect outBounds = r.getBounds();
|
||||
@@ -2298,7 +2298,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
|
||||
void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
|
||||
super.removeChild(child);
|
||||
if (BackNavigationController.isEnabled()) {
|
||||
if (BackNavigationController.isScreenshotEnabled()) {
|
||||
//TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
|
||||
// implemented
|
||||
ActivityRecord r = child.asActivityRecord();
|
||||
|
||||
@@ -509,7 +509,12 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
|
||||
ParsingPackage::setInheritKeyStoreKeys,
|
||||
true
|
||||
),
|
||||
getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT"))
|
||||
getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")),
|
||||
getSetByValue(
|
||||
AndroidPackage::isOnBackInvokedCallbackEnabled,
|
||||
ParsingPackage::setOnBackInvokedCallbackEnabled,
|
||||
true
|
||||
)
|
||||
)
|
||||
|
||||
override fun initialObject() = PackageImpl.forParsing(
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
|
||||
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
|
||||
import static android.window.BackNavigationInfo.typeToString;
|
||||
|
||||
@@ -117,6 +118,9 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
||||
private Task createTopTaskWithActivity() {
|
||||
Task task = createTask(mDefaultDisplay);
|
||||
ActivityRecord record = createActivityRecord(task);
|
||||
// enable OnBackInvokedCallbacks
|
||||
record.info.applicationInfo.privateFlagsExt |=
|
||||
PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
|
||||
createWindow(null, FIRST_APPLICATION_WINDOW, record, "window");
|
||||
when(record.mSurfaceControl.isValid()).thenReturn(true);
|
||||
mAtm.setFocusedTask(task.mTaskId, record);
|
||||
|
||||
Reference in New Issue
Block a user