summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tony Mak <tonymak@google.com> 2025-02-17 19:59:06 +0000
committer Tony Mak <tonymak@google.com> 2025-02-17 20:13:10 +0000
commit164e6cde1e2bdaeae53762ecebf5e1c4d244fe1e (patch)
treebd7f4198ce7a4847cc1a37ae2b4d8f3dd476b18b
parent387b32ce9d1d80a58c58f50f1993e5de085bee06 (diff)
Use BIND_FOREGROUND_SERVICE only if the caller has the permission
Bug: 392614489 Test: atest CtsAppFunctionTestCases Flag: EXEMPT bugfix Change-Id: I2bb081b46aa902bf0b1526f2a34a02b33ae1c8e6
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java55
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java32
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java67
3 files changed, 73 insertions, 81 deletions
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index d0ee7af1bbfb..5191fb5f51cb 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -20,6 +20,8 @@ import static android.app.appfunctions.AppFunctionRuntimeMetadata.APP_FUNCTION_R
import static android.app.appfunctions.AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_NAMESPACE;
import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_EXECUTOR;
+import static com.android.server.appfunctions.CallerValidator.CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION;
+import static com.android.server.appfunctions.CallerValidator.CAN_EXECUTE_APP_FUNCTIONS_DENIED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -236,30 +238,42 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
requestInternal.getCallingPackage(),
targetPackageName,
requestInternal.getClientRequest().getFunctionIdentifier())
- .thenAccept(
- canExecute -> {
- if (!canExecute) {
- throw new SecurityException(
+ .thenCompose(
+ canExecuteResult -> {
+ if (canExecuteResult == CAN_EXECUTE_APP_FUNCTIONS_DENIED) {
+ return AndroidFuture.failedFuture(new SecurityException(
"Caller does not have permission to execute the"
- + " appfunction");
+ + " appfunction"));
}
+ return isAppFunctionEnabled(
+ requestInternal
+ .getClientRequest()
+ .getFunctionIdentifier(),
+ requestInternal
+ .getClientRequest()
+ .getTargetPackageName(),
+ getAppSearchManagerAsUser(
+ requestInternal.getUserHandle()),
+ THREAD_POOL_EXECUTOR)
+ .thenApply(
+ isEnabled -> {
+ if (!isEnabled) {
+ throw new DisabledAppFunctionException(
+ "The app function is disabled");
+ }
+ return canExecuteResult;
+ });
})
- .thenCompose(
- isEnabled ->
- isAppFunctionEnabled(
- requestInternal.getClientRequest().getFunctionIdentifier(),
- requestInternal.getClientRequest().getTargetPackageName(),
- getAppSearchManagerAsUser(requestInternal.getUserHandle()),
- THREAD_POOL_EXECUTOR))
.thenAccept(
- isEnabled -> {
- if (!isEnabled) {
- throw new DisabledAppFunctionException(
- "The app function is disabled");
+ canExecuteResult -> {
+ int bindFlags = Context.BIND_AUTO_CREATE;
+ if (canExecuteResult
+ == CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION) {
+ // If the caller doesn't have the permission, do not use
+ // BIND_FOREGROUND_SERVICE to avoid it raising its process state by
+ // calling its own AppFunctions.
+ bindFlags |= Context.BIND_FOREGROUND_SERVICE;
}
- })
- .thenAccept(
- unused -> {
Intent serviceIntent =
mInternalServiceHelper.resolveAppFunctionService(
targetPackageName, targetUser);
@@ -294,8 +308,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
targetUser,
localCancelTransport,
safeExecuteAppFunctionCallback,
- /* bindFlags= */ Context.BIND_AUTO_CREATE
- | Context.BIND_FOREGROUND_SERVICE,
+ bindFlags,
callerBinder,
callingUid);
})
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
index 98ef974b9443..c8038a4e56df 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -17,12 +17,16 @@
package com.android.server.appfunctions;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.UserHandle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Interface for validating that the caller has the correct privilege to call an AppFunctionManager
* API.
@@ -70,7 +74,8 @@ public interface CallerValidator {
* @param functionId The id of the app function to execute.
* @return Whether the caller can execute the specified app function.
*/
- AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
+ @CanExecuteAppFunctionResult
+ AndroidFuture<Integer> verifyCallerCanExecuteAppFunction(
int callingUid,
int callingPid,
@NonNull UserHandle targetUser,
@@ -78,6 +83,31 @@ public interface CallerValidator {
@NonNull String targetPackageName,
@NonNull String functionId);
+ @IntDef(
+ prefix = {"CAN_EXECUTE_APP_FUNCTIONS_"},
+ value = {
+ CAN_EXECUTE_APP_FUNCTIONS_DENIED,
+ CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_SAME_PACKAGE,
+ CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CanExecuteAppFunctionResult {}
+
+ /** Callers are not allowed to execute app functions. */
+ int CAN_EXECUTE_APP_FUNCTIONS_DENIED = 0;
+
+ /**
+ * Callers can execute app functions because they are calling app functions from the same
+ * package.
+ */
+ int CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_SAME_PACKAGE = 1;
+
+ /**
+ * Callers can execute app functions because they have the necessary permission.
+ * This case also applies when a caller with the permission invokes their own app functions.
+ */
+ int CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION = 2;
+
/**
* Checks if the app function policy is allowed.
*
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
index fe163d77c4fc..3f8b2e3316dc 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -16,24 +16,12 @@
package com.android.server.appfunctions;
-import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_METADATA_DB;
-import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_NAMESPACE;
-import static android.app.appfunctions.AppFunctionStaticMetadataHelper.getDocumentIdForAppFunction;
-
-import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_EXECUTOR;
-
import android.Manifest;
import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.AppFunctionsPolicy;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchManager;
-import android.app.appsearch.AppSearchManager.SearchContext;
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.GetByDocumentIdRequest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -84,64 +72,25 @@ class CallerValidatorImpl implements CallerValidator {
@Override
@RequiresPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS)
- public AndroidFuture<Boolean> verifyCallerCanExecuteAppFunction(
+ @CanExecuteAppFunctionResult
+ public AndroidFuture<Integer> verifyCallerCanExecuteAppFunction(
int callingUid,
int callingPid,
@NonNull UserHandle targetUser,
@NonNull String callerPackageName,
@NonNull String targetPackageName,
@NonNull String functionId) {
- if (callerPackageName.equals(targetPackageName)) {
- return AndroidFuture.completedFuture(true);
- }
-
boolean hasExecutionPermission =
mContext.checkPermission(
- Manifest.permission.EXECUTE_APP_FUNCTIONS, callingPid, callingUid)
+ Manifest.permission.EXECUTE_APP_FUNCTIONS, callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED;
-
- if (!hasExecutionPermission) {
- return AndroidFuture.completedFuture(false);
+ if (hasExecutionPermission) {
+ return AndroidFuture.completedFuture(CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION);
}
-
- FutureAppSearchSession futureAppSearchSession =
- new FutureAppSearchSessionImpl(
- Objects.requireNonNull(
- mContext.createContextAsUser(targetUser, 0)
- .getSystemService(AppSearchManager.class)),
- THREAD_POOL_EXECUTOR,
- new SearchContext.Builder(APP_FUNCTION_STATIC_METADATA_DB).build());
-
- String documentId = getDocumentIdForAppFunction(targetPackageName, functionId);
-
- return futureAppSearchSession
- .getByDocumentId(
- new GetByDocumentIdRequest.Builder(APP_FUNCTION_STATIC_NAMESPACE)
- .addIds(documentId)
- .build())
- .thenApply(
- batchResult -> getGenericDocumentFromBatchResult(batchResult, documentId))
- // At this point, already checked the app has the permission.
- .thenApply(document -> true)
- .whenComplete(
- (result, throwable) -> {
- futureAppSearchSession.close();
- });
- }
-
- private static GenericDocument getGenericDocumentFromBatchResult(
- AppSearchBatchResult<String, GenericDocument> result, String documentId) {
- if (result.isSuccess()) {
- return result.getSuccesses().get(documentId);
+ if (callerPackageName.equals(targetPackageName)) {
+ return AndroidFuture.completedFuture(CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_SAME_PACKAGE);
}
-
- AppSearchResult<GenericDocument> failedResult = result.getFailures().get(documentId);
- throw new AppSearchException(
- failedResult.getResultCode(),
- "Unable to retrieve document with id: "
- + documentId
- + " due to "
- + failedResult.getErrorMessage());
+ return AndroidFuture.completedFuture(CAN_EXECUTE_APP_FUNCTIONS_DENIED);
}
@Override