diff --git a/api/current.txt b/api/current.txt
index e736d78aa24ba..126318655f61f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3820,6 +3820,7 @@ package android.app {
method public void startWatchingMode(java.lang.String, java.lang.String, android.app.AppOpsManager.OnOpChangedListener);
method public void stopWatchingMode(android.app.AppOpsManager.OnOpChangedListener);
field public static final int MODE_ALLOWED = 0; // 0x0
+ field public static final int MODE_DEFAULT = 3; // 0x3
field public static final int MODE_ERRORED = 2; // 0x2
field public static final int MODE_IGNORED = 1; // 0x1
field public static final java.lang.String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -8877,6 +8878,7 @@ package android.content.pm {
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int FLAG_COSTS_MONEY = 1; // 0x1
field public static final int PROTECTION_DANGEROUS = 1; // 0x1
+ field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
field public static final int PROTECTION_FLAG_DEVELOPMENT = 32; // 0x20
field public static final int PROTECTION_FLAG_SYSTEM = 16; // 0x10
field public static final int PROTECTION_MASK_BASE = 15; // 0xf
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 990ea85f99e1c..caadecb29dc73 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -94,6 +94,13 @@ public class AppOpsManager {
*/
public static final int MODE_ERRORED = 2;
+ /**
+ * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller should
+ * use its default security check. This mode is not normally used; it should only be used
+ * with appop permissions, and callers must explicitly check for it and deal with it.
+ */
+ public static final int MODE_DEFAULT = 3;
+
// when adding one of these:
// - increment _NUM_OP
// - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode
@@ -588,7 +595,7 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
- AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
+ AppOpsManager.MODE_DEFAULT, // OP_GET_USAGE_STATS
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 5e55ba72e6a89..4b339a1e1774c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -110,6 +110,8 @@ interface IPackageManager {
int getFlagsForUid(int uid);
+ String[] getAppOpPermissionPackages(String permissionName);
+
ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags, int userId);
boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5a63e5f656f53..af574dbeb80c5 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -68,6 +68,13 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
*/
public static final int PROTECTION_FLAG_DEVELOPMENT = 0x20;
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the development value of
+ * {@link android.R.attr#protectionLevel}.
+ */
+ public static final int PROTECTION_FLAG_APPOP = 0x40;
+
/**
* Mask for {@link #protectionLevel}: the basic protection type.
*/
@@ -153,6 +160,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
protLevel += "|development";
}
+ if ((level&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ protLevel += "|appop";
+ }
return protLevel;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a2afb44fae94b..7f97726a6ff21 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2461,7 +2461,7 @@
+ android:protectionLevel="signature|system|development|appop" />
@@ -2469,7 +2469,7 @@
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:label="@string/permlab_batteryStats"
android:description="@string/permdesc_batteryStats"
- android:protectionLevel="signature|system" />
+ android:protectionLevel="signature|system|development" />
+
+
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6de8a8b77187b..69f2f32ac7c0b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -474,6 +474,9 @@ public class PackageManagerService extends IPackageManager.Stub {
final SparseArray mPendingVerification
= new SparseArray();
+ /** Set of packages associated with each app op permission. */
+ final ArrayMap> mAppOpPermissionPackages = new ArrayMap<>();
+
final PackageInstallerService mInstallerService;
HashSet mDeferredDexOpt = null;
@@ -2916,6 +2919,17 @@ public class PackageManagerService extends IPackageManager.Stub {
return 0;
}
+ @Override
+ public String[] getAppOpPermissionPackages(String permissionName) {
+ synchronized (mPackages) {
+ ArraySet pkgs = mAppOpPermissionPackages.get(permissionName);
+ if (pkgs == null) {
+ return null;
+ }
+ return pkgs.toArray(new String[pkgs.size()]);
+ }
+ }
+
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
@@ -6591,6 +6605,31 @@ public class PackageManagerService extends IPackageManager.Stub {
r.append(p.info.name);
}
}
+ if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ ArraySet appOpPerms = mAppOpPermissionPackages.get(p.info.name);
+ if (appOpPerms != null) {
+ appOpPerms.remove(pkg.packageName);
+ }
+ }
+ }
+ if (r != null) {
+ if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ }
+
+ N = pkg.requestedPermissions.size();
+ r = null;
+ for (i=0; i appOpPerms = mAppOpPermissionPackages.get(perm);
+ if (appOpPerms != null) {
+ appOpPerms.remove(pkg.packageName);
+ if (appOpPerms.isEmpty()) {
+ mAppOpPermissionPackages.remove(perm);
+ }
+ }
+ }
}
if (r != null) {
if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
@@ -6775,6 +6814,15 @@ public class PackageManagerService extends IPackageManager.Stub {
final String perm = bp.name;
boolean allowed;
boolean allowedSig = false;
+ if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+ // Keep track of app op permissions.
+ ArraySet pkgs = mAppOpPermissionPackages.get(bp.name);
+ if (pkgs == null) {
+ pkgs = new ArraySet<>();
+ mAppOpPermissionPackages.put(bp.name, pkgs);
+ }
+ pkgs.add(pkg.packageName);
+ }
final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
if (level == PermissionInfo.PROTECTION_NORMAL
|| level == PermissionInfo.PROTECTION_DANGEROUS) {
@@ -6837,7 +6885,9 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " (protectionLevel=" + bp.protectionLevel
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
+ ")");
- } else {
+ } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
+ // Don't print warning for app op permissions, since it is fine for them
+ // not to be granted, there is a UI for the user to decide.
Slog.w(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
+ " (protectionLevel=" + bp.protectionLevel
@@ -12426,6 +12476,22 @@ public class PackageManagerService extends IPackageManager.Stub {
if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
+ if (packageName == null) {
+ for (int iperm=0; iperm pkgs = mAppOpPermissionPackages.valueAt(iperm);
+ for (int ipkg=0; ipkg
+
+