一篇文章学会SettingsProvider

1,133 阅读7分钟

什么是SettingsProvider?

首先来看下类的描述

This class is a content provider that publishes the system settings.It can be accessed via the content provider APIs or via custom call commands. The latter is a bit faster and is the preferred way to access the platform settings.(google翻译:此类是发布系统设置的内容提供者。它可以通过内容提供者 API 或通过自定义调用命令访问。后者更快一些,并且是访问平台设置的首选方式。)

再来看下类的继承关系

public class SettingsProvider extends ContentProvider {

从源码可以看到,SettingsProvider继承于ContentProvider,ContentProvider的作用是为不同的应用之间数据共享,提供统一的接口。

不难看出,SettingsProvider是用于增删查改系统设置项。

怎么使用SettingsProvider?

核心代码

frameworks/base/core/java/android/provider/Settings.java封装了三个关键内部类System、Global和Secure。这三个内部类中都定义了原生的系统设置项和SettingsProvider的操作的接口。

public static final class System extends NameValueTable {
    public static String getString(ContentResolver resolver, String name) {
        return getStringForUser(resolver, name, resolver.getUserId());
    }
    ...
    public static boolean putString(ContentResolver resolver, String name, String value) {
        return putStringForUser(resolver, name, value, resolver.getUserId());
    }
    ...
    public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
    ...
}

上方截取了部分System内部类的代码,putString方法用于保存,getString用于查询,Screen_OFF_TIMEOUT常量则表示设备自动灭屏时间(单位ms)。

接受类型

从System、Global和Secure的接口定义可以看出:

只接受三种类型int,float和String,int和float最终都转换成String向下传递。

使用方法

代码使用

System、Global和Secure的用法相近;int、float和String的用法相近。下面只使用System对int类型的操作进行示范。

Settings封装接口

ContentResolver cr = getContentResolver();
//往System表的screen_off_timeout字段写入60000
Settings.System.putInt(cr, Settings.System.SCREEN_OFF_TIMEOUT, 60000);
//读取System表中screen_off_timeout的值,由于没有缺省值,需要捕获Settings.SettingNotFoundException
Settings.System.getInt(cr, Settings.System.SCREEN_OFF_TIMEOUT);
//读取System表中screen_off_timeout的值,如果值为空,则返回30000
Settings.System.getInt(cr, Settings.System.SCREEN_OFF_TIMEOUT, 30000);

ContentProvider

SettingsProvider继承于ContentProvider,所以也能通过ContentResolver去查询。

查询
Cursor cursor = getContentResolver().query(Settings.System.getUriFor(Settings.System.SCREEN_OFF_TIMEOUT), null, null, null, null);
if(cursor != null){
    while (cursor.moveToNext()) {
        Log.d(TAG, "value:" + cursor.getInt(cursor.getColumnIndex("value")));
    }
    cursor.close();
}

监听变化
ContentObserver observer = new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        //感知Settings.System.SCREEN_OFF_TIMEOUT对应的值的变化
    }
};
getContentResolver().registerContentObserver(Settings.System.getUriFor(Settings.System.SCREEN_OFF_TIMEOUT), true,
                mBrightnessModeObserver);

adb使用

Settings封装接口

qinbin@qinbindeMacBook-Pro StudyRecyclerView % adb shell settings -h

Settings provider (settings) commands:

help

Print this help text.

get [--user <USER_ID> | current] NAMESPACE KEY

Retrieve the current value of KEY.

put [--user <USER_ID> | current] NAMESPACE KEY VALUE [TAG] [default]

Change the contents of KEY to VALUE.

TAG to associate with the setting.

{default} to set as the default, case-insensitive only for global/secure namespace

delete [--user <USER_ID> | current] NAMESPACE KEY

Delete the entry for KEY.

reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}

Reset the global/secure table for a package with mode.

RESET_MODE is one of {untrusted_defaults, untrusted_clear, trusted_defaults}, case-insensitive

list [--user <USER_ID> | current] NAMESPACE

Print all defined keys.

NAMESPACE is one of {system, secure, global}, case-insensitive

常用的就是get和put

adb shell settings get system screen_off_timeout

adb shell settings put system screen_off_timeout 60000

adb命令支持指定user

adb shell settings get --user 0 system screen_off_timeout

adb shell settings put --user 1 system screen_off_timeout 60000

ContentProvider

adb shell content query --uri content://settings/system

System、Global和Secure有什么差异?

类注释的翻译

  • System:系统设置,包含各种系统首选项。 该表包含简单的名称/值对。 有访问各个设置条目的便利功能。
  • Global:全局系统设置,包含始终适用于所有已定义用户的首选项。 应用程序可以读取这些但不允许写入; 与“安全”设置一样,这些设置用于用户必须通过系统 UI 或针对这些值的专用 API 显式修改的首选项。
  • Secure:安全系统设置,包含应用程序可以读取但不允许写入的系统首选项。 这些是用户必须通过系统应用程序的 UI 显式修改的首选项。 普通应用程序无法直接或通过调用此类包含的“放置”方法来修改安全设置数据库。

数据存放位置

/data/system/users/<userId>/settings_system.xml

/data/system/users/<userId>/settings_secure.xml

/data/system/users/0/settings_global.xml

主用户userId为0,global不区分user,所以只有在主用户目录下存在global表。

Global不区分用户,所有用户都共享一份。

权限

读取权限

读取权限不做限制。

写入权限

权限定义

权限定义路径:frameworks/base/core/res/AndroidManifest.xml

