diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java new file mode 100644 index 0000000000000..cb66a39376a2f --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java @@ -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. + * + *
Only system services which are part of mainline modules should call this function. + * + *
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();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ec486437734b5..c63bd52900e8d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -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();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c2d4bfdab4e5f..9c41a2bca0eca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -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