Allow to exempt apps from restrictions to RECORD_AUDIO

- Extend the concept if "isPrivilidged" to a generic "RestrictionBypass"
  class so that we can add more variants of bypassing.
- Add a new way of bypassing by using a new permission.
- Always except resolvable UIDs (root, shell, media_uid, audioserver,
  cameraserver, system server) from restrictions.

Fixes: 141210120
Test: atest -m CtsAppOpsTestCases FrameworksMockingServicesTests:AppOpsServiceTest FrameworksMockingServicesTests:AppOpsUpgradeTest
Change-Id: I3ca555e9370aa0003400429ee7ab12e95c62a042
diff --git a/api/system-current.txt b/api/system-current.txt
index 25f16d3..9510a3f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -79,6 +79,7 @@
     field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER";
     field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE";
     field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
+    field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
     field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index e2ecf85..4bc1fa0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1988,108 +1988,108 @@
     };
 
     /**
-     * This specifies whether each option should allow the system
-     * (and system ui) to bypass the user restriction when active.
+     * In which cases should an app be allowed to bypass the {@link #setUserRestriction user
+     * restriction} for a certain app-op.
      */
-    private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] {
-            true, //COARSE_LOCATION
-            true, //FINE_LOCATION
-            false, //GPS
-            false, //VIBRATE
-            false, //READ_CONTACTS
-            false, //WRITE_CONTACTS
-            false, //READ_CALL_LOG
-            false, //WRITE_CALL_LOG
-            false, //READ_CALENDAR
-            false, //WRITE_CALENDAR
-            true, //WIFI_SCAN
-            false, //POST_NOTIFICATION
-            false, //NEIGHBORING_CELLS
-            false, //CALL_PHONE
-            false, //READ_SMS
-            false, //WRITE_SMS
-            false, //RECEIVE_SMS
-            false, //RECEIVE_EMERGECY_SMS
-            false, //RECEIVE_MMS
-            false, //RECEIVE_WAP_PUSH
-            false, //SEND_SMS
-            false, //READ_ICC_SMS
-            false, //WRITE_ICC_SMS
-            false, //WRITE_SETTINGS
-            true, //SYSTEM_ALERT_WINDOW
-            false, //ACCESS_NOTIFICATIONS
-            false, //CAMERA
-            false, //RECORD_AUDIO
-            false, //PLAY_AUDIO
-            false, //READ_CLIPBOARD
-            false, //WRITE_CLIPBOARD
-            false, //TAKE_MEDIA_BUTTONS
-            false, //TAKE_AUDIO_FOCUS
-            false, //AUDIO_MASTER_VOLUME
-            false, //AUDIO_VOICE_VOLUME
-            false, //AUDIO_RING_VOLUME
-            false, //AUDIO_MEDIA_VOLUME
-            false, //AUDIO_ALARM_VOLUME
-            false, //AUDIO_NOTIFICATION_VOLUME
-            false, //AUDIO_BLUETOOTH_VOLUME
-            false, //WAKE_LOCK
-            false, //MONITOR_LOCATION
-            false, //MONITOR_HIGH_POWER_LOCATION
-            false, //GET_USAGE_STATS
-            false, //MUTE_MICROPHONE
-            true, //TOAST_WINDOW
-            false, //PROJECT_MEDIA
-            false, //ACTIVATE_VPN
-            false, //WALLPAPER
-            false, //ASSIST_STRUCTURE
-            false, //ASSIST_SCREENSHOT
-            false, //READ_PHONE_STATE
-            false, //ADD_VOICEMAIL
-            false, // USE_SIP
-            false, // PROCESS_OUTGOING_CALLS
-            false, // USE_FINGERPRINT
-            false, // BODY_SENSORS
-            false, // READ_CELL_BROADCASTS
-            false, // MOCK_LOCATION
-            false, // READ_EXTERNAL_STORAGE
-            false, // WRITE_EXTERNAL_STORAGE
-            false, // TURN_ON_SCREEN
-            false, // GET_ACCOUNTS
-            false, // RUN_IN_BACKGROUND
-            false, // AUDIO_ACCESSIBILITY_VOLUME
-            false, // READ_PHONE_NUMBERS
-            false, // REQUEST_INSTALL_PACKAGES
-            false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
-            false, // INSTANT_APP_START_FOREGROUND
-            false, // ANSWER_PHONE_CALLS
-            false, // OP_RUN_ANY_IN_BACKGROUND
-            false, // OP_CHANGE_WIFI_STATE
-            false, // OP_REQUEST_DELETE_PACKAGES
-            false, // OP_BIND_ACCESSIBILITY_SERVICE
-            false, // ACCEPT_HANDOVER
-            false, // MANAGE_IPSEC_HANDOVERS
-            false, // START_FOREGROUND
-            true, // BLUETOOTH_SCAN
-            false, // USE_BIOMETRIC
-            false, // ACTIVITY_RECOGNITION
-            false, // SMS_FINANCIAL_TRANSACTIONS
-            false, // READ_MEDIA_AUDIO
-            false, // WRITE_MEDIA_AUDIO
-            false, // READ_MEDIA_VIDEO
-            false, // WRITE_MEDIA_VIDEO
-            false, // READ_MEDIA_IMAGES
-            false, // WRITE_MEDIA_IMAGES
-            false, // LEGACY_STORAGE
-            false, // ACCESS_ACCESSIBILITY
-            false, // READ_DEVICE_IDENTIFIERS
-            false, // ACCESS_MEDIA_LOCATION
-            false, // QUERY_ALL_PACKAGES
-            false, // MANAGE_EXTERNAL_STORAGE
-            false, // INTERACT_ACROSS_PROFILES
-            false, // ACTIVATE_PLATFORM_VPN
-            false, // LOADER_USAGE_STATS
-            false, // ACCESS_CALL_AUDIO
-            false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+    private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] {
+            new RestrictionBypass(true, false), //COARSE_LOCATION
+            new RestrictionBypass(true, false), //FINE_LOCATION
+            null, //GPS
+            null, //VIBRATE
+            null, //READ_CONTACTS
+            null, //WRITE_CONTACTS
+            null, //READ_CALL_LOG
+            null, //WRITE_CALL_LOG
+            null, //READ_CALENDAR
+            null, //WRITE_CALENDAR
+            new RestrictionBypass(true, false), //WIFI_SCAN
+            null, //POST_NOTIFICATION
+            null, //NEIGHBORING_CELLS
+            null, //CALL_PHONE
+            null, //READ_SMS
+            null, //WRITE_SMS
+            null, //RECEIVE_SMS
+            null, //RECEIVE_EMERGECY_SMS
+            null, //RECEIVE_MMS
+            null, //RECEIVE_WAP_PUSH
+            null, //SEND_SMS
+            null, //READ_ICC_SMS
+            null, //WRITE_ICC_SMS
+            null, //WRITE_SETTINGS
+            new RestrictionBypass(true, false), //SYSTEM_ALERT_WINDOW
+            null, //ACCESS_NOTIFICATIONS
+            null, //CAMERA
+            new RestrictionBypass(false, true), //RECORD_AUDIO
+            null, //PLAY_AUDIO
+            null, //READ_CLIPBOARD
+            null, //WRITE_CLIPBOARD
+            null, //TAKE_MEDIA_BUTTONS
+            null, //TAKE_AUDIO_FOCUS
+            null, //AUDIO_MASTER_VOLUME
+            null, //AUDIO_VOICE_VOLUME
+            null, //AUDIO_RING_VOLUME
+            null, //AUDIO_MEDIA_VOLUME
+            null, //AUDIO_ALARM_VOLUME
+            null, //AUDIO_NOTIFICATION_VOLUME
+            null, //AUDIO_BLUETOOTH_VOLUME
+            null, //WAKE_LOCK
+            null, //MONITOR_LOCATION
+            null, //MONITOR_HIGH_POWER_LOCATION
+            null, //GET_USAGE_STATS
+            null, //MUTE_MICROPHONE
+            new RestrictionBypass(true, false), //TOAST_WINDOW
+            null, //PROJECT_MEDIA
+            null, //ACTIVATE_VPN
+            null, //WALLPAPER
+            null, //ASSIST_STRUCTURE
+            null, //ASSIST_SCREENSHOT
+            null, //READ_PHONE_STATE
+            null, //ADD_VOICEMAIL
+            null, // USE_SIP
+            null, // PROCESS_OUTGOING_CALLS
+            null, // USE_FINGERPRINT
+            null, // BODY_SENSORS
+            null, // READ_CELL_BROADCASTS
+            null, // MOCK_LOCATION
+            null, // READ_EXTERNAL_STORAGE
+            null, // WRITE_EXTERNAL_STORAGE
+            null, // TURN_ON_SCREEN
+            null, // GET_ACCOUNTS
+            null, // RUN_IN_BACKGROUND
+            null, // AUDIO_ACCESSIBILITY_VOLUME
+            null, // READ_PHONE_NUMBERS
+            null, // REQUEST_INSTALL_PACKAGES
+            null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
+            null, // INSTANT_APP_START_FOREGROUND
+            null, // ANSWER_PHONE_CALLS
+            null, // OP_RUN_ANY_IN_BACKGROUND
+            null, // OP_CHANGE_WIFI_STATE
+            null, // OP_REQUEST_DELETE_PACKAGES
+            null, // OP_BIND_ACCESSIBILITY_SERVICE
+            null, // ACCEPT_HANDOVER
+            null, // MANAGE_IPSEC_HANDOVERS
+            null, // START_FOREGROUND
+            new RestrictionBypass(true, false), // BLUETOOTH_SCAN
+            null, // USE_BIOMETRIC
+            null, // ACTIVITY_RECOGNITION
+            null, // SMS_FINANCIAL_TRANSACTIONS
+            null, // READ_MEDIA_AUDIO
+            null, // WRITE_MEDIA_AUDIO
+            null, // READ_MEDIA_VIDEO
+            null, // WRITE_MEDIA_VIDEO
+            null, // READ_MEDIA_IMAGES
+            null, // WRITE_MEDIA_IMAGES
+            null, // LEGACY_STORAGE
+            null, // ACCESS_ACCESSIBILITY
+            null, // READ_DEVICE_IDENTIFIERS
+            null, // ACCESS_MEDIA_LOCATION
+            null, // QUERY_ALL_PACKAGES
+            null, // MANAGE_EXTERNAL_STORAGE
+            null, // INTERACT_ACROSS_PROFILES
+            null, // ACTIVATE_PLATFORM_VPN
+            null, // LOADER_USAGE_STATS
+            null, // ACCESS_CALL_AUDIO
+            null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
     };
 
     /**
@@ -2485,11 +2485,11 @@
     }
 
     /**
-     * Retrieve whether the op allows the system (and system ui) to
-     * bypass the user restriction.
+     * Retrieve whether the op allows to bypass the user restriction.
+     *
      * @hide
      */
