Merge "Allow per-package exemptions for restricted AppOps" into nyc-dev

This commit is contained in:
Ruben Brunk
2016-03-11 22:48:37 +00:00
committed by Android (Google) Code Review
4 changed files with 159 additions and 30 deletions

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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 opRestrictions;
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
}
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) {

View File

@@ -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();
}
}