From 0fb4f28f5dd87643a0abf1e9532188add297cbee Mon Sep 17 00:00:00 2001 From: Mohammed Rashidy Date: Tue, 6 Dec 2022 16:28:39 +0000 Subject: [PATCH] Disable resolving while intercepting based on a flag Adding a flag to ActivityInterceptResult which could disable resolving if interception happened. This flag is needed in case that SDK sandbox is intercepting the activity starting flow, as it would modify ActivityInfo (not the intent), so there is no need to resolve it again. Test: atest WmTests:ActivityStartInterceptorTest CTS-Coverage-Bug: 261605379 API-Coverage-Bug: 261605379 Bug: 248531721 Change-Id: Ie4b018d9fdf996bd10aa2e9a1350c6046f28fd14 --- services/api/current.txt | 49 +++++++++++++++++++ .../wm/ActivityInterceptorCallback.java | 37 ++++++++++++-- .../ActivityInterceptorCallbackRegistry.java | 2 + .../server/wm/ActivityStartInterceptor.java | 3 ++ .../wm/ActivityStartInterceptorTest.java | 34 ++++++++++++- 5 files changed, 121 insertions(+), 4 deletions(-) diff --git a/services/api/current.txt b/services/api/current.txt index da5b1fcaae99f..b5798d56f0c92 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -169,3 +169,52 @@ package com.android.server.wifi { } +package com.android.server.wm { + + public interface ActivityInterceptorCallback { + method public default void onActivityLaunched(@NonNull android.app.TaskInfo, @NonNull android.content.pm.ActivityInfo, @NonNull com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo); + method @Nullable public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult onInterceptActivityLaunch(@NonNull com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo); + field public static final int MAINLINE_SDK_SANDBOX_ORDER_ID = 1001; // 0x3e9 + } + + public static final class ActivityInterceptorCallback.ActivityInterceptResult { + ctor public ActivityInterceptorCallback.ActivityInterceptResult(@NonNull android.content.Intent, @NonNull android.app.ActivityOptions, boolean); + method @NonNull public android.app.ActivityOptions getActivityOptions(); + method @NonNull public android.content.Intent getIntent(); + method public boolean isActivityResolved(); + } + + public static final class ActivityInterceptorCallback.ActivityInterceptorInfo { + method @NonNull public android.content.pm.ActivityInfo getActivityInfo(); + method @Nullable public String getCallingFeatureId(); + method @Nullable public String getCallingPackage(); + method public int getCallingPid(); + method public int getCallingUid(); + method @Nullable public android.app.ActivityOptions getCheckedOptions(); + method @Nullable public Runnable getClearOptionsAnimationRunnable(); + method @NonNull public android.content.Intent getIntent(); + method public int getRealCallingPid(); + method public int getRealCallingUid(); + method @NonNull public android.content.pm.ResolveInfo getResolveInfo(); + method @Nullable public String getResolvedType(); + method public int getUserId(); + } + + public static final class ActivityInterceptorCallback.ActivityInterceptorInfo.Builder { + ctor public ActivityInterceptorCallback.ActivityInterceptorInfo.Builder(int, int, int, int, int, @NonNull android.content.Intent, @NonNull android.content.pm.ResolveInfo, @NonNull android.content.pm.ActivityInfo); + method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo build(); + method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setCallingFeatureId(@NonNull String); + method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setCallingPackage(@NonNull String); + method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setCheckedOptions(@NonNull android.app.ActivityOptions); + method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setClearOptionsAnimationRunnable(@NonNull Runnable); + method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setResolvedType(@NonNull String); + } + + public class ActivityInterceptorCallbackRegistry { + method @NonNull public static com.android.server.wm.ActivityInterceptorCallbackRegistry getInstance(); + method public void registerActivityInterceptorCallback(int, @NonNull com.android.server.wm.ActivityInterceptorCallback); + method public void unregisterActivityInterceptorCallback(int); + } + +} + diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java index c593fa3d91b97..ff1d44293dca8 100644 --- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java +++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java @@ -19,6 +19,7 @@ package com.android.server.wm; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.ActivityOptions; import android.app.TaskInfo; import android.content.Intent; @@ -33,6 +34,7 @@ import java.lang.annotation.RetentionPolicy; * be called with the WindowManagerGlobalLock held. * @hide */ +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public interface ActivityInterceptorCallback { /** * Called to allow intercepting activity launching based on the provided launch parameters and @@ -165,6 +167,7 @@ public interface ActivityInterceptorCallback { * Data class for storing the various arguments needed for activity interception. * @hide */ + @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) final class ActivityInterceptorInfo { private final int mCallingUid; private final int mCallingPid; @@ -389,6 +392,7 @@ public interface ActivityInterceptorCallback { * Data class for storing the intercept result. * @hide */ + @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) final class ActivityInterceptResult { @NonNull private final Intent mIntent; @@ -396,15 +400,35 @@ public interface ActivityInterceptorCallback { @NonNull private final ActivityOptions mActivityOptions; - /** Generates the result of intercepting launching the {@link android.app.Activity} + private final boolean mActivityResolved; + + /** + * This constructor should only be used if both {@link ActivityInfo} and {@link ResolveInfo} + * did not get resolved while interception. + * @hide + */ + public ActivityInterceptResult(@NonNull Intent intent, + @NonNull ActivityOptions activityOptions) { + this(intent, activityOptions, false /* activityResolved */); + } + + /** + * Generates the result of intercepting launching the {@link android.app.Activity} + * + *

Interceptor should return non-{@code null} result when {@link + * #onInterceptActivityLaunch(ActivityInterceptorInfo)} gets called as an indicator that + * interception has happened. * * @param intent is the modified {@link Intent} after interception. * @param activityOptions holds the {@link ActivityOptions} after interception. + * @param activityResolved should be {@code true} only if {@link ActivityInfo} or {@link + * ResolveInfo} gets resolved, otherwise should be {@code false}. */ - public ActivityInterceptResult( - @NonNull Intent intent, @NonNull ActivityOptions activityOptions) { + public ActivityInterceptResult(@NonNull Intent intent, + @NonNull ActivityOptions activityOptions, boolean activityResolved) { this.mIntent = intent; this.mActivityOptions = activityOptions; + this.mActivityResolved = activityResolved; } /** Returns the intercepted {@link Intent} */ @@ -419,5 +443,12 @@ public interface ActivityInterceptorCallback { public ActivityOptions getActivityOptions() { return mActivityOptions; } + + /** + * Returns if the {@link ActivityInfo} or {@link ResolveInfo} gets resolved. + */ + public boolean isActivityResolved() { + return mActivityResolved; + } } } diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java index cb66a39376a2f..bb9462f5b0341 100644 --- a/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java +++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Binder; import android.os.Process; @@ -30,6 +31,7 @@ import com.android.server.LocalServices; * int, ActivityInterceptorCallback)}. * @hide */ +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public class ActivityInterceptorCallbackRegistry { private static final ActivityInterceptorCallbackRegistry sInstance = diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 84100a7ab02d6..d4d0256d52566 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -244,6 +244,9 @@ class ActivityStartInterceptor { mActivityOptions = interceptResult.getActivityOptions(); mCallingPid = mRealCallingPid; mCallingUid = mRealCallingUid; + if (interceptResult.isActivityResolved()) { + return true; + } mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid); mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index b0461c2276dde..7d16fb2e02554 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -30,6 +30,7 @@ import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SAN import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; @@ -358,6 +359,12 @@ public class ActivityStartInterceptorTest { public void addMockInterceptorCallback( @Nullable Intent intent, @Nullable ActivityOptions activityOptions) { + addMockInterceptorCallback(intent, activityOptions, false); + } + + public void addMockInterceptorCallback( + @Nullable Intent intent, @Nullable ActivityOptions activityOptions, + boolean skipResolving) { int size = mActivityInterceptorCallbacks.size(); mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() { @Override @@ -368,7 +375,8 @@ public class ActivityStartInterceptorTest { } return new ActivityInterceptResult( intent != null ? intent : info.getIntent(), - activityOptions != null ? activityOptions : info.getCheckedOptions()); + activityOptions != null ? activityOptions : info.getCheckedOptions(), + skipResolving); } }); } @@ -400,6 +408,30 @@ public class ActivityStartInterceptorTest { assertEquals("android.test.second", mInterceptor.mIntent.getAction()); } + @Test + public void testInterceptionCallback_skipResolving() { + addMockInterceptorCallback( + new Intent("android.test.foo"), + ActivityOptions.makeBasic().setLaunchDisplayId(3), true); + ActivityInfo aInfo = mAInfo; + assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null)); + assertEquals("android.test.foo", mInterceptor.mIntent.getAction()); + assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId()); + assertEquals(aInfo, mInterceptor.mAInfo); // mAInfo should not be resolved + } + + @Test + public void testInterceptionCallback_NoSkipResolving() throws InterruptedException { + addMockInterceptorCallback( + new Intent("android.test.foo"), + ActivityOptions.makeBasic().setLaunchDisplayId(3)); + ActivityInfo aInfo = mAInfo; + assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null)); + assertEquals("android.test.foo", mInterceptor.mIntent.getAction()); + assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId()); + assertNotEquals(aInfo, mInterceptor.mAInfo); // mAInfo should be resolved after intercept + } + @Test public void testActivityLaunchedCallback_singleCallback() { addMockInterceptorCallback(null, null);