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;
}