Create non-bypassable op restrictions

Even if the app is privileged or has the audio retriction bypass it
can't bypass these restrictions.

These will be used for the mic and camera toggles since under no
circumstances should sensitive information reach the application layer
while the toggles are muting.

Test: Verify privileged app couldn't access mic when toggle is blocking
Fixes: 188122748
Change-Id: Ib1bad074b3744fd565e8c8ff1c726be91c830d3e
This commit is contained in:
Evan Severson
2021-05-14 12:43:31 -07:00
parent 393d109f64
commit a023924afd
4 changed files with 124 additions and 17 deletions

View File

@@ -7413,8 +7413,18 @@ public class AppOpsManager {
*/
public void setUserRestrictionForUser(int code, boolean restricted, IBinder token,
@Nullable Map<String, String[]> excludedPackageTags, int userId) {
setUserRestrictionForUser(code, restricted, token, excludedPackageTags, userId, false);
}
/**
* An empty array of attribution tags means exclude all tags under that package.
* @hide
*/
public void setUserRestrictionForUser(int code, boolean restricted, IBinder token,
@Nullable Map<String, String[]> excludedPackageTags, int userId, boolean rejectBypass) {
try {
mService.setUserRestriction(code, restricted, token, userId, excludedPackageTags);
mService.setUserRestriction(code, restricted, token, userId, excludedPackageTags,
rejectBypass);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -92,7 +92,7 @@ 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, in Map<String, String[]> excludedPackageTags);
void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in Map<String, String[]> excludedPackageTags, boolean rejectBypass);
void removeUser(int userHandle);
void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback);

View File

