android-framework-SettingsProvider

一、SettingsProvider简介

        SettingsProvider由Android系统框架提供,包含全局、系统级别的用户偏好设置,系统中的setting应用和它存在十分紧密的关系。SettingsProvider作为一个系统apk,随框架一起编译,在目录树种的位置:"frameworks\base\packages\SettingsProvider"。

        为了方便使用,系统对SettingsProvider做了封装处理,封装的代码“frameworks\base\core\java\android\provider\Settings.java”,所以用户调用Settings中的方法就能很轻易的访问SettinsProvider。SettinsProvider和其他系统Provider一样,在SystemServer启动Services时,调用ActivityManagerService#installSystemProviders创建启动。

二、SettingsProvider 的功能和用途

        SettingsProvider 为获取和管理系统的应用设置信息提供了一个标准化的方式,通常包括了功能和用途细分如下:

2.1. 系统级设置管理

        系统级设置包括各种基础的系统设置, 比如Wi-Fi设置、蓝牙设置、语言与输入设置、日期与时间等,所有应用程序和用户都能够使用这些设置。这些设置还可以在应用程序中使用 Preference API 进行存储和读取。以下是获取并编辑 Wi-Fi 设置的示例代码:

Uri wifiUri = Settings.System.getUriFor(Settings.System.WIFI_SLEEP_POLICY);

ContentResolver cr = getContentResolver(); cr.registerContentObserver(wifiUri, false, wifiObserver);

Settings.System.putInt(cr,Settings.System.WIFI_SLEEP_POLICY,Settings.System.WIFI_SLEEP_POLICY_NEVER);

2.2. 应用级设置管理

        应用级设置允许应用程序将其特定设置保存在数据库中,然后在应用程序中进行读取和管理。这些设置通常是针对单个应用程序,其他应用程序和系统无法访问。以下是所述存储在数据库中的内容的示例:

public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/accounts");

public static final String ACCOUNT_NAME = "name";

public static final String ACCOUNT_TYPE = "type";

public static final String ACCOUNT_PASSWORD = "password";


2.3. 设备管理员设置

        设备管理员设置为企业应用程序提供了额外的安全性,并可以控制一些特定的功能。设备管理员通常是管理员、公司或组织,而用户必须通过设备管理员策略来使资产得到管理。以下是示例代码,用于设置设备策略:

DevicePolicyManager devicePolicyManager =(DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);

ComponentName adminComponentName = new ComponentName(this, DeviceAdminReceiver.class);

Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);

intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, adminComponentName); startActivityForResult(intent, ADD_DEVICE_ADMIN_REQUEST_CODE); devicePolicyManager.setPasswordQuality(adminComponentName, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);

三、关键设计和结构

3.1. 数据分类和存储

SettingsProvider对数据进行了分类:Global、System、Secure,其中:

Global:全局的偏好设置,对系统中所有用户公开,第三方App没有写权限;
System:用户偏好系统设置;
Secure:安全相关的用户偏好设置,第三方App没有写权限。
Android6.0版本之后SettingsProvider管理的用户偏好设置数据从原来的settings.db数据库文件中转移到下面的3个xml文件中:

data/system/users/0/settings_global.xml
data/system/users/userid/settings_system.xml
data/system/users/userid/settings_secure.xml
备注:

1、在Android多用户环境下,Global分类数据是面向所有用户的,所以settings_global.xml只在0用户下存在;

2、SettingsProvider还管理着一些数据存储在文件“data/system/users/userid/settings_ssaid.xml”中,本文中暂时不对相关的数据和代码做分析。

3.2. 关键设计

3.2.1. 兼容性设计

        为了兼容之前版本的设计(网上很多大牛都这样分析),Android 9.0代码中依然保留了数据库相关的逻辑设计。SettingsProvider在启动时,如果检测到settings_global.xml不存在,会创建settings.db数据库,并将SettingsProvider管理的偏好设置的默认设置写入到settings.db中,然后将settings.db中的数据保存到相应的xml文件下,最后删除settings.db数据库。数据库操作逻封装在DatabaseHelper类中。

备注:

