Allow device/profile owners to change app ops modes.
This allows them to continue to have this capability the
same as before we locked down access to it.
Bug: 78480444
Test: manual
Change-Id: If2b0722945235eb67676ace3f54efaa71a64bcde
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 7f26575..b06454c 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -21,6 +21,7 @@
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -172,6 +173,9 @@
final AtomicFile mFile;
final Handler mHandler;
+ private final AppOpsManagerInternalImpl mAppOpsManagerInternal
+ = new AppOpsManagerInternalImpl();
+
boolean mWriteScheduled;
boolean mFastWriteScheduled;
final Runnable mWriteRunner = new Runnable() {
@@ -200,6 +204,8 @@
*/
private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>();
+ SparseIntArray mProfileOwners;
+
/**
* All times are in milliseconds. These constants are kept synchronized with the system
* global Settings. Any access to this class or its fields should be done while
@@ -554,6 +560,7 @@
public void publish(Context context) {
mContext = context;
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
+ LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal);
}
public void systemReady() {
@@ -923,12 +930,27 @@
}
}
+ void enforceManageAppOpsModes(int callingPid, int callingUid, int targetUid) {
+ if (callingPid == Process.myPid()) {
+ return;
+ }
+ final int callingUser = UserHandle.getUserId(callingUid);
+ synchronized (this) {
+ if (mProfileOwners != null && mProfileOwners.get(callingUser, -1) == callingUid) {
+ if (targetUid >= 0 && callingUser == UserHandle.getUserId(targetUid)) {
+ // Profile owners are allowed to change modes but only for apps
+ // within their user.
+ return;
+ }
+ }
+ }
+ mContext.enforcePermission(android.Manifest.permission.MANAGE_APP_OPS_MODES,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ }
+
@Override
public void setUidMode(int code, int uid, int mode) {
- if (Binder.getCallingPid() != Process.myPid()) {
- mContext.enforcePermission(android.Manifest.permission.MANAGE_APP_OPS_MODES,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- }
+ enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
code = AppOpsManager.opToSwitch(code);
@@ -1031,10 +1053,7 @@
@Override
public void setMode(int code, int uid, String packageName, int mode) {
- if (Binder.getCallingPid() != Process.myPid()) {
- mContext.enforcePermission(android.Manifest.permission.MANAGE_APP_OPS_MODES,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
- }
+ enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
ArraySet<ModeCallback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
@@ -1153,8 +1172,6 @@
public void resetAllModes(int reqUserId, String reqPackageName) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
- mContext.enforcePermission(android.Manifest.permission.MANAGE_APP_OPS_MODES,
- callingPid, callingUid, null);
reqUserId = ActivityManager.handleIncomingUser(callingPid, callingUid, reqUserId,
true, true, "resetAllModes", null);
@@ -1168,6 +1185,8 @@
}
}
+ enforceManageAppOpsModes(callingPid, callingUid, reqUid);
+
HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks = null;
synchronized (this) {
boolean changed = false;
@@ -1430,10 +1449,9 @@
@Override
public void setAudioRestriction(int code, int usage, int uid, int mode,
String[] exceptionPackages) {
+ enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingUid(uid);
verifyIncomingOp(code);
- mContext.enforcePermission(android.Manifest.permission.MANAGE_APP_OPS_MODES,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
synchronized (this) {
SparseArray<Restriction> usageRestrictions = mAudioRestrictions.get(code);
if (usageRestrictions == null) {
@@ -2811,9 +2829,8 @@
return 0;
}
case "write-settings": {
- shell.mInternal.mContext.enforcePermission(
- android.Manifest.permission.MANAGE_APP_OPS_MODES,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
+ shell.mInternal.enforceManageAppOpsModes(Binder.getCallingPid(),
+ Binder.getCallingUid(), -1);
long token = Binder.clearCallingIdentity();
try {
synchronized (shell.mInternal) {
@@ -2827,9 +2844,8 @@
return 0;
}
case "read-settings": {
- shell.mInternal.mContext.enforcePermission(
- android.Manifest.permission.MANAGE_APP_OPS_MODES,
- Binder.getCallingPid(), Binder.getCallingUid(), null);
+ shell.mInternal.enforceManageAppOpsModes(Binder.getCallingPid(),
+ Binder.getCallingUid(), -1);
long token = Binder.clearCallingIdentity();
try {
shell.mInternal.readState();
@@ -2991,6 +3007,17 @@
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
final Date date = new Date();
boolean needSep = false;
+ if (dumpOp < 0 && dumpMode < 0 && dumpPackage == null && mProfileOwners != null) {
+ pw.println(" Profile owners:");
+ for (int poi = 0; poi < mProfileOwners.size(); poi++) {
+ pw.print(" User #");
+ pw.print(mProfileOwners.keyAt(poi));
+ pw.print(": ");
+ UserHandle.formatUid(pw, mProfileOwners.valueAt(poi));
+ pw.println();
+ }
+ pw.println();
+ }
if (mOpModeWatchers.size() > 0) {
boolean printedHeader = false;
for (int i=0; i<mOpModeWatchers.size(); i++) {
@@ -3704,4 +3731,12 @@
return true;
}
}
+
+ private final class AppOpsManagerInternalImpl extends AppOpsManagerInternal {
+ @Override public void setDeviceAndProfileOwners(SparseIntArray owners) {
+ synchronized (AppOpsService.this) {
+ mProfileOwners = owners;
+ }
+ }
+ }
}