1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.permission; 18 19 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT; 20 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED; 21 import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED; 22 import static android.permission.PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED; 23 import static android.permission.PermissionControllerManager.COUNT_WHEN_SYSTEM; 24 25 import static com.android.internal.util.Preconditions.checkArgument; 26 import static com.android.internal.util.Preconditions.checkArgumentNonnegative; 27 import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; 28 import static com.android.internal.util.Preconditions.checkFlagsArgument; 29 import static com.android.internal.util.Preconditions.checkNotNull; 30 import static com.android.internal.util.Preconditions.checkStringNotEmpty; 31 32 import android.Manifest; 33 import android.annotation.BinderThread; 34 import android.annotation.NonNull; 35 import android.annotation.SystemApi; 36 import android.app.Service; 37 import android.app.admin.DevicePolicyManager.PermissionGrantState; 38 import android.content.Intent; 39 import android.content.pm.PackageInfo; 40 import android.content.pm.PackageManager; 41 import android.os.Bundle; 42 import android.os.IBinder; 43 import android.os.ParcelFileDescriptor; 44 import android.os.RemoteCallback; 45 import android.os.UserHandle; 46 import android.permission.PermissionControllerManager.CountPermissionAppsFlag; 47 import android.util.ArrayMap; 48 import android.util.Log; 49 50 import com.android.internal.util.Preconditions; 51 52 import java.io.IOException; 53 import java.io.InputStream; 54 import java.io.OutputStream; 55 import java.util.ArrayList; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.concurrent.CountDownLatch; 59 import java.util.function.Consumer; 60 import java.util.function.IntConsumer; 61 62 /** 63 * This service is meant to be implemented by the app controlling permissions. 64 * 65 * @see PermissionControllerManager 66 * 67 * @hide 68 */ 69 @SystemApi 70 public abstract class PermissionControllerService extends Service { 71 private static final String LOG_TAG = PermissionControllerService.class.getSimpleName(); 72 73 /** 74 * The {@link Intent} action that must be declared as handled by a service 75 * in its manifest for the system to recognize it as a runtime permission 76 * presenter service. 77 */ 78 public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; 79 80 /** 81 * Revoke a set of runtime permissions for various apps. 82 * 83 * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>} 84 * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them 85 * @param reason Why the permission should be revoked 86 * @param callerPackageName The package name of the calling app 87 * @param callback Callback waiting for the actually removed permissions as 88 * {@code Map<packageName, List<permission>>} 89 */ 90 @BinderThread onRevokeRuntimePermissions( @onNull Map<String, List<String>> requests, boolean doDryRun, @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName, @NonNull Consumer<Map<String, List<String>>> callback)91 public abstract void onRevokeRuntimePermissions( 92 @NonNull Map<String, List<String>> requests, boolean doDryRun, 93 @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName, 94 @NonNull Consumer<Map<String, List<String>>> callback); 95 96 /** 97 * Create a backup of the runtime permissions. 98 * 99 * @param user The user to back up 100 * @param backup The stream to write the backup to 101 * @param callback Callback waiting for operation to be complete 102 */ 103 @BinderThread onGetRuntimePermissionsBackup(@onNull UserHandle user, @NonNull OutputStream backup, @NonNull Runnable callback)104 public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user, 105 @NonNull OutputStream backup, @NonNull Runnable callback); 106 107 /** 108 * Restore a backup of the runtime permissions. 109 * 110 * <p>If an app mentioned in the backup is not installed the state should be saved to later 111 * be restored via {@link #onRestoreDelayedRuntimePermissionsBackup}. 112 * 113 * @param user The user to restore 114 * @param backup The stream to read the backup from 115 * @param callback Callback waiting for operation to be complete 116 */ 117 @BinderThread onRestoreRuntimePermissionsBackup(@onNull UserHandle user, @NonNull InputStream backup, @NonNull Runnable callback)118 public abstract void onRestoreRuntimePermissionsBackup(@NonNull UserHandle user, 119 @NonNull InputStream backup, @NonNull Runnable callback); 120 121 /** 122 * Restore the permission state of an app that was provided in 123 * {@link #onRestoreRuntimePermissionsBackup} but could not be restored back then. 124 * 125 * @param packageName The app to restore 126 * @param user The user to restore 127 * @param callback Callback waiting for whether there is still delayed backup left 128 */ 129 @BinderThread onRestoreDelayedRuntimePermissionsBackup(@onNull String packageName, @NonNull UserHandle user, @NonNull Consumer<Boolean> callback)130 public abstract void onRestoreDelayedRuntimePermissionsBackup(@NonNull String packageName, 131 @NonNull UserHandle user, @NonNull Consumer<Boolean> callback); 132 133 /** 134 * Gets the runtime permissions for an app. 135 * 136 * @param packageName The package for which to query. 137 * @param callback Callback waiting for the descriptions of the runtime permissions of the app 138 */ 139 @BinderThread onGetAppPermissions(@onNull String packageName, @NonNull Consumer<List<RuntimePermissionPresentationInfo>> callback)140 public abstract void onGetAppPermissions(@NonNull String packageName, 141 @NonNull Consumer<List<RuntimePermissionPresentationInfo>> callback); 142 143 /** 144 * Revokes the permission {@code permissionName} for app {@code packageName} 145 * 146 * @param packageName The package for which to revoke 147 * @param permissionName The permission to revoke 148 * @param callback Callback waiting for operation to be complete 149 */ 150 @BinderThread onRevokeRuntimePermission(@onNull String packageName, @NonNull String permissionName, @NonNull Runnable callback)151 public abstract void onRevokeRuntimePermission(@NonNull String packageName, 152 @NonNull String permissionName, @NonNull Runnable callback); 153 154 /** 155 * Count how many apps have one of a set of permissions. 156 * 157 * @param permissionNames The permissions the app might have 158 * @param flags Modify which apps to count. By default all non-system apps that request a 159 * permission are counted 160 * @param callback Callback waiting for the number of apps that have one of the permissions 161 */ 162 @BinderThread onCountPermissionApps(@onNull List<String> permissionNames, @CountPermissionAppsFlag int flags, @NonNull IntConsumer callback)163 public abstract void onCountPermissionApps(@NonNull List<String> permissionNames, 164 @CountPermissionAppsFlag int flags, @NonNull IntConsumer callback); 165 166 /** 167 * Count how many apps have used permissions. 168 * 169 * @param countSystem Also count system apps 170 * @param numMillis The number of milliseconds in the past to check for uses 171 * @param callback Callback waiting for the descriptions of the users of permissions 172 */ 173 @BinderThread onGetPermissionUsages(boolean countSystem, long numMillis, @NonNull Consumer<List<RuntimePermissionUsageInfo>> callback)174 public abstract void onGetPermissionUsages(boolean countSystem, long numMillis, 175 @NonNull Consumer<List<RuntimePermissionUsageInfo>> callback); 176 177 /** 178 * Grant or upgrade runtime permissions. The upgrade could be performed 179 * based on whether the device upgraded, whether the permission database 180 * version is old, or because the permission policy changed. 181 * 182 * @param callback Callback waiting for operation to be complete 183 * 184 * @see PackageManager#isDeviceUpgrading() 185 * @see PermissionManager#getRuntimePermissionsVersion() 186 * @see PermissionManager#setRuntimePermissionsVersion(int) 187 */ 188 @BinderThread onGrantOrUpgradeDefaultRuntimePermissions(@onNull Runnable callback)189 public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable callback); 190 191 /** 192 * Set the runtime permission state from a device admin. 193 * 194 * @param callerPackageName The package name of the admin requesting the change 195 * @param packageName Package the permission belongs to 196 * @param permission Permission to change 197 * @param grantState State to set the permission into 198 * @param callback Callback waiting for whether the state could be set or not 199 */ 200 @BinderThread onSetRuntimePermissionGrantStateByDeviceAdmin( @onNull String callerPackageName, @NonNull String packageName, @NonNull String permission, @PermissionGrantState int grantState, @NonNull Consumer<Boolean> callback)201 public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin( 202 @NonNull String callerPackageName, @NonNull String packageName, 203 @NonNull String permission, @PermissionGrantState int grantState, 204 @NonNull Consumer<Boolean> callback); 205 206 @Override onBind(Intent intent)207 public final @NonNull IBinder onBind(Intent intent) { 208 return new IPermissionController.Stub() { 209 @Override 210 public void revokeRuntimePermissions( 211 Bundle bundleizedRequest, boolean doDryRun, int reason, 212 String callerPackageName, RemoteCallback callback) { 213 checkNotNull(bundleizedRequest, "bundleizedRequest"); 214 checkNotNull(callerPackageName); 215 checkNotNull(callback); 216 217 Map<String, List<String>> request = new ArrayMap<>(); 218 for (String packageName : bundleizedRequest.keySet()) { 219 Preconditions.checkNotNull(packageName); 220 221 ArrayList<String> permissions = 222 bundleizedRequest.getStringArrayList(packageName); 223 Preconditions.checkCollectionElementsNotNull(permissions, "permissions"); 224 225 request.put(packageName, permissions); 226 } 227 228 enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null); 229 230 // Verify callerPackageName 231 try { 232 PackageInfo pkgInfo = getPackageManager().getPackageInfo(callerPackageName, 0); 233 checkArgument(getCallingUid() == pkgInfo.applicationInfo.uid); 234 } catch (PackageManager.NameNotFoundException e) { 235 throw new RuntimeException(e); 236 } 237 238 onRevokeRuntimePermissions(request, 239 doDryRun, reason, callerPackageName, revoked -> { 240 checkNotNull(revoked); 241 Bundle bundledizedRevoked = new Bundle(); 242 for (Map.Entry<String, List<String>> appRevocation : 243 revoked.entrySet()) { 244 checkNotNull(appRevocation.getKey()); 245 checkCollectionElementsNotNull(appRevocation.getValue(), 246 "permissions"); 247 248 bundledizedRevoked.putStringArrayList(appRevocation.getKey(), 249 new ArrayList<>(appRevocation.getValue())); 250 } 251 252 Bundle result = new Bundle(); 253 result.putBundle(PermissionControllerManager.KEY_RESULT, 254 bundledizedRevoked); 255 callback.sendResult(result); 256 }); 257 } 258 259 @Override 260 public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) { 261 checkNotNull(user); 262 checkNotNull(pipe); 263 264 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); 265 266 try (OutputStream backup = new ParcelFileDescriptor.AutoCloseOutputStream(pipe)) { 267 CountDownLatch latch = new CountDownLatch(1); 268 onGetRuntimePermissionsBackup(user, backup, latch::countDown); 269 latch.await(); 270 } catch (IOException e) { 271 Log.e(LOG_TAG, "Could not open pipe to write backup to", e); 272 } catch (InterruptedException e) { 273 Log.e(LOG_TAG, "getRuntimePermissionBackup timed out", e); 274 } 275 } 276 277 @Override 278 public void restoreRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) { 279 checkNotNull(user); 280 checkNotNull(pipe); 281 282 enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null); 283 284 try (InputStream backup = new ParcelFileDescriptor.AutoCloseInputStream(pipe)) { 285 CountDownLatch latch = new CountDownLatch(1); 286 onRestoreRuntimePermissionsBackup(user, backup, latch::countDown); 287 latch.await(); 288 } catch (IOException e) { 289 Log.e(LOG_TAG, "Could not open pipe to read backup from", e); 290 } catch (InterruptedException e) { 291 Log.e(LOG_TAG, "restoreRuntimePermissionBackup timed out", e); 292 } 293 } 294 295 @Override 296 public void restoreDelayedRuntimePermissionBackup(String packageName, UserHandle user, 297 RemoteCallback callback) { 298 checkNotNull(packageName); 299 checkNotNull(user); 300 checkNotNull(callback); 301 302 enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null); 303 304 onRestoreDelayedRuntimePermissionsBackup(packageName, user, 305 hasMoreBackup -> { 306 Bundle result = new Bundle(); 307 result.putBoolean(PermissionControllerManager.KEY_RESULT, 308 hasMoreBackup); 309 callback.sendResult(result); 310 }); 311 } 312 313 @Override 314 public void getAppPermissions(String packageName, RemoteCallback callback) { 315 checkNotNull(packageName, "packageName"); 316 checkNotNull(callback, "callback"); 317 318 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); 319 320 onGetAppPermissions(packageName, 321 permissions -> { 322 if (permissions != null && !permissions.isEmpty()) { 323 Bundle result = new Bundle(); 324 result.putParcelableList(PermissionControllerManager.KEY_RESULT, 325 permissions); 326 callback.sendResult(result); 327 } else { 328 callback.sendResult(null); 329 } 330 }); 331 } 332 333 @Override 334 public void revokeRuntimePermission(String packageName, String permissionName) { 335 checkNotNull(packageName, "packageName"); 336 checkNotNull(permissionName, "permissionName"); 337 338 enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null); 339 340 CountDownLatch latch = new CountDownLatch(1); 341 PermissionControllerService.this.onRevokeRuntimePermission(packageName, 342 permissionName, latch::countDown); 343 try { 344 latch.await(); 345 } catch (InterruptedException e) { 346 Log.e(LOG_TAG, "revokeRuntimePermission timed out", e); 347 } 348 } 349 350 @Override 351 public void countPermissionApps(List<String> permissionNames, int flags, 352 RemoteCallback callback) { 353 checkCollectionElementsNotNull(permissionNames, "permissionNames"); 354 checkFlagsArgument(flags, COUNT_WHEN_SYSTEM | COUNT_ONLY_WHEN_GRANTED); 355 checkNotNull(callback, "callback"); 356 357 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); 358 359 onCountPermissionApps(permissionNames, flags, numApps -> { 360 Bundle result = new Bundle(); 361 result.putInt(PermissionControllerManager.KEY_RESULT, numApps); 362 callback.sendResult(result); 363 }); 364 } 365 366 @Override 367 public void getPermissionUsages(boolean countSystem, long numMillis, 368 RemoteCallback callback) { 369 checkArgumentNonnegative(numMillis); 370 checkNotNull(callback, "callback"); 371 372 enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null); 373 374 onGetPermissionUsages(countSystem, numMillis, users -> { 375 if (users != null && !users.isEmpty()) { 376 Bundle result = new Bundle(); 377 result.putParcelableList(PermissionControllerManager.KEY_RESULT, users); 378 callback.sendResult(result); 379 } else { 380 callback.sendResult(null); 381 } 382 }); 383 } 384 385 @Override 386 public void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, 387 String packageName, String permission, int grantState, 388 RemoteCallback callback) { 389 checkStringNotEmpty(callerPackageName); 390 checkStringNotEmpty(packageName); 391 checkStringNotEmpty(permission); 392 checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED 393 || grantState == PERMISSION_GRANT_STATE_DENIED 394 || grantState == PERMISSION_GRANT_STATE_DEFAULT); 395 checkNotNull(callback); 396 397 if (grantState == PERMISSION_GRANT_STATE_DENIED) { 398 enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null); 399 } 400 401 if (grantState == PERMISSION_GRANT_STATE_DENIED) { 402 enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null); 403 } 404 405 enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, 406 null); 407 408 onSetRuntimePermissionGrantStateByDeviceAdmin(callerPackageName, 409 packageName, permission, grantState, wasSet -> { 410 Bundle result = new Bundle(); 411 result.putBoolean(PermissionControllerManager.KEY_RESULT, wasSet); 412 callback.sendResult(result); 413 }); 414 } 415 416 @Override 417 public void grantOrUpgradeDefaultRuntimePermissions(@NonNull RemoteCallback callback) { 418 checkNotNull(callback, "callback"); 419 420 enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, 421 null); 422 423 onGrantOrUpgradeDefaultRuntimePermissions(() -> callback.sendResult(Bundle.EMPTY)); 424 } 425 }; 426 } 427 } 428