1、个人数据库在xml文件生成的过程中最大的作用就是作为一个数据中转载体,这样的兼容性设计别不是十分必要;

2、在系统调试过程中如果怀疑数据库中转过程出了问题,可以讲SettingsProvider.DROP_DATABASE_ON_MIGRATION常量设置为false,这样settings.db文件不会在使用完毕之后从磁盘上删除,而是会备份为settings-backup.db,可以用作对比分析。

3.2.2 关键数据组织

        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类关联到某项具体的用户设置数据。

        在阅读源码的过程中要对这个数据组织模型有清晰的认识,另外Key值的清楚认识可以帮助我们更好的理解源码中蕴藏的逻辑,因为源码中有很多关键的地方都有它的存在。下面对Key的构成规则做一个分析,源码位于SettingsState.java类中:

public static int makeKey(int type, int userId) {

        return (type << SETTINGS_TYPE_SHIFT) | userId;

}

public static int getTypeFromKey(int key) {

        return key >>> SETTINGS_TYPE_SHIFT;

}

public static int getUserIdFromKey(int key) {

        return key & ~SETTINGS_TYPE_MASK;

}

type=[0|1|2]、SETTINGS_TYPE_SHIFT=28、SETTINGS_TYPE_MASK=0xF0000000,因为在Android多用户定义中,userId有效位为低16位,所以上面代码给出的计算是可逆的,能够从Key逆运算得到数据类型和用户id。这样做的目的是通过对type和userId的组合得到"data/system/users/userid/*.xml"文件的唯一标识。
协议,转载请附上原文出处链接及本声明。

3.2.3 缓存设计

        因为SettingsProvider被系统中很多模块访问,为了方便使用系统提供了Settings类对SettingsProvider进行封装,同时提供了2级缓存机制以提高SettingsProvider的使用性能,SettingsProvider的2级缓存结构如下图所示。

1. 第一级缓存,SettingsProvider通过内部类SettingsRegistry间接维护了所有用户的所有偏好设置数据,这些数据以xml文件为单位采用“用时加载”的策略保持于xml文件数据同步;

2. 第二级缓存,Settings对SettingsProvider数据提供了封装,Settings根据SettingsProvider的数据分类实现了3个静态内部类访问SetingsProvider中的提供的数据。在三个静态内部类中通过NameValueCache维护了当前用户设置数据的缓存,Global类型数据除外,它是所有用户共享的。NameValueCache以设置数据条目(键值对)为单位与SettingsProvider.SettingsRegistry间接维护的缓存中的设置数据条目<String,Setting>保持“用时同步”。

3. 两级缓存之间通过"Generation"-int数值维护数据版本,当数据版本发生改变时,NameValueCache数据清空,当某键值对数据发生第一次访问之后,直到SettingsProvider缓存的版本和Settings缓存维护的版本不一致之前,NameValueCache中的数据可用。

3.3. 关键源码解读

3.3.1 相关源码

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/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/SettingsBackupAgent.java

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

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

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

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

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

frameworks/base/packages/SettingsProvider/AndroidManifest.xml

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

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

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

frameworks/base/core/java/android/app/ActivityThread.java

3.3.2 SettingsProvider AndroidManifest.xml文件

AndroidManifest.xml

<manifest ...
android:sharedUserId="android.uid.system">
<application android:allowClearUserData="false"
android:label="@string/app_label"
android:process="system"
android:backupAgent="SettingsBackupAgent"
...
android:directBootAware="true">

<provider android:name="SettingsProvider"
android:authorities="settings"
android:multiprocess="false"
android:exported="true"
android:singleUser="true"
android:initOrder="100"
android:visibleToInstantApps="true" />
</application>
</manifest>

分析Manifest文件,从shareUserId知道SettingsProvider运行在系统进程中,从backupAgent知道SettingsProvider使用系统的备份框架对关键数据进行了备份操作,从authorities知道SettingsProvider Uri的Authority是settings。

3.3.3 SettingsProvider启动过程

SettingsProvider是一个系统Provider,启动流程见上图,和其他系统Provider的启动流程一样,在SystemServer启动系统服务的过程中安装进系统,源码:

