Android 11.0 SettingsProvider 源码分析

文章目录

    • 一、SettingsProvider 的概述
    • 二、SettingsProvider 的启动流程
    • 三、对 SettingsProvider 进行操作方法
    • 四、客制化示例


一、SettingsProvider 的概述

SettingsProvider 是一个为 Android 系统设置提供数据共享的 Provider,它包含全局、安全和系统级别的用户偏好设置。并且,它是跟随 framework 一起编译,以apk的形式内置到系统的应用程序,所以源码的位置在:

frameworks\base\packages\SettingsProvider

数据分类:
SettingsProvider 对数据进行了分类,主要分为 Global、System 和 Secure 三种类型:

  • Global:全局偏好设置 ,对系统中所有用户公开;
  • System:系统偏好设置
  • Secure:安全偏好设置。

此外,为了方便对数据的操作,系统对 SettingsProvider 的一些接口进行封装处理。在 Settings 类中,分别声明了 Global、Secure、System 三个静态内部类,分别对应上述的三种数据类型。通过调用 这三个内部类中定义好的方法及属性,就可以对系统设置的数据进行相应的操作。 Settings 类的路径在:

frameworks\base\core\java\android\provider\Settings.java

下面是它的部分代码:

/*** The Settings provider contains global system-level device preferences.*/
public final class Settings {……	//内部类 Systempublic static final class System extends NameValueTable {private static final float DEFAULT_FONT_SCALE = 1.0f;public static String getString(ContentResolver resolver, String name) {return getStringForUser(resolver, name, resolver.getUserId());}public static String getStringForUser(ContentResolver resolver, String name,int userHandle) {if (MOVED_TO_SECURE.contains(name)) {Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"+ " to android.provider.Settings.Secure, returning read-only value.");return Secure.getStringForUser(resolver, name, userHandle);}if (MOVED_TO_GLOBAL.contains(name) || MOVED_TO_SECURE_THEN_GLOBAL.contains(name)) {Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"+ " to android.provider.Settings.Global, returning read-only value.");return Global.getStringForUser(resolver, name, userHandle);}return sNameValueCache.getStringForUser(resolver, name, userHandle);}public static boolean putString(ContentResolver resolver, String name, String value) {return putStringForUser(resolver, name, value, resolver.getUserId());}public static boolean putStringForUser(ContentResolver resolver, String name, String value,int userHandle) {if (MOVED_TO_SECURE.contains(name)) {Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"+ " to android.provider.Settings.Secure, value is unchanged.");return false;}if (MOVED_TO_GLOBAL.contains(name) || MOVED_TO_SECURE_THEN_GLOBAL.contains(name)) {Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"+ " to android.provider.Settings.Global, value is unchanged.");return false;}return sNameValueCache.putStringForUser(resolver, name, value, null, false, userHandle);}……}……//内部类 Securepublic static final class Secure extends NameValueTable {public static final Uri CONTENT_URI =Uri.parse("content://" + AUTHORITY + "/secure");@UnsupportedAppUsageprivate static final ContentProviderHolder sProviderHolder =new ContentProviderHolder(CONTENT_URI);……        }               ……//内部类 Globalpublic static final class Global extends NameValueTable {public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/global");public static final String NOTIFICATION_BUBBLES = "notification_bubbles";private static final Validator NOTIFICATION_BUBBLES_VALIDATOR = BOOLEAN_VALIDATOR;……}       

数据储存位置及方式:

Android 6.0(M) 以前:

/data/data/com.android.providers.settings/databases/settings.db

Android 6.0(M) 及其以后:

/data/system/users/ <user_id>/settings_system.xml
/data/system/users/ <user_id>/settings_global.xml
/data/system/users/<user_id>/settings_secure.xml

数据存储方式:SettingsProvider只接受 int、float 和 String 等类型的数据,且这些数据最终都会被转换为 String 类型,然后以键-值对的形式存放在对应的 xml 文件中。

改变存储方式的目的主要有以下三点:

