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

Commit aeb53657 authored by Lucas Silva's avatar Lucas Silva Committed by Android (Google) Code Review
Browse files

Merge "Add internal WM API for registering activity interceptors." into sc-v2-dev

parents 5250bc61 5c971042
Loading
Loading
Loading
Loading
+98 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

package com.android.server.wm;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Callback to intercept activity starts and possibly block/redirect them.
 */
public abstract class ActivityInterceptorCallback {
    /**
     * Intercept the launch intent based on various signals. If an interception happened, returns
     * a new/existing non-null {@link Intent} which may redirect to another activity.
     *
     * @return null if no interception occurred, or a non-null intent which replaces the
     * existing intent.
     */
    public abstract @Nullable Intent intercept(ActivityInterceptorInfo info);

    /**
     * The unique id of each interceptor which determines the order it will execute in.
     */
    @IntDef(suffix = { "_ORDERED_ID" }, value = {
            FIRST_ORDERED_ID,
            LAST_ORDERED_ID // Update this when adding new ids
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface OrderedId {}

    /**
     * The first id, used by the framework to determine the valid range of ids.
     */
    static final int FIRST_ORDERED_ID = 0;

    /**
     * The final id, used by the framework to determine the valid range of ids. Update this when
     * adding new ids.
     */
    static final int LAST_ORDERED_ID = FIRST_ORDERED_ID;

    /**
     * Data class for storing the various arguments needed for activity interception.
     */
    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 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) {
            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;
        }
    }
}
+28 −1
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppActivity;
@@ -178,7 +179,33 @@ class ActivityStartInterceptor {
            // before issuing the work challenge.
            return true;
        }
        return interceptLockedManagedProfileIfNeeded();
        if (interceptLockedManagedProfileIfNeeded()) {
            return true;
        }

        final SparseArray<ActivityInterceptorCallback> callbacks =
                mService.getActivityInterceptorCallbacks();
        final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
                new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
                        mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
                        mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
                        mActivityOptions);

        for (int i = 0; i < callbacks.size(); i++) {
            final ActivityInterceptorCallback callback = callbacks.valueAt(i);
            final Intent newIntent = callback.intercept(interceptorInfo);
            if (newIntent == null) {
                continue;
            }
            mIntent = newIntent;
            mCallingPid = mRealCallingPid;
            mCallingUid = mRealCallingUid;
            mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid);
            mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
                    null /*profilerInfo*/);
            return true;
        }
        return false;
    }

    private boolean hasCrossProfileAnimation() {
+8 −0
Original line number Diff line number Diff line
@@ -678,4 +678,12 @@ public abstract class ActivityTaskManagerInternal {

    /** Called when the device is waking up */
    public abstract void notifyWakingUp();

    /**
     * Registers a callback which can intercept activity starts.
     * @throws IllegalArgumentException if duplicate ids are provided
     */
    public abstract void registerActivityStartInterceptor(
            @ActivityInterceptorCallback.OrderedId int id,
            ActivityInterceptorCallback callback);
}
+26 −1
Original line number Diff line number Diff line
@@ -92,6 +92,8 @@ 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.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
@@ -458,6 +460,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    /** The controller for all operations related to locktask. */
    private LockTaskController mLockTaskController;
    private ActivityStartController mActivityStartController;
    private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
            new SparseArray<>();
    PackageConfigPersister mPackageConfigPersister;

    boolean mSuppressResizeConfigChanges;
@@ -1115,6 +1119,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        return mBackgroundActivityStartCallback;
    }

    SparseArray<ActivityInterceptorCallback> getActivityInterceptorCallbacks() {
        return mActivityInterceptorCallbacks;
    }

    private void start() {
        LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
    }
@@ -6583,5 +6591,22 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            getTransitionController().requestTransitionIfNeeded(TRANSIT_WAKE, 0 /* flags */,
                    null /* trigger */, mRootWindowContainer.getDefaultDisplay());
        }

        @Override
        public void registerActivityStartInterceptor(
                @ActivityInterceptorCallback.OrderedId int id,
                ActivityInterceptorCallback callback) {
            synchronized (mGlobalLock) {
                if (mActivityInterceptorCallbacks.contains(id)) {
                    throw new IllegalArgumentException("Duplicate id provided: " + id);
                }
                if (id > LAST_ORDERED_ID || id < FIRST_ORDERED_ID) {
                    throw new IllegalArgumentException(
                            "Provided id " + id + " is not in range of valid ids ["
                                    + FIRST_ORDERED_ID + "," + LAST_ORDERED_ID + "]");
                }
                mActivityInterceptorCallbacks.put(id, callback);
            }
        }
    }
}
+43 −1
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;

import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
@@ -42,6 +43,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.SparseArray;

import androidx.test.filters.SmallTest;

@@ -114,6 +116,9 @@ public class ActivityStartInterceptorTest {
    private ActivityStartInterceptor mInterceptor;
    private ActivityInfo mAInfo = new ActivityInfo();

    private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
            new SparseArray<>();

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
@@ -157,6 +162,9 @@ public class ActivityStartInterceptorTest {
                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
                .thenReturn(true);

        // Mock the activity start callbacks
        when(mService.getActivityInterceptorCallbacks()).thenReturn(mActivityInterceptorCallbacks);

        // Initialise activity info
        mAInfo.applicationInfo = new ApplicationInfo();
        mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -285,4 +293,38 @@ public class ActivityStartInterceptorTest {
        // THEN calling intercept returns false
        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
    }

    public void addMockInterceptorCallback(@Nullable Intent intent) {
        int size = mActivityInterceptorCallbacks.size();
        mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() {
            @Override
            public Intent intercept(ActivityInterceptorInfo info) {
                return intent;
            }
        });
    }

    @Test
    public void testInterceptionCallback_singleCallback() {
        addMockInterceptorCallback(new Intent("android.test.foo"));

        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
        assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
    }

    @Test
    public void testInterceptionCallback_singleCallbackReturnsNull() {
        addMockInterceptorCallback(null);

        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
    }

    @Test
    public void testInterceptionCallback_fallbackToSecondCallback() {
        addMockInterceptorCallback(null);
        addMockInterceptorCallback(new Intent("android.test.second"));

        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
        assertEquals("android.test.second", mInterceptor.mIntent.getAction());
    }
}
Loading