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

Commit 1d096f20 authored by Evan Severson's avatar Evan Severson
Browse files

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
parent d616f1ed
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -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);
}
+1 −0
Original line number Diff line number Diff line
@@ -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);
+38 −27
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;
@@ -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,
+119 −9
Original line number Diff line number Diff line
@@ -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();
                }
            }
        }
    }

    /**