Merge "Allow to exempt apps from restrictions to RECORD_AUDIO" into rvc-dev am: 093ba7e506

Change-Id: Id726e9441b9a0059c871c106d95c1219a6f4ef2f
This commit is contained in:
Automerger Merge Worker
2020-03-11 16:22:32 +00:00
5 changed files with 237 additions and 242 deletions

View File

@@ -79,6 +79,7 @@ package android {
field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER"; 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 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 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_BACK = "android.permission.FORCE_BACK";
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; 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"; field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";

View File

@@ -1988,108 +1988,108 @@ public class AppOpsManager {
}; };
/** /**
* This specifies whether each option should allow the system * In which cases should an app be allowed to bypass the {@link #setUserRestriction user
* (and system ui) to bypass the user restriction when active. * restriction} for a certain app-op.
*/ */
private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] { private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] {
true, //COARSE_LOCATION new RestrictionBypass(true, false), //COARSE_LOCATION
true, //FINE_LOCATION new RestrictionBypass(true, false), //FINE_LOCATION
false, //GPS null, //GPS
false, //VIBRATE null, //VIBRATE
false, //READ_CONTACTS null, //READ_CONTACTS
false, //WRITE_CONTACTS null, //WRITE_CONTACTS
false, //READ_CALL_LOG null, //READ_CALL_LOG
false, //WRITE_CALL_LOG null, //WRITE_CALL_LOG
false, //READ_CALENDAR null, //READ_CALENDAR
false, //WRITE_CALENDAR null, //WRITE_CALENDAR
true, //WIFI_SCAN new RestrictionBypass(true, false), //WIFI_SCAN
false, //POST_NOTIFICATION null, //POST_NOTIFICATION
false, //NEIGHBORING_CELLS null, //NEIGHBORING_CELLS
false, //CALL_PHONE null, //CALL_PHONE
false, //READ_SMS null, //READ_SMS
false, //WRITE_SMS null, //WRITE_SMS
false, //RECEIVE_SMS null, //RECEIVE_SMS
false, //RECEIVE_EMERGECY_SMS null, //RECEIVE_EMERGECY_SMS
false, //RECEIVE_MMS null, //RECEIVE_MMS
false, //RECEIVE_WAP_PUSH null, //RECEIVE_WAP_PUSH
false, //SEND_SMS null, //SEND_SMS
false, //READ_ICC_SMS null, //READ_ICC_SMS
false, //WRITE_ICC_SMS null, //WRITE_ICC_SMS
false, //WRITE_SETTINGS null, //WRITE_SETTINGS
true, //SYSTEM_ALERT_WINDOW new RestrictionBypass(true, false), //SYSTEM_ALERT_WINDOW
false, //ACCESS_NOTIFICATIONS null, //ACCESS_NOTIFICATIONS
false, //CAMERA null, //CAMERA
false, //RECORD_AUDIO new RestrictionBypass(false, true), //RECORD_AUDIO
false, //PLAY_AUDIO null, //PLAY_AUDIO
false, //READ_CLIPBOARD null, //READ_CLIPBOARD
false, //WRITE_CLIPBOARD null, //WRITE_CLIPBOARD
false, //TAKE_MEDIA_BUTTONS null, //TAKE_MEDIA_BUTTONS
false, //TAKE_AUDIO_FOCUS null, //TAKE_AUDIO_FOCUS
false, //AUDIO_MASTER_VOLUME null, //AUDIO_MASTER_VOLUME
false, //AUDIO_VOICE_VOLUME null, //AUDIO_VOICE_VOLUME
false, //AUDIO_RING_VOLUME null, //AUDIO_RING_VOLUME
false, //AUDIO_MEDIA_VOLUME null, //AUDIO_MEDIA_VOLUME
false, //AUDIO_ALARM_VOLUME null, //AUDIO_ALARM_VOLUME
false, //AUDIO_NOTIFICATION_VOLUME null, //AUDIO_NOTIFICATION_VOLUME
false, //AUDIO_BLUETOOTH_VOLUME null, //AUDIO_BLUETOOTH_VOLUME
false, //WAKE_LOCK null, //WAKE_LOCK
false, //MONITOR_LOCATION null, //MONITOR_LOCATION
false, //MONITOR_HIGH_POWER_LOCATION null, //MONITOR_HIGH_POWER_LOCATION
false, //GET_USAGE_STATS null, //GET_USAGE_STATS
false, //MUTE_MICROPHONE null, //MUTE_MICROPHONE
true, //TOAST_WINDOW new RestrictionBypass(true, false), //TOAST_WINDOW
false, //PROJECT_MEDIA null, //PROJECT_MEDIA
false, //ACTIVATE_VPN null, //ACTIVATE_VPN
false, //WALLPAPER null, //WALLPAPER
false, //ASSIST_STRUCTURE null, //ASSIST_STRUCTURE
false, //ASSIST_SCREENSHOT null, //ASSIST_SCREENSHOT
false, //READ_PHONE_STATE null, //READ_PHONE_STATE
false, //ADD_VOICEMAIL null, //ADD_VOICEMAIL
false, // USE_SIP null, // USE_SIP
false, // PROCESS_OUTGOING_CALLS null, // PROCESS_OUTGOING_CALLS
false, // USE_FINGERPRINT null, // USE_FINGERPRINT
false, // BODY_SENSORS null, // BODY_SENSORS
false, // READ_CELL_BROADCASTS null, // READ_CELL_BROADCASTS
false, // MOCK_LOCATION null, // MOCK_LOCATION
false, // READ_EXTERNAL_STORAGE null, // READ_EXTERNAL_STORAGE
false, // WRITE_EXTERNAL_STORAGE null, // WRITE_EXTERNAL_STORAGE
false, // TURN_ON_SCREEN null, // TURN_ON_SCREEN
false, // GET_ACCOUNTS null, // GET_ACCOUNTS
false, // RUN_IN_BACKGROUND null, // RUN_IN_BACKGROUND
false, // AUDIO_ACCESSIBILITY_VOLUME null, // AUDIO_ACCESSIBILITY_VOLUME
false, // READ_PHONE_NUMBERS null, // READ_PHONE_NUMBERS
false, // REQUEST_INSTALL_PACKAGES null, // REQUEST_INSTALL_PACKAGES
false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
false, // INSTANT_APP_START_FOREGROUND null, // INSTANT_APP_START_FOREGROUND
false, // ANSWER_PHONE_CALLS null, // ANSWER_PHONE_CALLS
false, // OP_RUN_ANY_IN_BACKGROUND null, // OP_RUN_ANY_IN_BACKGROUND
false, // OP_CHANGE_WIFI_STATE null, // OP_CHANGE_WIFI_STATE
false, // OP_REQUEST_DELETE_PACKAGES null, // OP_REQUEST_DELETE_PACKAGES
false, // OP_BIND_ACCESSIBILITY_SERVICE null, // OP_BIND_ACCESSIBILITY_SERVICE
false, // ACCEPT_HANDOVER null, // ACCEPT_HANDOVER
false, // MANAGE_IPSEC_HANDOVERS null, // MANAGE_IPSEC_HANDOVERS
false, // START_FOREGROUND null, // START_FOREGROUND
true, // BLUETOOTH_SCAN new RestrictionBypass(true, false), // BLUETOOTH_SCAN
false, // USE_BIOMETRIC null, // USE_BIOMETRIC
false, // ACTIVITY_RECOGNITION null, // ACTIVITY_RECOGNITION
false, // SMS_FINANCIAL_TRANSACTIONS null, // SMS_FINANCIAL_TRANSACTIONS
false, // READ_MEDIA_AUDIO null, // READ_MEDIA_AUDIO
false, // WRITE_MEDIA_AUDIO null, // WRITE_MEDIA_AUDIO
false, // READ_MEDIA_VIDEO null, // READ_MEDIA_VIDEO
false, // WRITE_MEDIA_VIDEO null, // WRITE_MEDIA_VIDEO
false, // READ_MEDIA_IMAGES null, // READ_MEDIA_IMAGES
false, // WRITE_MEDIA_IMAGES null, // WRITE_MEDIA_IMAGES
false, // LEGACY_STORAGE null, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY null, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS null, // READ_DEVICE_IDENTIFIERS
false, // ACCESS_MEDIA_LOCATION null, // ACCESS_MEDIA_LOCATION
false, // QUERY_ALL_PACKAGES null, // QUERY_ALL_PACKAGES
false, // MANAGE_EXTERNAL_STORAGE null, // MANAGE_EXTERNAL_STORAGE
false, // INTERACT_ACROSS_PROFILES null, // INTERACT_ACROSS_PROFILES
false, // ACTIVATE_PLATFORM_VPN null, // ACTIVATE_PLATFORM_VPN
false, // LOADER_USAGE_STATS null, // LOADER_USAGE_STATS
false, // ACCESS_CALL_AUDIO null, // ACCESS_CALL_AUDIO
false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
}; };
/** /**
@@ -2485,11 +2485,11 @@ public class AppOpsManager {
} }
/** /**
* Retrieve whether the op allows the system (and system ui) to * Retrieve whether the op allows to bypass the user restriction.
* bypass the user restriction. *
* @hide * @hide
*/ */
public static boolean opAllowSystemBypassRestriction(int op) { public static RestrictionBypass opAllowSystemBypassRestriction(int op) {
return sOpAllowSystemRestrictionBypass[op]; return sOpAllowSystemRestrictionBypass[op];
} }
@@ -2535,6 +2535,29 @@ public class AppOpsManager {
return !sOpDisableReset[op]; return !sOpDisableReset[op];
} }
/**
* 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. * Class holding all of the operation information associated with an app.
* @hide * @hide

View File

@@ -1187,6 +1187,16 @@
android:description="@string/permdesc_callCompanionApp" android:description="@string/permdesc_callCompanionApp"
android:protectionLevel="normal" /> 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 <!-- 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> 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 When the handover of a call from one app to another takes place, there are two devices

View File

@@ -1217,6 +1217,14 @@
device. This includes information such as call numbers for calls and the state of the device. This includes information such as call numbers for calls and the state of the
calls.</string> 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 <!-- 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 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 could be in a voice call over their carrier's mobile network, and a third party video

View File

@@ -40,6 +40,7 @@ import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OpEventProxyInfo; 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_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM; import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND; import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
@@ -55,6 +56,7 @@ import static android.app.AppOpsManager.extractFlagsFromKey;
import static android.app.AppOpsManager.extractUidStateFromKey; import static android.app.AppOpsManager.extractUidStateFromKey;
import static android.app.AppOpsManager.makeKey; import static android.app.AppOpsManager.makeKey;
import static android.app.AppOpsManager.modeToName; import static android.app.AppOpsManager.modeToName;
import static android.app.AppOpsManager.opAllowSystemBypassRestriction;
import static android.app.AppOpsManager.opToName; import static android.app.AppOpsManager.opToName;
import static android.app.AppOpsManager.opToPublicName; import static android.app.AppOpsManager.opToPublicName;
import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState; import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
@@ -73,7 +75,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AppGlobals; import android.app.AppGlobals;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOps; import android.app.AppOpsManager.HistoricalOps;
@@ -92,8 +93,6 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal;
@@ -666,15 +665,19 @@ public class AppOpsService extends IAppOpsService.Stub {
final static class Ops extends SparseArray<Op> { final static class Ops extends SparseArray<Op> {
final String packageName; final String packageName;
final UidState uidState; 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 */ /** Lazily populated cache of featureIds of this package */
final @NonNull ArraySet<String> knownFeatureIds = new ArraySet<>(); final @NonNull ArraySet<String> knownFeatureIds = new ArraySet<>();
Ops(String _packageName, UidState _uidState, boolean _isPrivileged) { Ops(String _packageName, UidState _uidState) {
packageName = _packageName; packageName = _packageName;
uidState = _uidState; uidState = _uidState;
isPrivileged = _isPrivileged;
} }
} }
@@ -1519,7 +1522,11 @@ public class AppOpsService extends IAppOpsService.Stub {
return; return;
} }
// Reset cached package properties to re-initialize when needed
ops.bypass = null;
ops.knownFeatureIds.clear(); ops.knownFeatureIds.clear();
// Merge data collected for removed features into their successor features
int numOps = ops.size(); int numOps = ops.size();
for (int opNum = 0; opNum < numOps; opNum++) { for (int opNum = 0; opNum < numOps; opNum++) {
Op op = ops.valueAt(opNum); Op op = ops.valueAt(opNum);
@@ -1953,8 +1960,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return Collections.emptyList(); return Collections.emptyList();
} }
synchronized (this) { synchronized (this) {
Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false /* isPrivileged */, Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false /* edit */);
false /* edit */);
if (pkgOps == null) { if (pkgOps == null) {
return null; return null;
} }
@@ -2087,8 +2093,7 @@ public class AppOpsService extends IAppOpsService.Stub {
op.removeFeaturesWithNoTime(); op.removeFeaturesWithNoTime();
if (op.mFeatures.size() == 0) { if (op.mFeatures.size() == 0) {
Ops ops = getOpsRawLocked(uid, packageName, null, false /* isPrivileged */, Ops ops = getOpsLocked(uid, packageName, null, null, false /* edit */);
false /* edit */);
if (ops != null) { if (ops != null) {
ops.remove(op.op); ops.remove(op.op);
if (ops.size() <= 0) { if (ops.size() <= 0) {
@@ -2390,9 +2395,9 @@ public class AppOpsService extends IAppOpsService.Stub {
ArraySet<ModeCallback> repCbs = null; ArraySet<ModeCallback> repCbs = null;
code = AppOpsManager.opToSwitch(code); code = AppOpsManager.opToSwitch(code);
boolean isPrivileged; RestrictionBypass bypass;
try { try {
isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null); bypass = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) { } catch (SecurityException e) {
Slog.e(TAG, "Cannot setMode", e); Slog.e(TAG, "Cannot setMode", e);
return; return;
@@ -2400,7 +2405,7 @@ public class AppOpsService extends IAppOpsService.Stub {
synchronized (this) { synchronized (this) {
UidState uidState = getUidStateLocked(uid, false); 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 != null) {
if (op.mode != mode) { if (op.mode != mode) {
op.mode = mode; op.mode = mode;
@@ -2798,10 +2803,9 @@ public class AppOpsService extends IAppOpsService.Stub {
*/ */
private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName, private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
boolean raw) { boolean raw) {
boolean isPrivileged; RestrictionBypass bypass;
try { try {
isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null); bypass = verifyAndGetBypass(uid, packageName, null);
} catch (SecurityException e) { } catch (SecurityException e) {
Slog.e(TAG, "checkOperation", e); Slog.e(TAG, "checkOperation", e);
return AppOpsManager.opToDefaultMode(code); return AppOpsManager.opToDefaultMode(code);
@@ -2811,7 +2815,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_IGNORED; return AppOpsManager.MODE_IGNORED;
} }
synchronized (this) { synchronized (this) {
if (isOpRestrictedLocked(uid, code, packageName, null, isPrivileged)) { if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
return AppOpsManager.MODE_IGNORED; return AppOpsManager.MODE_IGNORED;
} }
code = AppOpsManager.opToSwitch(code); code = AppOpsManager.opToSwitch(code);
@@ -2821,7 +2825,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final int rawMode = uidState.opModes.get(code); final int rawMode = uidState.opModes.get(code);
return raw ? rawMode : uidState.evalMode(code, rawMode); 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) { if (op == null) {
return AppOpsManager.opToDefaultMode(code); return AppOpsManager.opToDefaultMode(code);
} }
@@ -2884,7 +2888,7 @@ public class AppOpsService extends IAppOpsService.Stub {
public int checkPackage(int uid, String packageName) { public int checkPackage(int uid, String packageName) {
Objects.requireNonNull(packageName); Objects.requireNonNull(packageName);
try { try {
verifyAndGetIsPrivileged(uid, packageName, null); verifyAndGetBypass(uid, packageName, null);
return AppOpsManager.MODE_ALLOWED; return AppOpsManager.MODE_ALLOWED;
} catch (SecurityException ignored) { } catch (SecurityException ignored) {
@@ -2961,17 +2965,16 @@ public class AppOpsService extends IAppOpsService.Stub {
@Nullable String featureId, int proxyUid, String proxyPackageName, @Nullable String featureId, int proxyUid, String proxyPackageName,
@Nullable String proxyFeatureId, @OpFlags int flags, boolean shouldCollectAsyncNotedOp, @Nullable String proxyFeatureId, @OpFlags int flags, boolean shouldCollectAsyncNotedOp,
@Nullable String message) { @Nullable String message) {
boolean isPrivileged; RestrictionBypass bypass;
try { try {
isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); bypass = verifyAndGetBypass(uid, packageName, featureId);
} catch (SecurityException e) { } catch (SecurityException e) {
Slog.e(TAG, "noteOperation", e); Slog.e(TAG, "noteOperation", e);
return AppOpsManager.MODE_ERRORED; return AppOpsManager.MODE_ERRORED;
} }
synchronized (this) { synchronized (this) {
final Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged, final Ops ops = getOpsLocked(uid, packageName, featureId, bypass, true /* edit */);
true /* edit */);
if (ops == null) { if (ops == null) {
scheduleOpNotedIfNeededLocked(code, uid, packageName, scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_IGNORED); AppOpsManager.MODE_IGNORED);
@@ -2981,7 +2984,7 @@ public class AppOpsService extends IAppOpsService.Stub {
} }
final Op op = getOpLocked(ops, code, uid, true); final Op op = getOpLocked(ops, code, uid, true);
final FeatureOp featureOp = op.getOrCreateFeature(op, featureId); final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
if (isOpRestrictedLocked(uid, code, packageName, featureId, isPrivileged)) { if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
scheduleOpNotedIfNeededLocked(code, uid, packageName, scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_IGNORED); AppOpsManager.MODE_IGNORED);
return AppOpsManager.MODE_IGNORED; return AppOpsManager.MODE_IGNORED;
@@ -3205,7 +3208,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int uid = Binder.getCallingUid(); int uid = Binder.getCallingUid();
Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid); Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
verifyAndGetIsPrivileged(uid, packageName, null); verifyAndGetBypass(uid, packageName, null);
synchronized (this) { synchronized (this) {
RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key); RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -3235,7 +3238,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int uid = Binder.getCallingUid(); int uid = Binder.getCallingUid();
Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid); Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
verifyAndGetIsPrivileged(uid, packageName, null); verifyAndGetBypass(uid, packageName, null);
synchronized (this) { synchronized (this) {
RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key); RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -3254,7 +3257,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int uid = Binder.getCallingUid(); int uid = Binder.getCallingUid();
verifyAndGetIsPrivileged(uid, packageName, null); verifyAndGetBypass(uid, packageName, null);
synchronized (this) { synchronized (this) {
return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid)); return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid));
@@ -3272,16 +3275,16 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_IGNORED; return AppOpsManager.MODE_IGNORED;
} }
boolean isPrivileged; RestrictionBypass bypass;
try { try {
isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); bypass = verifyAndGetBypass(uid, packageName, featureId);
} catch (SecurityException e) { } catch (SecurityException e) {
Slog.e(TAG, "startOperation", e); Slog.e(TAG, "startOperation", e);
return AppOpsManager.MODE_ERRORED; return AppOpsManager.MODE_ERRORED;
} }
synchronized (this) { synchronized (this) {
final Ops ops = getOpsRawLocked(uid, resolvedPackageName, featureId, isPrivileged, final Ops ops = getOpsLocked(uid, resolvedPackageName, featureId, bypass,
true /* edit */); true /* edit */);
if (ops == null) { if (ops == null) {
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
@@ -3289,7 +3292,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_ERRORED; return AppOpsManager.MODE_ERRORED;
} }
final Op op = getOpLocked(ops, code, uid, true); 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; return AppOpsManager.MODE_IGNORED;
} }
final FeatureOp featureOp = op.getOrCreateFeature(op, featureId); final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
@@ -3347,16 +3350,16 @@ public class AppOpsService extends IAppOpsService.Stub {
return; return;
} }
boolean isPrivileged; RestrictionBypass bypass;
try { try {
isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId); bypass = verifyAndGetBypass(uid, packageName, featureId);
} catch (SecurityException e) { } catch (SecurityException e) {
Slog.e(TAG, "Cannot finishOperation", e); Slog.e(TAG, "Cannot finishOperation", e);
return; return;
} }
synchronized (this) { synchronized (this) {
Op op = getOpLocked(code, uid, resolvedPackageName, featureId, isPrivileged, true); Op op = getOpLocked(code, uid, resolvedPackageName, featureId, bypass, true);
if (op == null) { if (op == null) {
return; return;
} }
@@ -3617,7 +3620,22 @@ public class AppOpsService extends IAppOpsService.Stub {
} }
/** /**
* 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 uid The uid the package belongs to
* @param packageName The package the might belong to the uid * @param packageName The package the might belong to the uid
@@ -3625,11 +3643,11 @@ public class AppOpsService extends IAppOpsService.Stub {
* *
* @return {@code true} iff the package is privileged * @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) { @Nullable String featureId) {
if (uid == Process.ROOT_UID) { if (uid == Process.ROOT_UID) {
// For backwards compatibility, don't check package name for 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 // Do not check if uid/packageName/featureId is already known
@@ -3638,13 +3656,14 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidState != null && uidState.pkgOps != null) { if (uidState != null && uidState.pkgOps != null) {
Ops ops = uidState.pkgOps.get(packageName); Ops ops = uidState.pkgOps.get(packageName);
if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))) { if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))
return ops.isPrivileged; && ops.bypass != null) {
return ops.bypass;
} }
} }
} }
boolean isPrivileged = false; RestrictionBypass bypass = null;
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
int pkgUid; int pkgUid;
@@ -3668,14 +3687,14 @@ public class AppOpsService extends IAppOpsService.Stub {
pkgUid = UserHandle.getUid( pkgUid = UserHandle.getUid(
UserHandle.getUserId(uid), UserHandle.getAppId(pkg.getUid())); UserHandle.getUserId(uid), UserHandle.getAppId(pkg.getUid()));
isPrivileged = pkg.isPrivileged(); bypass = getBypassforPackage(pkg);
} else { } else {
// Allow any feature id for resolvable uids // Allow any feature id for resolvable uids
isFeatureIdValid = true; isFeatureIdValid = true;
pkgUid = resolveUid(packageName); pkgUid = resolveUid(packageName);
if (pkgUid >= 0) { if (pkgUid >= 0) {
isPrivileged = false; bypass = RestrictionBypass.UNRESTRICTED;
} }
} }
if (pkgUid != uid) { if (pkgUid != uid) {
@@ -3692,7 +3711,7 @@ public class AppOpsService extends IAppOpsService.Stub {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
return isPrivileged; return bypass;
} }
/** /**
@@ -3701,13 +3720,13 @@ public class AppOpsService extends IAppOpsService.Stub {
* @param uid The uid the package belongs to * @param uid The uid the package belongs to
* @param packageName The name of the package * @param packageName The name of the package
* @param featureId The feature in 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? * @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, private Ops getOpsLocked(int uid, String packageName, @Nullable String featureId,
boolean isPrivileged, boolean edit) { @Nullable RestrictionBypass bypass, boolean edit) {
UidState uidState = getUidStateLocked(uid, edit); UidState uidState = getUidStateLocked(uid, edit);
if (uidState == null) { if (uidState == null) {
return null; return null;
@@ -3725,54 +3744,19 @@ public class AppOpsService extends IAppOpsService.Stub {
if (!edit) { if (!edit) {
return null; 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);
}
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);
uidState.pkgOps.put(packageName, ops); uidState.pkgOps.put(packageName, ops);
} }
if (edit && featureId != null) { if (edit) {
if (bypass != null) {
ops.bypass = bypass;
}
if (featureId != null) {
ops.knownFeatureIds.add(featureId); ops.knownFeatureIds.add(featureId);
} }
}
return ops; return ops;
} }
@@ -3800,15 +3784,14 @@ public class AppOpsService extends IAppOpsService.Stub {
* @param uid The uid the of the package * @param uid The uid the of the package
* @param packageName The package name for which to get the state for * @param packageName The package name for which to get the state for
* @param featureId The feature in the package * @param featureId The feature in the package
* @param isPrivileged Whether the package is privileged or not (only used if {@code edit * @param bypass When to bypass certain op restrictions (can be null if edit == false)
* == true})
* @param edit Iff {@code true} create the {@link Op} object if not yet created * @param edit Iff {@code true} create the {@link Op} object if not yet created
* *
* @return The {@link Op state} of the op * @return The {@link Op state} of the op
*/ */
private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
@Nullable String featureId, boolean isPrivileged, boolean edit) { @Nullable String featureId, @Nullable RestrictionBypass bypass, boolean edit) {
Ops ops = getOpsRawNoVerifyLocked(uid, packageName, featureId, edit, isPrivileged); Ops ops = getOpsLocked(uid, packageName, featureId, bypass, edit);
if (ops == null) { if (ops == null) {
return null; return null;
} }
@@ -3839,7 +3822,7 @@ public class AppOpsService extends IAppOpsService.Stub {
} }
private boolean isOpRestrictedLocked(int uid, int code, String packageName, private boolean isOpRestrictedLocked(int uid, int code, String packageName,
@Nullable String featureId, boolean isPrivileged) { @Nullable RestrictionBypass appBypass) {
int userHandle = UserHandle.getUserId(uid); int userHandle = UserHandle.getUserId(uid);
final int restrictionSetCount = mOpUserRestrictions.size(); final int restrictionSetCount = mOpUserRestrictions.size();
@@ -3848,12 +3831,15 @@ public class AppOpsService extends IAppOpsService.Stub {
// package is exempt from the restriction. // package is exempt from the restriction.
ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i); ClientRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
if (restrictionState.hasRestriction(code, packageName, userHandle)) { 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 // If we are the system, bypass user restrictions for certain codes
synchronized (this) { synchronized (this) {
Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged, if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) {
true /* edit */); return false;
if ((ops != null) && ops.isPrivileged) { }
if (opBypass.isRecordAudioRestrictionExcept && appBypass != null
&& appBypass.isRecordAudioRestrictionExcept) {
return false; return false;
} }
} }
@@ -4043,28 +4029,6 @@ public class AppOpsService extends IAppOpsService.Stub {
throws NumberFormatException, XmlPullParserException, IOException { throws NumberFormatException, XmlPullParserException, IOException {
int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
final UidState uidState = getUidStateLocked(uid, true); 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 outerDepth = parser.getDepth();
int type; int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4074,7 +4038,7 @@ public class AppOpsService extends IAppOpsService.Stub {
} }
String tagName = parser.getName(); String tagName = parser.getName();
if (tagName.equals("op")) { if (tagName.equals("op")) {
readOp(parser, uidState, pkgName, isPrivileged); readOp(parser, uidState, pkgName);
} else { } else {
Slog.w(TAG, "Unknown element under <pkg>: " Slog.w(TAG, "Unknown element under <pkg>: "
+ parser.getName()); + parser.getName());
@@ -4108,8 +4072,8 @@ public class AppOpsService extends IAppOpsService.Stub {
} }
} }
private void readOp(XmlPullParser parser, @NonNull UidState uidState, private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName)
@NonNull String pkgName, boolean isPrivileged) throws NumberFormatException, throws NumberFormatException,
XmlPullParserException, IOException { XmlPullParserException, IOException {
int opCode = Integer.parseInt(parser.getAttributeValue(null, "n")); int opCode = Integer.parseInt(parser.getAttributeValue(null, "n"));
if (isIgnoredAppOp(opCode)) { if (isIgnoredAppOp(opCode)) {
@@ -4143,7 +4107,7 @@ public class AppOpsService extends IAppOpsService.Stub {
} }
Ops ops = uidState.pkgOps.get(pkgName); Ops ops = uidState.pkgOps.get(pkgName);
if (ops == null) { if (ops == null) {
ops = new Ops(pkgName, uidState, isPrivileged); ops = new Ops(pkgName, uidState);
uidState.pkgOps.put(pkgName, ops); uidState.pkgOps.put(pkgName, ops);
} }
ops.put(op.op, op); ops.put(op.op, op);
@@ -4235,17 +4199,6 @@ public class AppOpsService extends IAppOpsService.Stub {
} }
out.startTag(null, "uid"); out.startTag(null, "uid");
out.attribute(null, "n", Integer.toString(pkg.getUid())); 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(); List<AppOpsManager.OpEntry> ops = pkg.getOps();
for (int j=0; j<ops.size(); j++) { for (int j=0; j<ops.size(); j++) {
AppOpsManager.OpEntry op = ops.get(j); AppOpsManager.OpEntry op = ops.get(j);
@@ -5574,7 +5527,7 @@ public class AppOpsService extends IAppOpsService.Stub {
} }
// TODO moltmann: Allow to check for feature op activeness // TODO moltmann: Allow to check for feature op activeness
synchronized (AppOpsService.this) { synchronized (AppOpsService.this) {
Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false, false); Ops pkgOps = getOpsLocked(uid, resolvedPackageName, null, null, false);
if (pkgOps == null) { if (pkgOps == null) {
return false; return false;
} }