(1)概述
(A)
SettingsProvider顾名思义是一个提供设置数据共享的Provider,SettingsProvider和Android系统其它Provider有很多不一样的地方,如:
- SettingsProvider只接受int、float、string等基本类型的数据;
- SettingsProvider由Android系统framework进行了封装,使用更加快捷方便;
- SettingsProvider的数据由键值对组成;
(B)
SettingsProvider有点类似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)使用;
在Android 6.0版本时,SettingsProvider被重构,Android从性能、安全等方面考虑,把SettingsProvider中原本保存在settings.db中的数据,目前全部保存在XML文件中。
(C)
SettingsProvider对数据进行了分类,分别是Global、System、Secure三种类型,它们的区别如下:
- Global:所有的偏好设置对系统的所有用户公开,第三方APP有读没有写的权限;
- System:包含各种各样的用户偏好系统设置;
- Secure:安全性的用户偏好系统设置,第三方APP有读没有写的权限;
上面对Global、System、Secure分别生成一个File对象实例,它们的File对象分别对应的文件是:
/data/system/users/0/settings_global.xml /data/system/users/0/settings_system.xml /data/system/users/0/settings_secure.xml
也就是说,Global类型的数据保存在文件settings_global.xml中,System类型的数据保存在文件settings_system.xml中,Secure类型的数据保存在文件settings_secure.xml中。
(2)AndroidManifest.xml
//frameworks/base/packages/SettingsProvider/AndroidManifest.xml
由上面Manifest配置的sharedUserId可知,SettingsProvider运行在系统进程中,定义的ContentProvider实现类是SettingsProvider。
(3)Code位置
在frameworks中跟Setting默认值相关的几个文件:
- /frameworks/base/packages/SettingsProvider/res/values/defaults.xml
- /frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
- /frameworks/base/core/java/android/provider/Settings.java
(4)defaults.xml
在defaults.xml文件中定义了相关的值,DatabaseHelper.java会把相应的值读取出来保存到ContentProvider。
//frameworks/base/packages/SettingsProvider/res/values/defaults.xmltrue 60000 -1 false false cell,bluetooth,wifi,nfc,wimax bluetooth,wifi,nfc 0 true true false 102 false 100% 100% true true false false 3 true true true false 2 true true false com.android.localtransport/.LocalTransport true true false true true 1 /product/media/audio/ui/LowBattery.ogg 0 0 /product/media/audio/ui/Dock.ogg /product/media/audio/ui/Undock.ogg /product/media/audio/ui/Dock.ogg /product/media/audio/ui/Undock.ogg 1 /product/media/audio/ui/Lock.ogg /product/media/audio/ui/Unlock.ogg /product/media/audio/ui/Trusted.ogg /product/media/audio/ui/WirelessChargingStarted.ogg /product/media/audio/ui/ChargingStarted.ogg 1000 15000 false false 1 true true true true false 200% false true 0 -1 -1 400 300 false 0 true true false 9 false 0 0 1 true 1 %1$s %2$s %1$s true true false 0x2 false true 0 false false true true true false false false false false
(5)DatabaseHelper.java
//frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "SettingsProvider";
private static final String DATAbase_NAME = "settings.db";
private static final int DATAbase_VERSION = 118;
private static final String DATAbase_BACKUP_SUFFIX = "-backup";
private static final String TABLE_SYSTEM = "system";
private static final String TABLE_SECURE = "secure";
private static final String TABLE_GLOBAL = "global";
private void createSecureTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE secure (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT UNIQUE ON ConFLICT REPLACE," +
"value TEXT" +
");");
db.execSQL("CREATE INDEX secureIndex1 ON secure (name);");
}
private void createGlobalTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE global (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT UNIQUE ON ConFLICT REPLACE," +
"value TEXT" +
");");
db.execSQL("CREATE INDEX globalIndex1 ON global (name);");
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE system (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT UNIQUE ON ConFLICT REPLACE," +
"value TEXT" +
");");
db.execSQL("CREATE INDEX systemIndex1 ON system (name);");
createSecureTable(db);
// only create the global table for the singleton 'owner/system' user
if (mUserHandle == UserHandle.USER_SYSTEM) {
createGlobalTable(db);
}
db.execSQL("CREATE TABLE bluetooth_devices (" +
"_id INTEGER PRIMARY KEY," +
"name TEXT," +
"addr TEXT," +
"channel INTEGER," +
"type INTEGER" +
");");
db.execSQL("CREATE TABLE bookmarks (" +
"_id INTEGER PRIMARY KEY," +
"title TEXT," +
"folder TEXT," +
"intent TEXT," +
"shortcut INTEGER," +
"ordering INTEGER" +
");");
db.execSQL("CREATE INDEX bookmarksIndex1 ON bookmarks (folder);");
db.execSQL("CREATE INDEX bookmarksIndex2 ON bookmarks (shortcut);");
// Populate bookmarks table with initial bookmarks
boolean onlyCore = false;
try {
onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService(
"package")).isOnlyCoreApps();
} catch (RemoteException e) {
}
if (!onlyCore) {
loadBookmarks(db);
}
// Load initial volume levels into DB
loadVolumeLevels(db);
// Load inital settings values
loadSettings(db);
}
//...
private void loadSettings(SQLiteDatabase db) {
loadSystemSettings(db);
loadSecureSettings(db);
// The global table only exists for the 'owner/system' user
if (mUserHandle == UserHandle.USER_SYSTEM) {
loadGlobalSettings(db);
}
}
private void loadSystemSettings(SQLiteDatabase db) {
SQLiteStatement stmt = null;
try {
stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ " VALUES(?,?);");
loadBooleanSetting(stmt, Settings.System.DIM_SCREEN,
R.bool.def_dim_screen);
loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
R.integer.def_screen_off_timeout);
// Set default cdma DTMF type
loadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0);
// Set default hearing aid
loadSetting(stmt, Settings.System.HEARING_AID, 0);
// Set default tty mode
loadSetting(stmt, Settings.System.TTY_MODE, 0);
loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
R.integer.def_screen_brightness);
loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
com.android.internal.R.integer.config_screenBrightnessForVrSettingDefault);
loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE,
R.bool.def_screen_brightness_automatic_mode);
loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION,
R.bool.def_accelerometer_rotation);
loadDefaultHapticSettings(stmt);
loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
R.bool.def_notification_pulse);
loadUISoundEffectsSettings(stmt);
loadIntegerSetting(stmt, Settings.System.POINTER_SPEED,
R.integer.def_pointer_speed);
} finally {
if (stmt != null) stmt.close();
}
}
}
在DatabaseHelper.java的方法中用于加载defaults.xml定义的相关字段。
(6)封装SettingsProvider接口(Settings.java)
对ContentProvider的一些接口进行封装,以保证在整个Android的java层任何一个地方都能方便、快捷的使用SettingsProvider进行数据查询,数据更新和数据插入。所以,framework有一个类Settings.java对SettingsProvider进行了封装。如下:
//frameworks/base/core/java/android/provider/Settings.java
public final class Settings {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SETTINGS = "android.settings.SETTINGS";
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_USER_SETTINGS =
"android.settings.USER_SETTINGS";
//...
public static final class Global extends NamevalueTable {
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/global");
public static final String AIRPLANE_MODE_ON = "airplane_mode_on";
public static final String THEATER_MODE_ON = "theater_mode_on";
//...
}
public static final class System extends NamevalueTable {
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/system");
}
public static final class Secure extends NamevalueTable {
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/secure");
}
上面的代码中,分别声明了Global、Secure、System三个静态内部类,分别对应SettingsProvider中的Global、Secure、System三种数据类型。Global、Secure、System三个静态内部类会分别持有NamevalueCache的实例变量,进而通过AIDL远程调用IContentProvider。
查询数据需要经过NamevalueCache的getStringForUser()方法,插入数据需要经过putStringForUser()方法。
(7)操作SettingsProvider
由于Settings.java对SettingsProvider进行了封装,所以,使用起来相当简单简洁。Global、Secure、System三种数据类型的使用是几乎相同。
//查询数据 String globalValue = Settings.Global.getString(getContentResolver(), Settings.Global.AIRPLANE_MODE_ON); //插入数据 boolean isSuccess = Settings.System.putInt(getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
(8)第三方APP使用SettingsProvider
第三方APP可以通过framework的Settings.java查询SettingsProvider中的设置项,第三APP是否可以修改SettingsProvider的设置项?Android系统不允许第三方APP修改SettingsProvider中的设置项。
查阅SettingsProvider的设置项不需要声明任何权限。
修改SettingsProvider需要权限:
- android.permission.WRITE_SETTINGS
- android.permission.WRITE_SECURE_SETTINGS