-    public static boolean opAllowSystemBypassRestriction(int op) {
+    public static RestrictionBypass opAllowSystemBypassRestriction(int op) {
         return sOpAllowSystemRestrictionBypass[op];
     }
 
@@ -2536,6 +2536,29 @@
     }
 
     /**
+     * When to not enforce {@link #setUserRestriction restrictions}.
+     *
+     * @hide
+     */
+    public static class RestrictionBypass {
+        /** Does the app need to be privileged to bypass the restriction */
+        public boolean isPrivileged;
+
+        /**
+         * Does the app need to have the EXEMPT_FROM_AUDIO_RESTRICTIONS permission to bypass the
+         * restriction
+         */
+        public boolean isRecordAudioRestrictionExcept;
+
+        public RestrictionBypass(boolean isPrivileged, boolean isRecordAudioRestrictionExcept) {
+            this.isPrivileged = isPrivileged;
+            this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept;
+        }
+
+        public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(true, true);
+    }
+
+    /**
      * Class holding all of the operation information associated with an app.
      * @hide
      */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8851170..ed3fb07 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1187,6 +1187,16 @@
                 android:description="@string/permdesc_callCompanionApp"
                 android:protectionLevel="normal" />
 
+    <!-- Exempt this uid from restrictions to background audio recoding
+     <p>Protection level: signature|privileged
+     @hide
+     @SystemApi
+    -->
+    <permission android:name="android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS"
+                android:label="@string/permlab_exemptFromAudioRecordRestrictions"
+                android:description="@string/permdesc_exemptFromAudioRecordRestrictions"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Allows a calling app to continue a call which was started in another app.  An example is a
          video calling app that wants to continue a voice call on the user's mobile network.<p>
          When the handover of a call from one app to another takes place, there are two devices
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d30fdce..f7206b9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1212,6 +1212,14 @@
         device. This includes information such as call numbers for calls and the state of the
         calls.</string>
 
