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

Commit f2bfd385 authored by Philip P. Moltmann's avatar Philip P. Moltmann Committed by Android (Google) Code Review
Browse files

Merge "Plumming of perm restore via permission controller"

parents c1131c01 7532c615
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -5546,6 +5546,8 @@ package android.permission {
    method @NonNull public abstract java.util.List<android.permission.RuntimePermissionUsageInfo> onGetPermissionUsages(boolean, long);
    method public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream);
    method public abstract boolean onIsApplicationQualifiedForRole(@NonNull String, @NonNull String);
    method @BinderThread public abstract boolean onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle);
    method @BinderThread public abstract void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream);
    method public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String);
    method @NonNull public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String);
    field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
+3 −0
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@ oneway interface IPermissionController {
    void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason,
            String callerPackageName, in RemoteCallback callback);
    void getRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe);
    void restoreRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe);
    void restoreDelayedRuntimePermissionBackup(String packageName, in UserHandle user,
            in RemoteCallback callback);
    void getAppPermissions(String packageName, in RemoteCallback callback);
    void revokeRuntimePermission(String packageName, String permissionName);
    void countPermissionApps(in List<String> permissionNames, int flags,
+211 −0
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import static com.android.internal.util.Preconditions.checkFlagsArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkStringNotEmpty;

import static java.lang.Math.min;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -60,6 +62,7 @@ import libcore.io.IoUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -277,6 +280,49 @@ public final class PermissionControllerManager {
                user, executor, callback));
    }

    /**
     * Restore a backup of the runtime permissions.
     *
     * @param backup the backup to restore. The backup is sent asynchronously, hence it should not
     *               be modified after calling this method.
     * @param user The user to be restore
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
    public void restoreRuntimePermissionBackup(@NonNull byte[] backup, @NonNull UserHandle user) {
        checkNotNull(backup);
        checkNotNull(user);

        sRemoteService.scheduleAsyncRequest(
                new PendingRestoreRuntimePermissionBackup(sRemoteService, backup, user));
    }

    /**
     * Restore a backup of the runtime permissions that has been delayed.
     *
     * @param packageName The package that is ready to have it's permissions restored.
     * @param user The user to restore
     * @param executor Executor to execute the callback on
     * @param callback Is called with {@code true} iff there is still more delayed backup left
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
    public void restoreDelayedRuntimePermissionBackup(@NonNull String packageName,
            @NonNull UserHandle user,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<Boolean> callback) {
        checkNotNull(packageName);
        checkNotNull(user);
        checkNotNull(executor);
        checkNotNull(callback);

        sRemoteService.scheduleRequest(
                new PendingRestoreDelayedRuntimePermissionBackup(sRemoteService, packageName,
                        user, executor, callback));
    }

    /**
     * Gets the runtime permissions for an app.
     *
@@ -519,6 +565,80 @@ public final class PermissionControllerManager {
        }
    }

    /**
     * Task to send a large amount of data to a remote service.
     */
    private static class FileWriterTask extends AsyncTask<byte[], Void, Void> {
        private static final int CHUNK_SIZE = 4 * 1024;

        private ParcelFileDescriptor mLocalPipe;
        private ParcelFileDescriptor mRemotePipe;

        @Override
        protected void onPreExecute() {
            ParcelFileDescriptor[] pipe;
            try {
                pipe = ParcelFileDescriptor.createPipe();
            } catch (IOException e) {
                Log.e(TAG, "Could not create pipe needed to send runtime permission backup",
                        e);
                return;
            }

            mRemotePipe = pipe[0];
            mLocalPipe = pipe[1];
        }

        /**
         * Get the file descriptor the remote service should read the data from.
         *
         * @return The file the data should be read from
         */
        ParcelFileDescriptor getRemotePipe() {
            return mRemotePipe;
        }

        /**
         * Send the data to the remove service.
         *
         * @param in The data to send
         *
         * @return ignored
         */
        @Override
        protected Void doInBackground(byte[]... in) {
            byte[] buffer = in[0];
            try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(mLocalPipe)) {
                for (int offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
                    out.write(buffer, offset, min(CHUNK_SIZE, buffer.length - offset));
                }
            } catch (IOException | NullPointerException e) {
                Log.e(TAG, "Error sending runtime permission backup", e);
            }

            return null;
        }

        /**
         * Interrupt the send of the data.
         *
         * <p>Needs to be called when canceling this task as it might be hung.
         */
        void interruptRead() {
            IoUtils.closeQuietly(mLocalPipe);
        }

        @Override
        protected void onCancelled() {
            onPostExecute(null);
        }

        @Override
        protected void onPostExecute(Void ignored) {
            IoUtils.closeQuietly(mLocalPipe);
        }
    }

    /**
     * Request for {@link #revokeRuntimePermissions}
     */
