Merge "Plumming of perm restore via permission controller"
This commit is contained in:
committed by
Android (Google) Code Review
commit
f2bfd385c9
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user