+    <!-- Title of an application permission. When granted the app is exempt from audio record
+    restrictions.
+    [CHAR LIMIT=NONE]-->
+    <string name="permlab_exemptFromAudioRecordRestrictions">exempt from audio record restrictions</string>
+    <!-- Description of an application permission. When granted the app is exempt from audio record
+    restrictions. [CHAR LIMIT=NONE]-->
+    <string name="permdesc_exemptFromAudioRecordRestrictions">Exempt the app from restrictions to record audio.</string>
+
     <!-- Title of an application permission.  When granted the user is giving access to a third
          party app to continue a call which originated in another app.  For example, the user
          could be in a voice call over their carrier's mobile network, and a third party video
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7774633..471c97b 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -40,6 +40,7 @@
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OpEventProxyInfo;
+import static android.app.AppOpsManager.RestrictionBypass;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
 import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
@@ -55,6 +56,7 @@
 import static android.app.AppOpsManager.extractUidStateFromKey;
 import static android.app.AppOpsManager.makeKey;
 import static android.app.AppOpsManager.modeToName;
+import static android.app.AppOpsManager.opAllowSystemBypassRestriction;
 import static android.app.AppOpsManager.opToName;
 import static android.app.AppOpsManager.opToPublicName;
 import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
@@ -73,7 +75,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
-import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.HistoricalOps;
@@ -92,8 +93,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -666,15 +665,19 @@
     final static class Ops extends SparseArray<Op> {
         final String packageName;
         final UidState uidState;
-        final boolean isPrivileged;
+
+        /**
+         * The restriction properties of the package. If {@code null} it could not have been read
+         * yet and has to be refreshed.
+         */
+        @Nullable RestrictionBypass bypass;
 
         /** Lazily populated cache of featureIds of this package */
         final @NonNull ArraySet<String> knownFeatureIds = new ArraySet<>();
 
-        Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
+        Ops(String _packageName, UidState _uidState) {
             packageName = _packageName;
             uidState = _uidState;
-            isPrivileged = _isPrivileged;
         }
     }
 