private void startOtherServices() {

        ... ...

        try {

                ... ...

                traceBeginAndSlog("InstallSystemProviders");

                mActivityManagerService.installSystemProviders(); //安装系统Providers

                // Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags

                SQLiteCompatibilityWalFlags.reset();

                traceEnd();

                ... ...

        } catch (RuntimeException e) {

                Slog.e("System", "******************************************");

                Slog.e("System", "************ Failure starting core service", e);

        }

        ... ...

}

上面的代码显示系统Provider在系统启动的时候在startOtherServices()中调用ActivityManagerService的installSystemProviders()完成安装创建,SettingsProvider也包括在其中,ActivityManagerService#installSystemProviders()源码:

public final void installSystemProviders() {

        ... ...

        //1

        List<ProviderInfo> providers;

        synchronized (this) {

                ProcessRecord app = 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

if (providers != null) {

        mSystemThread.installSystemProviders(providers);

}

... ...

synchronized (this) {

        mSystemProvidersInstalled = true;

}

... ...

// Now that the settings provider is published we can consider sending

// in a rescue party.

// 3

RescueParty.onSettingsProviderPublished(mContext);

... ...

}

上面的代码在注释1处获取所有的系统Provider,这个过程最终会调用到包管理模块的queryContentProviders()函数,关于获取系统Provider的过程细节我们这里不做分析,感兴趣的从这里向下继续跟源码;在注释2处,调用ActivityThread的installSystemProviders方法完成系统Provider的安装启动,包括SettingsProvider;在注释3处理SettingsProvider安装之前某些依赖救援程序(Android 8.0之后引入)相关逻辑,这里不做详细分析。ActivityThread#installSystemProviders涉及到比较复杂的处理逻辑,和我们分析SettingsProvider的启动流程关系不大,本文这里不做分析。最终,通过调用ActivityThread#installSystemProviders会调用到SettingsProvider的onCreate函数,SettingsProvider#onCreate的调用流程如下:

 SettingsProvider的关键启时序见上图,onCreate源码:

public boolean onCreate() {

        ... ...

        synchronized (mLock) {

                ... ...

                //1

                mHandlerThread = new HandlerThread(LOG_TAG,

                Process.THREAD_PRIORITY_BACKGROUND);

                mHandlerThread.start();

                mHandler = new Handler(mHandlerThread.getLooper());

                //2

                mSettingsRegistry = new SettingsRegistry();

        }

                //3

                mHandler.post(() -> {

                        registerBroadcastReceivers();

                        startWatchingUserRestrictionChanges();

                });

                //4

                ServiceManager.addService("settings", new SettingsService(this));

                return true;

}

上面代码注释1处创建一个HandlerThread,用来执行一些异步操作;注释3处会注册一些关心的系统广播,比如用户变化、App卸载等,本文不具体分析所有广播的回调逻辑;注释4处会向系统添加SettingsService服务,关于SettingsService服务提供的功能再以后的文章中单独分析。注释2处会创建SettingsRegistry对象,是上面是时序图核心时序逻辑的开始,下面就具体分析SettingsRegistry类创建过程中都做了哪些事情,SettingsRegistry构造函数源码:

public SettingsRegistry() {

        mHandler = new MyHandler(getContext().getMainLooper());

        //1

        mGenerationRegistry = new GenerationRegistry(mLock);

        //2

        mBackupManager = new BackupManager(getContext());

        //3

        migrateAllLegacySettingsIfNeeded();

        syncSsaidTableOnStart();

}

上面代码注释1处创建了一个GenerationRegistry对象,GenerationRegistry对象的核心作用类似于对xml文件的更改做版本管理;注释2处创建BackupManager对象,和系统备份有关,有兴趣的话可以看一看android的备份机制;代码3是本文要分析的核心逻辑,这个函数十分重要,源码:

private void migrateAllLegacySettingsIfNeeded() {

        synchronized (mLock) {

                //1

                final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);

                File globalFile = getSettingsFile(key);

                if (SettingsState.stateFileExists(globalFile)) {

                        return;

                }

                ... ...

                try {

                        //2

                        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

                        DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId);

                        SQLiteDatabase database = dbHelper.getWritableDatabase();

                        //4

                        migrateLegacySettingsForUserLocked(dbHelper, database, userId);

                         ... ...

                } finally {

                        ... ...

                }

        }

}

