diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index ed0043646608a..92756b6b1391b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -7413,8 +7413,18 @@ public class AppOpsManager { */ public void setUserRestrictionForUser(int code, boolean restricted, IBinder token, @Nullable Map 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 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(); } diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 3cf46214fbec8..3cc7e6401462e 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -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 excludedPackageTags); + void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in Map excludedPackageTags, boolean rejectBypass); void removeUser(int userHandle); void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback); diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index baec5449d8960..6d0d5896b7738 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -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; diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 76abfbfc8b41b..0c046c457d3e4 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -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 excludedPackageTags) { + Map 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 excludedPackageTags) { + int userHandle, Map 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 perUserRestrictions; + SparseArray perUserRejectBypasses; SparseArray> 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 excludedPackageTags, int userId) { + Map 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);