在Android S上,有一个新增的设计——定位开关被关闭以后,应用申请的定位权限OP_FINE_LOCATION和OP_COARSE_LOCATION会被系统AppOps限制。
基于上面的背景,有些app会出现这种问题。app在打开以后,已经动态申请了权限android.permission.ACCESS_FINE_LOCATION和android.permission.ACCESS_COARSE_LOCATION,但是后来用户关闭了定位开关,这时候,如果app需要执行一些与定位无关但是却需要定位权限的操作时(例如进行蓝牙扫描,建立P2P连接等),就会发现上述操作无法正确执行成功。
在LocationManagerService.java中,当定位开关被用户开启或者关闭的时候,会走到
onLocationModeChangedhttp://aosp.opersys.com/xref/android-12.0.0_r2/s?refs=onLocationModeChanged&project=frameworks
private void onLocationModeChanged(int userId) {
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
LocationManager.invalidateLocalLocationEnabledCaches();
if (D) {
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
}
EVENT_LOG.logLocationEnabled(userId, enabled);
Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
refreshAppOpsRestrictions(userId);
}
然后会执行到一个新增的方法:refreshAppOpsRestrictionshttp://aosp.opersys.com/xref/android-12.0.0_r2/s?refs=refreshAppOpsRestrictions&project=frameworks
private void refreshAppOpsRestrictions(int userId) {
if (userId == UserHandle.USER_ALL) {
final int[] runningUserIds = mInjector.getUserInfoHelper().getRunningUserIds();
for (int i = 0; i < runningUserIds.length; i++) {
refreshAppOpsRestrictions(runningUserIds[i]);
}
return;
}
Preconditions.checkArgument(userId >= 0);
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
PackageTagsList allowedPackages = null;
if (!enabled) {
PackageTagsList.Builder builder = new PackageTagsList.Builder();
for (LocationProviderManager manager : mProviderManagers) {
CallerIdentity identity = manager.getIdentity();
if (identity != null) {
builder.add(identity.getPackageName(), identity.getAttributionTag());
}
}
builder.add(mInjector.getSettingsHelper().getIgnoreSettingsAllowlist());
allowedPackages = builder.build();
}
AppOpsManager appOpsManager = Objects.requireNonNull(
mContext.getSystemService(AppOpsManager.class));
appOpsManager.setUserRestrictionForUser(
AppOpsManager.OP_COARSE_LOCATION,
!enabled,
LocationManagerService.this,
allowedPackages,
userId);
appOpsManager.setUserRestrictionForUser(
AppOpsManager.OP_FINE_LOCATION,
!enabled,
LocationManagerService.this,
allowedPackages,
userId);
}
当定位开关被关闭后,
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
得到的enabled的值为false,
下面的if判断就会走进去:
PackageTagsList allowedPackages = null;
if (!enabled) {
PackageTagsList.Builder builder = new PackageTagsList.Builder();
for (LocationProviderManager manager : mProviderManagers) {
CallerIdentity identity = manager.getIdentity();
if (identity != null) {
builder.add(identity.getPackageName(), identity.getAttributionTag());
}
}
builder.add(mInjector.getSettingsHelper().getIgnoreSettingsAllowlist());
allowedPackages = builder.build();
}
allowedPackages看命名方式,它就是白名单的意思。
上述的变量赋值以后,会走到下面的限制权限的地方:
AppOpsManager appOpsManager = Objects.requireNonNull(
mContext.getSystemService(AppOpsManager.class));
appOpsManager.setUserRestrictionForUser(
AppOpsManager.OP_COARSE_LOCATION,
!enabled,
LocationManagerService.this,
allowedPackages,
userId);
appOpsManager.setUserRestrictionForUser(
AppOpsManager.OP_FINE_LOCATION,
!enabled,
LocationManagerService.this,
allowedPackages,
userId);
当enabled为false的时候,会将userId的用户空间的所有除了allowedPackages的package的OP_FINE_LOCATION和OP_COARSE_LOCATION限制;
当enabled为true的时候,会将userId的用户空间的所有除了allowedPackages的package的OP_FINE_LOCATION和OP_COARSE_LOCATION释放;
总结一下就是,当定位开关被关闭后,所有白名单以外的package的定位权限会被限制,只有当定位开关重新打开以后才会被释放。
在上面的代码设计中讲到,下面的if语句是添加白名单的地方,
PackageTagsList allowedPackages = null;
if (!enabled) {
PackageTagsList.Builder builder = new PackageTagsList.Builder();
for (LocationProviderManager manager : mProviderManagers) {
CallerIdentity identity = manager.getIdentity();
if (identity != null) {
builder.add(identity.getPackageName(), identity.getAttributionTag());
}
}
builder.add(mInjector.getSettingsHelper().getIgnoreSettingsAllowlist());
allowedPackages = builder.build();
}
当enabled为true的时候,allowedPackages为null,不谈;
当enabled为false的时候,先遍历LocationProviderManager,将4种provider(passive network fused gps)加入到白名单中;然后通过mInjector.getSettingsHelper().getIgnoreSettingsAllowlist()去获取已经添加过后并保存好的白名单。
那么,问题解决的方案可以从mInjector.getSettingsHelper().getIgnoreSettingsAllowlist()入手了。
@Override
public PackageTagsList getIgnoreSettingsAllowlist() {
return mIgnoreSettingsPackageAllowlist.getValue();
}
mIgnoreSettingsPackageAllowlist定义处:
private final PackageTagsListSetting mIgnoreSettingsPackageAllowlist;
mIgnoreSettingsPackageAllowlist初始化:
mIgnoreSettingsPackageAllowlist = new PackageTagsListSetting(
IGNORE_SETTINGS_ALLOWLIST,
() -> SystemConfig.getInstance().getAllowIgnoreLocationSettings());
从SystemConfig.java中获取AllowIgnoreLocationSettings
public ArrayMap> getAllowIgnoreLocationSettings() {
return mAllowIgnoreLocationSettings;
}
mAllowIgnoreLocationSettings定义、初始化:
final ArrayMap> mAllowIgnoreLocationSettings = new ArrayMap<>();
新增元素,这边就是新增白名单的地方。
case "allow-ignore-location-settings": {
if (allowOverrideAppRestrictions) {
String pkgname = parser.getAttributeValue(null, "package");
String attributionTag = parser.getAttributeValue(null,
"attributionTag");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
ArraySet tags = mAllowIgnoreLocationSettings.get(pkgname);
if (tags == null || !tags.isEmpty()) {
if (tags == null) {
tags = new ArraySet<>(1);
mAllowIgnoreLocationSettings.put(pkgname, tags);
}
if (!"*".equals(attributionTag)) {
if ("null".equals(attributionTag)) {
attributionTag = null;
}
tags.add(attributionTag);
}
}
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
看代码实现,这边是解析xml元素"package"和"attributionTag"的属性值,然后解析后的属性值添加到mAllowIgnoreLocationSettings中。
读取Permissons
private void readPermissionsFromXml(File permFile, int permissionFlag) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
} catch (FileNotFoundException e) {
Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
return;
}
Slog.i(TAG, "Reading permissions from " + permFile);
final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);
int type;
while ((type=parser.next()) != parser.START_TAG
&& type != parser.END_DOCUMENT) {
;
}
if (type != parser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
throw new XmlPullParserException("Unexpected start tag in " + permFile
+ ": found " + parser.getName() + ", expected 'permissions' or 'config'");
}
final boolean allowAll = permissionFlag == ALLOW_ALL;
final boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
final boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
final boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
final boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
final boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS)
!= 0;
final boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
final boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING)
!= 0;
final boolean allowAssociations = (permissionFlag & ALLOW_ASSOCIATIONS) != 0;
final boolean allowOverrideAppRestrictions =
(permissionFlag & ALLOW_OVERRIDE_APP_RESTRICTIONS) != 0;
final boolean allowImplicitBroadcasts = (permissionFlag & ALLOW_IMPLICIT_BROADCASTS)
!= 0;
final boolean allowVendorApex = (permissionFlag & ALLOW_VENDOR_APEX) != 0;
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}
String name = parser.getName();
if (name == null) {
XmlUtils.skipCurrentTag(parser);
continue;
}
switch (name) {
// ......
case "allow-ignore-location-settings": {
if (allowOverrideAppRestrictions) {
String pkgname = parser.getAttributeValue(null, "package");
String attributionTag = parser.getAttributeValue(null,
"attributionTag");
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
ArraySet tags = mAllowIgnoreLocationSettings.get(pkgname);
if (tags == null || !tags.isEmpty()) {
if (tags == null) {
tags = new ArraySet<>(1);
mAllowIgnoreLocationSettings.put(pkgname, tags);
}
if (!"*".equals(attributionTag)) {
if ("null".equals(attributionTag)) {
attributionTag = null;
}
tags.add(attributionTag);
}
}
}
} else {
logNotAllowedInPartition(name, permFile, parser);
}
XmlUtils.skipCurrentTag(parser);
} break;
// ......
}
}
} catch (XmlPullParserException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} catch (IOException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} finally {
IoUtils.closeQuietly(permReader);
}
// ......
}
加载XML,从手机的各个目录下的/etc/的sysconfig和permissions中加载。
private void readAllPermissions() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
// Vendors are only allowed to customize these
int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
| ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX;
if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.O_MR1) {
// For backward compatibility
vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
}
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);
String vendorSkuProperty = SystemProperties.get(VENDOR_SKU_PROPERTY, "");
if (!vendorSkuProperty.isEmpty()) {
String vendorSkuDir = "sku_" + vendorSkuProperty;
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "sysconfig", vendorSkuDir),
vendorPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "permissions", vendorSkuDir),
vendorPermissionFlag);
}
// Allow ODM to customize system configs as much as Vendor, because /odm is another
// vendor partition other than /vendor.
int odmPermissionFlag = vendorPermissionFlag;
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
String skuProperty = SystemProperties.get(SKU_PROPERTY, "");
if (!skuProperty.isEmpty()) {
String skuDir = "sku_" + skuProperty;
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "sysconfig", skuDir), odmPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions", skuDir),
odmPermissionFlag);
}
// Allow OEM to customize these
int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS
| ALLOW_VENDOR_APEX;
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);
// Allow Product to customize these configs
// TODO(b/157203468): ALLOW_HIDDENAPI_WHITELISTING must be removed because we prohibited
// the use of hidden APIs from the product partition.
int productPermissionFlag = ALLOW_FEATURES | ALLOW_LIBS | ALLOW_PERMISSIONS
| ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS | ALLOW_HIDDENAPI_WHITELISTING
| ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS
| ALLOW_VENDOR_APEX;
if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) {
// TODO(b/157393157): This must check product interface enforcement instead of
// DEVICE_INITIAL_SDK_INT for the devices without product interface enforcement.
productPermissionFlag = ALLOW_ALL;
}
readPermissions(Environment.buildPath(
Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag);
// Allow /system_ext to customize all system configs
readPermissions(Environment.buildPath(
Environment.getSystemExtDirectory(), "etc", "sysconfig"), ALLOW_ALL);
readPermissions(Environment.buildPath(
Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL);
// Skip loading configuration from apex if it is not a system process.
if (!isSystemProcess()) {
return;
}
// Read configuration of libs from apex module.
// TODO: Use a solid way to filter apex module folders?
for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) {
if (f.isFile() || f.getPath().contains("@")) {
continue;
}
readPermissions(Environment.buildPath(f, "etc", "permissions"), ALLOW_LIBS);
}
}
@VisibleForTesting
public void readPermissions(File libraryDir, int permissionFlag) {
// Read permissions from given directory.
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
if (permissionFlag == ALLOW_ALL) {
Slog.w(TAG, "No directory " + libraryDir + ", skipping");
}
return;
}
if (!libraryDir.canRead()) {
Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
return;
}
// Iterate over the files in the directory and scan .xml files
File platformFile = null;
for (File f : libraryDir.listFiles()) {
if (!f.isFile()) {
continue;
}
// We'll read platform.xml last
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
platformFile = f;
continue;
}
if (!f.getPath().endsWith(".xml")) {
Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
continue;
}
if (!f.canRead()) {
Slog.w(TAG, "Permissions library file " + f + " cannot be read");
continue;
}
readPermissionsFromXml(f, permissionFlag);
}
// Read platform permissions last so it will take precedence
if (platformFile != null) {
readPermissionsFromXml(platformFile, permissionFlag);
}
}
最后我们参照google添加的默认的白名单:
Bypass Allow Packages:
com.google.android.dialer[*]
com.google.android.gms[.thunderbird]
我们可以在/product/etc/sysconfig/google.xml中,可以我们需要的内容:
到这里解决方案呼之欲出了,我们新开一个小节单独总结下。
我们可以在/system/etc/sysconfig/下新增一个文件,这里我们可以命名(也可以定义为你想要的fashion的名字)为location-settings-conf.xml,
元素package的属性值,写入你要添加进白名单的package;
元素attributionTAG的属性值,可以直接写入*,也可以写上你想要的字符。
添加成功后,我们通过adb shell dumpsys location可以看到下面的dump信息。
Bypass Allow Packages:
com.android.package.one[*]
com.google.android.dialer[*]
com.google.android.gms[.thunderbird]
再去验证之前碰到的问题,pass。
上述的方案合入以后,会过不了GTS测试
run gts -m GtsLocationTestCases -t com.google.android.location.gts.LocationIgnoreSettingsWhitelistTest#testIgnoreSettingsAllowlist_31Plus
@SdkSuppress(minSdkVersion = 31)
@Test
public void testIgnoreSettingsAllowlist_31Plus() {
if (BuildCompat.isAtLeastS()) {
LocationManager locationManager = (LocationManager) this.mContext.getSystemService(LocationManager.class);
Objects.requireNonNull(locationManager);
PackageTagsList devicePackageTags = locationManager.getIgnoreSettingsAllowlist();
Builder builder = new Builder();
for (String packageName : this.mDynamicConfig.getValues(IGNORE_LOCATION_SETTINGS_PACKAGE_WHITELIST_KEY)) {
builder.add(packageName);
}
PackageTagsList configPackageTags = builder.build();
StringBuilder sb = new StringBuilder();
sb.append(configPackageTags);
sb.append(" does not contain all of ");
sb.append(devicePackageTags);
Assert.assertTrue(sb.toString(), configPackageTags.contains(devicePackageTags)); //报错行
}
}
调用LocationManager.getIgnoreSettingsAllowlist()最终读取手机中的/system/etc/sysconfig/location-settings-conf.xml
case从Google服务器读取GtsLocationTestCases.dynamic配置文件中的ignore_location_settings_package_whitelist名单,最终要求上面读取的应用配置必须包含在Google的白名单中。
这个白名单不是随便能加的,必须得得到Google同意,需要向Google申请waiver。
那么如果不走白名单申请还有其他的方法吗?
有的,将你的应用改成特权应用就没事了。
/**
* In which cases should an app be allowed to bypass the {@link #setUserRestriction user
* restriction} for a certain app-op.
*/
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, // deprecated operation
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
null, // NO_ISOLATED_STORAGE
null, // PHONE_CALL_MICROPHONE
null, // PHONE_CALL_CAMERA
null, // RECORD_AUDIO_HOTWORD
null, // MANAGE_ONGOING_CALLS
null, // MANAGE_CREDENTIALS
null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
null, // RECORD_AUDIO_OUTPUT
null, // SCHEDULE_EXACT_ALARM
null, // ACCESS_FINE_LOCATION_SOURCE
null, // ACCESS_COARSE_LOCATION_SOURCE
null, // MANAGE_MEDIA
null, // BLUETOOTH_CONNECT
null, // UWB_RANGING
null, // ACTIVITY_RECOGNITION_SOURCE
null, // BLUETOOTH_ADVERTISE
null, // RECORD_INCOMING_PHONE_AUDIO
};
/**
* 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);
}