summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tony Mak <tonymak@google.com> 2024-09-19 22:04:32 +0100
committer Tony Mak <tonymak@google.com> 2024-09-19 21:17:41 +0000
commitb2af38ed58dc24f00c375ff4e9b306c89d194f98 (patch)
treed07a364d5c64e05dc3906deb8dc4e0a40065ebd7
parent10bd1cf8af5523612eef4c929c9543774d78230c (diff)
Move executeAppFunctionInternal off binder thread
so that the threading model is more clear. ExecuteAppFunction is running in the binder thread and ExecuteAppFunctionInternal is not. Also, only close a session when we have finished using it. And improve the latency of onUserUnlocked. Now it only takes 14ms Flag: android.app.appfunctions.flags.enable_app_function_manager Bug: 360864791 Fixes: 368153367 Test: atest CtsAppFunctionTestCases Change-Id: Iabd87cab440fd0459acda30ece1ddd9ef576d6af
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java129
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java8
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java73
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java3
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java11
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java16
6 files changed, 124 insertions, 116 deletions
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index cf039df3bc96..469a1a2251e8 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -20,6 +20,7 @@ import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_E
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.WorkerThread;
import android.app.appfunctions.AppFunctionStaticMetadataHelper;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
import android.app.appfunctions.ExecuteAppFunctionResponse;
@@ -45,7 +46,6 @@ import com.android.server.SystemService.TargetUser;
import com.android.server.appfunctions.RemoteServiceCaller.RunServiceCallCallback;
import com.android.server.appfunctions.RemoteServiceCaller.ServiceUsageCompleteListener;
-import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.CompletionException;
@@ -83,23 +83,6 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
mServiceConfig = serviceConfig;
}
- @Override
- public void executeAppFunction(
- @NonNull ExecuteAppFunctionAidlRequest requestInternal,
- @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback) {
- Objects.requireNonNull(requestInternal);
- Objects.requireNonNull(executeAppFunctionCallback);
-
- final SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback =
- new SafeOneTimeExecuteAppFunctionCallback(executeAppFunctionCallback);
-
- try {
- executeAppFunctionInternal(requestInternal, safeExecuteAppFunctionCallback);
- } catch (Exception e) {
- safeExecuteAppFunctionCallback.onResult(mapExceptionToExecuteAppFunctionResponse(e));
- }
- }
-
/** Called when the user is unlocked. */
public void onUserUnlocked(TargetUser user) {
Objects.requireNonNull(user);
@@ -120,18 +103,22 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
}
}
- private void executeAppFunctionInternal(
- ExecuteAppFunctionAidlRequest requestInternal,
- SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback) {
+ @Override
+ public void executeAppFunction(
+ @NonNull ExecuteAppFunctionAidlRequest requestInternal,
+ @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback) {
+ Objects.requireNonNull(requestInternal);
+ Objects.requireNonNull(executeAppFunctionCallback);
+
+ final SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback =
+ new SafeOneTimeExecuteAppFunctionCallback(executeAppFunctionCallback);
String validatedCallingPackage;
- UserHandle targetUser;
try {
validatedCallingPackage =
mCallerValidator.validateCallingPackage(requestInternal.getCallingPackage());
- targetUser =
- mCallerValidator.verifyTargetUserHandle(
- requestInternal.getUserHandle(), validatedCallingPackage);
+ mCallerValidator.verifyTargetUserHandle(
+ requestInternal.getUserHandle(), validatedCallingPackage);
} catch (SecurityException exception) {
safeExecuteAppFunctionCallback.onResult(
ExecuteAppFunctionResponse.newFailure(
@@ -141,6 +128,30 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
return;
}
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingUid();
+ THREAD_POOL_EXECUTOR.execute(
+ () -> {
+ try {
+ executeAppFunctionInternal(
+ requestInternal,
+ callingUid,
+ callingPid,
+ safeExecuteAppFunctionCallback);
+ } catch (Exception e) {
+ safeExecuteAppFunctionCallback.onResult(
+ mapExceptionToExecuteAppFunctionResponse(e));
+ }
+ });
+ }
+
+ @WorkerThread
+ private void executeAppFunctionInternal(
+ ExecuteAppFunctionAidlRequest requestInternal,
+ int callingUid,
+ int callingPid,
+ SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback) {
+ UserHandle targetUser = requestInternal.getUserHandle();
// TODO(b/354956319): Add and honor the new enterprise policies.
if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
safeExecuteAppFunctionCallback.onResult(
@@ -165,7 +176,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
var unused =
mCallerValidator
.verifyCallerCanExecuteAppFunction(
- validatedCallingPackage,
+ callingUid,
+ callingPid,
+ requestInternal.getCallingPackage(),
targetPackageName,
requestInternal.getClientRequest().getFunctionIdentifier())
.thenAccept(
@@ -191,19 +204,14 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
/* extras= */ null));
return;
}
- final long token = Binder.clearCallingIdentity();
- try {
- bindAppFunctionServiceUnchecked(
- requestInternal,
- serviceIntent,
- targetUser,
- safeExecuteAppFunctionCallback,
- /* bindFlags= */ Context.BIND_AUTO_CREATE,
- /* timeoutInMillis= */ mServiceConfig
- .getExecuteAppFunctionTimeoutMillis());
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ bindAppFunctionServiceUnchecked(
+ requestInternal,
+ serviceIntent,
+ targetUser,
+ safeExecuteAppFunctionCallback,
+ /* bindFlags= */ Context.BIND_AUTO_CREATE,
+ /* timeoutInMillis= */ mServiceConfig
+ .getExecuteAppFunctionTimeoutMillis());
})
.exceptionally(
ex -> {
@@ -332,30 +340,27 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
Slog.d(TAG, "AppSearch Manager not found for user: " + user.getUserIdentifier());
return;
}
- try (FutureGlobalSearchSession futureGlobalSearchSession =
+ FutureGlobalSearchSession futureGlobalSearchSession =
new FutureGlobalSearchSession(
- perUserAppSearchManager, AppFunctionExecutors.THREAD_POOL_EXECUTOR)) {
- AppFunctionMetadataObserver appFunctionMetadataObserver =
- new AppFunctionMetadataObserver(
- user.getUserHandle(),
- mContext.createContextAsUser(user.getUserHandle(), /* flags= */ 0));
- var unused =
- futureGlobalSearchSession
- .registerObserverCallbackAsync(
- "android",
- new ObserverSpec.Builder().build(),
- THREAD_POOL_EXECUTOR,
- appFunctionMetadataObserver)
- .whenComplete(
- (voidResult, ex) -> {
- if (ex != null) {
- Slog.e(TAG, "Failed to register observer: ", ex);
- }
- });
-
- } catch (IOException ex) {
- Slog.e(TAG, "Failed to close observer session: ", ex);
- }
+ perUserAppSearchManager, AppFunctionExecutors.THREAD_POOL_EXECUTOR);
+ AppFunctionMetadataObserver appFunctionMetadataObserver =
+ new AppFunctionMetadataObserver(
+ user.getUserHandle(),
+ mContext.createContextAsUser(user.getUserHandle(), /* flags= */ 0));
+ var unused =
+ futureGlobalSearchSession
+ .registerObserverCallbackAsync(
+ "android",
+ new ObserverSpec.Builder().build(),
+ THREAD_POOL_EXECUTOR,
+ appFunctionMetadataObserver)
+ .whenComplete(
+ (voidResult, ex) -> {
+ if (ex != null) {
+ Slog.e(TAG, "Failed to register observer: ", ex);
+ }
+ futureGlobalSearchSession.close();
+ });
}
private void trySyncRuntimeMetadata(@NonNull TargetUser user) {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index e7a861e0a0dc..3592ed587ab0 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -60,9 +60,9 @@ public interface CallerValidator {
* Validates that the caller can execute the specified app function.
*
* <p>The caller can execute if the app function's package name is the same as the caller's
- * package or the caller has either {@link Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or
- * {@link Manifest.permission.EXECUTE_APP_FUNCTIONS} granted. In some cases, app functions can
- * still opt-out of caller having {@link Manifest.permission.EXECUTE_APP_FUNCTIONS}.
+ * package or the caller has either {@link Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} or
+ * {@link Manifest.permission#EXECUTE_APP_FUNCTIONS} granted. In some cases, app functions can
+ * still opt-out of caller having {@link Manifest.permission#EXECUTE_APP_FUNCTIONS}.
*
* @param callerPackageName The calling package (as previously validated).
* @param targetPackageName The package that owns the app function to execute.
@@ -70,6 +70,8 @@ public interface CallerValidator {
* @return Whether the caller can execute the specified app function.
*/
AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
+ int callingUid,
+ int callingPid,
@NonNull String callerPackageName,
@NonNull String targetPackageName,
@NonNull String functionId);
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index 94a63b43dd17..8b6251a59e3a 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -20,6 +20,7 @@ import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCT
import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_NAMESPACE;
import static android.app.appfunctions.AppFunctionStaticMetadataHelper.STATIC_PROPERTY_RESTRICT_CALLERS_WITH_EXECUTE_APP_FUNCTIONS;
import static android.app.appfunctions.AppFunctionStaticMetadataHelper.getDocumentIdForAppFunction;
+
import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_EXECUTOR;
import android.Manifest;
@@ -41,6 +42,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.infra.AndroidFuture;
+
import java.util.Objects;
/* Validates that caller has the correct privilege to call an AppFunctionManager Api. */
@@ -82,7 +84,6 @@ class CallerValidatorImpl implements CallerValidator {
}
@Override
- @BinderThread
@RequiresPermission(
anyOf = {
Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
@@ -90,6 +91,8 @@ class CallerValidatorImpl implements CallerValidator {
},
conditional = true)
public AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
+ int callingUid,
+ int callingPid,
@NonNull String callerPackageName,
@NonNull String targetPackageName,
@NonNull String functionId) {
@@ -97,11 +100,11 @@ class CallerValidatorImpl implements CallerValidator {
return AndroidFuture.completedFuture(true);
}
- int pid = Binder.getCallingPid();
- int uid = Binder.getCallingUid();
boolean hasTrustedExecutionPermission =
mContext.checkPermission(
- Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, pid, uid)
+ Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+ callingPid,
+ callingUid)
== PackageManager.PERMISSION_GRANTED;
if (hasTrustedExecutionPermission) {
@@ -109,40 +112,34 @@ class CallerValidatorImpl implements CallerValidator {
}
boolean hasExecutionPermission =
- mContext.checkPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS, pid, uid)
+ mContext.checkPermission(
+ Manifest.permission.EXECUTE_APP_FUNCTIONS, callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED;
if (!hasExecutionPermission) {
return AndroidFuture.completedFuture(false);
}
- final long token = Binder.clearCallingIdentity();
- try {
- FutureAppSearchSession futureAppSearchSession =
- new FutureAppSearchSessionImpl(
- mContext.getSystemService(AppSearchManager.class),
- THREAD_POOL_EXECUTOR,
- new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
+ FutureAppSearchSession futureAppSearchSession =
+ new FutureAppSearchSessionImpl(
+ mContext.getSystemService(AppSearchManager.class),
+ THREAD_POOL_EXECUTOR,
+ new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
- String documentId = getDocumentIdForAppFunction(targetPackageName, functionId);
+ String documentId = getDocumentIdForAppFunction(targetPackageName, functionId);
- return futureAppSearchSession
- .getByDocumentId(
- new GetByDocumentIdRequest.Builder(APP_FUNCTION_STATIC_NAMESPACE)
- .addIds(documentId)
- .build())
- .thenApply(
- batchResult ->
- getGenericDocumentFromBatchResult(batchResult, documentId))
- .thenApply(
- CallerValidatorImpl::getRestrictCallersWithExecuteAppFunctionsProperty)
- .thenApply(
- restrictCallersWithExecuteAppFunctions ->
- !restrictCallersWithExecuteAppFunctions
- && hasExecutionPermission);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return futureAppSearchSession
+ .getByDocumentId(
+ new GetByDocumentIdRequest.Builder(APP_FUNCTION_STATIC_NAMESPACE)
+ .addIds(documentId)
+ .build())
+ .thenApply(
+ batchResult -> getGenericDocumentFromBatchResult(batchResult, documentId))
+ .thenApply(document -> !getRestrictCallersWithExecuteAppFunctionsProperty(document))
+ .whenComplete(
+ (result, throwable) -> {
+ futureAppSearchSession.close();
+ });
}
private static GenericDocument getGenericDocumentFromBatchResult(
@@ -167,19 +164,13 @@ class CallerValidatorImpl implements CallerValidator {
}
@Override
- @BinderThread
public boolean isUserOrganizationManaged(@NonNull UserHandle targetUser) {
- final long callingIdentityToken = Binder.clearCallingIdentity();
- try {
- if (Objects.requireNonNull(mContext.getSystemService(DevicePolicyManager.class))
- .isDeviceManaged()) {
- return true;
- }
- return Objects.requireNonNull(mContext.getSystemService(UserManager.class))
- .isManagedProfile(targetUser.getIdentifier());
- } finally {
- Binder.restoreCallingIdentity(callingIdentityToken);
+ if (Objects.requireNonNull(mContext.getSystemService(DevicePolicyManager.class))
+ .isDeviceManaged()) {
+ return true;
}
+ return Objects.requireNonNull(mContext.getSystemService(UserManager.class))
+ .isManagedProfile(targetUser.getIdentifier());
}
/**
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
index 0044b4b958cb..de2034b5be2f 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
@@ -84,6 +84,9 @@ public interface FutureAppSearchSession extends Closeable {
AndroidFuture<FutureSearchResults> search(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+ @Override
+ void close();
+
/** A future API wrapper of {@link android.app.appsearch.SearchResults}. */
interface FutureSearchResults {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
index 3079d9f51bf3..d24bb871c393 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSessionImpl.java
@@ -38,7 +38,6 @@ import android.app.appsearch.SetSchemaResponse;
import com.android.internal.infra.AndroidFuture;
-import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -183,7 +182,15 @@ public class FutureAppSearchSessionImpl implements FutureAppSearchSession {
}
@Override
- public void close() throws IOException {}
+ public void close() {
+ getSessionAsync()
+ .whenComplete(
+ (appSearchSession, throwable) -> {
+ if (appSearchSession != null) {
+ appSearchSession.close();
+ }
+ });
+ }
private static final class FutureSearchResultsImpl implements FutureSearchResults {
private final SearchResults mSearchResults;
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
index 0c2262456032..874c5daa1662 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
@@ -23,12 +23,10 @@ import android.app.appsearch.GlobalSearchSession;
import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.observer.ObserverCallback;
import android.app.appsearch.observer.ObserverSpec;
-import android.util.Slog;
import com.android.internal.infra.AndroidFuture;
import java.io.Closeable;
-import java.io.IOException;
import java.util.concurrent.Executor;
/** A wrapper around {@link GlobalSearchSession} that provides a future-based API. */
@@ -84,11 +82,13 @@ public class FutureGlobalSearchSession implements Closeable {
}
@Override
- public void close() throws IOException {
- try {
- getSessionAsync().get().close();
- } catch (Exception ex) {
- Slog.e(TAG, "Failed to close global search session", ex);
- }
+ public void close() {
+ getSessionAsync()
+ .whenComplete(
+ (appSearchSession, throwable) -> {
+ if (appSearchSession != null) {
+ appSearchSession.close();
+ }
+ });
}
}