Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a023924a authored by Evan Severson's avatar Evan Severson
Browse files

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
parent 393d109f
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -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();
        }
+1 −1
Original line number Diff line number Diff line
@@ -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);
+66 −9
Original line number Diff line number Diff line
@@ -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;
+46 −6
Original line number Diff line number Diff line
@@ -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);