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

Commit 44036d20 authored by Mohammed Rashidy's avatar Mohammed Rashidy
Browse files

Adding ActivityInterceptorCallbackRegistry

Adding ActivityInterceptorCallbackRegistry as a class which is visible
to the mainline modules, as it is not feasible to unhide
ActivityTaskManagerInternal.

Mainline modules will use this class to register
ActivityInterceptorCallback.

Adding undegister function to follow go/android-api-guidelines

Test: atest com.android.server.wm.ActivityInterceptorCallbackRegistryTest &&
      atest com.android.server.wm.ActivityStartInterceptorTest &&
      atest com.android.server.wm.ActivityTaskManagerServiceTests
Bug: 248531721
CTS-Coverage-Bug: 261598402
API-Coverage-Bug: 261598402
Change-Id: I790ea5672d39f7e1a7ffc936477f848ac5c559e3
parent f47192f8
Loading
Loading
Loading
Loading
+120 −0
Original line number Original line Diff line number Diff line
/*
 * 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.
 */

package com.android.server.wm;

import android.annotation.NonNull;
import android.os.Binder;
import android.os.Process;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;

/**
 * This class should be used by system services which are part of mainline modules to register
 * {@link ActivityInterceptorCallback}. For other system services, this function should be used
 * instead {@link ActivityTaskManagerInternal#registerActivityStartInterceptor(
 * int, ActivityInterceptorCallback)}.
 * @hide
 */
public class ActivityInterceptorCallbackRegistry {

    private static final ActivityInterceptorCallbackRegistry sInstance =
            new ActivityInterceptorCallbackRegistry();

    private ActivityInterceptorCallbackRegistry() {}

    /** Returns an already initialised singleton instance of this class. */
    @NonNull
    public static ActivityInterceptorCallbackRegistry getInstance() {
        return sInstance;
    }

