Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e41ab18c authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use BIND_FOREGROUND_SERVICE only if the caller has the permission" into main

parents 9e2b6df7 164e6cde
Loading
Loading
Loading
Loading
+34 −21
Original line number Diff line number Diff line
@@ -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"));
                            }
                        })
                .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);
@@ -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);
                        })
+31 −1
Original line number Diff line number Diff line
@@ -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.
     *
+8 −59
Original line number Diff line number Diff line
@@ -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)
                        == 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