diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index da2c5162e6e18..f669062307839 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -134,21 +134,22 @@ public class VirtualDeviceManagerService extends SystemService { @Nullable @Override - public ActivityInterceptResult intercept(ActivityInterceptorInfo info) { - if (info.callingPackage == null) { + public ActivityInterceptResult onInterceptActivityLaunch(@NonNull + ActivityInterceptorInfo info) { + if (info.getCallingPackage() == null) { return null; } - PendingTrampoline pt = mPendingTrampolines.remove(info.callingPackage); + PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage()); if (pt == null) { return null; } pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null); - ActivityOptions options = info.checkedOptions; + ActivityOptions options = info.getCheckedOptions(); if (options == null) { options = ActivityOptions.makeBasic(); } return new ActivityInterceptResult( - info.intent, options.setLaunchDisplayId(pt.mDisplayId)); + info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId)); } }; diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index b8225419cb9bf..68f79a2900ac8 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -147,7 +147,8 @@ public final class DreamManagerService extends SystemService { new ActivityInterceptorCallback() { @Nullable @Override - public ActivityInterceptResult intercept(ActivityInterceptorInfo info) { + public ActivityInterceptResult onInterceptActivityLaunch(@NonNull + ActivityInterceptorInfo info) { return null; } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index d6cac33523212..db44e1414ca76 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -1130,18 +1130,18 @@ public final class PermissionPolicyService extends SystemService { new ActivityInterceptorCallback() { @Nullable @Override - public ActivityInterceptorCallback.ActivityInterceptResult intercept( - ActivityInterceptorInfo info) { + public ActivityInterceptorCallback.ActivityInterceptResult + onInterceptActivityLaunch(@NonNull ActivityInterceptorInfo info) { return null; } @Override public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo, ActivityInterceptorInfo info) { - super.onActivityLaunched(taskInfo, activityInfo, info); if (!shouldShowNotificationDialogOrClearFlags(taskInfo, - activityInfo.packageName, info.callingPackage, info.intent, - info.checkedOptions, activityInfo.name, true) + activityInfo.packageName, info.getCallingPackage(), + info.getIntent(), info.getCheckedOptions(), activityInfo.name, + true) || isNoDisplayActivity(activityInfo)) { return; } @@ -1318,12 +1318,12 @@ public final class PermissionPolicyService extends SystemService { ACTION_REQUEST_PERMISSIONS_FOR_OTHER); grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName); - final boolean remoteAnimation = info != null && info.checkedOptions != null - && info.checkedOptions.getAnimationType() == ANIM_REMOTE_ANIMATION - && info.clearOptionsAnimation != null; + final boolean remoteAnimation = info != null && info.getCheckedOptions() != null + && info.getCheckedOptions().getAnimationType() == ANIM_REMOTE_ANIMATION + && info.getClearOptionsAnimationRunnable() != null; ActivityOptions options = remoteAnimation ? ActivityOptions.makeRemoteAnimation( - info.checkedOptions.getRemoteAnimationAdapter(), - info.checkedOptions.getRemoteTransition()) + info.getCheckedOptions().getRemoteAnimationAdapter(), + info.getCheckedOptions().getRemoteTransition()) : new ActivityOptions(new Bundle()); options.setTaskOverlay(true, false); options.setLaunchTaskId(taskId); @@ -1333,7 +1333,7 @@ public final class PermissionPolicyService extends SystemService { // animation from the intercepted activity and its siblings to prevent duplication. // This should trigger ActivityRecord#clearOptionsAnimationForSiblings for the // intercepted activity. - info.clearOptionsAnimation.run(); + info.getClearOptionsAnimationRunnable().run(); } try { mContext.startActivityAsUser(grantPermission, options.toBundle(), user); diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java index 48e6f972fc8ee..c593fa3d91b97 100644 --- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java +++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java @@ -31,119 +31,393 @@ import java.lang.annotation.RetentionPolicy; /** * Callback to intercept activity starts and possibly block/redirect them. The callback methods will * be called with the WindowManagerGlobalLock held. + * @hide */ -public abstract class ActivityInterceptorCallback { +public interface ActivityInterceptorCallback { /** - * Intercept the launch intent based on various signals. If an interception happened, returns - * a new/existing non-null {@link ActivityInterceptResult} which may redirect to another - * activity or with new {@link ActivityOptions}. + * Called to allow intercepting activity launching based on the provided launch parameters and + * intent resolution. * - * @return null if no interception occurred, or a non-null result which replaces the existing - * intent and activity options. + *
If the interceptor decides to change the {@link Intent} or return different {@link + * ActivityOptions}, it should return a non-{@code null} {@link ActivityInterceptResult} which + * may redirect to another activity or use new {@link ActivityOptions}. Otherwise, the + * interceptor should return {@code null} to indicate that passed {@link Intent} should not be + * changed. + * + * @param info the information about the {@link Intent} that is being intercepted to launch an + * {@link android.app.Activity}. + * @return {@code null} if the interceptor decides not to change the existing intent, or a non- + * {@code null} result which replaces the existing intent and activity options. */ - public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info); + @Nullable + ActivityInterceptResult onInterceptActivityLaunch(@NonNull ActivityInterceptorInfo info); /** - * Called when an activity is successfully launched. The intent included in the - * ActivityInterceptorInfo may have changed from the one sent in - * {@link #intercept(ActivityInterceptorInfo)}, due to the return from - * {@link #intercept(ActivityInterceptorInfo)}. + * Called when an activity is successfully launched. + * + *
The intent included in the ActivityInterceptorInfo may have changed from the one sent in + * {@link #onInterceptActivityLaunch(ActivityInterceptorInfo)}, due to the changes might applied + * during internception. + * + *
There is no callback in case that the {@link android.app.Activity} is failed to launch, + * and this is not necessary to be added for the known use-cases. + * + * @param taskInfo the information about the @{@link Task} holds the launched + * {@link android.app.Activity}. + * @param activityInfo the information about the launched {@link android.app.Activity}. + * @param info the information about the {@link Intent} after calling {@link + * #onInterceptActivityLaunch(ActivityInterceptorInfo)}. */ - public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo, - ActivityInterceptorInfo info) { - } + default void onActivityLaunched(@NonNull TaskInfo taskInfo, @NonNull ActivityInfo activityInfo, + @NonNull ActivityInterceptorInfo info) {} /** - * The unique id of each interceptor which determines the order it will execute in. + * The unique id of each interceptor registered by a system service which determines the order + * it will execute in. + * @hide */ @IntDef(suffix = { "_ORDERED_ID" }, value = { - FIRST_ORDERED_ID, + // Order Ids for system services + SYSTEM_FIRST_ORDERED_ID, PERMISSION_POLICY_ORDERED_ID, VIRTUAL_DEVICE_SERVICE_ORDERED_ID, DREAM_MANAGER_ORDERED_ID, - LAST_ORDERED_ID // Update this when adding new ids + SYSTEM_LAST_ORDERED_ID, // Update this when adding new ids + // Order Ids for mainline module services + MAINLINE_FIRST_ORDERED_ID, + MAINLINE_SDK_SANDBOX_ORDER_ID, + MAINLINE_LAST_ORDERED_ID // Update this when adding new mainline module ids }) @Retention(RetentionPolicy.SOURCE) - public @interface OrderedId {} + @interface OrderedId {} /** * The first id, used by the framework to determine the valid range of ids. + * @hide */ - static final int FIRST_ORDERED_ID = 0; + int SYSTEM_FIRST_ORDERED_ID = 0; /** * The identifier for {@link com.android.server.policy.PermissionPolicyService} interceptor + * @hide */ - public static final int PERMISSION_POLICY_ORDERED_ID = 1; + int PERMISSION_POLICY_ORDERED_ID = 1; /** * The identifier for {@link com.android.server.companion.virtual.VirtualDeviceManagerService} * interceptor. + * @hide */ - public static final int VIRTUAL_DEVICE_SERVICE_ORDERED_ID = 3; + int VIRTUAL_DEVICE_SERVICE_ORDERED_ID = 3; /** * The identifier for {@link com.android.server.dreams.DreamManagerService} interceptor. + * @hide */ - public static final int DREAM_MANAGER_ORDERED_ID = 4; + int DREAM_MANAGER_ORDERED_ID = 4; /** * The final id, used by the framework to determine the valid range of ids. Update this when * adding new ids. + * @hide */ - static final int LAST_ORDERED_ID = DREAM_MANAGER_ORDERED_ID; + int SYSTEM_LAST_ORDERED_ID = DREAM_MANAGER_ORDERED_ID; + + /** + * The first mainline module id, used by the framework to determine the valid range of ids + * could be used by mainline modules. + * @hide + */ + int MAINLINE_FIRST_ORDERED_ID = 1000; + + /** + * The identifier for {@link com.android.server.sdksandbox.SdkSandboxManagerService.Lifecycle} + * interceptor. + */ + int MAINLINE_SDK_SANDBOX_ORDER_ID = 1001; + + /** + * The final mainline module id, used by the framework to determine the valid range of ids + * could be used by mainline modules. Update this when adding new ids for mainline modules. + * @hide + */ + int MAINLINE_LAST_ORDERED_ID = MAINLINE_SDK_SANDBOX_ORDER_ID; + + /** + * Returns {@code true} if the id is in the range of valid system services including mainline + * module services. + * @hide + */ + static boolean isValidOrderId(int id) { + return isValidMainlineOrderId(id) + || (id >= SYSTEM_FIRST_ORDERED_ID && id <= SYSTEM_LAST_ORDERED_ID); + } + + /** + * Returns {@code true} if the id is in the range of valid mainline module services. + * @hide + */ + static boolean isValidMainlineOrderId(int id) { + return id >= MAINLINE_FIRST_ORDERED_ID && id <= MAINLINE_LAST_ORDERED_ID; + } /** * Data class for storing the various arguments needed for activity interception. + * @hide */ - public static final class ActivityInterceptorInfo { - public final int realCallingUid; - public final int realCallingPid; - public final int userId; - public final String callingPackage; - public final String callingFeatureId; - public final Intent intent; - public final ResolveInfo rInfo; - public final ActivityInfo aInfo; - public final String resolvedType; - public final int callingPid; - public final int callingUid; - public final ActivityOptions checkedOptions; - public final @Nullable Runnable clearOptionsAnimation; + final class ActivityInterceptorInfo { + private final int mCallingUid; + private final int mCallingPid; + private final int mRealCallingUid; + private final int mRealCallingPid; + private final int mUserId; + private final Intent mIntent; + @NonNull + private final ResolveInfo mResolveInfo; + @NonNull + private final ActivityInfo mActivityInfo; + @Nullable + private final String mResolvedType; + @Nullable + private final String mCallingPackage; + @Nullable + private final String mCallingFeatureId; + @Nullable + private final ActivityOptions mCheckedOptions; + @Nullable + private final Runnable mClearOptionsAnimation; - public ActivityInterceptorInfo(int realCallingUid, int realCallingPid, int userId, - String callingPackage, String callingFeatureId, Intent intent, - ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, int callingPid, - int callingUid, ActivityOptions checkedOptions, - @Nullable Runnable clearOptionsAnimation) { - this.realCallingUid = realCallingUid; - this.realCallingPid = realCallingPid; - this.userId = userId; - this.callingPackage = callingPackage; - this.callingFeatureId = callingFeatureId; - this.intent = intent; - this.rInfo = rInfo; - this.aInfo = aInfo; - this.resolvedType = resolvedType; - this.callingPid = callingPid; - this.callingUid = callingUid; - this.checkedOptions = checkedOptions; - this.clearOptionsAnimation = clearOptionsAnimation; + /** + * @hide + */ + public ActivityInterceptorInfo(Builder builder) { + this.mCallingUid = builder.mCallingUid; + this.mCallingPid = builder.mCallingPid; + this.mRealCallingUid = builder.mRealCallingUid; + this.mRealCallingPid = builder.mRealCallingPid; + this.mUserId = builder.mUserId; + this.mIntent = builder.mIntent; + this.mResolveInfo = builder.mResolveInfo; + this.mActivityInfo = builder.mActivityInfo; + this.mResolvedType = builder.mResolvedType; + this.mCallingPackage = builder.mCallingPackage; + this.mCallingFeatureId = builder.mCallingFeatureId; + this.mCheckedOptions = builder.mCheckedOptions; + this.mClearOptionsAnimation = builder.mClearOptionsAnimation; + } + + /** + * Builder class to build instances of {@link ActivityInterceptorInfo}. + */ + public static final class Builder { + private final int mCallingUid; + private final int mCallingPid; + private final int mRealCallingUid; + private final int mRealCallingPid; + private final int mUserId; + private final Intent mIntent; + @NonNull + private final ResolveInfo mResolveInfo; + @NonNull + private final ActivityInfo mActivityInfo; + @Nullable + private String mResolvedType; + @Nullable + private String mCallingPackage = null; + @Nullable + private String mCallingFeatureId = null; + @Nullable + private ActivityOptions mCheckedOptions = null; + @Nullable + private Runnable mClearOptionsAnimation = null; + + /** + * Constructor of {@link ActivityInterceptorInfo.Builder}. + */ + public Builder(int callingUid, int callingPid, int realCallingUid, + int realCallingPid, int userId, @NonNull Intent intent, + @NonNull ResolveInfo rInfo, @NonNull ActivityInfo aInfo) { + this.mCallingUid = callingUid; + this.mCallingPid = callingPid; + this.mRealCallingUid = realCallingUid; + this.mRealCallingPid = realCallingPid; + this.mUserId = userId; + this.mIntent = intent; + this.mResolveInfo = rInfo; + this.mActivityInfo = aInfo; + } + + /** + * Returns a new instance of {@link ActivityInterceptorInfo} based on the {@link + * Builder} fields. + * + * @return a new instance of {@link ActivityInterceptorInfo}. + */ + @NonNull + public ActivityInterceptorInfo build() { + return new ActivityInterceptorInfo(this); + } + + /** + * Sets the value for the resolved type. + * @param resolvedType the resolved type. + */ + @NonNull + public Builder setResolvedType(@NonNull String resolvedType) { + mResolvedType = resolvedType; + return this; + } + + /** + * Sets the value for the calling package. + * @param callingPackage the calling package. + */ + @NonNull + public Builder setCallingPackage(@NonNull String callingPackage) { + mCallingPackage = callingPackage; + return this; + } + + /** + * Sets the value for the calling feature id. + * @param callingFeatureId the calling feature id. + */ + @NonNull + public Builder setCallingFeatureId(@NonNull String callingFeatureId) { + mCallingFeatureId = callingFeatureId; + return this; + } + + /** + * Sets the value for the {@link ActivityOptions}. + * @param checkedOptions the {@link ActivityOptions}. + */ + @NonNull + public Builder setCheckedOptions(@NonNull ActivityOptions checkedOptions) { + mCheckedOptions = checkedOptions; + return this; + } + + /** + * Sets the value for the {@link Runnable} object to clear options Animation. + * @param clearOptionsAnimationRunnable the calling package. + */ + @NonNull + public Builder setClearOptionsAnimationRunnable(@NonNull + Runnable clearOptionsAnimationRunnable) { + mClearOptionsAnimation = clearOptionsAnimationRunnable; + return this; + } + } + + /** Returns the calling uid. */ + public int getCallingUid() { + return mCallingUid; + } + + /** Returns the calling pid. */ + public int getCallingPid() { + return mCallingPid; + } + + /** Returns the real calling uid. */ + public int getRealCallingUid() { + return mRealCallingUid; + } + + /** Returns the real calling pid. */ + public int getRealCallingPid() { + return mRealCallingPid; + } + + /** Returns the user id. */ + public int getUserId() { + return mUserId; + } + + /** Returns the {@link Intent}. */ + @SuppressWarnings("IntentBuilderName") + @NonNull + public Intent getIntent() { + return mIntent; + } + + /** Returns the {@link ResolveInfo}. */ + @NonNull + public ResolveInfo getResolveInfo() { + return mResolveInfo; + } + + /** Returns the {@link ActivityInfo}. */ + @NonNull + public ActivityInfo getActivityInfo() { + return mActivityInfo; + } + + /** Returns the real resolved type. */ + @Nullable + public String getResolvedType() { + return mResolvedType; + } + + /** Returns the calling package. */ + @Nullable + public String getCallingPackage() { + return mCallingPackage; + } + + /** Returns the calling feature id. */ + @Nullable + public String getCallingFeatureId() { + return mCallingFeatureId; + } + + /** Returns the {@link ActivityOptions}. */ + @Nullable + public ActivityOptions getCheckedOptions() { + return mCheckedOptions; + } + + /** Returns the {@link Runnable} object to clear options Animation. */ + @Nullable + public Runnable getClearOptionsAnimationRunnable() { + return mClearOptionsAnimation; } } /** * Data class for storing the intercept result. + * @hide */ - public static final class ActivityInterceptResult { - @NonNull public final Intent intent; - @NonNull public final ActivityOptions activityOptions; + final class ActivityInterceptResult { + @NonNull + private final Intent mIntent; + @NonNull + private final ActivityOptions mActivityOptions; + + /** Generates the result of intercepting launching the {@link android.app.Activity} + * + * @param intent is the modified {@link Intent} after interception. + * @param activityOptions holds the {@link ActivityOptions} after interception. + */ public ActivityInterceptResult( - @NonNull Intent intent, - @NonNull ActivityOptions activityOptions) { - this.intent = intent; - this.activityOptions = activityOptions; + @NonNull Intent intent, @NonNull ActivityOptions activityOptions) { + this.mIntent = intent; + this.mActivityOptions = activityOptions; + } + + /** Returns the intercepted {@link Intent} */ + @SuppressWarnings("IntentBuilderName") + @NonNull + public Intent getIntent() { + return mIntent; + } + + /** Returns the intercepted {@link ActivityOptions} */ + @NonNull + public ActivityOptions getActivityOptions() { + return mActivityOptions; } } } diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 719f72c8ee522..84100a7ab02d6 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -23,6 +23,7 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION; import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES; +import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY; import static android.content.Context.KEYGUARD_SERVICE; import static android.content.Intent.EXTRA_INTENT; import static android.content.Intent.EXTRA_PACKAGE_NAME; @@ -33,7 +34,9 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.KeyguardManager; @@ -226,13 +229,19 @@ class ActivityStartInterceptor { getInterceptorInfo(null /* clearOptionsAnimation */); for (int i = 0; i < callbacks.size(); i++) { + final int orderId = callbacks.keyAt(i); + if (!shouldInterceptActivityLaunch(orderId, interceptorInfo)) { + continue; + } + final ActivityInterceptorCallback callback = callbacks.valueAt(i); - final ActivityInterceptResult interceptResult = callback.intercept(interceptorInfo); + final ActivityInterceptResult interceptResult = callback.onInterceptActivityLaunch( + interceptorInfo); if (interceptResult == null) { continue; } - mIntent = interceptResult.intent; - mActivityOptions = interceptResult.activityOptions; + mIntent = interceptResult.getIntent(); + mActivityOptions = interceptResult.getActivityOptions(); mCallingPid = mRealCallingPid; mCallingUid = mRealCallingUid; mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid); @@ -459,6 +468,11 @@ class ActivityStartInterceptor { ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo( r::clearOptionsAnimationForSiblings); for (int i = 0; i < callbacks.size(); i++) { + final int orderId = callbacks.keyAt(i); + if (!shouldNotifyOnActivityLaunch(orderId, info)) { + continue; + } + final ActivityInterceptorCallback callback = callbacks.valueAt(i); callback.onActivityLaunched(taskInfo, r.info, info); } @@ -466,9 +480,33 @@ class ActivityStartInterceptor { private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo( @Nullable Runnable clearOptionsAnimation) { - return new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid, - mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent, - mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid, - mActivityOptions, clearOptionsAnimation); + return new ActivityInterceptorCallback.ActivityInterceptorInfo.Builder(mCallingUid, + mCallingPid, mRealCallingUid, mRealCallingPid, mUserId, mIntent, mRInfo, mAInfo) + .setResolvedType(mResolvedType) + .setCallingPackage(mCallingPackage) + .setCallingFeatureId(mCallingFeatureId) + .setCheckedOptions(mActivityOptions) + .setClearOptionsAnimationRunnable(clearOptionsAnimation) + .build(); + } + + private boolean shouldInterceptActivityLaunch( + @ActivityInterceptorCallback.OrderedId int orderId, + @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) { + if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) { + return info.getIntent() != null && info.getIntent().getAction() != null + && info.getIntent().getAction().equals(ACTION_START_SANDBOXED_ACTIVITY); + } + return true; + } + + private boolean shouldNotifyOnActivityLaunch( + @ActivityInterceptorCallback.OrderedId int orderId, + @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) { + if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) { + return info.getIntent() != null && info.getIntent().getAction() != null + && info.getIntent().getAction().equals(ACTION_START_SANDBOXED_ACTIVITY); + } + return true; } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index e475eb6e992c0..c2d4bfdab4e5f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -94,8 +94,10 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE; import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen; import static com.android.server.am.EventLogTags.writeConfigurationChanged; -import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID; -import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID; +import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID; +import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID; +import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID; +import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID; import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK; @@ -6811,10 +6813,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (mActivityInterceptorCallbacks.contains(id)) { throw new IllegalArgumentException("Duplicate id provided: " + id); } - if (id > LAST_ORDERED_ID || id < FIRST_ORDERED_ID) { + if (!ActivityInterceptorCallback.isValidOrderId(id)) { throw new IllegalArgumentException( - "Provided id " + id + " is not in range of valid ids [" - + FIRST_ORDERED_ID + "," + LAST_ORDERED_ID + "]"); + "Provided id " + id + " is not in range of valid ids for system " + + "services [" + SYSTEM_FIRST_ORDERED_ID + "," + + SYSTEM_LAST_ORDERED_ID + "] nor in range of valid ids for " + + "mainline module services [" + MAINLINE_FIRST_ORDERED_ID + "," + + MAINLINE_LAST_ORDERED_ID + "]"); } mActivityInterceptorCallbacks.put(id, callback); } diff --git a/services/core/java/com/android/server/wm/package-info.java b/services/core/java/com/android/server/wm/package-info.java new file mode 100644 index 0000000000000..6ab5835019015 --- /dev/null +++ b/services/core/java/com/android/server/wm/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @hide + * TODO(b/146466118) remove this javadoc tag + */ +@android.annotation.Hide +package com.android.server.wm; 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 8a15c30afbb15..b0461c2276dde 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; @@ -25,6 +26,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -33,7 +35,9 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; @@ -68,6 +72,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; /** @@ -356,13 +361,14 @@ public class ActivityStartInterceptorTest { int size = mActivityInterceptorCallbacks.size(); mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() { @Override - public ActivityInterceptResult intercept(ActivityInterceptorInfo info) { + public ActivityInterceptResult onInterceptActivityLaunch(@NonNull + ActivityInterceptorInfo info) { if (intent == null && activityOptions == null) { return null; } return new ActivityInterceptResult( - intent != null ? intent : info.intent, - activityOptions != null ? activityOptions : info.checkedOptions); + intent != null ? intent : info.getIntent(), + activityOptions != null ? activityOptions : info.getCheckedOptions()); } }); } @@ -405,4 +411,30 @@ public class ActivityStartInterceptorTest { verify(callback, times(1)).onActivityLaunched(any(), any(), any()); } + + @Test + public void testSandboxServiceInterceptionHappensToSandboxedActivityAction() + throws InterruptedException { + + ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null); + mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback); + + Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + + verify(spyCallback, times(1)).onInterceptActivityLaunch( + any(ActivityInterceptorCallback.ActivityInterceptorInfo.class)); + } + + @Test + public void testSandboxServiceInterceptionNotCalledForNotSandboxedActivityAction() { + ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null); + mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback); + + Intent intent = new Intent(); + mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null); + + verify(spyCallback, never()).onInterceptActivityLaunch( + any(ActivityInterceptorCallback.ActivityInterceptorInfo.class)); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 368b750967c07..693d32eba3e8a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -25,8 +25,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID; -import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID; +import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID; +import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID; +import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityRecord.State.RESUMED; @@ -45,6 +46,7 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; @@ -957,11 +959,12 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { @Test(expected = IllegalArgumentException.class) public void testRegisterActivityStartInterceptor_IndexTooSmall() { - mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID - 1, + mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID - 1, new ActivityInterceptorCallback() { @Nullable @Override - public ActivityInterceptResult intercept(ActivityInterceptorInfo info) { + public ActivityInterceptResult onInterceptActivityLaunch( + @NonNull ActivityInterceptorInfo info) { return null; } }); @@ -969,11 +972,12 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { @Test(expected = IllegalArgumentException.class) public void testRegisterActivityStartInterceptor_IndexTooLarge() { - mAtm.mInternal.registerActivityStartInterceptor(LAST_ORDERED_ID + 1, + mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_LAST_ORDERED_ID + 1, new ActivityInterceptorCallback() { @Nullable @Override - public ActivityInterceptResult intercept(ActivityInterceptorInfo info) { + public ActivityInterceptResult onInterceptActivityLaunch( + @NonNull ActivityInterceptorInfo info) { return null; } }); @@ -981,19 +985,21 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { @Test(expected = IllegalArgumentException.class) public void testRegisterActivityStartInterceptor_DuplicateId() { - mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID, + mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID, new ActivityInterceptorCallback() { @Nullable @Override - public ActivityInterceptResult intercept(ActivityInterceptorInfo info) { + public ActivityInterceptResult onInterceptActivityLaunch( + @NonNull ActivityInterceptorInfo info) { return null; } }); - mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID, + mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID, new ActivityInterceptorCallback() { @Nullable @Override - public ActivityInterceptResult intercept(ActivityInterceptorInfo info) { + public ActivityInterceptResult onInterceptActivityLaunch( + @NonNull ActivityInterceptorInfo info) { return null; } }); @@ -1003,16 +1009,22 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { public void testRegisterActivityStartInterceptor() { assertEquals(0, mAtm.getActivityInterceptorCallbacks().size()); - mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID, + mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID, new ActivityInterceptorCallback() { @Nullable @Override - public ActivityInterceptResult intercept(ActivityInterceptorInfo info) { + public ActivityInterceptResult onInterceptActivityLaunch( + @NonNull ActivityInterceptorInfo info) { return null; } }); assertEquals(1, mAtm.getActivityInterceptorCallbacks().size()); - assertTrue(mAtm.getActivityInterceptorCallbacks().contains(FIRST_ORDERED_ID)); + assertTrue(mAtm.getActivityInterceptorCallbacks().contains(SYSTEM_FIRST_ORDERED_ID)); + } + + @Test + public void testSystemAndMainlineOrderIdsNotOverlapping() { + assertTrue(MAINLINE_FIRST_ORDERED_ID - SYSTEM_LAST_ORDERED_ID > 1); } }