System对应的写入权限:"android.permission.WRITE_SETTINGS"

Global和Secure对应的写入权限:"android.permission.WRITE_SECURE_SETTINGS"

    <!-- Allows an application to read or write the system settings.

        <p class="note"><strong>Note:</strong> If the app targets API level 23
        or higher, the app user
        must explicitly grant this permission to the app through a permission management screen.
        The app requests the user's approval by sending an intent with action
        {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}. The app
        can check whether it has this authorization by calling {@link
        android.provider.Settings.System#canWrite Settings.System.canWrite()}.

        <p>Protection level: signature|preinstalled|appop|pre23
    -->
    <permission android:name="android.permission.WRITE_SETTINGS"
        android:label="@string/permlab_writeSettings"
        android:description="@string/permdesc_writeSettings"
        android:protectionLevel="signature|preinstalled|appop|pre23|role" />
        
    <!-- Allows an application to read or write the secure system settings.
    <p>Not for use by third-party applications. -->
    <permission android:name="android.permission.WRITE_SECURE_SETTINGS"
        android:protectionLevel="signature|privileged|development|role|installer" />

权限类型

下表列出了所有基本权限类型。如需查看标志列表,请参阅 protectionLevel

含义
“normal”默认值。具有较低风险的权限,此类权限允许请求授权的应用访问隔离的应用级功能,对其他应用、系统或用户的风险非常小。 系统会自动向在安装时请求授权的应用授予此类权限,无需征得用户的明确许可(但用户始终可以选择在安装之前查看这些权限)。
“dangerous”具有较高风险的权限,此类权限允许请求授权的应用访问用户私人数据或获取可对用户造成不利影响的设备控制权。由于此类权限会带来潜在风险,因此系统可能不会自动向请求授权的应用授予此类权限。例如,应用请求的任何危险权限都可能会向用户显示并且获得确认才会继续执行操作,或者系统会采取一些其他方法来避免用户自动允许使用此类功能。
“signature”只有在请求授权的应用使用与声明权限的应用相同的证书进行签名时系统才会授予的权限。如果证书匹配,则系统会在不通知用户或征得用户明确许可的情况下自动授予权限。
“signatureOrSystem”"signature|privileged" 的旧同义词。在 API 级别 23 中已弃用。系统仅向位于 Android 系统映像的专用文件夹中的应用或使用与声明权限的应用相同的证书进行签名的应用授予的权限。不要使用此选项,因为 signature 保护级别应足以满足大多数需求,无论应用安装在何处,该保护级别都能正常发挥作用。“signatureOrSystem”权限适用于以下特殊情况:多个供应商将应用内置到一个系统映像中,并且需要明确共享特定功能,因为这些功能是一起构建的。

总结:

1.Global和Secure最严格,需要框架或系统应用才能写入。

2.System最宽松,框架和系统应用可以写入的同时,第三方应用具有platform签名并申请对应权限也能写入。

如何新增一个系统设置项?

类型确认

首先,我们需要确认系统设置项的类型。比如是全局生效的需要定义在Global类中,安全级别较高的需要定义在Secure中,其余的放到System。

新增字段

Flyme在原生frameworks/base/core/java/android/provider/Settings.java类基础上拓展了frameworks/base/core/java/android/provider/MzSettings.java。在AOSP基础上新增的字段都需要放到该类中。

示例代码:

public final class MzSettings {
    public static final class Global {
        /**
         * 自适应保持亮屏设置项
         * value : 0  disable
         *         1  enable
         * @hide
         */
        public static final String ADAPTIVE_KEEP_BRIGHT_SCREEN = "adaptive_keep_bright_screen";
    }
}

配置默认值

AOSP的系统设置项默认值配置:

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java

Flyme在DatabaseHelper.java的基础上拓展了frameworks/base/packages/SettingsProvider/src/com/meizu/utils/MzProvidersUtils.java。在AOSP基础上新增字段的默认值配置都放到该类中。

示例代码:

public class MzProvidersUtils {
    public void loadGlobalSettings(SQLiteDatabase db) {
        SQLiteStatement stmt = null;
        try {
            stmt = db.compileStatement("INSERT OR REPLACE INTO global(name,value)"
                    + " VALUES(?,?);");
            loadIntegerSetting(stmt, MzSettings.Global.ADAPTIVE_KEEP_BRIGHT_SCREEN, R.integer.def_adaptive_keep_bright_screen);
        }
    }
    
    private void loadIntegerSetting(SQLiteStatement stmt, String key, int resid) {
        loadSetting(stmt, key, Integer.toString(mRes.getInteger(resid)));
    }
    
    private void loadSetting(SQLiteStatement stmt, String key, Object value) {
        stmt.bindString(1, key);
        stmt.bindString(2, value.toString());
        stmt.execute();
    }
}

默认值定义

原生默认值定义路径:frameworks/base/packages/SettingsProvider/res/values/defaults.xml

Flyme新增默认值定义路径:

frameworks/base/packages/SettingsProvider/res_mz/values/defaults.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="def_adaptive_keep_bright_screen" translatable="false">1</integer>
</resources>

frameworks/base/packages/SettingsProvider/Android.bp中定义了

resource_dirs: ["res", "res_mz",]

res_mz在后,res_mz会覆盖res的同名资源。如果如果需要修改aosp原有的默认值,需要通过在res_mz新增同名默认值去overlay。

如果默认值需要根据机型做差异化。(例如有的机型最高刷新率为120,有的则是90)

需要在device/meizu/common/overlay目录下根据机型overlay原有目录下的资源。