Android S关闭定位开关后,定位权限被AppOps限制。

7 篇文章 2 订阅

一、背景

在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<String, ArraySet<String>> getAllowIgnoreLocationSettings() {
        return mAllowIgnoreLocationSettings;
    }

mAllowIgnoreLocationSettings定义、初始化:

final ArrayMap<String, ArraySet<String>> 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<String> 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<String> 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中,可以我们需要的内容:

<?xml version="1.0" encoding="utf-8"?>
<!-- These are configurations that must exist on all GMS devices. -->
<config>

    <allow-ignore-location-settings package="com.google.android.gms" attributionTag="com.google.android.gms.thunderbird" />
    <allow-ignore-location-settings package="com.google.android.dialer" attributionTag="*" />

</config>

到这里解决方案呼之欲出了,我们新开一个小节单独总结下。

五、 总结

我们可以在/system/etc/sysconfig/下新增一个文件,这里我们可以命名(也可以定义为你想要的fashion的名字)为location-settings-conf.xml,

<?xml version="1.0" encoding="utf-8"?>
<config>
     <allow-ignore-location-settings package="com.android.package.one" attributionTag="*" />
</config>

元素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);
    }

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阅后即奋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值