|
35 | 35 | import android.content.Intent;
|
36 | 36 | import android.content.ServiceConnection;
|
37 | 37 | import android.content.pm.InstrumentationInfo;
|
| 38 | +import android.content.pm.PackageInfo; |
38 | 39 | import android.content.pm.PackageManager;
|
39 | 40 | import android.content.pm.PackageManager.NameNotFoundException;
|
| 41 | +import android.content.pm.PermissionInfo; |
40 | 42 | import android.os.Build;
|
41 | 43 | import android.os.Bundle;
|
42 | 44 | import android.os.Debug;
|
|
60 | 62 | import java.io.IOException;
|
61 | 63 | import java.io.OutputStream;
|
62 | 64 | import java.io.PrintStream;
|
| 65 | +import java.util.ArrayList; |
63 | 66 | import java.util.Arrays;
|
| 67 | +import java.util.Collections; |
64 | 68 | import java.util.HashMap;
|
65 | 69 | import java.util.Iterator;
|
66 | 70 | import java.util.List;
|
@@ -141,6 +145,8 @@ public final class AndroidTestOrchestrator extends android.app.Instrumentation
|
141 | 145 | private static final List<String> RUNTIME_PERMISSIONS =
|
142 | 146 | Arrays.asList(permission.WRITE_EXTERNAL_STORAGE, permission.READ_EXTERNAL_STORAGE);
|
143 | 147 |
|
| 148 | + private final List<String> systemDevelopmentPermissionCache = null; |
| 149 | + |
144 | 150 | private final OrchestrationResult.Builder resultBuilder = new OrchestrationResult.Builder();
|
145 | 151 | private final OrchestrationResultPrinter resultPrinter = new OrchestrationResultPrinter();
|
146 | 152 | private final OrchestrationListenerManager listenerManager =
|
@@ -384,10 +390,87 @@ public void run() {
|
384 | 390 | getSecret(arguments),
|
385 | 391 | "pm",
|
386 | 392 | Arrays.asList("clear", getTargetInstrPackage(arguments)));
|
| 393 | + if (Build.VERSION.SDK_INT < 23) { |
| 394 | + // pm clear revokes development permissions on API 23+. |
| 395 | + // We have to do it manually on older platforms. |
| 396 | + revokeDevelopmentPermissions(); |
| 397 | + } |
387 | 398 | }
|
388 | 399 | });
|
389 | 400 | }
|
390 | 401 |
|
| 402 | + private void revokeDevelopmentPermissions() { |
| 403 | + String packageName = getTargetPackage(arguments); |
| 404 | + List<String> permissions = collectGrantedDevelopmentPermissions(packageName); |
| 405 | + revokePermissions(packageName, permissions); |
| 406 | + } |
| 407 | + |
| 408 | + private List<String> collectGrantedDevelopmentPermissions(String packageName) { |
| 409 | + try { |
| 410 | + PackageManager pm = getContext().getPackageManager(); |
| 411 | + PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); |
| 412 | + |
| 413 | + List<String> out = new ArrayList<>(); |
| 414 | + String permission; |
| 415 | + for (int i = 0, size = packageInfo.requestedPermissions.length; i < size; i++) { |
| 416 | + if ((packageInfo.requestedPermissionsFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) { |
| 417 | + permission = packageInfo.requestedPermissions[i]; |
| 418 | + if (isDevelopmentPermission(permission)) { |
| 419 | + out.add(permission); |
| 420 | + } |
| 421 | + } |
| 422 | + } |
| 423 | + return out; |
| 424 | + } catch (PackageManager.NameNotFoundException ignore) { |
| 425 | + return Collections.emptyList(); |
| 426 | + } |
| 427 | + } |
| 428 | + |
| 429 | + private boolean isDevelopmentPermission(String permission) { |
| 430 | + // We're assuming system-defined permissions don't change. |
| 431 | + if (getSystemDevelopmentPermissions().contains(permission)) { |
| 432 | + return true; |
| 433 | + } |
| 434 | + // App-defined permissions can change any time apps get (un)installed. |
| 435 | + try { |
| 436 | + PermissionInfo permissionInfo = getContext().getPackageManager().getPermissionInfo(permission, 0); |
| 437 | + return (permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0; |
| 438 | + } catch (PackageManager.NameNotFoundException ignore) { |
| 439 | + return false; |
| 440 | + } |
| 441 | + } |
| 442 | + |
| 443 | + private List<String> getSystemDevelopmentPermissions() { |
| 444 | + List<String> out = systemDevelopmentPermissionCache; |
| 445 | + if (out == null) { |
| 446 | + out = new ArrayList<>(); |
| 447 | + try { |
| 448 | + PackageInfo packageInfo = getContext().getPackageManager() |
| 449 | + .getPackageInfo("android", PackageManager.GET_PERMISSIONS); |
| 450 | + for (PermissionInfo permissionInfo : packageInfo.permissions) { |
| 451 | + if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) { |
| 452 | + out.add(permissionInfo.name); |
| 453 | + } |
| 454 | + } |
| 455 | + } catch (PackageManager.NameNotFoundException ignore) { |
| 456 | + } |
| 457 | + systemDevelopmentPermissionCache = Collections.unmodifiableList(out); |
| 458 | + } |
| 459 | + return out; |
| 460 | + } |
| 461 | + |
| 462 | + private void revokePermissions(String packageName, List<String> permissions) { |
| 463 | + if (permissions.isEmpty()) { |
| 464 | + return; |
| 465 | + } |
| 466 | + Context context = getContext(); |
| 467 | + PackageManager pm = context.getPackageManager(); |
| 468 | + String secret = getSecret(arguments); |
| 469 | + for (String permission : permissions) { |
| 470 | + execShellCommandSync(context, secret, "pm", Arrays.asList("revoke", packageName, permission)); |
| 471 | + } |
| 472 | + } |
| 473 | + |
391 | 474 | @VisibleForTesting
|
392 | 475 | static String addTestCoverageSupport(Bundle args, String filename) {
|
393 | 476 | // Only do the aggregate coverage mode if coverage was requested AND we're running in isolation
|
|
0 commit comments