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
This commit is contained in:
Mohammed Rashidy
2022-12-06 16:28:39 +00:00
parent f47192f8c3
commit 44036d2096
5 changed files with 310 additions and 1 deletions

View File

@@ -0,0 +1,120 @@
/*
* 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();
}
}

View File

@@ -680,12 +680,23 @@ public abstract class ActivityTaskManagerInternal {
/**
* 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(
@ActivityInterceptorCallback.OrderedId int id,
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). */
public abstract ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground();

View File

@@ -6813,6 +6813,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (mActivityInterceptorCallbacks.contains(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)) {
throw new IllegalArgumentException(
"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
public ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground() {
List<ActivityManager.RunningTaskInfo> runningTaskInfoList = getTasks(1);

View File

@@ -0,0 +1,141 @@
/*
* 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));
}
}

View File

@@ -1027,4 +1027,25 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
public void testSystemAndMainlineOrderIdsNotOverlapping() {
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);
}
}