diff options
| author | 2021-06-15 10:43:56 -0700 | |
|---|---|---|
| committer | 2021-06-28 22:22:59 +0000 | |
| commit | 1d096f209dfe76dc3fc475e671493a991ad24b39 (patch) | |
| tree | 7e9a36e766c0d79bf4503a48161e27a2f258ea39 | |
| parent | d616f1ed950b62f5a6682957d483f46f9547c3ed (diff) | |
Add global app op restrictions
Instead of managing across all users we can restrict globally and fast
fail if the restriction is set
Test: atest CtsSensorPrivacyTestCases
Bug: 191016260
Change-Id: Ida1a2d1734b7e3d40818485062bec0f96270bc4b
4 files changed, 165 insertions, 37 deletions
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index a757e32d0d75..c17d98f3dce5 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -16,9 +16,9 @@ package android.app; -import android.app.AppOpsManager.AttributionFlags; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AppOpsManager.AttributionFlags; import android.content.AttributionSource; import android.os.IBinder; import android.util.SparseArray; @@ -193,4 +193,10 @@ public abstract class AppOpsManagerInternal { */ public abstract void setModeFromPermissionPolicy(int code, int uid, @NonNull String packageName, int mode, @Nullable IAppOpsCallback callback); + + + /** + * Sets a global restriction on an op code. + */ + public abstract void setGlobalRestriction(int code, boolean restricted, IBinder token); } diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 9ad457200700..30da4b470ab6 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -97,6 +97,7 @@ interface IAppOpsService { void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle); void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in PackageTagsList excludedPackageTags); + 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 71609d2b557d..f85e91a6096a 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; @@ -44,6 +43,7 @@ import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.AppOpsManager; +import android.app.AppOpsManagerInternal; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; @@ -158,6 +158,7 @@ public final class SensorPrivacyService extends SystemService { private final ActivityManager mActivityManager; private final ActivityTaskManager mActivityTaskManager; private final AppOpsManager mAppOpsManager; + private final AppOpsManagerInternal mAppOpsManagerInternal; private final TelephonyManager mTelephonyManager; private final IBinder mAppOpsRestrictionToken = new Binder(); @@ -167,15 +168,17 @@ public final class SensorPrivacyService extends SystemService { private EmergencyCallHelper mEmergencyCallHelper; private KeyguardManager mKeyguardManager; + private int mCurrentUser = -1; + public SensorPrivacyService(Context context) { super(context); mContext = context; mAppOpsManager = context.getSystemService(AppOpsManager.class); + mAppOpsManagerInternal = getLocalService(AppOpsManagerInternal.class); mUserManagerInternal = getLocalService(UserManagerInternal.class); mActivityManager = context.getSystemService(ActivityManager.class); mActivityTaskManager = context.getSystemService(ActivityTaskManager.class); mTelephonyManager = context.getSystemService(TelephonyManager.class); - mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(); } @@ -195,6 +198,20 @@ public final class SensorPrivacyService extends SystemService { } } + @Override + public void onUserStarting(TargetUser user) { + if (mCurrentUser == -1) { + mCurrentUser = user.getUserIdentifier(); + setGlobalRestriction(); + } + } + + @Override + public void onUserSwitching(TargetUser from, TargetUser to) { + mCurrentUser = to.getUserIdentifier(); + setGlobalRestriction(); + } + class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements AppOpsManager.OnOpNotedListener, AppOpsManager.OnOpStartedListener, IBinder.DeathRecipient, UserManagerInternal.UserRestrictionsListener { @@ -256,17 +273,6 @@ public final class SensorPrivacyService extends SystemService { if (readPersistedSensorPrivacyStateLocked()) { persistSensorPrivacyStateLocked(); } - - for (int i = 0; i < mIndividualEnabled.size(); i++) { - int userId = mIndividualEnabled.keyAt(i); - SparseBooleanArray userIndividualEnabled = - mIndividualEnabled.valueAt(i); - for (int j = 0; j < userIndividualEnabled.size(); j++) { - int sensor = userIndividualEnabled.keyAt(j); - boolean enabled = userIndividualEnabled.valueAt(j); - setUserRestriction(userId, sensor, enabled); - } - } } int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, @@ -1351,7 +1357,10 @@ public final class SensorPrivacyService extends SystemService { SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser = mIndividualSensorListeners.get(userId); - setUserRestriction(userId, sensor, enabled); + setGlobalRestriction(); + if (userId == mCurrentUser) { + setGlobalRestriction(); + } if (listenersForUser == null) { return; @@ -1380,16 +1389,18 @@ 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); - } + private void setGlobalRestriction() { + boolean camState = + mSensorPrivacyServiceImpl + .isIndividualSensorPrivacyEnabled(mCurrentUser, CAMERA); + boolean micState = + mSensorPrivacyServiceImpl + .isIndividualSensorPrivacyEnabled(mCurrentUser, MICROPHONE); + + mAppOpsManagerInternal + .setGlobalRestriction(OP_CAMERA, camState, mAppOpsRestrictionToken); + mAppOpsManagerInternal + .setGlobalRestriction(OP_RECORD_AUDIO, micState, mAppOpsRestrictionToken); } private final class DeathRecipient implements IBinder.DeathRecipient { @@ -1507,7 +1518,7 @@ public final class SensorPrivacyService extends SystemService { } private class EmergencyCallHelper { - private OutogingEmergencyStateCallback mEmergencyStateCallback; + private OutgoingEmergencyStateCallback mEmergencyStateCallback; private CallStateCallback mCallStateCallback; private boolean mIsInEmergencyCall; @@ -1516,7 +1527,7 @@ public final class SensorPrivacyService extends SystemService { private Object mEmergencyStateLock = new Object(); EmergencyCallHelper() { - mEmergencyStateCallback = new OutogingEmergencyStateCallback(); + mEmergencyStateCallback = new OutgoingEmergencyStateCallback(); mCallStateCallback = new CallStateCallback(); mTelephonyManager.registerTelephonyCallback(FgThread.getExecutor(), @@ -1531,7 +1542,7 @@ public final class SensorPrivacyService extends SystemService { } } - private class OutogingEmergencyStateCallback extends TelephonyCallback implements + private class OutgoingEmergencyStateCallback extends TelephonyCallback implements TelephonyCallback.OutgoingEmergencyCallListener { @Override public void onOutgoingEmergencyCall(EmergencyNumber placedEmergencyNumber, diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 99a33e4462e2..12aad21428c6 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -335,7 +335,14 @@ public class AppOpsService extends IAppOpsService.Stub { /* * These are app op restrictions imposed per user from various parties. */ - private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>(); + private final ArrayMap<IBinder, ClientUserRestrictionState> mOpUserRestrictions = + new ArrayMap<>(); + + /* + * These are app op restrictions imposed globally from various parties within the system. + */ + private final ArrayMap<IBinder, ClientGlobalRestrictionState> mOpGlobalRestrictions = + new ArrayMap<>(); SparseIntArray mProfileOwners; @@ -4723,13 +4730,22 @@ public class AppOpsService extends IAppOpsService.Stub { private boolean isOpRestrictedLocked(int uid, int code, String packageName, String attributionTag, @Nullable RestrictionBypass appBypass) { + int restrictionSetCount = mOpGlobalRestrictions.size(); + + for (int i = 0; i < restrictionSetCount; i++) { + ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.valueAt(i); + if (restrictionState.hasRestriction(code)) { + return true; + } + } + int userHandle = UserHandle.getUserId(uid); - final int restrictionSetCount = mOpUserRestrictions.size(); + restrictionSetCount = mOpUserRestrictions.size(); for (int i = 0; i < restrictionSetCount; i++) { // For each client, check that the given op is not restricted, or that the given // package is exempt from the restriction. - ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); + ClientUserRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); if (restrictionState.hasRestriction(code, packageName, attributionTag, userHandle)) { RestrictionBypass opBypass = opAllowSystemBypassRestriction(code); if (opBypass != null) { @@ -6299,10 +6315,31 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(); } + final int globalRestrictionCount = mOpGlobalRestrictions.size(); + for (int i = 0; i < globalRestrictionCount; i++) { + IBinder token = mOpGlobalRestrictions.keyAt(i); + ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.valueAt(i); + ArraySet<Integer> restrictedOps = restrictionState.mRestrictedOps; + + pw.println(" Global restrictions for token " + token + ":"); + StringBuilder restrictedOpsValue = new StringBuilder(); + restrictedOpsValue.append("["); + final int restrictedOpCount = restrictedOps.size(); + for (int j = 0; j < restrictedOpCount; j++) { + if (restrictedOpsValue.length() > 1) { + restrictedOpsValue.append(", "); + } + restrictedOpsValue.append(AppOpsManager.opToName(restrictedOps.valueAt(j))); + } + restrictedOpsValue.append("]"); + pw.println(" Restricted ops: " + restrictedOpsValue); + + } + final int userRestrictionCount = mOpUserRestrictions.size(); for (int i = 0; i < userRestrictionCount; i++) { IBinder token = mOpUserRestrictions.keyAt(i); - ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); + ClientUserRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); boolean printedTokenHeader = false; if (dumpMode >= 0 || dumpWatchers || dumpHistory) { @@ -6448,11 +6485,11 @@ public class AppOpsService extends IAppOpsService.Stub { private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token, int userHandle, PackageTagsList excludedPackageTags) { synchronized (AppOpsService.this) { - ClientRestrictionState restrictionState = mOpUserRestrictions.get(token); + ClientUserRestrictionState restrictionState = mOpUserRestrictions.get(token); if (restrictionState == null) { try { - restrictionState = new ClientRestrictionState(token); + restrictionState = new ClientUserRestrictionState(token); } catch (RemoteException e) { return; } @@ -6532,7 +6569,7 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (AppOpsService.this) { final int tokenCount = mOpUserRestrictions.size(); for (int i = tokenCount - 1; i >= 0; i--) { - ClientRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i); + ClientUserRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i); opRestrictions.removeUser(userHandle); } removeUidsForUserLocked(userHandle); @@ -6990,12 +7027,12 @@ public class AppOpsService extends IAppOpsService.Stub { return packageNames; } - private final class ClientRestrictionState implements DeathRecipient { + private final class ClientUserRestrictionState implements DeathRecipient { private final IBinder token; SparseArray<boolean[]> perUserRestrictions; SparseArray<PackageTagsList> perUserExcludedPackageTags; - public ClientRestrictionState(IBinder token) + ClientUserRestrictionState(IBinder token) throws RemoteException { token.linkToDeath(this, 0); this.token = token; @@ -7086,6 +7123,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (perUserExclusions == null) { return true; } + return !perUserExclusions.contains(packageName, attributionTag); } @@ -7147,6 +7185,42 @@ public class AppOpsService extends IAppOpsService.Stub { } } + private final class ClientGlobalRestrictionState implements DeathRecipient { + final IBinder mToken; + final ArraySet<Integer> mRestrictedOps = new ArraySet<>(); + + ClientGlobalRestrictionState(IBinder token) + throws RemoteException { + token.linkToDeath(this, 0); + this.mToken = token; + } + + boolean setRestriction(int code, boolean restricted) { + if (restricted) { + return mRestrictedOps.add(code); + } else { + return mRestrictedOps.remove(code); + } + } + + boolean hasRestriction(int code) { + return mRestrictedOps.contains(code); + } + + boolean isDefault() { + return mRestrictedOps.isEmpty(); + } + + @Override + public void binderDied() { + destroy(); + } + + void destroy() { + mToken.unlinkToDeath(this, 0); + } + } + private final class AppOpsManagerInternalImpl extends AppOpsManagerInternal { @Override public void setDeviceAndProfileOwners(SparseIntArray owners) { synchronized (AppOpsService.this) { @@ -7171,6 +7245,42 @@ public class AppOpsService extends IAppOpsService.Stub { int mode, @Nullable IAppOpsCallback callback) { setMode(code, uid, packageName, mode, callback); } + + + @Override + public void setGlobalRestriction(int code, boolean restricted, IBinder token) { + if (Binder.getCallingPid() != Process.myPid()) { + // TODO instead of this enforcement put in AppOpsManagerInternal + throw new SecurityException("Only the system can set global restrictions"); + } + + synchronized (AppOpsService.this) { + ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.get(token); + + if (restrictionState == null) { + try { + restrictionState = new ClientGlobalRestrictionState(token); + } catch (RemoteException e) { + return; + } + mOpGlobalRestrictions.put(token, restrictionState); + } + + if (restrictionState.setRestriction(code, restricted)) { + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyWatchersOfChange, AppOpsService.this, code, + UID_ANY)); + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::updateStartedOpModeForUser, AppOpsService.this, + code, restricted, UserHandle.USER_ALL)); + } + + if (restrictionState.isDefault()) { + mOpGlobalRestrictions.remove(token); + restrictionState.destroy(); + } + } + } } /** |