diff options
4 files changed, 159 insertions, 30 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 82c4c51b6a04..64586a6d8415 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1267,8 +1267,15 @@ public class AppOpsManager { /** @hide */ public void setUserRestriction(int code, boolean restricted, IBinder token) { + setUserRestriction(code, restricted, token, /*exceptionPackages*/null); + } + + /** @hide */ + public void setUserRestriction(int code, boolean restricted, IBinder token, + String[] exceptionPackages) { try { - mService.setUserRestriction(code, restricted, token, mContext.getUserId()); + mService.setUserRestriction(code, restricted, token, mContext.getUserId(), + exceptionPackages); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index b13be97dfb98..3a31b370a417 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -45,6 +45,6 @@ interface IAppOpsService { void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages); void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle); - void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle); + void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in String[] exceptionPackages); void removeUser(int userHandle); } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 32f2d593f670..bfe9e8e74122 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -57,6 +58,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -107,8 +109,21 @@ public class AppOpsService extends IAppOpsService.Stub { private final SparseArray<UidState> mUidStates = new SparseArray<>(); - /** These are app op restrictions imposed per user from various parties */ - private final ArrayMap<IBinder, SparseArray<boolean[]>> mOpUserRestrictions = new ArrayMap<>(); + /* + * These are app op restrictions imposed per user from various parties. + * + * This is organized as follows: + * + * ArrayMap w/ mapping: + * IBinder (for client imposing restriction) --> SparseArray w/ mapping: + * User handle --> Pair containing: + * - Array w/ index = AppOp code, value = restricted status boolean + * - SparseArray w/ mapping: + * AppOp code --> Set of packages that are not restricted for this code + * + */ + private final ArrayMap<IBinder, SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>> + mOpUserRestrictions = new ArrayMap<>(); private static final class UidState { public final int uid; @@ -1267,11 +1282,35 @@ public class AppOpsService extends IAppOpsService.Stub { private boolean isOpRestricted(int uid, int code, String packageName) { int userHandle = UserHandle.getUserId(uid); 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]) { + // For each client, check that the given op is not restricted, or that the given + // package is exempt from the restriction. + + SparseArray<Pair<boolean[],SparseArray<ArraySet<String>>>> perUserRestrictions = + mOpUserRestrictions.valueAt(i); + + Pair<boolean[],SparseArray<ArraySet<String>>> restrictions = + perUserRestrictions.get(userHandle); + if (restrictions == null) { + continue; // No restrictions set by this client + } + + boolean[] opRestrictions = restrictions.first; + SparseArray<ArraySet<String>> opExceptions = restrictions.second; + + if (opRestrictions == null) { + continue; // No restrictions set by this client + } + + if (opRestrictions[code]) { + if (opExceptions != null && opExceptions.get(code) != null && + opExceptions.get(code).contains(packageName)) { + continue; // AppOps code is restricted, but this package is exempt + } + if (AppOpsManager.opAllowSystemBypassRestriction(code)) { + // If we are the system, bypass user restrictions for certain codes synchronized (this) { Ops ops = getOpsLocked(uid, packageName, true); if ((ops != null) && ops.isPrivileged) { @@ -1279,6 +1318,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } } + return true; } } @@ -2069,7 +2109,8 @@ public class AppOpsService extends IAppOpsService.Stub { } @Override - public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle) { + public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, + String[] exceptionPackages) { if (Binder.getCallingPid() != Process.myPid()) { mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS, Binder.getCallingPid(), Binder.getCallingUid(), null); @@ -2085,12 +2126,37 @@ public class AppOpsService extends IAppOpsService.Stub { } verifyIncomingOp(code); Preconditions.checkNotNull(token); - setUserRestrictionNoCheck(code, restricted, token, userHandle); + setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages); } private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, int userHandle) { + setUserRestrictionNoCheck(code, restricted, token, userHandle, /*exceptionPackages*/null); + } + + private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, + int userHandle, String[] exceptionPackages) { + final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle); + + if (restricted) { + final SparseArray<ArraySet<String>> opExceptions = + getUserPackageExemptionsForToken(token, userHandle); + + // If exceptionPackages is not null, update the exception packages for this AppOps code + ArraySet<String> exceptions = opExceptions.get(code); + if (exceptionPackages != null) { + if (exceptions == null) { + exceptions = new ArraySet<>(exceptionPackages.length); + opExceptions.put(code, exceptions); + } else { + exceptions.clear(); + } + + exceptions.addAll(Arrays.asList(exceptionPackages)); + } + } + if (opRestrictions[code] == restricted) { return; } @@ -2132,7 +2198,8 @@ public class AppOpsService extends IAppOpsService.Stub { checkSystemUid("removeUser"); final int tokenCount = mOpUserRestrictions.size(); for (int i = tokenCount - 1; i >= 0; i--) { - SparseArray<boolean[]> opRestrictions = mOpUserRestrictions.valueAt(i); + SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> opRestrictions = + mOpUserRestrictions.valueAt(i); if (opRestrictions != null) { opRestrictions.remove(userHandle); if (opRestrictions.size() <= 0) { @@ -2144,15 +2211,23 @@ public class AppOpsService extends IAppOpsService.Stub { private void pruneUserRestrictionsForToken(IBinder token, int userHandle) { - SparseArray<boolean[]> perTokenRestrictions = mOpUserRestrictions.get(token); + SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = + mOpUserRestrictions.get(token); if (perTokenRestrictions != null) { - final boolean[] opRestrictions = perTokenRestrictions.get(userHandle); - if (opRestrictions != null) { - for (boolean restriction : opRestrictions) { - if (restriction) { - return; + final Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = + perTokenRestrictions.get(userHandle); + + if (restrictions != null) { + final boolean[] opRestrictions = restrictions.first; + if (opRestrictions != null) { + for (boolean restriction : opRestrictions) { + if (restriction) { + return; + } } } + + // No restrictions set for this client perTokenRestrictions.remove(userHandle); if (perTokenRestrictions.size() <= 0) { mOpUserRestrictions.remove(token); @@ -2161,18 +2236,61 @@ public class AppOpsService extends IAppOpsService.Stub { } } + /** + * Get or create the user restrictions array for a given client if it doesn't already exist. + * + * @param token the binder client creating the restriction. + * @param userHandle the user handle to create a restriction for. + * + * @return the array of restriction states for each AppOps code. + */ private boolean[] getOrCreateUserRestrictionsForToken(IBinder token, int userHandle) { - SparseArray<boolean[]> perTokenRestrictions = mOpUserRestrictions.get(token); + SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = + mOpUserRestrictions.get(token); + if (perTokenRestrictions == null) { - perTokenRestrictions = new SparseArray<>(); + perTokenRestrictions = + new SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>(); mOpUserRestrictions.put(token, perTokenRestrictions); } - boolean[] opRestrictions = perTokenRestrictions.get(userHandle); - if (opRestrictions == null) { - opRestrictions = new boolean[AppOpsManager._NUM_OP]; - perTokenRestrictions.put(userHandle, opRestrictions); + + Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = + perTokenRestrictions.get(userHandle); + + if (restrictions == null) { + restrictions = new Pair<boolean[], SparseArray<ArraySet<String>>>( + new boolean[AppOpsManager._NUM_OP], new SparseArray<ArraySet<String>>()); + perTokenRestrictions.put(userHandle, restrictions); + } + + return restrictions.first; + } + + /** + * Get the per-package exemptions for each AppOps code for a given client and userHandle. + * + * @param token the binder client to get the exemptions for. + * @param userHandle the user handle to get the exemptions for. + * + * @return a mapping from the AppOps code to a set of packages exempt for that code. + */ + private SparseArray<ArraySet<String>> getUserPackageExemptionsForToken(IBinder token, + int userHandle) { + SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions = + mOpUserRestrictions.get(token); + + if (perTokenRestrictions == null) { + return null; // Don't create user restrictions accidentally } - return opRestrictions; + + Pair<boolean[], SparseArray<ArraySet<String>>> restrictions = + perTokenRestrictions.get(userHandle); + + if (restrictions == null) { + return null; // Don't create user restrictions accidentally + } + + return restrictions.second; } private void checkSystemUid(String function) { diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index f5914fafccf7..d0ee6e030c09 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -201,13 +201,16 @@ public class VrManagerService extends SystemService implements EnabledComponentC } } - private void updateOverlayStateLocked() { + private void updateOverlayStateLocked(ComponentName exemptedComponent) { final long identity = Binder.clearCallingIdentity(); try { AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); if (appOpsManager != null) { + String[] exemptions = (exemptedComponent == null) ? new String[0] : + new String[] { exemptedComponent.getPackageName() }; + appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, - mVrModeEnabled, mOverlayToken); + mVrModeEnabled, mOverlayToken, exemptions); } } finally { Binder.restoreCallingIdentity(identity); @@ -230,12 +233,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC private boolean updateCurrentVrServiceLocked(boolean enabled, @NonNull ComponentName component, int userId) { - // Always send mode change events. - changeVrModeLocked(enabled); - boolean validUserComponent = (mComponentObserver.isValid(component, userId) == EnabledComponentsObserver.NO_ERROR); + // Always send mode change events. + changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null); + if (!enabled || !validUserComponent) { // Unbind whatever is running if (mCurrentVrService != null) { @@ -275,8 +278,9 @@ public class VrManagerService extends SystemService implements EnabledComponentC * Note: Must be called while holding {@code mLock}. * * @param enabled new state of the VR mode. + * @param exemptedComponent a component to exempt from AppOps restrictions for overlays. */ - private void changeVrModeLocked(boolean enabled) { + private void changeVrModeLocked(boolean enabled, ComponentName exemptedComponent) { if (mVrModeEnabled != enabled) { mVrModeEnabled = enabled; @@ -284,7 +288,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled")); setVrModeNative(mVrModeEnabled); - updateOverlayStateLocked(); + updateOverlayStateLocked(exemptedComponent); onVrModeChangedLocked(); } } |