@@ -1519,7 +1522,11 @@
                         return;
                     }
 
+                    // Reset cached package properties to re-initialize when needed
+                    ops.bypass = null;
                     ops.knownFeatureIds.clear();
+
+                    // Merge data collected for removed features into their successor features
                     int numOps = ops.size();
                     for (int opNum = 0; opNum < numOps; opNum++) {
                         Op op = ops.valueAt(opNum);
@@ -1953,8 +1960,7 @@
             return Collections.emptyList();
         }
         synchronized (this) {
-            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false /* isPrivileged */,
-                    false /* edit */);
+            Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false /* edit */);
             if (pkgOps == null) {
                 return null;
             }
@@ -2087,8 +2093,7 @@
         op.removeFeaturesWithNoTime();
 
         if (op.mFeatures.size() == 0) {
-            Ops ops = getOpsRawLocked(uid, packageName, null, false /* isPrivileged */,
-                    false /* edit */);
+            Ops ops = getOpsLocked(uid, packageName, null, null, false /* edit */);
             if (ops != null) {
                 ops.remove(op.op);
                 if (ops.size() <= 0) {
@@ -2390,9 +2395,9 @@
         ArraySet<ModeCallback> repCbs = null;
         code = AppOpsManager.opToSwitch(code);
 
-        boolean isPrivileged;
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null);
+            bypass = verifyAndGetBypass(uid, packageName, null);
         } catch (SecurityException e) {
             Slog.e(TAG, "Cannot setMode", e);
             return;
@@ -2400,7 +2405,7 @@
 
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
-            Op op = getOpLocked(code, uid, packageName, null, isPrivileged, true);
+            Op op = getOpLocked(code, uid, packageName, null, bypass, true);
             if (op != null) {
                 if (op.mode != mode) {
                     op.mode = mode;
@@ -2798,10 +2803,9 @@
      */
     private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
                 boolean raw) {
-        boolean isPrivileged;
-
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null);
+            bypass = verifyAndGetBypass(uid, packageName, null);
         } catch (SecurityException e) {
             Slog.e(TAG, "checkOperation", e);
             return AppOpsManager.opToDefaultMode(code);
@@ -2811,7 +2815,7 @@
             return AppOpsManager.MODE_IGNORED;
         }
         synchronized (this) {
-            if (isOpRestrictedLocked(uid, code, packageName, null, isPrivileged)) {
+            if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             code = AppOpsManager.opToSwitch(code);
@@ -2821,7 +2825,7 @@
                 final int rawMode = uidState.opModes.get(code);
                 return raw ? rawMode : uidState.evalMode(code, rawMode);
             }
-            Op op = getOpLocked(code, uid, packageName, null, false, false);
+            Op op = getOpLocked(code, uid, packageName, null, bypass, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
@@ -2884,7 +2888,7 @@
     public int checkPackage(int uid, String packageName) {
         Objects.requireNonNull(packageName);
         try {
-            verifyAndGetIsPrivileged(uid, packageName, null);
+            verifyAndGetBypass(uid, packageName, null);
 
             return AppOpsManager.MODE_ALLOWED;
         } catch (SecurityException ignored) {
@@ -2961,17 +2965,16 @@
             @Nullable String featureId, int proxyUid, String proxyPackageName,
             @Nullable String proxyFeatureId, @OpFlags int flags, boolean shouldCollectAsyncNotedOp,
             @Nullable String message) {
-        boolean isPrivileged;
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
+            bypass = verifyAndGetBypass(uid, packageName, featureId);
         } catch (SecurityException e) {
             Slog.e(TAG, "noteOperation", e);
             return AppOpsManager.MODE_ERRORED;
         }
 
         synchronized (this) {
-            final Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged,
-                    true /* edit */);
+            final Ops ops = getOpsLocked(uid, packageName, featureId, bypass, true /* edit */);
             if (ops == null) {
                 scheduleOpNotedIfNeededLocked(code, uid, packageName,
                         AppOpsManager.MODE_IGNORED);
@@ -2981,7 +2984,7 @@
             }
             final Op op = getOpLocked(ops, code, uid, true);
             final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
-            if (isOpRestrictedLocked(uid, code, packageName, featureId, isPrivileged)) {
+            if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
                 scheduleOpNotedIfNeededLocked(code, uid, packageName,
                         AppOpsManager.MODE_IGNORED);
                 return AppOpsManager.MODE_IGNORED;
@@ -3205,7 +3208,7 @@
         int uid = Binder.getCallingUid();
         Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
 
-        verifyAndGetIsPrivileged(uid, packageName, null);
+        verifyAndGetBypass(uid, packageName, null);
 
         synchronized (this) {
             RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -3235,7 +3238,7 @@
         int uid = Binder.getCallingUid();
         Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
 
-        verifyAndGetIsPrivileged(uid, packageName, null);
+        verifyAndGetBypass(uid, packageName, null);
 
         synchronized (this) {
             RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -3254,7 +3257,7 @@
 
         int uid = Binder.getCallingUid();
 
-        verifyAndGetIsPrivileged(uid, packageName, null);
+        verifyAndGetBypass(uid, packageName, null);
 
         synchronized (this) {
             return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid));
@@ -3272,16 +3275,16 @@
             return  AppOpsManager.MODE_IGNORED;
         }
 
-        boolean isPrivileged;
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
+            bypass = verifyAndGetBypass(uid, packageName, featureId);
         } catch (SecurityException e) {
             Slog.e(TAG, "startOperation", e);
             return AppOpsManager.MODE_ERRORED;
         }
 
         synchronized (this) {
-            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, featureId, isPrivileged,
+            final Ops ops = getOpsLocked(uid, resolvedPackageName, featureId, bypass,
                     true /* edit */);
             if (ops == null) {
                 if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
@@ -3289,7 +3292,7 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             final Op op = getOpLocked(ops, code, uid, true);
-            if (isOpRestrictedLocked(uid, code, resolvedPackageName, featureId, isPrivileged)) {
+            if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
@@ -3347,16 +3350,16 @@
             return;
         }
 
-        boolean isPrivileged;
+        RestrictionBypass bypass;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
+            bypass = verifyAndGetBypass(uid, packageName, featureId);
         } catch (SecurityException e) {
             Slog.e(TAG, "Cannot finishOperation", e);
             return;
         }
 
         synchronized (this) {
-            Op op = getOpLocked(code, uid, resolvedPackageName, featureId, isPrivileged, true);
+            Op op = getOpLocked(code, uid, resolvedPackageName, featureId, bypass, true);
             if (op == null) {
                 return;
             }
@@ -3617,7 +3620,22 @@
     }
 
     /**
-     * Verify that package belongs to uid and return whether the package is privileged.
+     * Create a restriction description matching the properties of the package.
+     *
+     * @param context A context to use
+     * @param pkg The package to create the restriction description for
+     *
+     * @return The restriction matching the package
+     */
+    private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) {
+        return new RestrictionBypass(pkg.isPrivileged(), mContext.checkPermission(
+                android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
+                == PackageManager.PERMISSION_GRANTED);
+    }
+
+    /**
+     * Verify that package belongs to uid and return the {@link RestrictionBypass bypass
+     * description} for the package.
      *
      * @param uid The uid the package belongs to
      * @param packageName The package the might belong to the uid
@@ -3625,11 +3643,11 @@
      *
      * @return {@code true} iff the package is privileged
      */
-    private boolean verifyAndGetIsPrivileged(int uid, String packageName,
+    private @Nullable RestrictionBypass verifyAndGetBypass(int uid, String packageName,
             @Nullable String featureId) {
         if (uid == Process.ROOT_UID) {
             // For backwards compatibility, don't check package name for root UID.
-            return false;
+            return null;
         }
 
         // Do not check if uid/packageName/featureId is already known
@@ -3638,13 +3656,14 @@
             if (uidState != null && uidState.pkgOps != null) {
                 Ops ops = uidState.pkgOps.get(packageName);
 
-                if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))) {
-                    return ops.isPrivileged;
+                if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))
+                        && ops.bypass != null) {
+                    return ops.bypass;
                 }
             }
         }
 
-        boolean isPrivileged = false;
+        RestrictionBypass bypass = null;
         final long ident = Binder.clearCallingIdentity();
         try {
             int pkgUid;
@@ -3668,14 +3687,14 @@
 
                 pkgUid = UserHandle.getUid(
                         UserHandle.getUserId(uid), UserHandle.getAppId(pkg.getUid()));
-                isPrivileged = pkg.isPrivileged();
+                bypass = getBypassforPackage(pkg);
             } else {
                 // Allow any feature id for resolvable uids
                 isFeatureIdValid = true;
 
                 pkgUid = resolveUid(packageName);
                 if (pkgUid >= 0) {
-                    isPrivileged = false;
+                    bypass = RestrictionBypass.UNRESTRICTED;
                 }
             }
             if (pkgUid != uid) {
@@ -3692,7 +3711,7 @@
             Binder.restoreCallingIdentity(ident);
         }
 
-        return isPrivileged;
+        return bypass;
     }
 
     /**
@@ -3701,13 +3720,13 @@
      * @param uid The uid the package belongs to
      * @param packageName The name of the package
      * @param featureId The feature in the package
-     * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false)
+     * @param bypass When to bypass certain op restrictions (can be null if edit == false)
      * @param edit If an ops does not exist, create the ops?
 
-     * @return
+     * @return The ops
      */
-    private Ops getOpsRawLocked(int uid, String packageName, @Nullable String featureId,
-            boolean isPrivileged, boolean edit) {
+    private Ops getOpsLocked(int uid, String packageName, @Nullable String featureId,
+            @Nullable RestrictionBypass bypass, boolean edit) {
         UidState uidState = getUidStateLocked(uid, edit);
         if (uidState == null) {
             return null;
@@ -3725,53 +3744,18 @@
             if (!edit) {
                 return null;
             }
-            ops = new Ops(packageName, uidState, isPrivileged);
-            uidState.pkgOps.put(packageName, ops);
-        }
-        if (edit && featureId != null) {
-            ops.knownFeatureIds.add(featureId);
-        }
-        return ops;
-    }
-
-    /**
-     * Get the state of all ops for a package.
-     *
-     * <p>Usually callers should use {@link #getOpLocked} and not call this directly.
-     *
-     * @param uid The uid the of the package
-     * @param packageName The package name for which to get the state for
-     * @param featureId The feature in the package
-     * @param edit Iff {@code true} create the {@link Ops} object if not yet created
-     * @param isPrivileged Whether the package is privileged or not
-     *
-     * @return The {@link Ops state} of all ops for the package
-     */
-    private @Nullable Ops getOpsRawNoVerifyLocked(int uid, @NonNull String packageName,
-            @Nullable String featureId, boolean edit, boolean isPrivileged) {
-        UidState uidState = getUidStateLocked(uid, edit);
-        if (uidState == null) {
-            return null;
-        }
-
-        if (uidState.pkgOps == null) {
-            if (!edit) {
-                return null;
-            }
-            uidState.pkgOps = new ArrayMap<>();
-        }
-
-        Ops ops = uidState.pkgOps.get(packageName);
-        if (ops == null) {
-            if (!edit) {
-                return null;
-            }
-            ops = new Ops(packageName, uidState, isPrivileged);
+            ops = new Ops(packageName, uidState);
             uidState.pkgOps.put(packageName, ops);
         }
 
-        if (edit && featureId != null) {
-            ops.knownFeatureIds.add(featureId);
+        if (edit) {
+            if (bypass != null) {
+                ops.bypass = bypass;
+            }
+
+            if (featureId != null) {
+                ops.knownFeatureIds.add(featureId);
+            }
         }
 
         return ops;
@@ -3800,15 +3784,14 @@
      * @param uid The uid the of the package
      * @param packageName The package name for which to get the state for
      * @param featureId The feature in the package
-     * @param isPrivileged Whether the package is privileged or not (only used if {@code edit
-     *                     == true})
+     * @param bypass When to bypass certain op restrictions (can be null if edit == false)
      * @param edit Iff {@code true} create the {@link Op} object if not yet created
      *
      * @return The {@link Op state} of the op
      */
     private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
-            @Nullable String featureId, boolean isPrivileged, boolean edit) {
-        Ops ops = getOpsRawNoVerifyLocked(uid, packageName, featureId, edit, isPrivileged);
+            @Nullable String featureId, @Nullable RestrictionBypass bypass, boolean edit) {
+        Ops ops = getOpsLocked(uid, packageName, featureId, bypass, edit);
         if (ops == null) {
             return null;
         }
@@ -3839,7 +3822,7 @@
     }
 
     private boolean isOpRestrictedLocked(int uid, int code, String packageName,
-            @Nullable String featureId, boolean isPrivileged) {
+            @Nullable RestrictionBypass appBypass) {
         int userHandle = UserHandle.getUserId(uid);
         final int restrictionSetCount = mOpUserRestrictions.size();
 
@@ -3848,12 +3831,15 @@
             // package is exempt from the restriction.
             ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
             if (restrictionState.hasRestriction(code, packageName, userHandle)) {
-                if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+                RestrictionBypass opBypass = opAllowSystemBypassRestriction(code);
+                if (opBypass != null) {
                     // If we are the system, bypass user restrictions for certain codes
                     synchronized (this) {
-                        Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged,
-                                true /* edit */);
-                        if ((ops != null) && ops.isPrivileged) {
+                        if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) {
+                            return false;
+                        }
+                        if (opBypass.isRecordAudioRestrictionExcept && appBypass != null
+                                && appBypass.isRecordAudioRestrictionExcept) {
                             return false;
                         }
                     }
@@ -4043,28 +4029,6 @@
             throws NumberFormatException, XmlPullParserException, IOException {
         int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
         final UidState uidState = getUidStateLocked(uid, true);
-        String isPrivilegedString = parser.getAttributeValue(null, "p");
-        boolean isPrivileged = false;
-        if (isPrivilegedString == null) {
-            try {
-                IPackageManager packageManager = ActivityThread.getPackageManager();
-                if (packageManager != null) {
-                    ApplicationInfo appInfo = ActivityThread.getPackageManager()
-                            .getApplicationInfo(pkgName, 0, UserHandle.getUserId(uid));
-                    if (appInfo != null) {
-                        isPrivileged = (appInfo.privateFlags
-                                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
-                    }
-                } else {
-                    // Could not load data, don't add to cache so it will be loaded later.
-                    return;
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Could not contact PackageManager", e);
-            }
-        } else {
-            isPrivileged = Boolean.parseBoolean(isPrivilegedString);
-        }
         int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4074,7 +4038,7 @@
             }
             String tagName = parser.getName();
             if (tagName.equals("op")) {
-                readOp(parser, uidState, pkgName, isPrivileged);
+                readOp(parser, uidState, pkgName);
             } else {
                 Slog.w(TAG, "Unknown element under <pkg>: "
                         + parser.getName());
@@ -4108,8 +4072,8 @@
         }
     }
 
-    private void readOp(XmlPullParser parser, @NonNull UidState uidState,
-        @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException,
+    private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName)
+            throws NumberFormatException,
         XmlPullParserException, IOException {
         int opCode = Integer.parseInt(parser.getAttributeValue(null, "n"));
         if (isIgnoredAppOp(opCode)) {
@@ -4143,7 +4107,7 @@
         }
         Ops ops = uidState.pkgOps.get(pkgName);
         if (ops == null) {
-            ops = new Ops(pkgName, uidState, isPrivileged);
+            ops = new Ops(pkgName, uidState);
             uidState.pkgOps.put(pkgName, ops);
         }
         ops.put(op.op, op);
@@ -4235,17 +4199,6 @@
                         }
                         out.startTag(null, "uid");
                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
-                        synchronized (this) {
-                            Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), null,
-                                    false /* isPrivileged */, false /* edit */);
-                            // Should always be present as the list of PackageOps is generated
-                            // from Ops.
-                            if (ops != null) {
-                                out.attribute(null, "p", Boolean.toString(ops.isPrivileged));
-                            } else {
-                                out.attribute(null, "p", Boolean.toString(false));
-                            }
-                        }
                         List<AppOpsManager.OpEntry> ops = pkg.getOps();
                         for (int j=0; j<ops.size(); j++) {
                             AppOpsManager.OpEntry op = ops.get(j);
@@ -5574,7 +5527,7 @@
         }
         // TODO moltmann: Allow to check for feature op activeness
         synchronized (AppOpsService.this) {
-            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false, false);
+            Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false);
             if (pkgOps == null) {
                 return false;
             }