上面代码注释1处,很重要,判断"/data/system/users/0/settings_global.xml"文件是否存在,如果不存在migrateAllLegacySettingsIfNeed()函数直接返回,也就是说在这种情况下不需要迁移数据(字面意思),而正常情况下settings_global.xml文件只有在系统首次启动的时候不存在,也就是说migrateAllLegacySettingsIfNeeded()数据迁移只会发生在系统首次启动。为了便于理解,我们先提前总结一下migrateAllLegacySettingsIfNeed()函数数据迁移的核心动作:遍历系统中的所有用户(一般情况只有0用户),循环为每个用户创建一个临时数据库,并将系统各个模块的默认设置写入数据库,接着调用migrateLegacySettingsForUserLocked()将数据库中的内容写入到“/data/system/users/userid/*.xml”文件中,也就是xml创建和初始化的过程。注释2处就是获取系统中所有用户,并通过for循环遍历对每个用户执行数据迁移的过程。注释3处的逻辑很重要,它通过DatabaseHelper类创建了数据库和表,并使用默认设置对数据库表数据初始化。注释4处的代码是为每个用户初始化xml表的核心代码。下面首先分析数据库创建初始化的核心过程,接着再分析数据从数据库表迁移到xml文件的逻辑。DatabaseHelperd的onCreate源码:

public void onCreate(SQLiteDatabase db) {

        //1

        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

        createSecureTable(db);

        //3

        // Only create the global table for the singleton 'owner/system' user

        if (mUserHandle == UserHandle.USER_SYSTEM) {

                createGlobalTable(db);

        }

... ...

        //4

        loadVolumeLevels(db);

        loadSettings(db);

}

上面代码注释1、2、3处为用户创建3中类型数据的数据表,注释3处多了一个判断,因为settings_global.xml文件是所有用户共享的,而只存储在0用户下;注释4处调用loadVolumeLevels()和loadSettings()以默认设置数据填充数据库表,具体填充了哪些数据,本文不做分析,填充逻辑仅仅是向相关的表中插入数据项。下面接着分析最终要得一个函数调用,migrateLegacySettingsForUserLocked(),源码:

private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,SQLiteDatabase database, int userId) {

        //1

        final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);

        ensureSettingsStateLocked(systemKey);

        SettingsState systemSettings = mSettingsStates.get(systemKey);

        migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);

        systemSettings.persistSyncLocked();

        //2

        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

        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 (mSettingsCreationBuildId != null) {

                        globalSettings.insertSettingLocked(Settings.Global.DATABASE_CREATION_BUILDID,

                        mSettingsCreationBuildId, null, true,

                        SettingsState.SYSTEM_PACKAGE_NAME);

                }

                globalSettings.persistSyncLocked();

        }

        //4

        if (DROP_DATABASE_ON_MIGRATION) {

                dbHelper.dropDatabase();

        } else {

                dbHelper.backupDatabase();

        }

}

上面的代码注释1处,为用户处理System类型数据,1.首先生成与xml文件唯一对应的key;2. 调用ensureSettingsStateLocked()方法确保 :SparseArray<SettingsState>容器中有xml文件对应的:SettingsState对象;3.调用 migrateLegacySettingsLocked方法将临时数据库中设置数据更新到:SettingsState对象;4.调用:SettingsState.persistSyncLocked()方法把数据写入到xml文件中。

3.3.4 数据获取流程  

设置数据获取大体时序逻辑见上图,通过Settings类封装之后SettingsProvider的数据获取变得相当简单(数据更新同样),在代码中只需要向下面这样调用Settings.System类的getString方法就能轻松获得某个属性数据:

String SystemValue = Settings.System.getString(getContentResolver(), Settings.System.ACTION_SETTINGS);

 Settings.System.getString()方法调用了getStringForUser()方法,方法源码:

public static String getStringForUser(ContentResolver resolver, String name,int userHandle) {

        ... ...

        return sNameValueCache.getStringForUser(resolver, name, userHandle);

}

Settings.System.getStringForUser()方法调用了NameValueCache缓存类的同名方法,方法源码:

public String getStringForUser(ContentResolver cr, String name, final int userHandle) {

        //1

        final boolean isSelf = (userHandle == UserHandle.myUserId());

        int currentGeneration = -1;

        if (isSelf) {

                synchronized (NameValueCache.this) {

                        if (mGenerationTracker != null) {

                                if (mGenerationTracker.isGenerationChanged()) {

                                        ... ...

                                        mValues.clear();

                        } else if (mValues.containsKey(name)) {

                                return mValues.get(name);

                        }

                        if (mGenerationTracker != null) {

                                currentGeneration = mGenerationTracker.getCurrentGeneration();

                        }

                }

        }

}

IContentProvider cp = mProviderHolder.getProvider(cr);

        if (mCallGetCommand != null) {

                try {

                        Bundle args = null;

                        ... ...

                        Bundle b;

                        if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {

                                final long token = Binder.clearCallingIdentity();

                                try {

                                        //2

                                        b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);

                                } finally {

                                        Binder.restoreCallingIdentity(token);

                                }

                        } else {

                                b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);

                        }                        

                        if (b != null) {

                                String value = b.getString(Settings.NameValueTable.VALUE);

                                //3

                                ... ...

                                return value;

                        }

                } catch (RemoteException e) {}

        }

        ... ...

}

上面的代码注释1处检查NameValueCache缓存是否命中,检查条件是用户id和Generation,命中直接返回缓存中的值;注释2处的代码通过binder调用SettingsProvider的call方法获取数据;注释3处的代码主要是检查更新Generation,保持缓存值是最新的。下面接着分析SettingsProvider.call()方法,方法源码:

public Bundle call(String method, String name, Bundle args) {

        final int requestingUserId = getRequestingUserId(args);

                switch (method) {

                        ... ...

                        case Settings.CALL_METHOD_GET_SYSTEM: {

                                //1

                                Setting setting = getSystemSetting(name, requestingUserId);

                                //2

                                return packageValueForCallResult(setting, isTrackingGeneration(args));

                        }

                        ... ...

                }

        return null;

}

SettingsProvider提供call()方法来向外提供数据,这里优先并没使用ContentProvider的CURD方法,call()方法中通过通过传递过来的method确定客户端请求的操作,Settings.System.getString方法最终传递过来的method就是Settings.CALL_GET_SYSTEM。源码中注释1处调用getSystemSetting从SystemsRegistry中维护的缓存中提取数据,后面的分析中会看到如果缓存中数据尚未加载会从xml,会在这时候加载,这是为什么上面我们使用“用时加载”这个名词的原因;注释2处代码打包返回结果,另外还会维护Generation,这个过程本文不会详细分析。接下来我们分析一下getSystemSetting()方法,方法源码:

private Setting getSystemSetting(String name, int requestingUserId) {

        //1

        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);

        enforceSettingReadable(name, SETTINGS_TYPE_SYSTEM, UserHandle.getCallingUserId());

        final int owningUserId = resolveOwningUserIdForSystemSettingLocked(callingUserId, name);

        //2

        synchronized (mLock) {

                return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SYSTEM, owningUserId,                                 name);

        }

}

源码注释1处代码完成多用户和权限相关的处理;注释2处调用SettingsRegistry的getSettingLocked()方法获取数据,SettingsRegistry.getSettingsLocked()方法源码:

public Setting getSettingLocked(int type, int userId, String name) {

        final int key = makeKey(type, userId);

        //1

        SettingsState settingsState = peekSettingsStateLocked(key);

        if (settingsState == null) {

                return null;

        }

        //2

        return settingsState.getSettingLocked(name);

}

源码注释1处的代码很重要,方法SettingsRegistry.peekSettingsStateLocked()根据传入的key值会找到xml文件对应的SettingsState对象,这个过程中如果SettingsState对象不存在,会创建并加载xml数据;注释2处的代码就是从SettingsState对象中维护的设置数据中找到name对应的value并返回。下面着重分析一下peekSettingsStateLocked(),源码:

private SettingsState peekSettingsStateLocked(int key) {

        //1

        SettingsState settingsState = mSettingsStates.get(key);

        if (settingsState != null) {

                return settingsState;

        }

        //2

        if (!ensureSettingsForUserLocked(getUserIdFromKey(key))) {

                return null;

        }

        return mSettingsStates.get(key);

}

注释1处的代码先从缓存SparseArray<SettingsState>对象mSettingsStates中查找到key对应的:SettingsState,如果在mSettingsStates中找到了key值关联的对象,直接将:SettingsState返回,如果key值关联的对象并未在缓存中,也即时xml文件并未加载,这时接着执行下面的逻辑;注释2处执行xml为加载的情况下的逻辑,SettingsRegistry.ensureSettingsForUserLocked()源码:

public boolean ensureSettingsForUserLocked(int userId) {

        ... ...

        // 1

        migrateLegacySettingsForUserIfNeededLocked(userId);

        ... ...

        // 2

        final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);

        ensureSettingsStateLocked(systemKey);

        ... ...

        return true;

}

注释1处调用SettingsRegistry.migrateLegacySettingsForUserIfNeededLocked()迁移数据,迁移数据的逻辑和前文介绍系统首次启动时迁移数据的逻辑一样,不用的是内部只会调用migrateLegacySettingsForUserLocked()一次,为当前指定的用户执行数据迁移流程,这是应对新创建的用户xml文件不存在的情形;注释2处调用SettingsRegistry.ensureSettingsStateLocked加载xml文件中的数据到缓存,ensureSettingsStateLocked()源码:

private void ensureSettingsStateLocked(int key) {

        if (mSettingsStates.get(key) == null) {

                final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key));

                //1

                SettingsState settingsState = new SettingsState(getContext(), mLock,

                getSettingsFile(key), key, maxBytesPerPackage, mHandlerThread.getLooper());

                mSettingsStates.put(key, settingsState);

        }

}

注释1处,为给定的key值创建一个新的SettingsState对象,并将对象加入到SettingsRegistry持有的SparseArray<SettingsState>中,xml数据加载的过程在SettingsState的构造函数中完成,源码:

public SettingsState(Context context, Object lock, File file, int key,int maxBytesPerAppPackage, Looper looper) {

        ... ...

        synchronized (mLock) {

                //1

                readStateSyncLocked();

        }

}

  注释1处,调用SettingsState.readStateSyncLocked()方法,处理xml文件,源码:

private void readStateSyncLocked() {

        //1

        FileInputStream in;

        try {

                in = new AtomicFile(mStatePersistFile).openRead();

        } catch (FileNotFoundException fnfe) {

                ... ...

        }

        try {

                XmlPullParser parser = Xml.newPullParser();

                parser.setInput(in, StandardCharsets.UTF_8.name());

                //2

                parseStateLocked(parser);

        } catch (XmlPullParserException | IOException e) {

                ... ...

        } finally {

                ... ...

        }

}

注释1处,打开xml文件操作;注释2处调用SettingsState.parseStateLocked()方法正在完成xml文件解析,并将数据存入到SettingsState缓存中,源码:

private void parseStateLocked(XmlPullParser parser)throws IOException, XmlPullParserException {

        ... ...

        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT&& (type !=                         XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {

                ... ...

               String tagName = parser.getName();

                if (tagName.equals(TAG_SETTINGS)) {

                        //1

                        parseSettingsLocked(parser);

                }

        }

}

 parseStateLocked在While循环中为每个TAG_SETTINGS=settings标签调用注释1处的SettingsState.parseSettingsLocked()完成xml文件解析,源码:

private void parseSettingsLocked(XmlPullParser parser)throws IOException,         XmlPullParserException {

        //1

        mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));

        ... ...

        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT&& (type !=                 XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {

                 ... ...

                String tagName = parser.getName();

                if (tagName.equals(TAG_SETTING)) {

                        //2

                        String id = parser.getAttributeValue(null, ATTR_ID);

                        String name = parser.getAttributeValue(null, ATTR_NAME);

                        String value = getValueAttribute(parser, ATTR_VALUE, ATTR_VALUE_BASE64);

                        String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);

                        String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE,

                                                        ATTR_DEFAULT_VALUE_BASE64);

                        String tag = null;

                        DebugUtil.Log("loadsetting- name: "+name);

                        boolean fromSystem = false;

                        if (defaultValue != null) {

                                fromSystem = Boolean.parseBoolean(parser.getAttributeValue(null,                                                                                         ATTR_DEFAULT_SYS_SET));

                                tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64);

                        }

                        //3

                        mSettings.put(name, new Setting(name, value, defaultValue, packageName,                                                 tag,fromSystem, id));

                        }

                }

}
 

代码注释1处从xml文件中提取版本信息,和上文说的Generation相关;代码注释2处提取每条设置的属性值;注释3处以提取到的完整的设置条目数据构建Setting对象加入到SettingsState维护的Map中。

到这里,以System类型介绍数据获取的整个流程就基本介绍完了,总结一下,1. 首先客户端程序调用Settings.System.getString()或者Settings.System.getStringForUser()方法发起数据获取流程;2. 优先区NameValueCahce缓存中查找客户端请求的数据是否存在,存在就从缓存总返回结果,不存在或者Generation更新了,需要重新维护缓存,从下级缓存中提取数据;3. 从SettingsRegistry维护的缓存中区查找数据,如果SettingsRegistry缓存中依然找不到请求的数据(这里是以xml文件为单位,2中以xml文件的setting条目为单位),加载xml文件,加载过程中如果xml文件不存在还需要先创建和初始化xml文件,最终将客户端请求的数据返回。

备注:本章以System类型的数据分析获取流程,对于Global和Sercure类型的数据获取流程基本一样,只是在某些处理细节上存在差异。
 

3.3.5 数据设置流程

数据设置流程和数据上文讲的数据设置流程调用过程基本类似,客户端调用时使用调用Settings.System.putString()或Settings.System.GetString()方法设置System类型的数据,本文就不重复分析这部分的源码了。

四、在应用程序中使用 SettingsProvider

任何具有“android.permission.WRITE_SETTINGS”权限的应用程序均可以访问 SettingsProvider 数据。以下是一些在应用程序中使用 SettingsProvider 的代码示例:

Uri soundUri = Settings.System.DEFAULT_RINGTONE_URI;
Ringtone ringtone = RingtoneManager.getRingtone(this, soundUri);
ringtone.play()
  • 22
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Framework 层是 Android 系统中非常重要的一层,它提供了丰富的 API 和服务,包括 Activity、Service、Content Provider、Broadcast Receiver、PackageManager、Window Manager 等等,它们为应用程序提供了一个丰富的运行环境和开发框架。 Android Framework 层的源码主要包括以下几个部分: 1. Activity Manager:负责管理 Android 应用程序的生命周期、进程和任务栈等。 2. Package Manager:负责管理应用程序的安装、卸载和更新等操作。 3. Content Provider:提供了一种标准的接口,让应用程序之间可以共享数据。 4. Telephony Manager:提供了访问手机通讯功能的接口和服务。 5. Location Manager:提供了访问 GPS 和其他位置服务的接口和服务。 6. Notification Manager:提供了管理通知的接口和服务。 7. View System:提供了 Android 应用程序的 UI 界面的渲染和事件处理等功能。 8. Resource Manager:提供了访问 Android 应用程序的资源文件的接口和服务。 9. Window Manager:提供了窗口管理和界面绘制等功能。 10. Activity Manager Service:提供了 Activity Manager 的服务接口。 11. System Server:提供了 Android 系统的核心服务,包括 PackageManager、ActivityManager、WindowManager 等。 以上是 Android Framework 层源码的主要部分,通过阅读 Android Framework 层源码可以深入了解 Android 系统的实现原理和应用程序的开发框架。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值