Loading services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +34 −21 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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")); } }) .thenCompose( isEnabled -> isAppFunctionEnabled( requestInternal.getClientRequest().getFunctionIdentifier(), requestInternal.getClientRequest().getTargetPackageName(), getAppSearchManagerAsUser(requestInternal.getUserHandle()), THREAD_POOL_EXECUTOR)) .thenAccept( 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; }); }) .thenAccept( unused -> { 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; } Intent serviceIntent = mInternalServiceHelper.resolveAppFunctionService( targetPackageName, targetUser); Loading Loading @@ -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); }) Loading services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java +31 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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, Loading @@ -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. * Loading services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java +8 −59 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) == 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 Loading Loading
services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +34 −21 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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")); } }) .thenCompose( isEnabled -> isAppFunctionEnabled( requestInternal.getClientRequest().getFunctionIdentifier(), requestInternal.getClientRequest().getTargetPackageName(), getAppSearchManagerAsUser(requestInternal.getUserHandle()), THREAD_POOL_EXECUTOR)) .thenAccept( 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; }); }) .thenAccept( unused -> { 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; } Intent serviceIntent = mInternalServiceHelper.resolveAppFunctionService( targetPackageName, targetUser); Loading Loading @@ -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); }) Loading
services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java +31 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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, Loading @@ -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. * Loading
services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java +8 −59 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) == 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 Loading