Merge "Enforce cross-uid touch pass-though opt-in" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 3ffab90..c33c053 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4919,6 +4919,7 @@
method public int getPendingIntentBackgroundActivityStartMode();
method public int getPendingIntentCreatorBackgroundActivityStartMode();
method public int getSplashScreenStyle();
+ method @FlaggedApi("com.android.window.flags.touch_pass_through_opt_in") public boolean isAllowPassThroughOnTouchOutside();
method @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed();
method public boolean isShareIdentityEnabled();
method public static android.app.ActivityOptions makeBasic();
@@ -4932,6 +4933,7 @@
method public static android.app.ActivityOptions makeTaskLaunchBehind();
method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
method public void requestUsageTimeReport(android.app.PendingIntent);
+ method @FlaggedApi("com.android.window.flags.touch_pass_through_opt_in") public void setAllowPassThroughOnTouchOutside(boolean);
method public android.app.ActivityOptions setAppVerificationBundle(android.os.Bundle);
method public android.app.ActivityOptions setLaunchBounds(@Nullable android.graphics.Rect);
method public android.app.ActivityOptions setLaunchDisplayId(int);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 91aa225..0d183c7 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -26,6 +26,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -453,6 +454,10 @@
private static final String KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE =
"android.activity.pendingIntentCreatorBackgroundActivityStartMode";
+ /** See {@link #setAllowPassThroughOnTouchOutside(boolean)}. */
+ private static final String KEY_ALLOW_PASS_THROUGH_ON_TOUCH_OUTSIDE =
+ "android.activity.allowPassThroughOnTouchOutside";
+
/**
* @see #setLaunchCookie
* @hide
@@ -554,6 +559,7 @@
private int mPendingIntentCreatorBackgroundActivityStartMode =
MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
private boolean mDisableStartingWindow;
+ private boolean mAllowPassThroughOnTouchOutside;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -1416,6 +1422,7 @@
KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE,
MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
mDisableStartingWindow = opts.getBoolean(KEY_DISABLE_STARTING_WINDOW);
+ mAllowPassThroughOnTouchOutside = opts.getBoolean(KEY_ALLOW_PASS_THROUGH_ON_TOUCH_OUTSIDE);
mAnimationAbortListener = IRemoteCallback.Stub.asInterface(
opts.getBinder(KEY_ANIM_ABORT_LISTENER));
}
@@ -1839,6 +1846,39 @@
&& mLaunchIntoPipParams.isLaunchIntoPip();
}
+ /**
+ * Returns whether the source activity allows the overlaying activities from the to-be-launched
+ * app to pass through touch events to it when touches fall outside the content window.
+ *
+ * @see #setAllowPassThroughOnTouchOutside(boolean)
+ */
+ @FlaggedApi(com.android.window.flags.Flags.FLAG_TOUCH_PASS_THROUGH_OPT_IN)
+ public boolean isAllowPassThroughOnTouchOutside() {
+ return mAllowPassThroughOnTouchOutside;
+ }
+
+ /**
+ * Sets whether the source activity allows the overlaying activities from the to-be-launched
+ * app to pass through touch events to it when touches fall outside the content window.
+ *
+ * <p> By default, touches that fall on a translucent non-touchable area of an overlaying
+ * activity window are blocked from passing through to the activity below (source activity),
+ * unless the overlaying activity is from the same UID as the source activity. The source
+ * activity may use this method to opt in and allow the overlaying activities from the
+ * to-be-launched app to pass through touches to itself. The source activity needs to ensure
+ * that it trusts the overlaying activity and its content is not vulnerable to UI redressing
+ * attacks. The flag is ignored if the context calling
+ * {@link Context#startActivity(Intent, Bundle)} is not an activity.
+ *
+ * <p> For backward compatibility, apps with target SDK 35 and below may still receive
+ * pass-through touches without opt-in if the cross-uid activity is launched by the source
+ * activity.
+ */
+ @FlaggedApi(com.android.window.flags.Flags.FLAG_TOUCH_PASS_THROUGH_OPT_IN)
+ public void setAllowPassThroughOnTouchOutside(boolean allowed) {
+ mAllowPassThroughOnTouchOutside = allowed;
+ }
+
/** @hide */
public int getLaunchActivityType() {
return mLaunchActivityType;
@@ -2520,6 +2560,10 @@
if (mDisableStartingWindow) {
b.putBoolean(KEY_DISABLE_STARTING_WINDOW, mDisableStartingWindow);
}
+ if (mAllowPassThroughOnTouchOutside) {
+ b.putBoolean(KEY_ALLOW_PASS_THROUGH_ON_TOUCH_OUTSIDE,
+ mAllowPassThroughOnTouchOutside);
+ }
b.putBinder(KEY_ANIM_ABORT_LISTENER,
mAnimationAbortListener != null ? mAnimationAbortListener.asBinder() : null);
return b;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 12d733f..f8e8ca4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2154,7 +2154,10 @@
}
mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName);
- mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord);
+ final boolean appOptInTouchPassThrough =
+ options != null && options.isAllowPassThroughOnTouchOutside();
+ mActivityRecordInputSink = new ActivityRecordInputSink(
+ this, sourceRecord, appOptInTouchPassThrough);
mAppActivityEmbeddingSplitsEnabled = isAppActivityEmbeddingSplitsEnabled();
mAllowUntrustedEmbeddingStateSharing = getAllowUntrustedEmbeddingStateSharingProperty();
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index 1a19787..fa5beca 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -16,13 +16,18 @@
package com.android.server.wm;
+import android.app.ActivityOptions;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.os.Build;
import android.os.InputConfig;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import com.android.window.flags.Flags;
+
/**
* Creates a InputWindowHandle that catches all touches that would otherwise pass through an
* Activity.
@@ -35,6 +40,21 @@
@ChangeId
static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L;
+ // TODO(b/369605358) Update EnabledSince when SDK 36 version code is available.
+ /**
+ * If the app's target SDK is 36+, pass-through touches from a cross-uid overlaying activity is
+ * blocked by default. The activity may opt in to receive pass-through touches using
+ * {@link ActivityOptions#setAllowPassThroughOnTouchOutside}, which allows the to-be-launched
+ * cross-uid overlaying activity and other activities in that app to pass through touches. The
+ * activity needs to ensure that it trusts the overlaying app and its content is not vulnerable
+ * to UI redressing attacks.
+ *
+ * @see ActivityOptions#setAllowPassThroughOnTouchOutside
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
+ static final long ENABLE_OVERLAY_TOUCH_PASS_THROUGH_OPT_IN_ENFORCEMENT = 358129114L;
+
private final ActivityRecord mActivityRecord;
private final boolean mIsCompatEnabled;
private final String mName;
@@ -42,13 +62,24 @@
private InputWindowHandleWrapper mInputWindowHandleWrapper;
private SurfaceControl mSurfaceControl;
- ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) {
+ ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord,
+ boolean appOptInTouchPassThrough) {
mActivityRecord = activityRecord;
mIsCompatEnabled = CompatChanges.isChangeEnabled(ENABLE_TOUCH_OPAQUE_ACTIVITIES,
mActivityRecord.getUid());
mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink "
+ mActivityRecord.mActivityComponent.flattenToShortString();
- if (sourceRecord != null) {
+
+ if (sourceRecord == null) {
+ return;
+ }
+ // If the source activity has target sdk 36+, it is required to opt in to receive
+ // pass-through touches from the overlaying activity.
+ final boolean isTouchPassThroughOptInEnforced = CompatChanges.isChangeEnabled(
+ ENABLE_OVERLAY_TOUCH_PASS_THROUGH_OPT_IN_ENFORCEMENT,
+ sourceRecord.getUid());
+ if (!Flags.touchPassThroughOptIn() || !isTouchPassThroughOptInEnforced
+ || appOptInTouchPassThrough) {
sourceRecord.mAllowedTouchUid = mActivityRecord.getUid();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index 5787780..4cd75d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -308,6 +308,8 @@
// KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE
case "android.activity.launchCookie": // KEY_LAUNCH_COOKIE
case "android:activity.animAbortListener": // KEY_ANIM_ABORT_LISTENER
+ case "android.activity.allowPassThroughOnTouchOutside":
+ // KEY_ALLOW_PASS_THROUGH_ON_TOUCH_OUTSIDE
// Existing keys
break;