    /**
     * Registers a callback which can intercept activity launching flow.
     *
     * <p>Only system services which are part of mainline modules should call this function.
     *
     * <p>To avoid Activity launch delays, the callbacks must execute quickly and avoid acquiring
     * other system process locks.
     *
     * @param mainlineOrderId has to be one of the following [{@link
     *                        ActivityInterceptorCallback#MAINLINE_FIRST_ORDERED_ID}].
     * @param callback the {@link ActivityInterceptorCallback} to register.
     * @throws IllegalArgumentException if duplicate ids are provided, the provided id is not the
     * mainline module range or the provided {@code callback} is null.
     */
    // ExecutorRegistration is suppressed as the callback is called synchronously in the system
    // server.
    @SuppressWarnings("ExecutorRegistration")
    public void registerActivityInterceptorCallback(
            @ActivityInterceptorCallback.OrderedId int mainlineOrderId,
            @NonNull ActivityInterceptorCallback callback) {
        if (getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Only system server can register "
                    + "ActivityInterceptorCallback");
        }
        if (!ActivityInterceptorCallback.isValidMainlineOrderId(mainlineOrderId)) {
            throw new IllegalArgumentException("id is not in the mainline modules range, please use"
                    + "ActivityTaskManagerInternal.registerActivityStartInterceptor(OrderedId, "
                    + "ActivityInterceptorCallback) instead.");
        }
        if (callback == null) {
            throw new IllegalArgumentException("The passed ActivityInterceptorCallback can not be "
                    + "null");
        }
        ActivityTaskManagerInternal activityTaskManagerInternal =
                LocalServices.getService(ActivityTaskManagerInternal.class);
        activityTaskManagerInternal.registerActivityStartInterceptor(mainlineOrderId, callback);
    }

    /**
     * Unregisters an already registered {@link ActivityInterceptorCallback}.
     *
     * @param mainlineOrderId the order id of the {@link ActivityInterceptorCallback} should be
     *                        unregistered, this callback should be registered before by calling
     *                        {@link #registerActivityInterceptorCallback(int,
     *                        ActivityInterceptorCallback)} using the same order id.
     * @throws IllegalArgumentException if the provided id is not the mainline module range or is
     * not registered
     */
    public void unregisterActivityInterceptorCallback(
            @ActivityInterceptorCallback.OrderedId int mainlineOrderId) {
        if (getCallingUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Only system server can register "
                    + "ActivityInterceptorCallback");
        }
        if (!ActivityInterceptorCallback.isValidMainlineOrderId(mainlineOrderId)) {
            throw new IllegalArgumentException("id is not in the mainline modules range, please use"
                    + "ActivityTaskManagerInternal.unregisterActivityStartInterceptor(OrderedId) "
                    + "instead.");
        }
        ActivityTaskManagerInternal activityTaskManagerInternal =
                LocalServices.getService(ActivityTaskManagerInternal.class);
        activityTaskManagerInternal.unregisterActivityStartInterceptor(mainlineOrderId);
    }

    /**
     * This hidden function is for unit tests as a way to behave like as if they are called from
     * system server process uid by mocking it and returning {@link Process#SYSTEM_UID}.
     * Do not make this {@code public} to apps as apps should not have a way to change the uid.
     * @hide
     */
    @VisibleForTesting
    int getCallingUid() {
        return Binder.getCallingUid();
    }
}
+12 −1
Original line number Original line Diff line number Diff line
@@ -680,12 +680,23 @@ public abstract class ActivityTaskManagerInternal {


    /**
    /**
     * Registers a callback which can intercept activity starts.
     * Registers a callback which can intercept activity starts.
     * @throws IllegalArgumentException if duplicate ids are provided
     * @throws IllegalArgumentException if duplicate ids are provided or the provided {@code
     * callback} is null
     * @see ActivityInterceptorCallbackRegistry
     * #registerActivityInterceptorCallback(int, ActivityInterceptorCallback)
     */
     */
    public abstract void registerActivityStartInterceptor(
    public abstract void registerActivityStartInterceptor(
            @ActivityInterceptorCallback.OrderedId int id,
            @ActivityInterceptorCallback.OrderedId int id,
            ActivityInterceptorCallback callback);
            ActivityInterceptorCallback callback);


    /**
     * Unregisters an {@link ActivityInterceptorCallback}.
     * @throws IllegalArgumentException if id is not registered
     * @see ActivityInterceptorCallbackRegistry#unregisterActivityInterceptorCallback(int)
     */
    public abstract void unregisterActivityStartInterceptor(
            @ActivityInterceptorCallback.OrderedId int id);

    /** Get the most recent task excluding the first running task (the one on the front most). */
    /** Get the most recent task excluding the first running task (the one on the front most). */
    public abstract ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground();
    public abstract ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground();


+16 −0
Original line number Original line Diff line number Diff line
@@ -6813,6 +6813,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
                if (mActivityInterceptorCallbacks.contains(id)) {
                if (mActivityInterceptorCallbacks.contains(id)) {
                    throw new IllegalArgumentException("Duplicate id provided: " + id);
                    throw new IllegalArgumentException("Duplicate id provided: " + id);
                }
                }
                if (callback == null) {
                    throw new IllegalArgumentException("The passed ActivityInterceptorCallback "
                            + "can not be null");
                }
                if (!ActivityInterceptorCallback.isValidOrderId(id)) {
                if (!ActivityInterceptorCallback.isValidOrderId(id)) {
                    throw new IllegalArgumentException(
                    throw new IllegalArgumentException(
                            "Provided id " + id + " is not in range of valid ids for system "
                            "Provided id " + id + " is not in range of valid ids for system "
@@ -6825,6 +6829,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            }
            }
        }
        }


        @Override
        public void unregisterActivityStartInterceptor(
                @ActivityInterceptorCallback.OrderedId int id) {
            synchronized (mGlobalLock) {
                if (!mActivityInterceptorCallbacks.contains(id)) {
                    throw new IllegalArgumentException(
                            "ActivityInterceptorCallback with id (" + id + ") is not registered");
                }
                mActivityInterceptorCallbacks.remove(id);
            }
        }

        @Override
        @Override
        public ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground() {
        public ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground() {
            List<ActivityManager.RunningTaskInfo> runningTaskInfoList = getTasks(1);
            List<ActivityManager.RunningTaskInfo> runningTaskInfoList = getTasks(1);
+141 −0
Original line number Original line Diff line number Diff line
/*
 * 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.
 */

package com.android.server.wm;

import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;

import android.os.Process;
import android.platform.test.annotations.Presubmit;

import androidx.test.filters.MediumTest;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;

/**
 * Tests for the {@link ActivityInterceptorCallbackRegistry} class.
 */
@Presubmit
@MediumTest
@RunWith(WindowTestRunner.class)
public final class ActivityInterceptorCallbackRegistryTest extends WindowTestsBase {

    private ActivityInterceptorCallbackRegistry mRegistry;

    @Before
    public void setUp() {
        mRegistry = spy(ActivityInterceptorCallbackRegistry.getInstance());
        Mockito.doReturn(Process.SYSTEM_UID).when(mRegistry).getCallingUid();
    }

    @Test
    public void registerActivityInterceptorCallbackFailIfNotSystemId() {
        // default registry with test app uid
        ActivityInterceptorCallbackRegistry registry = spy(
                ActivityInterceptorCallbackRegistry.getInstance());
        assertThrows(
                SecurityException.class,
                () ->  registry.registerActivityInterceptorCallback(MAINLINE_LAST_ORDERED_ID + 1,
                        info -> null)
        );
    }

    @Test
    public void registerActivityInterceptorCallbackFailIfIdNotInRange() {
        assertThrows(
                IllegalArgumentException.class,
                () ->  mRegistry.registerActivityInterceptorCallback(MAINLINE_LAST_ORDERED_ID + 1,
                        info -> null)
        );

        assertThrows(
                IllegalArgumentException.class,
                () ->  mRegistry.registerActivityInterceptorCallback(MAINLINE_FIRST_ORDERED_ID - 1,
                        info -> null)
        );
    }

    @Test
    public void registerActivityInterceptorCallbackFailIfCallbackIsNull() {
        assertThrows(
                IllegalArgumentException.class,
                () ->  mRegistry.registerActivityInterceptorCallback(MAINLINE_FIRST_ORDERED_ID,
                        null)
        );
    }

    @Test
    public void registerActivityInterceptorCallbackSuccessfully() {
        int size = mAtm.getActivityInterceptorCallbacks().size();
        int orderId = MAINLINE_FIRST_ORDERED_ID;
        mRegistry.registerActivityInterceptorCallback(orderId,
                info -> null);
        assertEquals(size + 1, mAtm.getActivityInterceptorCallbacks().size());
        assertTrue(mAtm.getActivityInterceptorCallbacks().contains(orderId));
    }

    @Test
    public void unregisterActivityInterceptorCallbackFailIfNotSystemId() {
        // default registry with test app uid
        ActivityInterceptorCallbackRegistry registry = spy(
                ActivityInterceptorCallbackRegistry.getInstance());
        assertThrows(
                SecurityException.class,
                () ->  registry.unregisterActivityInterceptorCallback(MAINLINE_LAST_ORDERED_ID + 1)
        );
    }

    @Test
    public void unRegisterActivityInterceptorCallbackFailIfIdNotInRange() {
        assertThrows(
                IllegalArgumentException.class,
                () ->  mRegistry.unregisterActivityInterceptorCallback(
                        MAINLINE_LAST_ORDERED_ID + 1));
    }

    @Test
    public void unregisterActivityInterceptorCallbackFailIfNotRegistered() {
        assertThrows(
                IllegalArgumentException.class,
                () ->  mRegistry.unregisterActivityInterceptorCallback(MAINLINE_FIRST_ORDERED_ID)
        );
    }

    @Test
    public void unregisterActivityInterceptorCallbackSuccessfully() {
        int size = mAtm.getActivityInterceptorCallbacks().size();
        int orderId = MAINLINE_FIRST_ORDERED_ID;
        mRegistry.registerActivityInterceptorCallback(orderId,
                info -> null);
        assertEquals(size + 1, mAtm.getActivityInterceptorCallbacks().size());
        assertTrue(mAtm.getActivityInterceptorCallbacks().contains(orderId));

        mRegistry.unregisterActivityInterceptorCallback(orderId);
        assertEquals(size, mAtm.getActivityInterceptorCallbacks().size());
        assertFalse(mAtm.getActivityInterceptorCallbacks().contains(orderId));

    }
}
+21 −0
Original line number Original line Diff line number Diff line
@@ -1027,4 +1027,25 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
    public void testSystemAndMainlineOrderIdsNotOverlapping() {
    public void testSystemAndMainlineOrderIdsNotOverlapping() {
        assertTrue(MAINLINE_FIRST_ORDERED_ID - SYSTEM_LAST_ORDERED_ID > 1);
        assertTrue(MAINLINE_FIRST_ORDERED_ID - SYSTEM_LAST_ORDERED_ID > 1);
    }
    }

    @Test
    public void testUnregisterActivityStartInterceptor() {
        int size = mAtm.getActivityInterceptorCallbacks().size();
        int orderId = SYSTEM_FIRST_ORDERED_ID;

        mAtm.mInternal.registerActivityStartInterceptor(orderId,
                (ActivityInterceptorCallback) info -> null);
        assertEquals(size + 1, mAtm.getActivityInterceptorCallbacks().size());
        assertTrue(mAtm.getActivityInterceptorCallbacks().contains(orderId));

        mAtm.mInternal.unregisterActivityStartInterceptor(orderId);
        assertEquals(size, mAtm.getActivityInterceptorCallbacks().size());
        assertFalse(mAtm.getActivityInterceptorCallbacks().contains(orderId));
    }

    @Test(expected = IllegalArgumentException.class)
    public void testUnregisterActivityStartInterceptor_IdNotExist() {
        assertEquals(0, mAtm.getActivityInterceptorCallbacks().size());
        mAtm.mInternal.unregisterActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID);
    }
}
}