diff options
6 files changed, 146 insertions, 24 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index f0453e9c45cc..93452fda1e1a 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1265,6 +1265,14 @@ public class AppOpsManager { } /** @hide */ + public void setUserRestriction(int code, boolean restricted, IBinder token) { + try { + mService.setUserRestriction(code, restricted, token, mContext.getUserId()); + } catch (RemoteException e) { + } + } + + /** @hide */ public void setMode(int code, int uid, String packageName, int mode) { try { mService.setMode(code, uid, packageName, mode); diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 9fa2c23dc953..b13be97dfb98 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -44,6 +44,7 @@ interface IAppOpsService { int checkAudioOperation(int code, int usage, int uid, String packageName); void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages); - void setUserRestrictions(in Bundle restrictions, int userHandle); + void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle); + void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle); void removeUser(int userHandle); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index bb31a0207148..eeff00f54c86 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2008,6 +2008,12 @@ <permission android:name="android.permission.UPDATE_APP_OPS_STATS" android:protectionLevel="signature|privileged|installer" /> + <!-- Allows an application to update the user app op restrictions. + Not for use by third party apps. + @hide --> + <permission android:name="android.permission.MANAGE_APP_OPS_RESTRICTIONS" + android:protectionLevel="signature|installer" /> + <!-- @SystemApi Allows an application to open windows that are for use by parts of the system user interface. <p>Not for use by third-party applications. diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 91f58c56c675..32f2d593f670 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -31,6 +31,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import android.Manifest; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppGlobals; @@ -67,6 +68,7 @@ import com.android.internal.app.IAppOpsCallback; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import libcore.util.EmptyArray; @@ -103,9 +105,10 @@ public class AppOpsService extends IAppOpsService.Stub { } }; - final SparseArray<UidState> mUidStates = new SparseArray<>(); + private final SparseArray<UidState> mUidStates = new SparseArray<>(); - private final SparseArray<boolean[]> mOpRestrictions = new SparseArray<boolean[]>(); + /** These are app op restrictions imposed per user from various parties */ + private final ArrayMap<IBinder, SparseArray<boolean[]>> mOpUserRestrictions = new ArrayMap<>(); private static final class UidState { public final int uid; @@ -1263,17 +1266,21 @@ public class AppOpsService extends IAppOpsService.Stub { private boolean isOpRestricted(int uid, int code, String packageName) { int userHandle = UserHandle.getUserId(uid); - boolean[] opRestrictions = mOpRestrictions.get(userHandle); - if ((opRestrictions != null) && opRestrictions[code]) { - if (AppOpsManager.opAllowSystemBypassRestriction(code)) { - synchronized (this) { - Ops ops = getOpsLocked(uid, packageName, true); - if ((ops != null) && ops.isPrivileged) { - return false; + final int restrictionSetCount = mOpUserRestrictions.size(); + for (int i = 0; i < restrictionSetCount; i++) { + SparseArray<boolean[]> perUserRestrictions = mOpUserRestrictions.valueAt(i); + boolean[] opRestrictions = perUserRestrictions.get(userHandle); + if (opRestrictions != null && opRestrictions[code]) { + if (AppOpsManager.opAllowSystemBypassRestriction(code)) { + synchronized (this) { + Ops ops = getOpsLocked(uid, packageName, true); + if ((ops != null) && ops.isPrivileged) { + return false; + } } } + return true; } - return true; } return false; } @@ -2049,27 +2056,123 @@ public class AppOpsService extends IAppOpsService.Stub { } @Override - public void setUserRestrictions(Bundle restrictions, int userHandle) throws RemoteException { + public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) { checkSystemUid("setUserRestrictions"); - boolean[] opRestrictions = mOpRestrictions.get(userHandle); - if (opRestrictions == null) { - opRestrictions = new boolean[AppOpsManager._NUM_OP]; - mOpRestrictions.put(userHandle, opRestrictions); - } + Preconditions.checkNotNull(token); + final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle); for (int i = 0; i < opRestrictions.length; ++i) { String restriction = AppOpsManager.opToRestriction(i); - if (restriction != null) { - opRestrictions[i] = restrictions.getBoolean(restriction, false); - } else { - opRestrictions[i] = false; + final boolean restricted = restriction != null + && restrictions.getBoolean(restriction, false); + setUserRestrictionNoCheck(i, restricted, token, userHandle); + } + } + + @Override + public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle) { + if (Binder.getCallingPid() != Process.myPid()) { + mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS, + Binder.getCallingPid(), Binder.getCallingUid(), null); + } + if (userHandle != UserHandle.getCallingUserId()) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission + .INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED + && mContext.checkCallingOrSelfPermission(Manifest.permission + .INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Need INTERACT_ACROSS_USERS_FULL or" + + " INTERACT_ACROSS_USERS to interact cross user "); } } + verifyIncomingOp(code); + Preconditions.checkNotNull(token); + setUserRestrictionNoCheck(code, restricted, token, userHandle); + } + + private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, + int userHandle) { + final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle); + if (opRestrictions[code] == restricted) { + return; + } + opRestrictions[code] = restricted; + if (!restricted) { + pruneUserRestrictionsForToken(token, userHandle); + } + + final ArrayList<Callback> clonedCallbacks; + synchronized (this) { + ArrayList<Callback> callbacks = mOpModeWatchers.get(code); + if (callbacks == null) { + return; + } + clonedCallbacks = new ArrayList<>(callbacks); + } + + // There are components watching for mode changes such as window manager + // and location manager which are in our process. The callbacks in these + // components may require permissions our remote caller does not have. + final long identity = Binder.clearCallingIdentity(); + try { + final int callbackCount = clonedCallbacks.size(); + for (int i = 0; i < callbackCount; i++) { + Callback callback = clonedCallbacks.get(i); + try { + callback.mCallback.opChanged(code, -1, null); + } catch (RemoteException e) { + Log.w(TAG, "Error dispatching op op change", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override public void removeUser(int userHandle) throws RemoteException { checkSystemUid("removeUser"); - mOpRestrictions.remove(userHandle); + final int tokenCount = mOpUserRestrictions.size(); + for (int i = tokenCount - 1; i >= 0; i--) { + SparseArray<boolean[]> opRestrictions = mOpUserRestrictions.valueAt(i); + if (opRestrictions != null) { + opRestrictions.remove(userHandle); + if (opRestrictions.size() <= 0) { + mOpUserRestrictions.removeAt(i); + } + } + } + } + + + private void pruneUserRestrictionsForToken(IBinder token, int userHandle) { + SparseArray<boolean[]> perTokenRestrictions = mOpUserRestrictions.get(token); + if (perTokenRestrictions != null) { + final boolean[] opRestrictions = perTokenRestrictions.get(userHandle); + if (opRestrictions != null) { + for (boolean restriction : opRestrictions) { + if (restriction) { + return; + } + } + perTokenRestrictions.remove(userHandle); + if (perTokenRestrictions.size() <= 0) { + mOpUserRestrictions.remove(token); + } + } + } + } + + private boolean[] getOrCreateUserRestrictionsForToken(IBinder token, int userHandle) { + SparseArray<boolean[]> perTokenRestrictions = mOpUserRestrictions.get(token); + if (perTokenRestrictions == null) { + perTokenRestrictions = new SparseArray<>(); + mOpUserRestrictions.put(token, perTokenRestrictions); + } + boolean[] opRestrictions = perTokenRestrictions.get(userHandle); + if (opRestrictions == null) { + opRestrictions = new boolean[AppOpsManager._NUM_OP]; + perTokenRestrictions.put(userHandle, opRestrictions); + } + return opRestrictions; } private void checkSystemUid(String function) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index e7465c08832e..d5ed04a94f37 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -41,6 +41,7 @@ import android.os.Debug; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; +import android.os.IBinder; import android.os.IUserManager; import android.os.Message; import android.os.ParcelFileDescriptor; @@ -188,6 +189,8 @@ public class UserManagerService extends IUserManager.Stub { private final File mUsersDir; private final File mUserListFile; + private static final IBinder mUserRestriconToken = new Binder(); + /** * User-related information that is used for persisting to flash. Only UserInfo is * directly exposed to other system apps. @@ -1016,7 +1019,7 @@ public class UserManagerService extends IUserManager.Stub { if (mAppOpsService != null) { // We skip it until system-ready. final long token = Binder.clearCallingIdentity(); try { - mAppOpsService.setUserRestrictions(effective, userId); + mAppOpsService.setUserRestrictions(effective, mUserRestriconToken, userId); } catch (RemoteException e) { Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions"); } finally { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 44afbcc1052a..716b96fb4791 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -33,6 +33,7 @@ import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; +import android.Manifest; import android.app.ActivityManager; import android.app.ActivityManager.StackId; import android.app.ActivityManagerInternal; @@ -2035,7 +2036,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; } if (permission != null) { - if (permission == android.Manifest.permission.SYSTEM_ALERT_WINDOW) { + if (android.Manifest.permission.SYSTEM_ALERT_WINDOW.equals(permission)) { final int callingUid = Binder.getCallingUid(); // system processes will be automatically allowed privilege to draw if (callingUid == Process.SYSTEM_UID) { |