Skip to content

Commit 61d02c9

Browse files
committed
Clearing package data also revokes development permissions on API < 23
This matches the behavior on API 23 and newer.
1 parent 9ffb8f7 commit 61d02c9

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

runner/android_test_orchestrator/java/androidx/test/orchestrator/AndroidTestOrchestrator.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@
3535
import android.content.Intent;
3636
import android.content.ServiceConnection;
3737
import android.content.pm.InstrumentationInfo;
38+
import android.content.pm.PackageInfo;
3839
import android.content.pm.PackageManager;
3940
import android.content.pm.PackageManager.NameNotFoundException;
41+
import android.content.pm.PermissionInfo;
4042
import android.os.Build;
4143
import android.os.Bundle;
4244
import android.os.Debug;
@@ -60,7 +62,9 @@
6062
import java.io.IOException;
6163
import java.io.OutputStream;
6264
import java.io.PrintStream;
65+
import java.util.ArrayList;
6366
import java.util.Arrays;
67+
import java.util.Collections;
6468
import java.util.HashMap;
6569
import java.util.Iterator;
6670
import java.util.List;
@@ -141,6 +145,8 @@ public final class AndroidTestOrchestrator extends android.app.Instrumentation
141145
private static final List<String> RUNTIME_PERMISSIONS =
142146
Arrays.asList(permission.WRITE_EXTERNAL_STORAGE, permission.READ_EXTERNAL_STORAGE);
143147

148+
private final List<String> systemDevelopmentPermissionCache = null;
149+
144150
private final OrchestrationResult.Builder resultBuilder = new OrchestrationResult.Builder();
145151
private final OrchestrationResultPrinter resultPrinter = new OrchestrationResultPrinter();
146152
private final OrchestrationListenerManager listenerManager =
@@ -384,10 +390,87 @@ public void run() {
384390
getSecret(arguments),
385391
"pm",
386392
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+
}
387398
}
388399
});
389400
}
390401

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+
391474
@VisibleForTesting
392475
static String addTestCoverageSupport(Bundle args, String filename) {
393476
// Only do the aggregate coverage mode if coverage was requested AND we're running in isolation

0 commit comments

Comments
 (0)