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

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

Merge "Move permission backup into permission controller"

parents a9f50e95 97142e25
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -4863,6 +4863,7 @@ package android.permission {
    method public final android.os.IBinder onBind(android.content.Intent);
    method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
    method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
    method public abstract void onGetRuntimePermissionsBackup(android.os.UserHandle, java.io.OutputStream);
    method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
    method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String);
    field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
+3 −0
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package android.permission;

import android.os.RemoteCallback;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;

/**
 * Interface for system apps to communication with the permission controller.
@@ -27,6 +29,7 @@ import android.os.Bundle;
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 getAppPermissions(String packageName, in RemoteCallback callback);
    void revokeRuntimePermission(String packageName, String permissionName);
    void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
+187 −0
Original line number Diff line number Diff line
@@ -36,10 +36,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -51,6 +53,11 @@ import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.util.Preconditions;

import libcore.io.IoUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -58,6 +65,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Interface for communicating with the permission controller.
@@ -117,6 +125,20 @@ public final class PermissionControllerManager {
        public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked);
    }

    /**
     * Callback for delivering the result of {@link #getRuntimePermissionBackup}.
     *
     * @hide
     */
    public interface OnGetRuntimePermissionBackupCallback {
        /**
         * The result for {@link #getRuntimePermissionBackup}.
         *
         * @param backup The backup file
         */
        void onGetRuntimePermissionsBackup(@NonNull byte[] backup);
    }

    /**
     * Callback for delivering the result of {@link #getAppPermissions}.
     *
@@ -218,6 +240,26 @@ public final class PermissionControllerManager {
                request, doDryRun, reason, mContext.getPackageName(), executor, callback));
    }

    /**
     * Create a backup of the runtime permissions.
     *
     * @param user The user to be backed up
     * @param executor Executor on which to invoke the callback
     * @param callback Callback to receive the result
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
    public void getRuntimePermissionBackup(@NonNull UserHandle user,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull OnGetRuntimePermissionBackupCallback callback) {
        checkNotNull(executor);
        checkNotNull(callback);

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

    /**
     * Gets the runtime permissions for an app.
     *
@@ -354,6 +396,89 @@ public final class PermissionControllerManager {
        }
    }

    /**
     * Task to read a large amount of data from a remote service.
     */
    private static class FileReaderTask<Callback extends Consumer<byte[]>>
            extends AsyncTask<Void, Void, byte[]> {
        private ParcelFileDescriptor mLocalPipe;
        private ParcelFileDescriptor mRemotePipe;

        private final @NonNull Callback mCallback;

        FileReaderTask(@NonNull Callback callback) {
            mCallback = callback;
        }

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

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

        /**
         * Get the file descriptor the remote service should write the data to.
         *
         * <p>Needs to be closed <u>locally</u> before the FileReader can finish.
         *
         * @return The file the data should be written to
         */
        ParcelFileDescriptor getRemotePipe() {
            return mRemotePipe;
        }

        @Override
        protected byte[] doInBackground(Void... ignored) {
            ByteArrayOutputStream combinedBuffer = new ByteArrayOutputStream();

            try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(mLocalPipe)) {
                byte[] buffer = new byte[16 * 1024];

                while (!isCancelled()) {
                    int numRead = in.read(buffer);
                    if (numRead == -1) {
                        break;
                    }

                    combinedBuffer.write(buffer, 0, numRead);
                }
            } catch (IOException | NullPointerException e) {
                Log.e(TAG, "Error reading runtime permission backup", e);
                combinedBuffer.reset();
            }

            return combinedBuffer.toByteArray();
        }

        /**
         * Interrupt the reading 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(new byte[]{});
        }

        @Override
        protected void onPostExecute(byte[] backup) {
            IoUtils.closeQuietly(mLocalPipe);
            mCallback.accept(backup);
        }
    }

    /**
     * Request for {@link #revokeRuntimePermissions}
     */
