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/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
new file mode 100644
index 0000000..24c5d23
--- /dev/null
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.util.SparseIntArray;
+
+/**
+ * App ops service local interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class AppOpsManagerInternal {
+ /**
+ * Set the currently configured device and profile owners. Specifies the package uid (value)
+ * that has been configured for each user (key) that has one. These will be allowed privileged
+ * access to app ops for their user.
+ */
+ public abstract void setDeviceAndProfileOwners(SparseIntArray owners);
+}
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;
+ }
+ }
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 729dba5..c370a00 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3351,6 +3351,7 @@
case SystemService.PHASE_LOCK_SETTINGS_READY:
onLockSettingsReady();
loadAdminDataAsync();
+ mOwners.systemReady();
break;
case SystemService.PHASE_BOOT_COMPLETED:
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 8366114..632f0aa 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -17,11 +17,14 @@
package com.android.server.devicepolicy;
import android.annotation.Nullable;
+import android.app.AppOpsManagerInternal;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.content.ComponentName;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.os.Binder;
import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
@@ -32,10 +35,12 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -99,6 +104,8 @@
private final UserManagerInternal mUserManagerInternal;
private final PackageManagerInternal mPackageManagerInternal;
+ private boolean mSystemReady;
+
// Internal state for the device owner package.
private OwnerInfo mDeviceOwner;
@@ -179,6 +186,7 @@
getDeviceOwnerUserId()));
}
pushToPackageManagerLocked();
+ pushToAppOpsLocked();
}
}
@@ -262,6 +270,7 @@
mUserManagerInternal.setDeviceManaged(true);
pushToPackageManagerLocked();
+ pushToAppOpsLocked();
}
}
@@ -272,6 +281,7 @@
mUserManagerInternal.setDeviceManaged(false);
pushToPackageManagerLocked();
+ pushToAppOpsLocked();
}
}
@@ -283,6 +293,7 @@
/* remoteBugreportHash =*/ null));
mUserManagerInternal.setUserManaged(userId, true);
pushToPackageManagerLocked();
+ pushToAppOpsLocked();
}
}
@@ -291,6 +302,7 @@
mProfileOwners.remove(userId);
mUserManagerInternal.setUserManaged(userId, false);
pushToPackageManagerLocked();
+ pushToAppOpsLocked();
}
}
@@ -302,6 +314,7 @@
ownerInfo.remoteBugreportHash);
mProfileOwners.put(userId, newOwnerInfo);
pushToPackageManagerLocked();
+ pushToAppOpsLocked();
}
}
@@ -313,6 +326,7 @@
mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri,
mDeviceOwner.remoteBugreportHash);
pushToPackageManagerLocked();
+ pushToAppOpsLocked();
}
}
@@ -581,6 +595,48 @@
}
}
+ void pushToAppOpsLocked() {
+ if (!mSystemReady) {
+ return;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final SparseIntArray owners = new SparseIntArray();
+ if (mDeviceOwner != null) {
+ final int uid = mPackageManagerInternal.getPackageUid(mDeviceOwner.packageName,
+ PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES,
+ mDeviceOwnerUserId);
+ if (uid >= 0) {
+ owners.put(mDeviceOwnerUserId, uid);
+ }
+ }
+ if (mProfileOwners != null) {
+ for (int poi = mProfileOwners.size() - 1; poi >= 0; poi--) {
+ final int uid = mPackageManagerInternal.getPackageUid(
+ mProfileOwners.valueAt(poi).packageName,
+ PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES,
+ mProfileOwners.keyAt(poi));
+ if (uid >= 0) {
+ owners.put(mProfileOwners.keyAt(poi), uid);
+ }
+ }
+ }
+ AppOpsManagerInternal appops = LocalServices.getService(AppOpsManagerInternal.class);
+ if (appops != null) {
+ appops.setDeviceAndProfileOwners(owners.size() > 0 ? owners : null);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void systemReady() {
+ synchronized (mLock) {
+ mSystemReady = true;
+ pushToAppOpsLocked();
+ }
+ }
+
private abstract static class FileReadWriter {
private final File mFile;