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

Commit 1aa54016 authored by MingWei's avatar MingWei
Browse files

Add uri granting logic for app functions

Flag: android.permission.flags.app_function_access_service_enabled
Test: atest CtsAppFunctionTestCases
Bug: 418982723
Change-Id: I16847e14d66a927f37acddc4ae32d85c4707dd8d
parent 167a1547
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -127,7 +127,9 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
     * @param resultDocument The return value of the executed function.
     * @param extras The additional metadata for this function execution response.
     * @param uriGrants The list of {@link AppFunctionUriGrant} to which
     *     the caller of this app function execution should have temporary access granted.
     *     the caller of this app function execution should have temporary access granted. These
     *     grants typically persist until the device reboots. Uri owner could consider clearing
     *     data associated with these URIs after a reboot.
     */
    @FlaggedApi(Flags.FLAG_APP_FUNCTION_ACCESS_API_ENABLED)
    public ExecuteAppFunctionResponse(
+22 −1
Original line number Diff line number Diff line
@@ -41,16 +41,20 @@ public class SafeOneTimeExecuteAppFunctionCallback {

    @Nullable private final CompletionCallback mCompletionCallback;

    @Nullable private final BeforeCompletionCallback mBeforeCompletionCallback;

    private final AtomicLong mExecutionStartTimeAfterBindMillis = new AtomicLong();

    public SafeOneTimeExecuteAppFunctionCallback(@NonNull IExecuteAppFunctionCallback callback) {
        this(callback, /* completionCallback= */ null);
        this(callback, /* beforeCompletionCallback= */ null, /* completionCallback= */ null);
    }

    public SafeOneTimeExecuteAppFunctionCallback(
            @NonNull IExecuteAppFunctionCallback callback,
            @Nullable BeforeCompletionCallback beforeCompletionCallback,
            @Nullable CompletionCallback completionCallback) {
        mCallback = Objects.requireNonNull(callback);
        mBeforeCompletionCallback = beforeCompletionCallback;
        mCompletionCallback = completionCallback;
    }

@@ -61,6 +65,9 @@ public class SafeOneTimeExecuteAppFunctionCallback {
            return;
        }
        try {
            if (mBeforeCompletionCallback != null) {
                mBeforeCompletionCallback.beforeOnSuccess(result);
            }
            mCallback.onSuccess(result);
            if (mCompletionCallback != null) {
                mCompletionCallback.finalizeOnSuccess(
@@ -121,4 +128,18 @@ public class SafeOneTimeExecuteAppFunctionCallback {
        /** Called after {@link IExecuteAppFunctionCallback#onError}. */
        void finalizeOnError(@NonNull AppFunctionException error, long executionStartTimeMillis);
    }

    /**
     * Provides a hook to execute additional actions before the {@link IExecuteAppFunctionCallback}
     * has been invoked.
     */
    public interface BeforeCompletionCallback {
        /**
         * Called before {@link IExecuteAppFunctionCallback#onSuccess(ExecuteAppFunctionResponse)}
         * is invoked.
         *
         * @param result The result that will be passed to the main callback.
         */
        void beforeOnSuccess(@NonNull ExecuteAppFunctionResponse result);
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.appfunctions;

import android.annotation.NonNull;
import android.app.UriGrantsManager;
import android.app.appfunctions.AppFunctionAccessServiceInterface;
import android.app.appfunctions.AppFunctionManagerConfiguration;
import android.content.Context;
@@ -24,6 +25,7 @@ import android.content.pm.PackageManagerInternal;

import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.uri.UriGrantsManagerInternal;

/** Service that manages app functions. */
public class AppFunctionManagerService extends SystemService {
@@ -34,7 +36,9 @@ public class AppFunctionManagerService extends SystemService {
        mServiceImpl =
                new AppFunctionManagerServiceImpl(
                        context, LocalServices.getService(PackageManagerInternal.class),
                        LocalServices.getService(AppFunctionAccessServiceInterface.class));
                        LocalServices.getService(AppFunctionAccessServiceInterface.class),
                        UriGrantsManager.getService(),
                        LocalServices.getService(UriGrantsManagerInternal.class));
    }

    @Override
+85 −4
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static com.android.server.appfunctions.CallerValidator.CAN_EXECUTE_APP_FU
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.IUriGrantsManager;
import android.app.appfunctions.AppFunctionAccessServiceInterface;
import android.app.appfunctions.AppFunctionException;
import android.app.appfunctions.AppFunctionManager;
@@ -34,6 +35,7 @@ import android.app.appfunctions.AppFunctionManagerHelper;
import android.app.appfunctions.AppFunctionManagerHelper.AppFunctionNotFoundException;
import android.app.appfunctions.AppFunctionRuntimeMetadata;
import android.app.appfunctions.AppFunctionStaticMetadataHelper;
import android.app.appfunctions.AppFunctionUriGrant;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
import android.app.appfunctions.ExecuteAppFunctionResponse;
import android.app.appfunctions.IAppFunctionEnabledCallback;
@@ -53,6 +55,8 @@ import android.app.appsearch.observer.DocumentChangeInfo;
import android.app.appsearch.observer.ObserverCallback;
import android.app.appsearch.observer.ObserverSpec;
import android.app.appsearch.observer.SchemaChangeInfo;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -85,6 +89,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import com.android.server.uri.UriGrantsManagerInternal;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -119,6 +124,12 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {

    private final AppFunctionAccessServiceInterface mAppFunctionAccessService;

    private final IUriGrantsManager mUriGrantsManager;

    private final UriGrantsManagerInternal mUriGrantsManagerInternal;

    private final IBinder mPermissionOwner;

    private final Object mAgentAllowlistLock = new Object();

    // The main agent allowlist, set by the updatable DeviceConfig System
@@ -127,7 +138,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {

    public AppFunctionManagerServiceImpl(
            @NonNull Context context, @NonNull PackageManagerInternal packageManagerInternal,
            @NonNull AppFunctionAccessServiceInterface appFunctionAccessServiceInterface) {
            @NonNull AppFunctionAccessServiceInterface appFunctionAccessServiceInterface,
            @NonNull IUriGrantsManager uriGrantsManager,
            @NonNull UriGrantsManagerInternal uriGrantsManagerInternal) {
        this(
                context,
                new RemoteServiceCallerImpl<>(
@@ -137,7 +150,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                new ServiceConfigImpl(),
                new AppFunctionsLoggerWrapper(context),
                packageManagerInternal,
                appFunctionAccessServiceInterface);
                appFunctionAccessServiceInterface,
                uriGrantsManager,
                uriGrantsManagerInternal);
    }

    @VisibleForTesting
@@ -149,7 +164,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            ServiceConfig serviceConfig,
            AppFunctionsLoggerWrapper loggerWrapper,
            PackageManagerInternal packageManagerInternal,
            AppFunctionAccessServiceInterface appFunctionAccessServiceInterface) {
            AppFunctionAccessServiceInterface appFunctionAccessServiceInterface,
            IUriGrantsManager uriGrantsManager,
            UriGrantsManagerInternal uriGrantsManagerInternal) {
        mContext = Objects.requireNonNull(context);
        mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
        mCallerValidator = Objects.requireNonNull(callerValidator);
@@ -158,6 +175,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
        mLoggerWrapper = loggerWrapper;
        mPackageManagerInternal = Objects.requireNonNull(packageManagerInternal);
        mAppFunctionAccessService = Objects.requireNonNull(appFunctionAccessServiceInterface);
        mUriGrantsManager = Objects.requireNonNull(uriGrantsManager);
        mUriGrantsManagerInternal = Objects.requireNonNull(uriGrantsManagerInternal);
        mPermissionOwner = mUriGrantsManagerInternal.newUriPermissionOwner("appfunctions");
    }

    /** Called when the user is unlocked. */
@@ -563,6 +583,59 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                && android.permission.flags.Flags.appFunctionAccessServiceEnabled();
    }

    /**
     * Grants temporary uri permission on behalf of the app that returns the response to the
     * caller that sends the request.
     *
     * <p>All {@link AppFunctionUriGrant} in {@link ExecuteAppFunctionResponse} would be granted
     * to the receiver until the system service is finished. That is usually until device reboots.
     */
    private void grantTemporaryUriPermissions(
            @NonNull ExecuteAppFunctionAidlRequest requestInternal,
            @NonNull ExecuteAppFunctionResponse response,
            int callingUid) {
        if (!accessCheckFlagsEnabled()) return;

        final int uriReceiverUserId = UserHandle.getUserId(callingUid);
        final int targetUserId = requestInternal.getUserHandle().getIdentifier();
        final String uriOwnerPackageName = requestInternal
                .getClientRequest()
                .getTargetPackageName();
        final int uriOwnerUid =
                mPackageManagerInternal.getPackageUid(
                        uriOwnerPackageName,
                        /* flags= */ 0,
                        /* userId= */ targetUserId
                );

        final long ident = Binder.clearCallingIdentity();
        try {
            final int uriGrantsSize = response.getUriGrants().size();
            for (int i = 0; i < uriGrantsSize; i++) {
                final AppFunctionUriGrant uriGrant = response.getUriGrants().get(i);
                if (!ContentResolver.SCHEME_CONTENT.equals(uriGrant.getUri().getScheme())) continue;

                final  String uriReceiverPackageName = requestInternal.getCallingPackage();
                final int uriOwnerUserid = ContentProvider.getUserIdFromUri(
                        uriGrant.getUri(),
                        UserHandle.getUserId(uriOwnerUid));
                mUriGrantsManager.grantUriPermissionFromOwner(
                        mPermissionOwner,
                        uriOwnerUid,
                        uriReceiverPackageName,
                        ContentProvider.getUriWithoutUserId(uriGrant.getUri()),
                        uriGrant.getModeFlags(),
                        uriOwnerUserid,
                        uriReceiverUserId
                );
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Granting URI permissions failed", e);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private static void reportException(
            @NonNull IAppFunctionEnabledCallback callback, @NonNull Exception exception) {
        try {
@@ -825,7 +898,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {

    /**
     * Returns a new {@link SafeOneTimeExecuteAppFunctionCallback} initialized with a {@link
     * SafeOneTimeExecuteAppFunctionCallback.CompletionCallback} that logs the results.
     * SafeOneTimeExecuteAppFunctionCallback.CompletionCallback} that logs the results and a
     * {@link SafeOneTimeExecuteAppFunctionCallback.BeforeCompletionCallback} that grants the
     * temporary uri permission to caller.
     */
    @VisibleForTesting
    SafeOneTimeExecuteAppFunctionCallback initializeSafeExecuteAppFunctionCallback(
@@ -834,6 +909,12 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            int callingUid) {
        return new SafeOneTimeExecuteAppFunctionCallback(
                executeAppFunctionCallback,
                new SafeOneTimeExecuteAppFunctionCallback.BeforeCompletionCallback() {
                    @Override
                    public void beforeOnSuccess(@NonNull ExecuteAppFunctionResponse result) {
                        grantTemporaryUriPermissions(requestInternal, result, callingUid);
                    }
                },
                new SafeOneTimeExecuteAppFunctionCallback.CompletionCallback() {
                    @Override
                    public void finalizeOnSuccess(
+9 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.appfunctions

import android.app.IUriGrantsManager
import android.app.appfunctions.AppFunctionAccessServiceInterface
import android.app.appfunctions.flags.Flags
import android.content.Context
@@ -27,6 +28,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.server.LocalServices
import com.android.server.uri.UriGrantsManagerInternal
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Ignore
@@ -47,8 +49,13 @@ class AppFunctionManagerServiceImplTest {
    private val context: Context
        get() = ApplicationProvider.getApplicationContext()

    private val serviceImpl = AppFunctionManagerServiceImpl(context, mock<PackageManagerInternal>(),
        mock<AppFunctionAccessServiceInterface>())
    private val serviceImpl = AppFunctionManagerServiceImpl(
        context,
        mock<PackageManagerInternal>(),
        mock<AppFunctionAccessServiceInterface>(),
        mock<IUriGrantsManager>(),
        mock<UriGrantsManagerInternal>()
    )

    @Test
    fun testGetLockForPackage_samePackage() {
Loading