Merge "Allow to exempt apps from restrictions to RECORD_AUDIO" into rvc-dev

This commit is contained in:
Philip P. Moltmann
2020-03-11 16:11:16 +00:00
committed by Android (Google) Code Review
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 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";

View File

@@ -1988,108 +1988,108 @@ public class AppOpsManager {
};
/**
* 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 @@ public class AppOpsManager {
}
/**
* 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];
}
@@ -2535,6 +2535,29 @@ public class AppOpsManager {
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.
* @hide

View File

@@ -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

View File

@@ -1217,6 +1217,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

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_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.extractFlagsFromKey;
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.NonNull;
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.ContentResolver;
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
@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 @@ public class AppOpsService extends IAppOpsService.Stub {
}
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
int uid = Binder.getCallingUid();
verifyAndGetIsPrivileged(uid, packageName, null);
verifyAndGetBypass(uid, packageName, null);
synchronized (this) {
return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid));
@@ -3272,16 +3275,16 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ 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 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
*/
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
* @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 @@ public class AppOpsService extends IAppOpsService.Stub {
}
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 @@ public class AppOpsService extends IAppOpsService.Stub {
// 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 @@ public class AppOpsService extends IAppOpsService.Stub {
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 @@ public class AppOpsService extends IAppOpsService.Stub {
}
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 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
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 @@ public class AppOpsService extends IAppOpsService.Stub {
}
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 @@ public class AppOpsService extends IAppOpsService.Stub {
}
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 @@ public class AppOpsService extends IAppOpsService.Stub {
}
// 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;
}