@@ -440,6 +565,68 @@ public final class PermissionControllerManager {
        }
    }

    /**
     * Request for {@link #getRuntimePermissionBackup}
     */
    private static final class PendingGetRuntimePermissionBackup extends
            AbstractRemoteService.PendingRequest<RemoteService, IPermissionController>
            implements Consumer<byte[]> {
        private final @NonNull FileReaderTask<PendingGetRuntimePermissionBackup> mBackupReader;
        private final @NonNull Executor mExecutor;
        private final @NonNull OnGetRuntimePermissionBackupCallback mCallback;
        private final @NonNull UserHandle mUser;

        private PendingGetRuntimePermissionBackup(@NonNull RemoteService service,
                @NonNull UserHandle user, @NonNull @CallbackExecutor Executor executor,
                @NonNull OnGetRuntimePermissionBackupCallback callback) {
            super(service);

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

            mBackupReader = new FileReaderTask<>(this);
        }

        @Override
        protected void onTimeout(RemoteService remoteService) {
            mBackupReader.cancel(true);
            mBackupReader.interruptRead();
        }

        @Override
        public void run() {
            mBackupReader.execute();

            ParcelFileDescriptor remotePipe = mBackupReader.getRemotePipe();
            try {
                getService().getServiceInterface().getRuntimePermissionBackup(mUser, remotePipe);
            } catch (RemoteException e) {
                Log.e(TAG, "Error getting runtime permission backup", e);
            } finally {
                // Remote pipe end is duped by binder call. Local copy is not needed anymore
                IoUtils.closeQuietly(remotePipe);
            }
        }

        /**
         * Called when the {@link #mBackupReader} finished reading the file.
         *
         * @param backup The data read
         */
        @Override
        public void accept(byte[] backup) {
            long token = Binder.clearCallingIdentity();
            try {
                mExecutor.execute(() -> mCallback.onGetRuntimePermissionsBackup(backup));
            } finally {
                Binder.restoreCallingIdentity(token);
            }

            finish();
        }
    }

    /**
     * Request for {@link #getAppPermissions}
     */
+36 −0
Original line number Diff line number Diff line
@@ -33,11 +33,16 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallback;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;

import com.android.internal.util.Preconditions;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -51,6 +56,7 @@ import java.util.Map;
 */
@SystemApi
public abstract class PermissionControllerService extends Service {
    private static final String LOG_TAG = PermissionControllerService.class.getSimpleName();

