Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ddb8f939 authored by Louis Chang's avatar Louis Chang Committed by Android (Google) Code Review
Browse files

Merge "Normalize home intent" into main

parents 71d5b160 67ace66f
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -138,3 +138,13 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    namespace: "windowing_sdk"
    name: "normalize_home_intent"
    description: "To ensure home is started in correct intent"
    bug: "378505461"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+94 −11
Original line number Diff line number Diff line
@@ -25,6 +25,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;
@@ -40,6 +43,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;
@@ -67,6 +71,7 @@ import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
import com.android.window.flags.Flags;

/**
 * A class that contains activity intercepting logic for {@link ActivityStarter#execute()}
@@ -119,6 +124,11 @@ class ActivityStartInterceptor {
     */
    TaskDisplayArea mPresumableLaunchDisplayArea;

    /**
     * Whether the component is specified originally in the given Intent.
     */
    boolean mComponentSpecified;

    ActivityStartInterceptor(
            ActivityTaskManagerService service, ActivityTaskSupervisor supervisor) {
        this(service, supervisor, service.mContext);
@@ -185,6 +195,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, TaskDisplayArea presumableLaunchDisplayArea) {
        return intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid,
                callingUid, activityOptions, presumableLaunchDisplayArea, 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.
@@ -193,7 +211,8 @@ class ActivityStartInterceptor {
     */
    boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
            Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
            ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea) {
            ActivityOptions activityOptions, TaskDisplayArea presumableLaunchDisplayArea,
            boolean componentSpecified) {
        mUserManager = UserManager.get(mServiceContext);

        mIntent = intent;
@@ -206,6 +225,7 @@ class ActivityStartInterceptor {
        mInTaskFragment = inTaskFragment;
        mActivityOptions = activityOptions;
        mPresumableLaunchDisplayArea = presumableLaunchDisplayArea;
        mComponentSpecified = componentSpecified;

        if (interceptQuietProfileIfNeeded()) {
            // If work profile is turned off, skip the work challenge since the profile can only
@@ -230,7 +250,8 @@ class ActivityStartInterceptor {
        }
        if (interceptHomeIfNeeded()) {
            // Replace primary home intents directed at displays that do not support primary home
            // but support secondary home with the relevant secondary home activity.
            // but support secondary home with the relevant secondary home activity. Or the home
            // intent is not in the correct format.
            return true;
        }

@@ -479,9 +500,78 @@ class ActivityStartInterceptor {
        if (mPresumableLaunchDisplayArea == null || mService.mRootWindowContainer == null) {
            return false;
        }

        boolean intercepted = false;
        if (Flags.normalizeHomeIntent()) {
            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;
            }
        } else {
            if (!ActivityRecord.isHomeIntent(mIntent)) {
                return false;
            }
        }

        intercepted |= replaceToSecondaryHomeIntentIfNeeded();
        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);
    }

    private boolean replaceToSecondaryHomeIntentIfNeeded() {
        if (!mIntent.hasCategory(Intent.CATEGORY_HOME)) {
            // Already a secondary home intent, leave it alone.
            return false;
@@ -506,13 +596,6 @@ class ActivityStartInterceptor {
        // and should not be moved to the caller's task. Also, activities cannot change their type,
        // e.g. a standard activity cannot become a home activity.
        mIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
        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 true;
    }

+2 −1
Original line number Diff line number Diff line
@@ -1341,7 +1341,8 @@ class ActivityStarter {
                callingPackage,
                callingFeatureId);
        if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
                callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) {
                callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea,
                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;
+19 −0
Original line number Diff line number Diff line
@@ -53,7 +53,9 @@ import android.content.pm.UserPackage;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.Pair;
import android.util.SparseArray;
@@ -66,6 +68,7 @@ import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.window.flags.Flags;

import org.junit.After;
import org.junit.Before;
@@ -133,6 +136,8 @@ public class ActivityStartInterceptorTest {
    private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
            new SparseArray<>();

    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Before
    public void setUp() throws RemoteException {
        MockitoAnnotations.initMocks(this);
@@ -236,6 +241,20 @@ public class ActivityStartInterceptorTest {
        return dialogInfo;
    }

    @Test
    @EnableFlags(Flags.FLAG_NORMALIZE_HOME_INTENT)
    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,
                mTaskDisplayArea, false));
        assertTrue(ActivityRecord.isHomeIntent(homeIntent));
    }

    @Test
    public void testInterceptLockTaskModeViolationPackage() {
        when(mLockTaskController.isActivityAllowed(