summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Louis Chang <louischang@google.com> 2025-03-05 19:06:25 -0800
committer Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> 2025-03-05 19:06:25 -0800
commitee0af51fb0666b489ad17174a117b4caf3bfe099 (patch)
treea385c1b25715a218d47ac7a1e95ab2af5c76f98b
parent3fb79a62ae618c403ad58dcd1d04a359afece5c4 (diff)
parent17f72a6129f66d415e1ed86de065d576ca9dd8be (diff)
RESTRICT AUTOMERGE Normalize home intent am: 9472387c9c am: 17f72a6129
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/31738532 Change-Id: I8dd712e61afe171bebaabc04c4fa02a4847aac36 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java96
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java13
4 files changed, 109 insertions, 4 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index add27078b00f..323b64f00d2e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2926,7 +2926,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
reparent(newTaskFrag, position);
}
- private boolean isHomeIntent(Intent intent) {
+ static boolean isHomeIntent(Intent intent) {
return ACTION_MAIN.equals(intent.getAction())
&& (intent.hasCategory(CATEGORY_HOME)
|| intent.hasCategory(CATEGORY_SECONDARY_HOME))
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 1eb56f1b7d1c..1c5865308594 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -24,6 +24,9 @@ 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.content.Context.KEYGUARD_SERVICE;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_HOME;
+import static android.content.Intent.CATEGORY_SECONDARY_HOME;
import static android.content.Intent.EXTRA_INTENT;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_TASK_ID;
@@ -41,6 +44,7 @@ import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.TaskInfo;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
@@ -55,6 +59,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -66,6 +71,7 @@ import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
+
/**
* A class that contains activity intercepting logic for {@link ActivityStarter#execute()}
* It's initialized via setStates and interception occurs via the intercept method.
@@ -74,6 +80,7 @@ import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult
* is no guarantee that other system services are already present.
*/
class ActivityStartInterceptor {
+ private static final String TAG = "ActivityStartInterceptor";
private final ActivityTaskManagerService mService;
private final ActivityTaskSupervisor mSupervisor;
@@ -110,6 +117,11 @@ class ActivityStartInterceptor {
TaskFragment mInTaskFragment;
ActivityOptions mActivityOptions;
+ /**
+ * Whether the component is specified originally in the given Intent.
+ */
+ boolean mComponentSpecified;
+
ActivityStartInterceptor(
ActivityTaskManagerService service, ActivityTaskSupervisor supervisor) {
this(service, supervisor, service.mRootWindowContainer, service.mContext);
@@ -158,7 +170,6 @@ class ActivityStartInterceptor {
return new IntentSender(target);
}
-
/**
* A helper function to obtain the targeted {@link TaskFragment} during
* {@link #intercept(Intent, ResolveInfo, ActivityInfo, String, Task, TaskFragment, int, int,
@@ -179,6 +190,14 @@ class ActivityStartInterceptor {
return TaskFragment.fromTaskFragmentToken(taskFragToken, mService);
}
+ // TODO: consolidate this method with the one below since this is used for test only.
+ boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
+ Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
+ ActivityOptions activityOptions) {
+ return intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid,
+ callingUid, activityOptions, false);
+ }
+
/**
* Intercept the launch intent based on various signals. If an interception happened the
* internal variables get assigned and need to be read explicitly by the caller.
@@ -187,7 +206,7 @@ class ActivityStartInterceptor {
*/
boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
- ActivityOptions activityOptions) {
+ ActivityOptions activityOptions, boolean componentSpecified) {
mUserManager = UserManager.get(mServiceContext);
mIntent = intent;
@@ -199,6 +218,7 @@ class ActivityStartInterceptor {
mInTask = inTask;
mInTaskFragment = inTaskFragment;
mActivityOptions = activityOptions;
+ mComponentSpecified = componentSpecified;
if (interceptQuietProfileIfNeeded()) {
// If work profile is turned off, skip the work challenge since the profile can only
@@ -221,6 +241,10 @@ class ActivityStartInterceptor {
if (interceptLockedManagedProfileIfNeeded()) {
return true;
}
+ if (interceptHomeIfNeeded()) {
+ // Replace primary home intents if the home intent is not in the correct format.
+ return true;
+ }
final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks();
@@ -481,6 +505,74 @@ class ActivityStartInterceptor {
return dpmi == null || dpmi.isKeepProfilesRunningEnabled();
}
+ private boolean interceptHomeIfNeeded() {
+ if (mService.mRootWindowContainer == null) {
+ return false;
+ }
+
+ boolean intercepted = false;
+ if (!ACTION_MAIN.equals(mIntent.getAction()) || (!mIntent.hasCategory(CATEGORY_HOME)
+ && !mIntent.hasCategory(CATEGORY_SECONDARY_HOME))) {
+ // not a home intent
+ return false;
+ }
+
+ if (mComponentSpecified) {
+ final ComponentName homeComponent = mIntent.getComponent();
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfo = mService.mRootWindowContainer.resolveHomeActivity(
+ mUserId, homeIntent);
+ if (!aInfo.getComponentName().equals(homeComponent)) {
+ // Do nothing if the intent is not for the default home component.
+ return false;
+ }
+ }
+
+ if (!ActivityRecord.isHomeIntent(mIntent) || mComponentSpecified) {
+ // This is not a standard home intent, make it so if possible.
+ normalizeHomeIntent();
+ intercepted = true;
+ }
+
+ if (intercepted) {
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mResolvedType = null;
+
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, /* flags= */ 0,
+ mRealCallingUid, mRealCallingPid);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, /*profilerInfo=*/
+ null);
+ }
+ return intercepted;
+ }
+
+ private void normalizeHomeIntent() {
+ Slog.w(TAG, "The home Intent is not correctly formatted");
+ if (mIntent.getCategories().size() > 1) {
+ Slog.d(TAG, "Purge home intent categories");
+ boolean isSecondaryHome = false;
+ final Object[] categories = mIntent.getCategories().toArray();
+ for (int i = categories.length - 1; i >= 0; i--) {
+ final String category = (String) categories[i];
+ if (CATEGORY_SECONDARY_HOME.equals(category)) {
+ isSecondaryHome = true;
+ }
+ mIntent.removeCategory(category);
+ }
+ mIntent.addCategory(isSecondaryHome ? CATEGORY_SECONDARY_HOME : CATEGORY_HOME);
+ }
+ if (mIntent.getType() != null || mIntent.getData() != null) {
+ Slog.d(TAG, "Purge home intent data/type");
+ mIntent.setType(null);
+ }
+ if (mComponentSpecified) {
+ Slog.d(TAG, "Purge home intent component, " + mIntent.getComponent());
+ mIntent.setComponent(null);
+ }
+ mIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ }
+
/**
* Called when an activity is successfully launched.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3c650e32beb9..69660a2efa8d 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1154,7 +1154,7 @@ class ActivityStarter {
mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
callingFeatureId);
if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
- callingPid, callingUid, checkedOptions)) {
+ callingPid, callingUid, checkedOptions, request.componentSpecified)) {
// activity start was intercepted, e.g. because the target user is currently in quiet
// mode (turn off work) or the target application is suspended
intent = mInterceptor.mIntent;
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 bcb0c6b5c269..1d4f13966d80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -238,6 +238,19 @@ public class ActivityStartInterceptorTest {
}
@Test
+ public void testInterceptIncorrectHomeIntent() {
+ // Create a non-standard home intent
+ final Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ // Ensure the intent is intercepted and normalized to standard home intent.
+ assertTrue(mInterceptor.intercept(homeIntent, null, mAInfo, null, null, null, 0, 0, null,
+ false));
+ assertTrue(ActivityRecord.isHomeIntent(homeIntent));
+ }
+
+ @Test
public void testInterceptLockTaskModeViolationPackage() {
when(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))