Loading api/system-current.txt +12 −0 Original line number Original line Diff line number Diff line Loading @@ -4599,6 +4599,17 @@ package android.os.storage { package android.permission { package android.permission { public final class PermissionControllerManager { method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback); field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2 field public static final int REASON_MALWARE = 1; // 0x1 } public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback { ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback(); method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>); } public abstract class PermissionControllerService extends android.app.Service { public abstract class PermissionControllerService extends android.app.Service { ctor public PermissionControllerService(); ctor public PermissionControllerService(); method public final void attachBaseContext(android.content.Context); method public final void attachBaseContext(android.content.Context); Loading @@ -4606,6 +4617,7 @@ package android.permission { method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean); 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 java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String); method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String); 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"; field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; } } Loading api/test-current.txt +15 −0 Original line number Original line Diff line number Diff line Loading @@ -985,6 +985,21 @@ package android.os.strictmode { } } package android.permission { public final class PermissionControllerManager { method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback); field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2 field public static final int REASON_MALWARE = 1; // 0x1 } public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback { ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback(); method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>); } } package android.print { package android.print { public final class PrintJobInfo implements android.os.Parcelable { public final class PrintJobInfo implements android.os.Parcelable { Loading core/java/android/permission/IPermissionController.aidl +3 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.permission; package android.permission; import android.os.RemoteCallback; import android.os.RemoteCallback; import android.os.Bundle; /** /** * Interface for system apps to communication with the permission controller. * Interface for system apps to communication with the permission controller. Loading @@ -24,6 +25,8 @@ import android.os.RemoteCallback; * @hide * @hide */ */ oneway interface IPermissionController { oneway interface IPermissionController { void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason, String callerPackageName, in RemoteCallback callback); void getAppPermissions(String packageName, in RemoteCallback callback); void getAppPermissions(String packageName, in RemoteCallback callback); void revokeRuntimePermission(String packageName, String permissionName); void revokeRuntimePermission(String packageName, String permissionName); void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted, void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted, Loading core/java/android/permission/PermissionControllerManager.java +176 −3 Original line number Original line Diff line number Diff line Loading @@ -22,46 +22,97 @@ import static com.android.internal.util.Preconditions.checkCollectionElementsNot import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull; import android.Manifest; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.ComponentName; import android.content.ComponentName; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.os.IBinder; import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; import android.util.Log; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.List; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; /** /** * Interface for communicating with the permission controller from system apps. All UI operations * Interface for communicating with the permission controller. * regarding permissions and any changes to the permission state should flow through this * interface. * * * @hide * @hide */ */ @TestApi @SystemApi @SystemService(Context.PERMISSION_CONTROLLER_SERVICE) @SystemService(Context.PERMISSION_CONTROLLER_SERVICE) public final class PermissionControllerManager { public final class PermissionControllerManager { private static final String TAG = PermissionControllerManager.class.getSimpleName(); private static final String TAG = PermissionControllerManager.class.getSimpleName(); /** /** * The key for retrieving the result from the returned bundle. * The key for retrieving the result from the returned bundle. * * @hide */ */ public static final String KEY_RESULT = public static final String KEY_RESULT = "android.permission.PermissionControllerManager.key.result"; "android.permission.PermissionControllerManager.key.result"; /** @hide */ @IntDef(prefix = { "REASON_" }, value = { REASON_MALWARE, REASON_INSTALLER_POLICY_VIOLATION, }) @Retention(RetentionPolicy.SOURCE) public @interface Reason {} /** The permissions are revoked because the apps holding the permissions are malware */ public static final int REASON_MALWARE = 1; /** * The permissions are revoked because the apps holding the permissions violate a policy of the * app that installed it. * * <p>If this reason is used only permissions of apps that are installed by the caller of the * API can be revoked. */ public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; /** * Callback for delivering the result of {@link #revokeRuntimePermissions}. */ public abstract static class OnRevokeRuntimePermissionsCallback { /** * The result for {@link #revokeRuntimePermissions}. * * @param revoked The actually revoked permissions as * {@code Map<packageName, List<permission>>} */ public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked); } /** /** * Callback for delivering the result of {@link #getAppPermissions}. * Callback for delivering the result of {@link #getAppPermissions}. * * @hide */ */ public interface OnGetAppPermissionResultCallback { public interface OnGetAppPermissionResultCallback { /** /** Loading @@ -75,6 +126,8 @@ public final class PermissionControllerManager { /** /** * Callback for delivering the result of {@link #countPermissionApps}. * Callback for delivering the result of {@link #countPermissionApps}. * * @hide */ */ public interface OnCountPermissionAppsResultCallback { public interface OnCountPermissionAppsResultCallback { /** /** Loading @@ -86,23 +139,61 @@ public final class PermissionControllerManager { void onCountPermissionApps(int numApps); void onCountPermissionApps(int numApps); } } private final @NonNull Context mContext; private final RemoteService mRemoteService; private final RemoteService mRemoteService; /** @hide */ public PermissionControllerManager(@NonNull Context context) { public PermissionControllerManager(@NonNull Context context) { Intent intent = new Intent(SERVICE_INTERFACE); Intent intent = new Intent(SERVICE_INTERFACE); intent.setPackage(context.getPackageManager().getPermissionControllerPackageName()); intent.setPackage(context.getPackageManager().getPermissionControllerPackageName()); ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0); ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0); mContext = context; mRemoteService = new RemoteService(context, mRemoteService = new RemoteService(context, serviceInfo.getComponentInfo().getComponentName()); serviceInfo.getComponentInfo().getComponentName()); } } /** * Revoke a set of runtime permissions for various apps. * * @param request The permissions to revoke as {@code Map<packageName, List<permission>>} * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them * @param reason Why the permission should be revoked * @param executor Executor on which to invoke the callback * @param callback Callback to receive the result */ @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull Map<String, List<String>> request, boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor, @NonNull OnRevokeRuntimePermissionsCallback callback) { // Check input to fail immediately instead of inside the async request checkNotNull(executor); checkNotNull(callback); checkNotNull(request); for (Map.Entry<String, List<String>> appRequest : request.entrySet()) { checkNotNull(appRequest.getKey()); checkCollectionElementsNotNull(appRequest.getValue(), "permissions"); } // Check required permission to fail immediately instead of inside the oneway binder call if (mContext.checkSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS + " required"); } mRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(mRemoteService, request, doDryRun, reason, mContext.getPackageName(), executor, callback)); } /** /** * Gets the runtime permissions for an app. * Gets the runtime permissions for an app. * * * @param packageName The package for which to query. * @param packageName The package for which to query. * @param callback Callback to receive the result. * @param callback Callback to receive the result. * @param handler Handler on which to invoke the callback. * @param handler Handler on which to invoke the callback. * * @hide */ */ @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String packageName, public void getAppPermissions(@NonNull String packageName, Loading @@ -119,6 +210,8 @@ public final class PermissionControllerManager { * * * @param packageName The package for which to revoke * @param packageName The package for which to revoke * @param permissionName The permission to revoke * @param permissionName The permission to revoke * * @hide */ */ @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String packageName, public void revokeRuntimePermission(@NonNull String packageName, Loading @@ -138,6 +231,8 @@ public final class PermissionControllerManager { * @param countSystem Also count system apps * @param countSystem Also count system apps * @param callback Callback to receive the result * @param callback Callback to receive the result * @param handler Handler on which to invoke the callback * @param handler Handler on which to invoke the callback * * @hide */ */ @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull List<String> permissionNames, public void countPermissionApps(@NonNull List<String> permissionNames, Loading Loading @@ -205,6 +300,84 @@ public final class PermissionControllerManager { } } } } /** * Request for {@link #revokeRuntimePermissions} */ private static final class PendingRevokeRuntimePermissionRequest extends AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> { private final @NonNull Map<String, List<String>> mRequest; private final boolean mDoDryRun; private final int mReason; private final @NonNull String mCallingPackage; private final @NonNull OnRevokeRuntimePermissionsCallback mCallback; private final @NonNull RemoteCallback mRemoteCallback; private PendingRevokeRuntimePermissionRequest(@NonNull RemoteService service, @NonNull Map<String, List<String>> request, boolean doDryRun, @Reason int reason, @NonNull String callingPackage, @NonNull @CallbackExecutor Executor executor, @NonNull OnRevokeRuntimePermissionsCallback callback) { super(service); mRequest = request; mDoDryRun = doDryRun; mReason = reason; mCallingPackage = callingPackage; mCallback = callback; mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> { long token = Binder.clearCallingIdentity(); try { Map<String, List<String>> revoked = new ArrayMap<>(); try { Bundle bundleizedRevoked = result.getBundle(KEY_RESULT); for (String packageName : bundleizedRevoked.keySet()) { Preconditions.checkNotNull(packageName); ArrayList<String> permissions = bundleizedRevoked.getStringArrayList(packageName); Preconditions.checkCollectionElementsNotNull(permissions, "permissions"); revoked.put(packageName, permissions); } } catch (Exception e) { Log.e(TAG, "Could not read result when revoking runtime permissions", e); } callback.onRevokeRuntimePermissions(revoked); } finally { Binder.restoreCallingIdentity(token); finish(); } }), null); } @Override protected void onTimeout(RemoteService remoteService) { mCallback.onRevokeRuntimePermissions(Collections.emptyMap()); } @Override public void run() { Bundle bundledizedRequest = new Bundle(); for (Map.Entry<String, List<String>> appRequest : mRequest.entrySet()) { bundledizedRequest.putStringArrayList(appRequest.getKey(), new ArrayList<>(appRequest.getValue())); } try { getService().getServiceInterface().revokeRuntimePermissions(bundledizedRequest, mDoDryRun, mReason, mCallingPackage, mRemoteCallback); } catch (RemoteException e) { Log.e(TAG, "Error revoking runtime permission", e); } } } /** /** * Request for {@link #getAppPermissions} * Request for {@link #getAppPermissions} */ */ Loading core/java/android/permission/PermissionControllerService.java +78 −0 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.permission; package android.permission; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; Loading @@ -26,12 +27,19 @@ import android.annotation.SystemApi; import android.app.Service; import android.app.Service; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.os.IBinder; import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteCallback; import android.util.ArrayMap; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; import java.util.List; import java.util.Map; /** /** * This service is meant to be implemented by the app controlling permissions. * This service is meant to be implemented by the app controlling permissions. Loading Loading @@ -59,6 +67,20 @@ public abstract class PermissionControllerService extends Service { mHandler = new Handler(base.getMainLooper()); mHandler = new Handler(base.getMainLooper()); } } /** * Revoke a set of runtime permissions for various apps. * * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>} * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them * @param reason Why the permission should be revoked * @param callerPackageName The package name of the calling app * * @return the actually removed permissions as {@code Map<packageName, List<permission>>} */ public abstract @NonNull Map<String, List<String>> onRevokeRuntimePermissions( @NonNull Map<String, List<String>> requests, boolean doDryRun, @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName); /** /** * Gets the runtime permissions for an app. * Gets the runtime permissions for an app. * * Loading Loading @@ -93,6 +115,41 @@ public abstract class PermissionControllerService extends Service { @Override @Override public final IBinder onBind(Intent intent) { public final IBinder onBind(Intent intent) { return new IPermissionController.Stub() { return new IPermissionController.Stub() { @Override public void revokeRuntimePermissions( Bundle bundleizedRequest, boolean doDryRun, int reason, String callerPackageName, RemoteCallback callback) { checkNotNull(bundleizedRequest, "bundleizedRequest"); checkNotNull(callerPackageName); checkNotNull(callback); Map<String, List<String>> request = new ArrayMap<>(); for (String packageName : bundleizedRequest.keySet()) { Preconditions.checkNotNull(packageName); ArrayList<String> permissions = bundleizedRequest.getStringArrayList(packageName); Preconditions.checkCollectionElementsNotNull(permissions, "permissions"); request.put(packageName, permissions); } enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null); // Verify callerPackageName try { PackageInfo pkgInfo = getPackageManager().getPackageInfo(callerPackageName, 0); checkArgument(getCallingUid() == pkgInfo.applicationInfo.uid); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException(e); } mHandler.sendMessage(obtainMessage( PermissionControllerService::revokeRuntimePermissions, PermissionControllerService.this, request, doDryRun, reason, callerPackageName, callback)); } @Override @Override public void getAppPermissions(String packageName, RemoteCallback callback) { public void getAppPermissions(String packageName, RemoteCallback callback) { checkNotNull(packageName, "packageName"); checkNotNull(packageName, "packageName"); Loading Loading @@ -133,6 +190,27 @@ public abstract class PermissionControllerService extends Service { }; }; } } private void revokeRuntimePermissions(@NonNull Map<String, List<String>> requests, boolean doDryRun, @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName, @NonNull RemoteCallback callback) { Map<String, List<String>> revoked = onRevokeRuntimePermissions(requests, doDryRun, reason, callerPackageName); checkNotNull(revoked); Bundle bundledizedRevoked = new Bundle(); for (Map.Entry<String, List<String>> appRevocation : revoked.entrySet()) { checkNotNull(appRevocation.getKey()); checkCollectionElementsNotNull(appRevocation.getValue(), "permissions"); bundledizedRevoked.putStringArrayList(appRevocation.getKey(), new ArrayList<>(appRevocation.getValue())); } Bundle result = new Bundle(); result.putBundle(PermissionControllerManager.KEY_RESULT, bundledizedRevoked); callback.sendResult(result); } private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) { private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) { List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName); List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName); if (permissions != null && !permissions.isEmpty()) { if (permissions != null && !permissions.isEmpty()) { Loading Loading
api/system-current.txt +12 −0 Original line number Original line Diff line number Diff line Loading @@ -4599,6 +4599,17 @@ package android.os.storage { package android.permission { package android.permission { public final class PermissionControllerManager { method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback); field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2 field public static final int REASON_MALWARE = 1; // 0x1 } public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback { ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback(); method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>); } public abstract class PermissionControllerService extends android.app.Service { public abstract class PermissionControllerService extends android.app.Service { ctor public PermissionControllerService(); ctor public PermissionControllerService(); method public final void attachBaseContext(android.content.Context); method public final void attachBaseContext(android.content.Context); Loading @@ -4606,6 +4617,7 @@ package android.permission { method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean); 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 java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String); method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String); 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"; field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; } } Loading
api/test-current.txt +15 −0 Original line number Original line Diff line number Diff line Loading @@ -985,6 +985,21 @@ package android.os.strictmode { } } package android.permission { public final class PermissionControllerManager { method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback); field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2 field public static final int REASON_MALWARE = 1; // 0x1 } public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback { ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback(); method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>); } } package android.print { package android.print { public final class PrintJobInfo implements android.os.Parcelable { public final class PrintJobInfo implements android.os.Parcelable { Loading
core/java/android/permission/IPermissionController.aidl +3 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.permission; package android.permission; import android.os.RemoteCallback; import android.os.RemoteCallback; import android.os.Bundle; /** /** * Interface for system apps to communication with the permission controller. * Interface for system apps to communication with the permission controller. Loading @@ -24,6 +25,8 @@ import android.os.RemoteCallback; * @hide * @hide */ */ oneway interface IPermissionController { oneway interface IPermissionController { void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason, String callerPackageName, in RemoteCallback callback); void getAppPermissions(String packageName, in RemoteCallback callback); void getAppPermissions(String packageName, in RemoteCallback callback); void revokeRuntimePermission(String packageName, String permissionName); void revokeRuntimePermission(String packageName, String permissionName); void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted, void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted, Loading
core/java/android/permission/PermissionControllerManager.java +176 −3 Original line number Original line Diff line number Diff line Loading @@ -22,46 +22,97 @@ import static com.android.internal.util.Preconditions.checkCollectionElementsNot import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull; import android.Manifest; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.ComponentName; import android.content.ComponentName; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.os.IBinder; import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; import android.util.Log; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.Collections; import java.util.List; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; /** /** * Interface for communicating with the permission controller from system apps. All UI operations * Interface for communicating with the permission controller. * regarding permissions and any changes to the permission state should flow through this * interface. * * * @hide * @hide */ */ @TestApi @SystemApi @SystemService(Context.PERMISSION_CONTROLLER_SERVICE) @SystemService(Context.PERMISSION_CONTROLLER_SERVICE) public final class PermissionControllerManager { public final class PermissionControllerManager { private static final String TAG = PermissionControllerManager.class.getSimpleName(); private static final String TAG = PermissionControllerManager.class.getSimpleName(); /** /** * The key for retrieving the result from the returned bundle. * The key for retrieving the result from the returned bundle. * * @hide */ */ public static final String KEY_RESULT = public static final String KEY_RESULT = "android.permission.PermissionControllerManager.key.result"; "android.permission.PermissionControllerManager.key.result"; /** @hide */ @IntDef(prefix = { "REASON_" }, value = { REASON_MALWARE, REASON_INSTALLER_POLICY_VIOLATION, }) @Retention(RetentionPolicy.SOURCE) public @interface Reason {} /** The permissions are revoked because the apps holding the permissions are malware */ public static final int REASON_MALWARE = 1; /** * The permissions are revoked because the apps holding the permissions violate a policy of the * app that installed it. * * <p>If this reason is used only permissions of apps that are installed by the caller of the * API can be revoked. */ public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; /** * Callback for delivering the result of {@link #revokeRuntimePermissions}. */ public abstract static class OnRevokeRuntimePermissionsCallback { /** * The result for {@link #revokeRuntimePermissions}. * * @param revoked The actually revoked permissions as * {@code Map<packageName, List<permission>>} */ public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked); } /** /** * Callback for delivering the result of {@link #getAppPermissions}. * Callback for delivering the result of {@link #getAppPermissions}. * * @hide */ */ public interface OnGetAppPermissionResultCallback { public interface OnGetAppPermissionResultCallback { /** /** Loading @@ -75,6 +126,8 @@ public final class PermissionControllerManager { /** /** * Callback for delivering the result of {@link #countPermissionApps}. * Callback for delivering the result of {@link #countPermissionApps}. * * @hide */ */ public interface OnCountPermissionAppsResultCallback { public interface OnCountPermissionAppsResultCallback { /** /** Loading @@ -86,23 +139,61 @@ public final class PermissionControllerManager { void onCountPermissionApps(int numApps); void onCountPermissionApps(int numApps); } } private final @NonNull Context mContext; private final RemoteService mRemoteService; private final RemoteService mRemoteService; /** @hide */ public PermissionControllerManager(@NonNull Context context) { public PermissionControllerManager(@NonNull Context context) { Intent intent = new Intent(SERVICE_INTERFACE); Intent intent = new Intent(SERVICE_INTERFACE); intent.setPackage(context.getPackageManager().getPermissionControllerPackageName()); intent.setPackage(context.getPackageManager().getPermissionControllerPackageName()); ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0); ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0); mContext = context; mRemoteService = new RemoteService(context, mRemoteService = new RemoteService(context, serviceInfo.getComponentInfo().getComponentName()); serviceInfo.getComponentInfo().getComponentName()); } } /** * Revoke a set of runtime permissions for various apps. * * @param request The permissions to revoke as {@code Map<packageName, List<permission>>} * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them * @param reason Why the permission should be revoked * @param executor Executor on which to invoke the callback * @param callback Callback to receive the result */ @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull Map<String, List<String>> request, boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor, @NonNull OnRevokeRuntimePermissionsCallback callback) { // Check input to fail immediately instead of inside the async request checkNotNull(executor); checkNotNull(callback); checkNotNull(request); for (Map.Entry<String, List<String>> appRequest : request.entrySet()) { checkNotNull(appRequest.getKey()); checkCollectionElementsNotNull(appRequest.getValue(), "permissions"); } // Check required permission to fail immediately instead of inside the oneway binder call if (mContext.checkSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS + " required"); } mRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(mRemoteService, request, doDryRun, reason, mContext.getPackageName(), executor, callback)); } /** /** * Gets the runtime permissions for an app. * Gets the runtime permissions for an app. * * * @param packageName The package for which to query. * @param packageName The package for which to query. * @param callback Callback to receive the result. * @param callback Callback to receive the result. * @param handler Handler on which to invoke the callback. * @param handler Handler on which to invoke the callback. * * @hide */ */ @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String packageName, public void getAppPermissions(@NonNull String packageName, Loading @@ -119,6 +210,8 @@ public final class PermissionControllerManager { * * * @param packageName The package for which to revoke * @param packageName The package for which to revoke * @param permissionName The permission to revoke * @param permissionName The permission to revoke * * @hide */ */ @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String packageName, public void revokeRuntimePermission(@NonNull String packageName, Loading @@ -138,6 +231,8 @@ public final class PermissionControllerManager { * @param countSystem Also count system apps * @param countSystem Also count system apps * @param callback Callback to receive the result * @param callback Callback to receive the result * @param handler Handler on which to invoke the callback * @param handler Handler on which to invoke the callback * * @hide */ */ @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) @RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull List<String> permissionNames, public void countPermissionApps(@NonNull List<String> permissionNames, Loading Loading @@ -205,6 +300,84 @@ public final class PermissionControllerManager { } } } } /** * Request for {@link #revokeRuntimePermissions} */ private static final class PendingRevokeRuntimePermissionRequest extends AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> { private final @NonNull Map<String, List<String>> mRequest; private final boolean mDoDryRun; private final int mReason; private final @NonNull String mCallingPackage; private final @NonNull OnRevokeRuntimePermissionsCallback mCallback; private final @NonNull RemoteCallback mRemoteCallback; private PendingRevokeRuntimePermissionRequest(@NonNull RemoteService service, @NonNull Map<String, List<String>> request, boolean doDryRun, @Reason int reason, @NonNull String callingPackage, @NonNull @CallbackExecutor Executor executor, @NonNull OnRevokeRuntimePermissionsCallback callback) { super(service); mRequest = request; mDoDryRun = doDryRun; mReason = reason; mCallingPackage = callingPackage; mCallback = callback; mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> { long token = Binder.clearCallingIdentity(); try { Map<String, List<String>> revoked = new ArrayMap<>(); try { Bundle bundleizedRevoked = result.getBundle(KEY_RESULT); for (String packageName : bundleizedRevoked.keySet()) { Preconditions.checkNotNull(packageName); ArrayList<String> permissions = bundleizedRevoked.getStringArrayList(packageName); Preconditions.checkCollectionElementsNotNull(permissions, "permissions"); revoked.put(packageName, permissions); } } catch (Exception e) { Log.e(TAG, "Could not read result when revoking runtime permissions", e); } callback.onRevokeRuntimePermissions(revoked); } finally { Binder.restoreCallingIdentity(token); finish(); } }), null); } @Override protected void onTimeout(RemoteService remoteService) { mCallback.onRevokeRuntimePermissions(Collections.emptyMap()); } @Override public void run() { Bundle bundledizedRequest = new Bundle(); for (Map.Entry<String, List<String>> appRequest : mRequest.entrySet()) { bundledizedRequest.putStringArrayList(appRequest.getKey(), new ArrayList<>(appRequest.getValue())); } try { getService().getServiceInterface().revokeRuntimePermissions(bundledizedRequest, mDoDryRun, mReason, mCallingPackage, mRemoteCallback); } catch (RemoteException e) { Log.e(TAG, "Error revoking runtime permission", e); } } } /** /** * Request for {@link #getAppPermissions} * Request for {@link #getAppPermissions} */ */ Loading
core/java/android/permission/PermissionControllerService.java +78 −0 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.permission; package android.permission; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; Loading @@ -26,12 +27,19 @@ import android.annotation.SystemApi; import android.app.Service; import android.app.Service; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.os.IBinder; import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteCallback; import android.util.ArrayMap; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; import java.util.List; import java.util.Map; /** /** * This service is meant to be implemented by the app controlling permissions. * This service is meant to be implemented by the app controlling permissions. Loading Loading @@ -59,6 +67,20 @@ public abstract class PermissionControllerService extends Service { mHandler = new Handler(base.getMainLooper()); mHandler = new Handler(base.getMainLooper()); } } /** * Revoke a set of runtime permissions for various apps. * * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>} * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them * @param reason Why the permission should be revoked * @param callerPackageName The package name of the calling app * * @return the actually removed permissions as {@code Map<packageName, List<permission>>} */ public abstract @NonNull Map<String, List<String>> onRevokeRuntimePermissions( @NonNull Map<String, List<String>> requests, boolean doDryRun, @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName); /** /** * Gets the runtime permissions for an app. * Gets the runtime permissions for an app. * * Loading Loading @@ -93,6 +115,41 @@ public abstract class PermissionControllerService extends Service { @Override @Override public final IBinder onBind(Intent intent) { public final IBinder onBind(Intent intent) { return new IPermissionController.Stub() { return new IPermissionController.Stub() { @Override public void revokeRuntimePermissions( Bundle bundleizedRequest, boolean doDryRun, int reason, String callerPackageName, RemoteCallback callback) { checkNotNull(bundleizedRequest, "bundleizedRequest"); checkNotNull(callerPackageName); checkNotNull(callback); Map<String, List<String>> request = new ArrayMap<>(); for (String packageName : bundleizedRequest.keySet()) { Preconditions.checkNotNull(packageName); ArrayList<String> permissions = bundleizedRequest.getStringArrayList(packageName); Preconditions.checkCollectionElementsNotNull(permissions, "permissions"); request.put(packageName, permissions); } enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null); // Verify callerPackageName try { PackageInfo pkgInfo = getPackageManager().getPackageInfo(callerPackageName, 0); checkArgument(getCallingUid() == pkgInfo.applicationInfo.uid); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException(e); } mHandler.sendMessage(obtainMessage( PermissionControllerService::revokeRuntimePermissions, PermissionControllerService.this, request, doDryRun, reason, callerPackageName, callback)); } @Override @Override public void getAppPermissions(String packageName, RemoteCallback callback) { public void getAppPermissions(String packageName, RemoteCallback callback) { checkNotNull(packageName, "packageName"); checkNotNull(packageName, "packageName"); Loading Loading @@ -133,6 +190,27 @@ public abstract class PermissionControllerService extends Service { }; }; } } private void revokeRuntimePermissions(@NonNull Map<String, List<String>> requests, boolean doDryRun, @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName, @NonNull RemoteCallback callback) { Map<String, List<String>> revoked = onRevokeRuntimePermissions(requests, doDryRun, reason, callerPackageName); checkNotNull(revoked); Bundle bundledizedRevoked = new Bundle(); for (Map.Entry<String, List<String>> appRevocation : revoked.entrySet()) { checkNotNull(appRevocation.getKey()); checkCollectionElementsNotNull(appRevocation.getValue(), "permissions"); bundledizedRevoked.putStringArrayList(appRevocation.getKey(), new ArrayList<>(appRevocation.getValue())); } Bundle result = new Bundle(); result.putBundle(PermissionControllerManager.KEY_RESULT, bundledizedRevoked); callback.sendResult(result); } private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) { private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) { List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName); List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName); if (permissions != null && !permissions.isEmpty()) { if (permissions != null && !permissions.isEmpty()) { Loading