Merge "Plumming of perm restore via permission controller"

This commit is contained in:
Philip P. Moltmann
2019-02-04 17:13:37 +00:00
committed by Android (Google) Code Review
4 changed files with 270 additions and 0 deletions

View File

@@ -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";

View File

@@ -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,

View File

@@ -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}
*/

View File

@@ -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");