@@ -667,6 +787,97 @@ public final class PermissionControllerManager {
        }
    }

    /**
     * Request for {@link #restoreRuntimePermissionBackup}
     */
    private static final class PendingRestoreRuntimePermissionBackup implements
            AbstractRemoteService.AsyncRequest<IPermissionController> {
        private final @NonNull FileWriterTask mBackupSender;
        private final @NonNull byte[] mBackup;
        private final @NonNull UserHandle mUser;

        private PendingRestoreRuntimePermissionBackup(@NonNull RemoteService service,
                @NonNull byte[] backup, @NonNull UserHandle user) {
            mBackup = backup;
            mUser = user;

            mBackupSender = new FileWriterTask();
        }

        @Override
        public void run(@NonNull IPermissionController service) {
            ParcelFileDescriptor remotePipe = mBackupSender.getRemotePipe();
            try {
                service.restoreRuntimePermissionBackup(mUser, remotePipe);
            } catch (RemoteException e) {
                Log.e(TAG, "Error sending runtime permission backup", e);
                mBackupSender.cancel(false);
            } finally {
                // Remote pipe end is duped by binder call. Local copy is not needed anymore
                IoUtils.closeQuietly(remotePipe);
            }

            mBackupSender.execute(mBackup);
        }
    }

    /**
     * Request for {@link #restoreDelayedRuntimePermissionBackup(String, UserHandle, Executor,
     * Consumer<Boolean>)}
     */
    private static final class PendingRestoreDelayedRuntimePermissionBackup extends
            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
        private final @NonNull String mPackageName;
        private final @NonNull UserHandle mUser;
        private final @NonNull Executor mExecutor;
        private final @NonNull Consumer<Boolean> mCallback;

        private final @NonNull RemoteCallback mRemoteCallback;

        private PendingRestoreDelayedRuntimePermissionBackup(@NonNull RemoteService service,
                @NonNull String packageName, @NonNull UserHandle user, @NonNull Executor executor,
                @NonNull Consumer<Boolean> callback) {
            super(service);

            mPackageName = packageName;
            mUser = user;
            mExecutor = executor;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
                long token = Binder.clearCallingIdentity();
                try {
                    callback.accept(result.getBoolean(KEY_RESULT, false));
                } finally {
                    Binder.restoreCallingIdentity(token);

                    finish();
                }
            }), null);
        }

        @Override
        protected void onTimeout(RemoteService remoteService) {
            long token = Binder.clearCallingIdentity();
            try {
                mExecutor.execute(
                        () -> mCallback.accept(true));
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void run() {
            try {
                getService().getServiceInterface().restoreDelayedRuntimePermissionBackup(
                        mPackageName, mUser, mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(TAG, "Error restoring delayed permissions for " + mPackageName, e);
            }
        }
    }

    /**
     * Request for {@link #getAppPermissions}
     */
+54 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static com.android.internal.util.Preconditions.checkStringNotEmpty;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

import android.Manifest;
import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
@@ -48,6 +49,7 @@ import android.util.Log;
import com.android.internal.util.Preconditions;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -103,6 +105,28 @@ public abstract class PermissionControllerService extends Service {
    public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
            @NonNull OutputStream backup);

    /**
     * Restore a backup of the runtime permissions.
     *
     * @param user The user to restore
     * @param backup The stream to read the backup from
     */
    @BinderThread
    public abstract void onRestoreRuntimePermissionsBackup(@NonNull UserHandle user,
            @NonNull InputStream backup);

    /**
     * Restore a delayed backup of the runtime permissions.
     *
     * @param packageName The app to restore
     * @param user The user to restore
     *
     * @return {@code true} iff there is still delayed backup left
     */
    @BinderThread
    public abstract boolean onRestoreDelayedRuntimePermissionsBackup(@NonNull String packageName,
            @NonNull UserHandle user);

    /**
     * Gets the runtime permissions for an app.
     *
@@ -206,6 +230,36 @@ public abstract class PermissionControllerService extends Service {
                        PermissionControllerService.this, user, pipe));
            }

            @Override
            public void restoreRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
                checkNotNull(user);
                checkNotNull(pipe);

                enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);

                try (InputStream backup = new ParcelFileDescriptor.AutoCloseInputStream(pipe)) {
                    onRestoreRuntimePermissionsBackup(user, backup);
                } catch (IOException e) {
                    Log.e(LOG_TAG, "Could not open pipe to read backup from", e);
                }
            }

            @Override
            public void restoreDelayedRuntimePermissionBackup(String packageName, UserHandle user,
                    RemoteCallback callback) {
                checkNotNull(packageName);
                checkNotNull(user);
                checkNotNull(callback);

                enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);

                boolean hasMoreBackup = onRestoreDelayedRuntimePermissionsBackup(packageName, user);

                Bundle result = new Bundle();
                result.putBoolean(PermissionControllerManager.KEY_RESULT, hasMoreBackup);
                callback.sendResult(result);
            }

            @Override
            public void getAppPermissions(String packageName, RemoteCallback callback) {
                checkNotNull(packageName, "packageName");