@@ -26,7 +26,6 @@ import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -55,6 +54,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.drawable.Icon;
import android.hardware.ISensorPrivacyListener;
import android.hardware.ISensorPrivacyManager;
@@ -164,6 +164,8 @@ public final class SensorPrivacyService extends SystemService {
private EmergencyCallHelper mEmergencyCallHelper;
private KeyguardManager mKeyguardManager;
private int mCurrentUser = -1;
public SensorPrivacyService(Context context) {
super(context);
mContext = context;
@@ -173,6 +175,19 @@ public final class SensorPrivacyService extends SystemService {
mActivityTaskManager = context.getSystemService(ActivityTaskManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
mUserManagerInternal.addUserLifecycleListener(
new UserManagerInternal.UserLifecycleListener() {
@Override
public void onUserCreated(UserInfo user, Object token) {
setCurrentUserRestriction();
}
@Override
public void onUserRemoved(UserInfo user) {
removeUserRestrictions(user.id);
}
});
}
@Override
@@ -191,6 +206,20 @@ public final class SensorPrivacyService extends SystemService {
}
}
@Override
public void onUserStarting(TargetUser user) {
if (mCurrentUser == -1) {
mCurrentUser = user.getUserIdentifier();
setCurrentUserRestriction();
}
}
@Override
public void onUserSwitching(TargetUser from, TargetUser to) {
mCurrentUser = to.getUserIdentifier();
setCurrentUserRestriction();
}
class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements
AppOpsManager.OnOpNotedListener, AppOpsManager.OnOpStartedListener,
IBinder.DeathRecipient {
@@ -1318,17 +1347,45 @@ public final class SensorPrivacyService extends SystemService {
}
private void setUserRestriction(int userId, int sensor, boolean enabled) {
if (sensor == CAMERA) {
mAppOpsManager.setUserRestrictionForUser(OP_CAMERA, enabled,
mAppOpsRestrictionToken, null, userId);
} else if (sensor == MICROPHONE) {
mAppOpsManager.setUserRestrictionForUser(OP_RECORD_AUDIO, enabled,
mAppOpsRestrictionToken, null, userId);
mAppOpsManager.setUserRestrictionForUser(OP_RECORD_AUDIO_HOTWORD, enabled,
mAppOpsRestrictionToken, null, userId);
if (userId == mCurrentUser) {
setCurrentUserRestriction(sensor, enabled);
}
}
private void setCurrentUserRestriction() {
boolean micState = mSensorPrivacyServiceImpl
.isIndividualSensorPrivacyEnabled(mCurrentUser, MICROPHONE);
boolean camState = mSensorPrivacyServiceImpl
.isIndividualSensorPrivacyEnabled(mCurrentUser, CAMERA);
setCurrentUserRestriction(MICROPHONE, micState);
setCurrentUserRestriction(CAMERA, camState);
}
private void setCurrentUserRestriction(int sensor, boolean enabled) {
int[] userIds = mUserManagerInternal.getUserIds();
int code;
if (sensor == MICROPHONE) {
code = OP_RECORD_AUDIO;
} else if (sensor == CAMERA) {
code = OP_CAMERA;
} else {
Log.w(TAG, "Invalid sensor id: " + sensor, new RuntimeException());
return;
}
for (int i = 0; i < userIds.length; i++) {
mAppOpsManager.setUserRestrictionForUser(code, enabled,
mAppOpsRestrictionToken, null, userIds[i], true);
}
}
private void removeUserRestrictions(int userId) {
mAppOpsManager.setUserRestrictionForUser(OP_RECORD_AUDIO, false,
mAppOpsRestrictionToken, null, userId, true);
mAppOpsManager.setUserRestrictionForUser(OP_CAMERA, false,
mAppOpsRestrictionToken, null, userId, true);
}
private final class DeathRecipient implements IBinder.DeathRecipient {
private ISensorPrivacyListener mListener;

View File

@@ -4564,6 +4564,9 @@ public class AppOpsService extends IAppOpsService.Stub {
// package is exempt from the restriction.
ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
if (restrictionState.hasRestriction(code, packageName, attributionTag, userHandle)) {
if (restrictionState.rejectBypass(code, userHandle)) {
return true;
}
RestrictionBypass opBypass = opAllowSystemBypassRestriction(code);
if (opBypass != null) {
// If we are the system, bypass user restrictions for certain codes
@@ -6145,6 +6148,8 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int j = 0; j < restrictionCount; j++) {
int userId = restrictionState.perUserRestrictions.keyAt(j);
boolean[] restrictedOps = restrictionState.perUserRestrictions.valueAt(j);
boolean[] rejectBypassOps =
restrictionState.perUserRejectBypasses.valueAt(j);
if (restrictedOps == null) {
continue;
}
@@ -6169,6 +6174,9 @@ public class AppOpsService extends IAppOpsService.Stub {
restrictedOpsValue.append(", ");
}
restrictedOpsValue.append(AppOpsManager.opToName(k));
if (rejectBypassOps != null && rejectBypassOps[k]) {
restrictedOpsValue.append(" rejectBypass=true");
}
}
}
restrictedOpsValue.append("]");
@@ -6249,14 +6257,14 @@ public class AppOpsService extends IAppOpsService.Stub {
String restriction = AppOpsManager.opToRestriction(i);
if (restriction != null) {
setUserRestrictionNoCheck(i, restrictions.getBoolean(restriction, false), token,
userHandle, null);
userHandle, null, false);
}
}
}
@Override
public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
Map<String, String[]> excludedPackageTags) {
Map<String, String[]> excludedPackageTags, boolean rejectBypass) {
if (Binder.getCallingPid() != Process.myPid()) {
mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
@@ -6272,11 +6280,12 @@ public class AppOpsService extends IAppOpsService.Stub {
}
verifyIncomingOp(code);
Objects.requireNonNull(token);
setUserRestrictionNoCheck(code, restricted, token, userHandle, excludedPackageTags);
setUserRestrictionNoCheck(code, restricted, token, userHandle, excludedPackageTags,
rejectBypass);
}
private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
int userHandle, Map<String, String[]> excludedPackageTags) {
int userHandle, Map<String, String[]> excludedPackageTags, boolean rejectBypass) {
synchronized (AppOpsService.this) {
ClientRestrictionState restrictionState = mOpUserRestrictions.get(token);
@@ -6290,7 +6299,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (restrictionState.setRestriction(code, restricted, excludedPackageTags,
userHandle)) {
userHandle, rejectBypass)) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyWatchersOfChange, this, code, UID_ANY));
mHandler.sendMessage(PooledLambda.obtainMessage(
@@ -6820,8 +6829,10 @@ public class AppOpsService extends IAppOpsService.Stub {
private final class ClientRestrictionState implements DeathRecipient {
private final IBinder token;
SparseArray<boolean[]> perUserRestrictions;
SparseArray<boolean[]> perUserRejectBypasses;
SparseArray<Map<String, String[]>> perUserExcludedPackageTags;
public ClientRestrictionState(IBinder token)
throws RemoteException {
token.linkToDeath(this, 0);
@@ -6829,13 +6840,17 @@ public class AppOpsService extends IAppOpsService.Stub {
}
public boolean setRestriction(int code, boolean restricted,
Map<String, String[]> excludedPackageTags, int userId) {
Map<String, String[]> excludedPackageTags, int userId, boolean rejectBypass) {
boolean changed = false;
if (perUserRestrictions == null && restricted) {
perUserRestrictions = new SparseArray<>();
}
if (perUserRejectBypasses == null && rejectBypass) {
perUserRejectBypasses = new SparseArray<>();
}
int[] users;
if (userId == UserHandle.USER_ALL) {
// TODO(b/162888972): this call is returning all users, not just live ones - we
@@ -6895,6 +6910,20 @@ public class AppOpsService extends IAppOpsService.Stub {
}
changed = true;
}
boolean[] userRejectBypasses = perUserRejectBypasses.get(thisUserId);
if (userRejectBypasses == null && rejectBypass) {
userRejectBypasses = new boolean[AppOpsManager._NUM_OP];
perUserRejectBypasses.put(thisUserId, userRejectBypasses);
}
if (userRejectBypasses != null
&& userRejectBypasses[code] != rejectBypass) {
userRejectBypasses[code] = rejectBypass;
if (!rejectBypass && isDefault(userRejectBypasses)) {
perUserRejectBypasses.remove(thisUserId);
}
changed = true;
}
}
}
}
@@ -6932,6 +6961,17 @@ public class AppOpsService extends IAppOpsService.Stub {
return !ArrayUtils.contains(excludedTags, attributionTag);
}
public boolean rejectBypass(int restriction, int userId) {
if (perUserRejectBypasses == null) {
return false;
}
boolean[] rejectBypasses = perUserRejectBypasses.get(userId);
if (rejectBypasses == null) {
return false;
}
return rejectBypasses[restriction];
}
public void removeUser(int userId) {
if (perUserExcludedPackageTags != null) {
perUserExcludedPackageTags.remove(userId);