diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java new file mode 100644 index 0000000000000..24c5d234c1209 --- /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 169f2a863e96f..4c914c2058158 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.ActivityManager; 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { */ private final ArrayMap 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 @@ -551,6 +557,7 @@ public class AppOpsService extends IAppOpsService.Stub { public void publish(Context context) { mContext = context; ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder()); + LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal); } public void systemReady() { @@ -921,12 +928,27 @@ public class AppOpsService extends IAppOpsService.Stub { } } + 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); @@ -1029,10 +1051,7 @@ public class AppOpsService extends IAppOpsService.Stub { @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 repCbs = null; code = AppOpsManager.opToSwitch(code); @@ -1151,8 +1170,6 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -1166,6 +1183,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } + enforceManageAppOpsModes(callingPid, callingUid, reqUid); + HashMap> callbacks = null; synchronized (this) { boolean changed = false; @@ -1428,10 +1447,9 @@ public class AppOpsService extends IAppOpsService.Stub { @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 usageRestrictions = mAudioRestrictions.get(code); if (usageRestrictions == null) { @@ -2809,9 +2827,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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) { @@ -2825,9 +2842,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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(); @@ -2989,6 +3005,17 @@ public class AppOpsService extends IAppOpsService.Stub { 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= 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;