  • 提高系统性能(400ms降低到10ms);
  • 为每一个用户独立储存偏好设置;
  • 限制了第三方应用对偏好设置的写入,增加了安全性。

此外,除了上述三个数据文件以外,在8.0以后,用户所安装的每个 APP (package) 都会产生一组独立的 ID (SSAID)和对应的描述,这些数据被存放在 “/data/system/users/<user_id>/settings_ssaid.xml"。下面是系统用户 0 存放上述 xml 文件的示例图 :

在这里插入图片描述

二、SettingsProvider 的启动流程

程序启动流程图:

img-blog.csdnimg.cn/d91f8c009f084dfc9c9f3324f1995430.png)

1、SettingsProvider 是一个系统 Provider,和其他系统 Provider 的启动流程一样,它会在 SystemServer 启动系统服务的过程中被安装进系统。以下是实现代码:

FilePath: frameworks/base/services/java/com/android/server/SystemServer.java

/*** Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.*/private void startOtherServices() {……traceBeginAndSlog("InstallSystemProviders");mActivityManagerService.installSystemProviders();// Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlagsSQLiteCompatibilityWalFlags.reset();traceEnd();……} 

上述代码中,在 SystemServer 启动系统服务的 startOtherServices() 方法中,通过调用 ActivityManagerService 的 installSystemProviders() 方法完成 SettingsProvider 的安装和创建。

2、ActivityManagerService 的 installSystemProviders() 方法。

FilePath: frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java


public class ActivityManagerService extends IActivityManager.Stubimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {……public final void installSystemProviders() {// 1、获取系统中所有的 Provider。这个过程最终会通过调用包管理模块 PackageManagerService 中的// queryContentProviders() 方法来获取所有的 ProviderList<ProviderInfo> providers;synchronized (this) {ProcessRecord app = mProcessList.mProcessNames.get("system", SYSTEM_UID);providers = generateApplicationProvidersLocked(app);if (providers != null) {for (int i=providers.size()-1; i>=0; i--) {ProviderInfo pi = (ProviderInfo)providers.get(i);if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {Slog.w(TAG, "Not installing system proc provider " + pi.name+ ": not system .apk");providers.remove(i);}}}}//2、调用 ActivityThread 的 installSystemProviders 方法完成系统 Provider 的安装启动,//其中就包括 SettingsProviderif (providers != null) {mSystemThread.installSystemProviders(providers);}synchronized (this) {mSystemProvidersInstalled = true;}mConstants.start(mContext.getContentResolver());mCoreSettingsObserver = new CoreSettingsObserver(this);mActivityTaskManager.installSystemProviders();mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();SettingsToPropertiesMapper.start(mContext.getContentResolver());mOomAdjuster.initSettings();// Now that the settings provider is published we can consider sending// in a rescue party.//3、处理 SettingsProvider 安装之前某些依赖程序RescueParty.onSettingsProviderPublished(mContext);//mUsageStatsService.monitorPackages();}……

其中,上述代码第二步调用到 ActivityThread 的 installSystemProviders() 方法涉及到比较复杂的处理逻辑,和 SettingsProvider 的启动流程关系不大,这里就不做分析了,但它最终是会调用到 SettingsProvider 中的 onCreate() 方法。

3、SettingsProvider 中的 onCreate() 方法。

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


@SuppressWarnings("deprecation")
public class SettingsProvider extends ContentProvider {
……@Overridepublic boolean onCreate() {Settings.setInSystemServer();// fail to boot if there're any backed up settings that don't have a non-null validatorensureAllBackedUpSystemSettingsHaveValidators();ensureAllBackedUpGlobalSettingsHaveValidators();ensureAllBackedUpSecureSettingsHaveValidators();synchronized (mLock) {mUserManager = UserManager.get(getContext());mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);mPackageManager = AppGlobals.getPackageManager();//1、创建 HandlerThread 对象,用来执行异步操作mHandlerThread = new HandlerThread(LOG_TAG,Process.THREAD_PRIORITY_BACKGROUND);mHandlerThread.start();mHandler = new Handler(mHandlerThread.getLooper());//2、创建 SettingsRegistry 对象mSettingsRegistry = new SettingsRegistry();}mHandler.post(() -> {//3、注册广播接收器registerBroadcastReceivers();startWatchingUserRestrictionChanges();});//4、向系统添加SettingsService、DeviceConfigService 服务ServiceManager.addService("settings", new SettingsService(this));ServiceManager.addService("device_config", new DeviceConfigService(this));return true;}
……

上述中的关键部分是创建 SettingsRegistry 对象,而 SettingsRegistry 是SettingsProvider 的内部类。

4、内部类 SettingsRegistry

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

final class SettingsRegistry {private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml";private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml";private static final String SETTINGS_FILE_SECURE = "settings_secure.xml";private static final String SETTINGS_FILE_SSAID = "settings_ssaid.xml";private static final String SETTINGS_FILE_CONFIG = "settings_config.xml";private static final String SSAID_USER_KEY = "userkey";private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();private GenerationRegistry mGenerationRegistry;private final Handler mHandler;private final BackupManager mBackupManager;private String mSettingsCreationBuildId;public SettingsRegistry() {mHandler = new MyHandler(getContext().getMainLooper());//1、创建 GenerationRegistry 对象,对xml文件的更改做版本管理mGenerationRegistry = new GenerationRegistry(mLock);//2、创建 BackupManager 对象,系统备份mBackupManager = new BackupManager(getContext());//3、迁移所有的系统设置数据migrateAllLegacySettingsIfNeeded();syncSsaidTableOnStart();}
……
}

在 SettingsRegistry 的构造方法中,注释1处创建了一个GenerationRegistry对象,GenerationRegistry对象的核心作用类似于对xml文件的更改做版本管理。
注释2处创建BackupManager对象,和系统备份有关。
注释3是迁移所有的系统设置数据,是重点方法。

5、migrateAllLegacySettingsIfNeeded()方法的分析:

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


private void migrateAllLegacySettingsIfNeeded() {synchronized (mLock) {final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);File globalFile = getSettingsFile(key);//1、判断"settings_global.xml"文件是否存在//存在,则不做数据迁移//不存在,则要做数据迁移//文件不存在的情况有:1.机器首次启动;2.恢复出厂设置后的首次开机;3.系统版本低于6.0if (SettingsState.stateFileExists(globalFile)) {return;}mSettingsCreationBuildId = Build.ID;final long identity = Binder.clearCallingIdentity();try {//2、获取系统中所有的用户(多用户,一般用户0)List<UserInfo> users = mUserManager.getUsers(true);final int userCount = users.size();for (int i = 0; i < userCount; i++) {final int userId = users.get(i).id;//3、创建数据库 settings.db 和数据表,然后将 系统默认设置 加载到数据表中(临时数据库)DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId);SQLiteDatabase database = dbHelper.getWritableDatabase();//4、生成xml文件,并迁移数据库数据到文件中migrateLegacySettingsForUserLocked(dbHelper, database, userId);// Upgrade to the latest version.UpgradeController upgrader = new UpgradeController(userId);upgrader.upgradeIfNeededLocked();// Drop from memory if not a running user.if (!mUserManager.isUserRunning(new UserHandle(userId))) {removeUserStateLocked(userId, false);}}} finally {Binder.restoreCallingIdentity(identity);}}}

注释1处先判断"/data/system/users/0/settings_global.xml"文件是否存在,如果不存在,migrateAllLegacySettingsIfNeed()方法会直接返回,即不做后续的数据迁移操作,而文件不存在的情况一般发生机器首次启动或恢复出厂设置后的第一次开机。如果存在,则对系统设置做数据迁移。
注释2处是获取系统中所有用户,并通过for循环遍历对每个用户执行数据迁移的过程。
注释3处通过 DatabaseHelper 类创建了数据库和数据表,并使用默认设置对数据库表数据初始化。此外,这里数据库是一个临时数据库。之所以创建这个临时数据库,是为了兼容 Android 6.0 以前的版本而设计。
注释4处的代码是为用户生成 xml 文件,并将数据库数据迁移到 xml 文件的核心代码。

6、数据库创建和初始化的核心:DatabaseHelper

构造方法 DatabaseHelper() 中执行了dbNameForUser() 方法,SQLiteOpenHelper 创建了数据库 settings.db 。
在 settings.db 数据库创建后,会回调onCreate() 方法,并在方法中创建了数据表。

FilePath: 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";// Please, please please. If you update the database version, check to make sure the// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'// is properly propagated through your change.  Not doing so will result in a loss of user// settings.//此处省略……//获取数据库名称/路径static String dbNameForUser(final int userHandle) {// The owner gets the unadorned db name;if (userHandle == UserHandle.USER_SYSTEM) {return DATABASE_NAME;} else {// Place the database in the user-specific data tree so that it's// cleaned up automatically when the user is deleted.File databaseFile = new File(Environment.getUserSystemDirectory(userHandle), DATABASE_NAME);// If databaseFile doesn't exist, database can be kept in memory. It's safe because the// database will be migrated and disposed of immediately after onCreate finishesif (!databaseFile.exists()) {Log.i(TAG, "No previous database file exists - running in in-memory mode");return null;}return databaseFile.getPath();}}public DatabaseHelper(Context context, int userHandle) {//创建数据库super(context, dbNameForUser(userHandle), null, DATABASE_VERSION);mContext = context;mUserHandle = userHandle;}//此处省略isValidTable()、 isInMemory()、dropDatabase()、backupDatabase()……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);");}@Overridepublic void onCreate(SQLiteDatabase db) {//1、创建 system 数据表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);");//2、创建 secure 数据表createSecureTable(db);//3、为用户0创建 global 数据表// Only create the global table for the singleton 'owner/system' userif (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 bookmarksboolean onlyCore = false;try {onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService("package")).isOnlyCoreApps();} catch (RemoteException e) {}if (!onlyCore) {loadBookmarks(db);}//4、将初始音量加载到DB中// Load initial volume levels into DBloadVolumeLevels(db);//5、将默认设置数据加载到数据表中// Load inital settings valuesloadSettings(db);}……

上述代码注释1、2、3处分别为用户创建system、secure和global三张数据表。此外,注释3处多了一个判断,这是因为 “settings_global.xml” 文件是所有用户共享的,所以只存储在0用户下。
注释4处的 loadVolumeLevels() 和 loadSettings() 方法是将默认数据填充数据表中。

以下是 loadSettings( ) 方法的内容:

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


private void loadSettings(SQLiteDatabase db) {//将三种数据类型的设置数据加载到数据表中loadSystemSettings(db);loadSecureSettings(db);// The global table only exists for the 'owner/system' userif (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 typeloadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0);// Set default hearing aidloadSetting(stmt, Settings.System.HEARING_AID, 0);// Set default tty modeloadSetting(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);loadIntegerSetting(stmt, Settings.System.SCREENSHOT_BUTTON_SHOW,R.integer.def_screenshot_button_show);/** IMPORTANT: Do not add any more upgrade steps here as the global,* secure, and system settings are no longer stored in a database* but are kept in memory and persisted to XML.** See: SettingsProvider.UpgradeController#onUpgradeLocked*/} finally {if (stmt != null) stmt.close();}}

在上述 loadSystemSettings() 方法中,加载了很多 defaults.xml 文件中定义的与系统设置相关的默认值。在此期间,这些默认值会被加载到相应数据表中。以下是 defaults.xml 文件的部分内容:

FilePath: frameworks/base/packages/SettingsProvider/res/values/defaults.xml


<?xml version="1.0" encoding="utf-8"?>
……
<resources><bool name="def_dim_screen">true</bool><integer name="def_screen_off_timeout">60000</integer><integer name="def_sleep_timeout">-1</integer><bool name="def_airplane_mode_on">false</bool><bool name="def_theater_mode_on">false</bool><!-- Comma-separated list of bluetooth, wifi, and cell. --><string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi,nfc,wimax</string><string name="airplane_mode_toggleable_radios" translatable="false">bluetooth,wifi,nfc</string><string name="def_bluetooth_disabled_profiles" translatable="false">0</string><bool name="def_auto_time">true</bool><bool name="def_auto_time_zone">true</bool><bool name="def_accelerometer_rotation">false</bool><!-- Default screen brightness, from 0 to 255.  102 is 40%. --><integer name="def_screen_brightness">102</integer><bool name="def_screen_brightness_automatic_mode">false</bool><fraction name="def_window_animation_scale">100%</fraction><fraction name="def_window_transition_scale">100%</fraction><bool name="def_haptic_feedback">true</bool><bool name="def_bluetooth_on">true</bool><bool name="def_wifi_display_on">false</bool><bool name="def_install_non_market_apps">false</bool><bool name="def_package_verifier_enable">true</bool><!-- 0 == off, 3 == on --><integer name="def_location_mode">3</integer><bool name="assisted_gps_enabled">true</bool><bool name="def_netstats_enabled">true</bool><bool name="def_usb_mass_storage_enabled">true</bool><bool name="def_wifi_on">false</bool><!-- 0 == never, 1 == only when plugged in, 2 == always --><integer name="def_wifi_sleep_policy">2</integer><bool name="def_wifi_wakeup_enabled">true</bool><bool name="def_networks_available_notification_on">true</bool><bool name="def_backup_enabled">false</bool><string name="def_backup_transport" translatable="false">com.android.localtransport/.LocalTransport</string><!-- Default value for whether or not to pulse the notification LED when there is apending notification --><bool name="def_notification_pulse">true</bool><bool name="def_mount_play_notification_snd">true</bool><bool name="def_mount_ums_autostart">false</bool><bool name="def_mount_ums_prompt">true</bool><bool name="def_mount_ums_notify_enabled">true</bool>……
<resources>

7、将 settings.db 数据库的数据迁移到 xml 文件中

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

private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,SQLiteDatabase database, int userId) {//1、迁移与 system 设置相关的数据// Move over the system settings.//1.1、生成与xml文件唯一对应的keyfinal int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);ensureSettingsStateLocked(systemKey);//1.2、将临时数据库中设置数据存放到 SettingsState 对象中SettingsState systemSettings = mSettingsStates.get(systemKey);migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);//1.3、把数据写入到xml文件中systemSettings.persistSyncLocked();//2、迁移与 secure 设置相关的数据// Move over the secure settings.// Do this after System settings, since this is the first thing we check when deciding// to skip over migration from db to xml for a secondary user.final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);ensureSettingsStateLocked(secureKey);SettingsState secureSettings = mSettingsStates.get(secureKey);migrateLegacySettingsLocked(secureSettings, database, TABLE_SECURE);ensureSecureSettingAndroidIdSetLocked(secureSettings);secureSettings.persistSyncLocked();//3、迁移与 global 设置相关的数据// Move over the global settings if owner.// Do this last, since this is the first thing we check when deciding// to skip over migration from db to xml for owner user.if (userId == UserHandle.USER_SYSTEM) {final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId);ensureSettingsStateLocked(globalKey);SettingsState globalSettings = mSettingsStates.get(globalKey);migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL);// If this was just createdif (mSettingsCreationBuildId != null) {globalSettings.insertSettingLocked(Settings.Global.DATABASE_CREATION_BUILDID,mSettingsCreationBuildId, null, true,SettingsState.SYSTEM_PACKAGE_NAME);}globalSettings.persistSyncLocked();}//4、删除数据库或备份数据库数据// Drop the database as now all is moved and persisted.if (DROP_DATABASE_ON_MIGRATION) {dbHelper.dropDatabase();} else {dbHelper.backupDatabase();}}

三、对 SettingsProvider 进行操作方法

1、操作方法

由于 framework下的 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);

2、权限限制

因为 SettingsProvider 是系统应用,所以对它的操作可能会有一些权限限制。首先查询 SettingsProvider 中的设置项数据是不需要声明任何权限,也就是说第三方应用也可以通过 Settings 类查询到 SettingsProvider 中的设置项数据,但是 Android 系统一般不允许第三方应用直接修改系统的设置项,需要用户授权才能修改,即修改设置项数据需要声明下权限:

  • android.permission.WRITE_SETTINGS
  • android.permission.WRITE_SECURE_SETTINGS

所以,一般可以直接修改系统设置的都是系统签名过的应用,也就是系统应用,如下图所示:

这里是引用

四、客制化示例

1、默认搜狗输入法

默认输入法属于安全设置, 设置项的数据类型是 Secure 类型。首先,在 defaults.xml 文件中并没有定义默认输入法设置项数据,所以只能在 DatabaseHelper 类里面的 loadSecureSettings() 添加相应方法可以进行修改,这个方法会在 loadSettings(SQLiteDatabase db) 方法被调用,然后将设置项数据加载到数据表中去。


@Deprecated
class DatabaseHelper extends SQLiteOpenHelper {private void loadSettings(SQLiteDatabase db) {loadSystemSettings(db);loadSecureSettings(db);// The global table only exists for the 'owner/system' userif (mUserHandle == UserHandle.USER_SYSTEM) {loadGlobalSettings(db);}}……private void loadSecureSettings(SQLiteDatabase db) {SQLiteStatement stmt = null;try {……
+				//启用搜狗输入法
+	            loadSetting(stmt, Settings.Secure.ENABLED_INPUT_METHODS, "com.sohu.inputmethod.sogou/.SogouIME");
+				//将搜狗输入法设置为默认输入法
+		        loadSetting(stmt, Settings.Secure.DEFAULT_INPUT_METHOD, "com.sohu.inputmethod.sogou/.SogouIME");/** IMPORTANT: Do not add any more upgrade steps here as the global,* secure, and system settings are no longer stored in a database* but are kept in memory and persisted to XML.** See: SettingsProvider.UpgradeController#onUpgradeLocked*/} finally {if (stmt != null) stmt.close();}}

获取输入法MID的命令:

adb shell ime list

2、自定义系统设置项开关

(1)在 Settings 应用相应的 xml 文件中定义一个开关控件 SwitchPreference,例如:


<SwitchPreferenceandroid:key="deep_sleep"android:title="@string/deep_sleep_title"settings:controller="com.android.settings.development.DeepSleepPreferenceController"/>

(2)定义开关的控制类,这个类需要继承一个抽象类TogglePreferenceController,然后重写 isChecked() 、setChecked()和 getAvailabilityStatus()方法,例如:


public class DeepSleepPreferenceController extends TogglePreferenceController{static final String DEEPSLEEP_ON = "1";static final String DEEPSLEEP_OFF = "0";private String mPreferenceKey = null;public DeepSleepPreferenceController(Context context, String preferenceKey) {super(context, preferenceKey);mPreferenceKey = preferenceKey;}//Synchronization switch status: set the previously saved switch status// when the user enters the interface where the switch is located again.@Overridepublic boolean isChecked() {boolean deepSleepFlag = false;if (DEEPSLEEP_ON.equals(Settings.Global.getString(mContext.getContentResolver(), mPreferenceKey))){deepSleepFlag = true;}return deepSleepFlag;}//When the user clicks the switch, the switch state is saved.@Overridepublic boolean setChecked(boolean isChecked) {final String newValue = isChecked ? DEEPSLEEP_ON : DEEPSLEEP_OFF;Settings.Global.putString(mContext.getContentResolver(), mPreferenceKey, newValue);return true;}@Overridepublic int getAvailabilityStatus() {//启用开关return AVAILABLE;}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/39418.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置WLAN 示例

规格 仅AR129CVW、AR129CGVW-L、AR109W、AR109GW-L、AR161W、AR161EW、AR161FGW-L、AR161FW、AR169FVW、AR169JFVW-4B4S、AR169JFVW-2S、AR169EGW-L、AR169EW、AR169FGW-L、AR169W-P-M9、AR1220EVW和AR301W支持WLAN-FAT AP功能。 组网需求 如图1所示&#xff0c;企业使用WLAN…

【拓展】理解AppID、OpenID、UnionID

目录 历史背景AppIDAppSecretOpenIDUnionID三者区别使用方法AppIDOpenID/**UnionID**拓展 历史背景 基本概念介绍 | 微信开放文档 微信小程序&#xff1a;一文彻底搞懂openid和unionid-腾讯云开发者社区-腾讯云 用户进行小程序登陆时&#xff0c;需要获取用户信息&#xff0c;…

通用的ERP系统功能清单有哪些?

一、通用的ERP系统功能清单 通用的ERP&#xff08;Enterprise Resource Planning&#xff0c;企业资源计划&#xff09;系统是一套集成的业务应用程序&#xff0c;旨在帮助企业有效管理财务、销售、运营等关键业务流程。以下是一个清晰的ERP系统功能清单&#xff0c;涵盖了其主…

【Flutter】列表流畅性优化

前言 在日常APP的开发中&#xff0c;列表是使用频率最高的&#xff0c;这里讲述在Flutter中优化列表的滑动速度与流畅度&#xff0c;以来提高用户的体验。 方案 1、使用ListView.builder代替ListView ListView.builder在创建列表的时候要比ListView更高效&#xff0c;因为L…

工程技术类SCI,低分快刊首选期刊,无版面费!

1、期刊概况 【期刊简介】IF&#xff1a;1.0-2.0&#xff0c;JCR2区&#xff0c;中科院4区&#xff1b; 【检索情况】SCIE在检 【版面类型】正刊&#xff0c;仅少量版面&#xff1b; 【出刊频率】年刊 2、征稿范围 本刊主要是发表有关能源转型和可再生能源需求相关的研究文…

Snappy使用

Snappy使用 Snappy是谷歌开源的压缩和解压的开发包&#xff0c;目标在于实现高速的压缩而不是最大的压缩 项目地址&#xff1a;GitHub - google/snappy&#xff1a;快速压缩器/解压缩器 Cmake版本升级 该项目需要比较新的cmake&#xff0c;CMake 3.16.3 or higher is requi…

一首歌的时间 写成永远

大家好&#xff0c;我是秋意零。 就在&#xff0c;2024年6月20日。我本科毕业了&#xff0c;之前专科毕业挺有感触&#xff0c;也写了一篇文章进行记录。如今又毕业了&#xff0c;还是写一篇文章记录吧&#xff01;&#xff01; 专科毕业总结&#xff1a;大学三年总结&#xf…

【SpringBoot3学习 | 第1篇】SpringBoot3介绍与配置文件

文章目录 前言 一. SpringBoot3介绍1.1 SpringBoot项目创建1. 创建Maven工程2. 添加依赖(springboot父工程依赖 , web启动器依赖)3. 编写启动引导类(springboot项目运行的入口)4. 编写处理器Controller5. 启动项目 1.2 项目理解1. 依赖不需要写版本原因2. 启动器(Starter)3. Sp…

二刷 动态规划

什么是动态规划 Dynamic Programming DP 如果某一问题有很多重叠子问题&#xff0c;使用动态规划时最有效的 动态规划中每一个状态是由上一个状态推导出来的。 动规五部曲 1.确定dp数组以及下标的含义 2.确定递归公式 3.dp数组如何初始化 4.确定遍历顺序 5.举例推导dp数…

【java计算机毕设】仓库管理系统 MySQL springboot vue3 Maven 项目源码代码

目录 1项目功能 2项目介绍 3项目地址 1项目功能 【java计算机毕设】仓库管理系统MySQL springboot vue3 Maven小组项目设计源代码 2项目介绍 系统功能&#xff1a; vue3仓库管理系统&#xff0c;主要功能包含&#xff1a;个人信息管理&#xff0c;仓库管理&#xff0c;员工…

java设计模式(七)适配器模式(Adapter Pattern)

1、模式介绍&#xff1a; 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将一个类的接口转换成客户希望的另外一个接口。适配器模式通常用于需要复用现有的类&#xff0c;但是接口与客户端的要求不完全匹配的情况。它包括两种形式&…

【深度学习】注意力机制

https://blog.csdn.net/weixin_43334693/article/details/130189238 https://blog.csdn.net/weixin_47936614/article/details/130466448 https://blog.csdn.net/qq_51320133/article/details/138305880 注意力机制&#xff1a;在处理信息的时候&#xff0c;会将注意力放在需要…

gitee项目上不同的项目分别使用不用的用户上传

最近使用根据需要&#xff0c;希望不同的项目使用不同的用户上传&#xff0c;让不同的仓库展示不同的用户名&#xff01;&#xff01;&#xff01; 第一步查看全局的用户信息&#xff1a; # 查看目前全局git配置信息 git config -l #会输出全局的git配置信息 第二步进入到要设…

大科技公司大量裁员背后的真相

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

冒泡排序、选择排序、菱形

冒泡排序、选择排序、菱形 文章目录 一、冒泡排序二、选择排序三、菱形 一、冒泡排序 思路&#xff1a; 外层&#xff08;第一层&#xff09;循环控制循环次数&#xff0c;和业务无关 内层&#xff08;第二层&#xff09;循环用于比较相邻的2个值的大小&#xff0c;根据小到大…

B站、小红书“崩”了!阿里云紧急回应

7月2日&#xff0c;“B站崩了”“小红书崩了”冲上微博热搜&#xff01;据悉&#xff0c;“崩了”的原因是阿里云上海服务出现异常。 B站App无法使用浏览历史关注等内容&#xff0c;消息界面、更新界面、客服界面均不可用&#xff0c;用户也无法评论和发弹幕&#xff0c;视频评…

鸿蒙开发设备管理:【@ohos.multimodalInput.touchEvent (触摸输入事件)】

触摸输入事件 设备上报的触屏事件。 说明&#xff1a; 本模块首批接口从API version 9开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import {Action,ToolType,SourceType,Touch,TouchEvent} from ohos.multimodalInput.touchEvent;…

2024年江西省研究生数学建模竞赛A题交通信号灯管理论文和代码分析

经过不懈的努力&#xff0c;2024年江西省研究生数学建模竞赛A题论文和代码已完成&#xff0c;代码为A题全部问题的代码&#xff0c;论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求解&#xff08;问题1模型的建立和求解、问题2模型的建立和求解、问题3模…

养猫家庭如何正确除猫毛?希喂、范罗士、米家浮毛空气净化器分享

作为一名6年资深铲屎官&#xff0c;我发现养猫已经成为年轻人的潮流&#xff0c;很多铲屎官跟风养了猫咪。但是她们并不知道撸猫虽然很快乐&#xff0c;但是猫咪的体味和猫浮毛也会让人很头疼。特别是宠物排泄物的气味&#xff0c;经常搞卫生很多人接受不了。这样导致很多人养一…

贪心算法算法,完全零基础小白教程,不是计算机的都能学会!超详解

目录 一、基本概念 二、举几个例子&#xff0c;便于理解 1、找零问题 2、最小路径和 3、背包问题 1&#xff09;只考虑体积的贪心策略&#xff1a; 2&#xff09; 只考虑价值的贪心策略&#xff1a; 三、贪心策略的特点 四、贪心策略证明 四、如何学习贪心 五、例题…