1. 基本介绍
SettingsProvider是一个提供设置数据共享的Provider,可以简单理解为数据库, 它包含全局性、系统级别的用户编好设置,例如在手机中的Settings应用,用户可以在Settings里面做很多设备的设置,这些用户偏好的设置很多就保存在SettingsProvider中。例如,飞行模式。
-
- SettingProvider作用及跟其他存储属性机制的区别
- SettingProvider作用
- SettingProvider作用及跟其他存储属性机制的区别
SettingProvider最主要的作用是提供跨进程数据访问,给应用之间数据共享提供便利。SettingsProvider是系统设置的内容提供者。SettingsProvider继承ContentProvider,ContentProvider在android中主要扮演着数据共享的角色。
在M(Android5.0)版本之前,SettingsProvider中系统设置是存储在settings.db数据库中;但是在L(Android6.0)之后,SettingsProvider中系统设置改为由xml存储在data分区。
主要源码:SettingsProvider的代码主要包含如下的java文件:
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
frameworks/base/core/java/android/provider/Settings.java
1.1.2 SettingsProvider和Android系统其它Provider的不同之处
- SettingsProvider只接受int、float、string等基本类型的数据;
- SettingsProvider由Android系统framework进行了封装,使用更加快捷方便
- SettingsProvider的数据由键值对组成
1.1.3 SettingsProvider和SystemProperties的不同之处
SettingsProvider有点类似Android的properties系统(Android属性系统):SystemProperties。SystemProperties除具有SettingsProvider以上的三个特性,SettingsProvider和SystemProperties的不同点在于:
- 数据保存方式不同
SystemProperties的数据保存属性文件中(/system/build.prop等),开机后会被加载到system properties store;SettingsProvider的数据保存在文件/data/system/users/0/settings_***.xml和数据库settings.db中;
- 作用范围不同
SystemProperties可以实现跨进程、跨层次调用,即底层的c/c++可以调用,java层也可以调用;SettingProvider只能能在java层(APP)使用;
- 公开程度不同
SettingProvider有部分功能上层第三方APP可以使用,SystemProperties上层第三方APP不可以使用。
1.2 数据分类
1.2.1 三种数据类型
SettingsProvider对数据进行了分类,分别是Global、System、Secure三种类型,它们的区别如下:
- Global:所有的偏好设置对系统的所有用户公开,第三方APP有读没有写的权限;
- System:包含各种各样的用户偏好系统设置;
- Secure:安全性的用户偏好系统设置,第三方APP有读没有写的权限
每种类型具体保存的偏好设置可以在设备如下的目录文件中查看详细的信息:
/data/system/users/0/settings_global.xml
/data/system/users/0/settings_system.xml
/data/system/users/0/settings_secure.xml
1.2.2 三张表的生成过程
在SettingsProvider初始化的过程中生成三张表, frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java文件主要的代码getSettingsFile()方法获取到一个File对象的实例。
对Global、System、Secure分别生成一个File对象实例,它们的File对象分别对应的文件是:
- 在Android R的代码中,会有生成settings_config.xml文件相关的代码,但是在实际的设备中并没有该文件的存在。
- /data/system/users/0/settings_global.xml
- /data/system/users/0/settings_system.xml
- /data/system/users/0/settings_secure.xml
- 在8.0以后,每位使用者所安装的每个 APP (package) 都会产生一组独立的 ID (SSAID),并将描述档案放置 “/data/system/users/0/settings_ssaid.xml"。
2. 启动流程
2.1 SettingProvider进程启动流程如下:
SystemServer开始运行时,先启动系统关键Server,例如AvtivityManagerService,最后启动otherService,这是会调用AMS中installSystemProvider方法,安装系统级别的provider,SettingProvider就是其中一个。
Provider启动后,处理SettingsProvider安装之前某些依赖救援程序相关逻辑。最终通过调用ActivityThread#installSystemProviders会调用到SettingProvider的onCreate函数,和打开一个Activity类似,会回调ContentProvider的生命周期方法
2.2 SettingProvider初始化流程
初始化流程主要包括如下三点:
1.在初始化中,如果发现xml文件不存在,那就需要将数据从数据库中迁移到xml,如果xml已经存在了,那就不用做迁移了。数据迁移方法migrateAllLegacySettingsIfNeeded(),该处理只有在设备第一次开机xml文件不存在时候才会做迁移,将数据库总的数据都迁移到xml文件中来。
2.设置广播监听用户id的停止和移除,应用的删除,以便及时更新xml中的数据。startWatchingUserRestrictionChanges()方法。
3.监听部分用户相关设置的变更。
三份数据global/system/secrue是以SettingsState为数据载体,保存在一个列表中mSettingsStates:
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
而SettingsState中的数据又是以内部类Setting为载体,保存在列表mSettings中
private final ArrayMap<String, Setting> mSettings = new ArrayMap<>();
同步数据就是将mSettings中的数据覆写到xml文件中一遍。
3. 操作SettingsProvider
Settings大部分操作的就是SettingsProvider中的数值,也有一些直接操作系统属性的等等。当用户在修改系统设置时,大部分实际上是在修改SettingsProvider中的值。当SettingsProvider数据库中的值被改变时,一些系统服务什么的就会监听到,这时候就会通过jni等当时操作底层,从而达到系统属性或配置改变的效果。如右图所示
由于SetttingsProvider是向整个Android系统提供用户偏好设置的提供程序,在所保存的数据类型和方式上也有一定的规定和约束,为能够保证在整个Android的Java层任意一个地方里面能够方便,快捷的使用SettingsProvider进行数据查询,数据插入,数据更新,所以在framework的provider里面有一个类Settings.java对使用SettingsProvider进行了封装。
SettingsProvider关键数据组织参见上图,在SettingsProvider中持有一个内部类SettingsRegistry的引用m_SettingsRegistry, m_SettingsRegistry通过一个稀疏数组间接持有了系统中所有用户偏好设置数据。稀疏数组m_SettingsStates的value类型是SettingsState类,Key是由偏好类型[Global|System|Secure]和Userid通过计算得出,计算规则在后面给出,SettingsState类的一个实例和上文介绍的某个xml文件关联(内存中的xml文件数据表示)。在SettingsState类中通过ArrayMap持有n个内部类setting的实例,n的值取决于xml文件中item的数量,通过用户偏好设置name可以从mSettings中取出某项具体的用户设置数据。setting类关联到某项具体的用户设置数据。
3.1 查询和插入数据实例
查询数据以查询Device name为例:
从源码packages/apps/Settings中查到设备名称的key为device_name
只需要一行代码即可查询到device_name的值。
mDeviceName = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DEVICE_NAME);
插入数据,以设置Device name为例
也只需要一行代码如下:
private void setSettingsGlobalDeviceName(String deviceName) {
Settings.Global.putString(mContext.getContentResolver(), Settings.Global.DEVICE_NAME, deviceName);
}
从以上源码中可以看出查询和插入数据的方法很简单。
深入Settings.java看getString()方法直接调用了getStringForUser()方法:
public static String getStringForUser(ContentResolver resolver, String name, int userHandle) {
……
return sNameValueCache.getStringForUser(resolver, name, userHandle);
}
在settings.java代码创建了三个静态内部类,System,Secure,Global分别对应SettingsProvider中的System,Secure,Global三种数据类型,Global、Secure、System三个静态内部类会分别持有自己NameValueCache的实例变量,每个NameValueCache持有指向SettingsProvider中的SettingsProvider.java的AIDL远程调用IContentProvider,因此,查询数据需要经过NameValueCache的getStringForUser()方法,插入数据需要经过putStringForUser()方法。同时,NameValueCache还持有一个变量mValues,用于保存查询过的设置项,以便下次再次发起查询时,能够快速返回。
数据的查询过程主要分为如下几点:
1.首先检测用户id是否正确,如果不正确是不能够获取数据的
2.查看mValues中是否已经存在了,存在了就直接将数据返回
3.获取contentprovider
4.如果mCallGetCommand命令不为空,则调用call命令来获取数据,获取到之后将其加入到mValues中,然后返回数据
5.获取命令为空就调用query方法获取数据,将其加入到mValues中,然后返回数据
插入数据的过程:
1.直接调用call方法传入命令和数据
2.修改对应SettingsState中的缓存数据mSettings列表,然后跟新xml文件
3.数据修改成功后contentResolver的notifyChange方法发出数据已被修改通知。
我们在调用android.provider.Settings修改一些设置时,Settings会调用真正的SettingsProvider去访问数据库。
3.2 第三方app使用SettingsProvider
第三方APP可以通过framework的Settings.java查询SettingsProvider中的设置项,3.1节中有具体方法getString()。
第三APP是否可以修改SettingsProvider的设置项?Android系统不允许第三方APP修改SettingsProvider中的设置项。
3.3 权限问题
查询SettingsProvider的设置项不需要声明任何权限。
修改SettingsProvider需要权限:
- android.permission.WRITE_SETTINGS,Protection level: signature
- Secure数据:android.permission.WRITE_SECURE_SETTINGS,Not for use by third-party applications.
- 对于Global和Secure模块,还需要关心上文中的isGlobalOrSecureSettingRestrictedForUser()方法设置到的限制。
4. 常用调试方法
1. 如何修改系统默认的一些设置配置值? 在如下xml文件中修改或添加设置的默认值
frameworks/base/packages/SettingsProvider/res/values/defaults.xml
vendor/mediatek/proprietary/packages/apps/SettingsProvider/res/values/defaults.xml
vendor/mediatek/proprietary/packages/apps/SettingsProvider/res/values/mtk_defaults.xml
注意:在defauls.xml设置的默认值,究竟是会存入settings_global.xml, settings_secure.xml, settings_system.xml 表,是由Settings.java中的class System, Global, Secure 类中管理添加的字段有关,比如MOVED_TO_GLOBAL, MOVED_TO_SECURE
2. 通过ADB修改
settings put system [key] [value] settings get system [key]
settings put global [key] [value] settings get global [key]
settings put secure [key] [value] settings get secure [key]
3. 代码中如何修改
@Settings.java (frameworks\base\core\java\android\provider)
比如:
Settings.System.putInt(mContext.getContentResolver(), name, value);
int value = Settings.System.getInt(mContext.getContentResolver(), name);
Settings.Global.putInt(...)
Settings.Secure.putInt(...)