    /**
     * The {@link Intent} action that must be declared as handled by a service
@@ -82,6 +88,15 @@ public abstract class PermissionControllerService extends Service {
            @NonNull Map<String, List<String>> requests, boolean doDryRun,
            @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName);

    /**
     * Create a backup of the runtime permissions.
     *
     * @param user The user to back up
     * @param out The stream to write the backup to
     */
    public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
            @NonNull OutputStream out);

    /**
     * Gets the runtime permissions for an app.
     *
@@ -162,6 +177,18 @@ public abstract class PermissionControllerService extends Service {
                        callerPackageName, callback));
            }

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

                enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);

                mHandler.sendMessage(obtainMessage(
                        PermissionControllerService::getRuntimePermissionsBackup,
                        PermissionControllerService.this, user, pipe));
            }

            @Override
            public void getAppPermissions(String packageName, RemoteCallback callback) {
                checkNotNull(packageName, "packageName");
@@ -237,6 +264,15 @@ public abstract class PermissionControllerService extends Service {
        callback.sendResult(result);
    }

    private void getRuntimePermissionsBackup(@NonNull UserHandle user,
            @NonNull ParcelFileDescriptor outFile) {
        try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(outFile)) {
            onGetRuntimePermissionsBackup(user, out);
        } catch (IOException e) {
            Log.e(LOG_TAG, "Could not open pipe to write backup tp", e);
        }
    }

    private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
        List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
        if (permissions != null && !permissions.isEmpty()) {
+25 −78
Original line number Diff line number Diff line
@@ -239,6 +239,7 @@ import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.permission.PermissionControllerManager;
import android.provider.MediaStore;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
@@ -317,7 +318,6 @@ import com.android.server.pm.permission.PermissionManagerInternal;
import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionsState;
import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -370,6 +370,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -443,6 +444,8 @@ public class PackageManagerService extends IPackageManager.Stub
    private static final boolean ENABLE_FREE_CACHE_V2 =
            SystemProperties.getBoolean("fw.free_cache_v2", true);
    private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
    private static final int RADIO_UID = Process.PHONE_UID;
    private static final int LOG_UID = Process.LOG_UID;
    private static final int NFC_UID = Process.NFC_UID;
@@ -19573,28 +19576,32 @@ public class PackageManagerService extends IPackageManager.Stub
            throw new SecurityException("Only the system may call getPermissionGrantBackup()");
        }
        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
        try {
            final XmlSerializer serializer = new FastXmlSerializer();
            serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
            serializer.startDocument(null, true);
            serializer.startTag(null, TAG_PERMISSION_BACKUP);
            synchronized (mPackages) {
                serializeRuntimePermissionGrantsLPr(serializer, userId);
        AtomicReference<byte[]> backup = new AtomicReference<>();
        mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup(
                UserHandle.of(userId), mContext.getMainExecutor(), (b) -> {
                    synchronized (backup) {
                        backup.set(b);
                        backup.notifyAll();
                    }
                });
            serializer.endTag(null, TAG_PERMISSION_BACKUP);
            serializer.endDocument();
            serializer.flush();
        } catch (Exception e) {
            if (DEBUG_BACKUP) {
                Slog.e(TAG, "Unable to write default apps for backup", e);
        long start = System.currentTimeMillis();
        synchronized (backup) {
            while (backup.get() == null) {
                long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis();
                if (timeLeft <= 0) {
                    return null;
                }
                try {
                    backup.wait(timeLeft);
                } catch (InterruptedException ignored) {
                    return null;
                }
            }
        }
        return dataStream.toByteArray();
        return backup.get();
    }
    @Override
@@ -19619,66 +19626,6 @@ public class PackageManagerService extends IPackageManager.Stub
        }
    }
    @GuardedBy("mPackages")
    private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId)
            throws IOException {
        serializer.startTag(null, TAG_ALL_GRANTS);
        final int N = mSettings.mPackages.size();
        for (int i = 0; i < N; i++) {
            final PackageSetting ps = mSettings.mPackages.valueAt(i);
            boolean pkgGrantsKnown = false;
            PermissionsState packagePerms = ps.getPermissionsState();
            for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) {
                final int grantFlags = state.getFlags();
                // only look at grants that are not system/policy fixed
                if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) {
                    final boolean isGranted = state.isGranted();
                    // And only back up the user-twiddled state bits
                    if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) {
                        final String packageName = mSettings.mPackages.keyAt(i);
                        if (!pkgGrantsKnown) {
                            serializer.startTag(null, TAG_GRANT);
                            serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
                            pkgGrantsKnown = true;
                        }
                        final boolean userSet =
                                (grantFlags & FLAG_PERMISSION_USER_SET) != 0;
                        final boolean userFixed =
                                (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0;
                        final boolean revoke =
                                (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
                        serializer.startTag(null, TAG_PERMISSION);
                        serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName());
                        if (isGranted) {
                            serializer.attribute(null, ATTR_IS_GRANTED, "true");
                        }
                        if (userSet) {
                            serializer.attribute(null, ATTR_USER_SET, "true");
                        }
                        if (userFixed) {
                            serializer.attribute(null, ATTR_USER_FIXED, "true");
                        }
                        if (revoke) {
                            serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
                        }
                        serializer.endTag(null, TAG_PERMISSION);
                    }
                }
            }
            if (pkgGrantsKnown) {
                serializer.endTag(null, TAG_GRANT);
            }
        }
        serializer.endTag(null, TAG_ALL_GRANTS);
    }
    @GuardedBy("mPackages")
    private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
            throws XmlPullParserException, IOException {