Android 11.0 SettingsProvider 源码分析

news2024/10/5 8:16:57

文章目录

    • 一、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 {

	……	
	//内部类 System
    public 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);
        }
        ……
	}
	
	……
	//内部类 Secure
    public static final class Secure extends NameValueTable {
        public static final Uri CONTENT_URI =
            Uri.parse("content://" + AUTHORITY + "/secure");

        @UnsupportedAppUsage
        private static final ContentProviderHolder sProviderHolder =
                new ContentProviderHolder(CONTENT_URI);
        ……        
        }               

	……
	//内部类 Global
    public 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 SQLiteCompatibilityWalFlags
        SQLiteCompatibilityWalFlags.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.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        ……
    public final void installSystemProviders() {
		// 1、获取系统中所有的 Provider。这个过程最终会通过调用包管理模块 PackageManagerService 中的
        // queryContentProviders() 方法来获取所有的 Provider
        List<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 的安装启动,
		//其中就包括 SettingsProvider
        if (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 {
……
    @Override
    public boolean onCreate() {
        Settings.setInSystemServer();

        // fail to boot if there're any backed up settings that don't have a non-null validator
        ensureAllBackedUpSystemSettingsHaveValidators();
        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.0
                if (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 finishes
            if (!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);");
    }

    @Override
    public 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' 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);
        }
		
		//4、将初始音量加载到DB中
        // Load initial volume levels into DB
        loadVolumeLevels(db);
		//5、将默认设置数据加载到数据表中
        // Load inital settings values
        loadSettings(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' 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);

            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 a
         pending 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文件唯一对应的key
            final 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 created
                if (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' user
	        if (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,例如:


<SwitchPreference
            android: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.
    @Override
    public 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.
    @Override
    public boolean setChecked(boolean isChecked) {
        final String newValue = isChecked ? DEEPSLEEP_ON : DEEPSLEEP_OFF;
        Settings.Global.putString(mContext.getContentResolver(), mPreferenceKey, newValue);
        return true;
    }

    @Override
    public int getAvailabilityStatus() {
    	//启用开关
        return AVAILABLE;
    }

}

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

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

相关文章

Linux屏幕驱动开发调试笔记

引言 首先了解下什么是MIPI-DSI&#xff1a; MIPI-DSI是一种应用于显示技术的串行接口&#xff0c;兼容DPI(显示像素接口&#xff0c;Display Pixel Interface)、DBI(显示总线接口&#xff0c;Display Bus Interface)和DCS(显示命令集&#xff0c;Display Command Set)&#…

Tech Talk:智能电视eMMC存储的五问五答

智能电视作为搭载操作系统的综合影音载体&#xff0c;以稳步扩大的市场规模走入越来越多的家庭&#xff0c;成为人们生活娱乐的重要组成部分。存储部件是智能电视不可或缺的组成部分&#xff0c;用于保存操作系统、应用程序、多媒体文件和用户数据等信息。智能电视使用eMMC作为…

前端FCP指标优化

优化前 第三方依赖按需引入之后&#xff0c;打包的总体积减小到初始值的55%&#xff0c;但是依然存在很大的js文件&#xff0c;需要继续优化 chunk-vendors.js进行分包之后 截图 compression-webpack-plugin压缩之后 截图

【MySQL备份】Percona XtraBackup总结篇

目录 1.前言 2.问题总结 2.1.为什么在恢复备份前需要准备备份 2.1.1. 保证数据一致性 2.1.2. 完成崩溃恢复过程 2.1.3. 解决非锁定备份的特殊需求 2.1.4. 支持增量和差异备份 2.1.5. 优化恢复性能 2.2.Percona XtraBackup的工作原理 3.注意事项 1.前言 在历经了详尽…

大语言模型系列-Transformer(二)

Transformer 模型的入门可以从以下几个方面开始&#xff1a; 1. 理解基本概念 序列到序列&#xff08;Sequence-to-Sequence&#xff09;任务&#xff1a;Transformer 模型主要用于这类任务&#xff0c;如机器翻译、文本摘要等。注意力机制&#xff08;Attention Mechanism&a…

使用dot来画流程图

Dot是一种图形描述语言&#xff0c;属于Graphviz软件的一部分。Graphviz是一个用于可视化图形&#xff08;图表、网络图等&#xff09;的开源工具集。使用Dot语言&#xff0c;你可以创建并描述节点和边&#xff0c;从而生成图形。以下是如何使用Dot语言画图的基本步骤&#xff…

Elasticsearch实战教程: 如何在海量级数据中进行快速搜索

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 Elasticsearch&#xff08;简称ES&#xff09;是一个基于Apache Lucene™的开源搜索引擎&#xff0c;无论在开源还是专有领…

matlab仿真 通信信号和系统分析(下)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第三章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; 一、离散傅里叶变换 clear all n0:30;%信号的时间范围 xsin(0.2*n).*exp(-0.1*n); k0:30;%频率范围 N31; Wnkexp(-j*2*pi/N).…

firewalld(6)自定义services、ipset

简介 在前面的文章中我们已经介绍了zone、rich rule 、--direct等功能和基本配置。在前面文章中&#xff0c;我们提到过firewalld内置了很多服务&#xff0c;可以通过firewall-cmd --get-services来查看服务&#xff0c;也可以通过配置文件查看这些服务/var/lib/firewalld/ser…

短视频电商源码的优势及软件架构解析

短视频电商源码是目前电商行业中非常火热的一个新兴领域&#xff0c;它通过短视频内容和电商商品的结合&#xff0c;为用户提供了一种新的购物体验。下面将介绍短视频电商源码的优势以及软件架构。 首先&#xff0c;短视频电商源码具有以下几个优势&#xff1a; 1、创新的购物体…

WGAN(Wassertein GAN)

WGAN E x ∼ P g [ log ⁡ ( 1 − D ( x ) ) ] E x ∼ P g [ − log ⁡ D ( x ) ] \begin{aligned} & \mathbb{E}_{x \sim P_g}[\log (1-D(x))] \\ & \mathbb{E}_{x \sim P_g}[-\log D(x)] \end{aligned} ​Ex∼Pg​​[log(1−D(x))]Ex∼Pg​​[−logD(x)]​ 原始 GAN …

项目范围管理(信息系统项目管理师)

收集需求的输出是需求文件、需求跟踪矩阵定义范围是制定项目和产品详细描述的过程。本过程的主要作用是描述产品、服务或成果的边界和验收标准。本过程需要在整个项目期间多次反复开展项目范围说明书&#xff1a;是对项目范围、主要可交付成果、假设条件和制约因素的描述。明确…

思维导图插件--jsMind的使用

vue引入jsmind&#xff08;右键菜单&#xff09;_jsmind.menu.js-CSDN博客 第一版 vue-JsMind思维导图实现&#xff08;包含鼠标右键自定义菜单&#xff09;_jsmind 右键菜单-CSDN博客 // 新增节点addNode() {console.log(this.get_selected_nodeid());this.get_selected_…

Jenkins 创建流水线任务

Jenkins是一个流行的持续集成&#xff08;Continuous Integration&#xff0c;CI&#xff09;工具。 Jenkins 创建任务 选择“流水线”类型&#xff0c;该类型的优点是定制化程度非常高 &#xff08;可选&#xff09;添加“参数化构建” 配置仓库选项(ssh连接、分支)和凭据…

Element-UI - el-table中自定义图片悬浮弹框 - 位置优化

该篇为前一篇“Element-UI - 解决el-table中图片悬浮被遮挡问题”的优化升级部分&#xff0c;解决当图片位于页面底部时&#xff0c;显示不全问题优化。 Vue.directive钩子函数已在上一篇中详细介绍&#xff0c;不清楚的朋友可以翻看上一篇&#xff0c; “Element-UI - 解决el-…

【Python】字典练习

python期考练习 目录 1. 首都名​编辑 2. 摩斯电码 3. 登录 4. 学生的姓名和年龄​编辑 5. 电商 6. 学生基本信息 7. 字母数 1. 首都名 初始字典 (可复制) : d{"China":"Beijing","America":"Washington","Norway":…

企业如何管理安全生产工作?(附模板)

总结一下在企业内管理安全中遇到的一些问题&#xff1a; 1、 管理方式落后&#xff0c;还在使用纸质记录 2、 人员信息杂乱无章&#xff0c;无人整理 3、出现问题找不到源头和负责人 我做系统管理已经7年了&#xff0c;题主说的这些问题我之前也遇到过&#xff0c;相信也有…

Java学习高级一

修饰符 static 类变量的应用场景 成员方法的分类 成员变量的执行原理 成员方法的执行原理 Java之 main 方法 类方法的常见应用场景 代码块 设计模式 单例设计模式 饿汉式单例设计模式 懒汉式单例设计模式 继承 权限修饰符

【MindSpore学习打卡】应用实践-计算机视觉-FCN图像语义分割-基于MindSpore实现FCN-8s进行图像语义分割的教程

图像语义分割是计算机视觉领域中的一个重要任务&#xff0c;它旨在对图像中的每个像素进行分类&#xff0c;从而实现对图像内容的详细理解。在众多图像语义分割算法中&#xff0c;全卷积网络&#xff08;Fully Convolutional Networks, FCN&#xff09;因其端到端的训练方式和高…

Git使用中遇到的问题(随时更新)

问题1.先创建本地库&#xff0c;后拉取远程仓库时上传失败的问题怎么解决&#xff1f; 操作主要步骤&#xff1a; step1 设置远程仓库地址: $ git remote add origin gitgitee.com:yourAccount/reponamexxx.git step2 推送到远程仓库: $ git push -u origin "master&qu…