SettingsProvider简单分析

SettingsProvider顾名思义是一个提供数据共享的Provider,SettingsProvider和Android系统其它Provider有很多不一样的地方:

1.SettingsProvider只接受int float String等基本类型的数据;

2.SettingsProvider由Android系统frameowrk进行了封装

3.SettingsProvider的数据由键值对组成

SettingsProvider有点类似Android的properties系统(Android属性系统):SystemProperites。SystemProperites除具有SettingsProvider以上的三个特性,SettingsProvider和SystemProperties的不同点在于:

1、数据保存方式不同:SystemProperies的数据保存属性文件中(/system/build.prop等),开机后会被加载到system properites store;SettingsProvider的数据保存在文件/data/system/users/0/settings_***.xml和数据库settings.db中;

2、作用范围不同:SettingsProvider可以实现跨进程、跨层次调用,即底层的c/c++可调用,java层也可以调用;SettingsProvider只能在java层调用

3、公开程度不同:SettingsProvider有部分功能上层第三方APP可以使用,SystemProvider上层第三方APP不可以使用。

在Android 6.0版本时,SettingsProvider被重构,Android从性能、安全等方面考虑,把SettingsProvider中原本保存在settings.db中的数据,目前全部保存在XML文件中。

数据分类

SettingsProvider主要有三种类型数据:Global、System、Secure三种类型

Global:所有的偏好设置对系统的所有用户公开,第三方有读没有写的权限

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

主要源码

SettingsProvider的代码数量不多,主要包含如下的java文件:

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

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

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

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

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

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

AndroidManifest.xml配置

SettingsProvider的AndroidManifest.xml文件对应和ContentProvider的配置如下:



    

        
    

上面的Manifest配置由sharedUserId可知,SettingsProvider运行在系统进程中,定义的ContentProvider实现类是SettingsProvider,Uri凭证是settings。

SettingsProvider的启动过程

系统启动SettingsProvider是在frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices() {
	//省略一部分代码
	//...
	
	traceBeginAndSlog("InstallSystemProviders");
	mActivityManagerService.installSystemProviders();
	// Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags
	SQLiteCompatibilityWalFlags.reset();
	traceEnd();
	
	//省略一部分代码
	//...
}

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

public final void installSystemProviders() {
	// 1、获取系统中所有的Provider,最终通过调用包管理PackageManagerService中的
//queryContentProviders()方法来查询所有的Provider
    List 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、调用ActivityThread中的installSystemProviders来完成Provider的安装,包括
//SettingsProvider
    if (providers != null) {
        mSystemThread.installSystemProviders(providers);
    }

    synchronized (this) {
        mSystemProvidersInstalled = true;
    }

    mConstants.start(mContext.getContentResolver());
    mCoreSettingsObserver = new CoreSettingsObserver(this);
    mFontScaleSettingObserver = new FontScaleSettingObserver();
    mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
    GlobalSettingsToPropertiesMapper.start(mContext.getContentResolver());

	// 3、Provider启动完成
    // Now that the settings provider is published we can consider sending
    // in a rescue party.
    RescueParty.onSettingsProviderPublished(mContext);

    //mUsageStatsService.monitorPackages();
}

其中第二步中,installSystemProviders()会启动SettingsProvider。启动SettingsProvider即运行SettingsProvider,首先调用OnCreate()方法。

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

@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();
        mHandlerThread = new HandlerThread(LOG_TAG,
                Process.THREAD_PRIORITY_BACKGROUND);
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
        mSettingsRegistry = new SettingsRegistry();
    }
    mHandler.post(() -> {
        //注册广播接收,关心设备用户变化以及APP卸载的广播
        registerBroadcastReceivers();
        startWatchingUserRestrictionChanges();
    });
    ServiceManager.addService("settings", new SettingsService(this));
    return true;
}

重点代码分析,上述代码中首先实例化一个HandlerThread的实例mHandlerThread,优先级是Process.THREAD_PRIORITY_BACKGROUND。关键的部分是:mSettingsRegistry = new SettingsRegistry()。

SettingsRegistry是SettingsProvider.java的内部类,先分析构造方法

    public SettingsRegistry() {
        mHandler = new MyHandler(getContext().getMainLooper());
        // 1、对xml的修改做版本管理
        mGenerationRegistry = new GenerationRegistry(mLock);
        // 2、创建Backup,做系统备份
        mBackupManager = new BackupManager(getContext());
        // 3、迁移所有的系统设置数据
        migrateAllLegacySettingsIfNeeded();
        syncSsaidTableOnStart();
    }

migrateAllLegacySettingsIfNeeded()方法,从命名上是迁移settings数据。

private void migrateAllLegacySettingsIfNeeded() {
            synchronized (mLock) {
                final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
                File globalFile = getSettingsFile(key);
                if (SettingsState.stateFileExists(globalFile)) {
                    return;
                }

                mSettingsCreationBuildId = Build.ID;

                final long identity = Binder.clearCallingIdentity();
                try {
                    //获取系统中所有的用户(多用户,一般user0)
                    List users = mUserManager.getUsers(true);

                    final int userCount = users.size();
                    for (int i = 0; i < userCount; i++) {
                        final int userId = users.get(i).id;
                        //关键代码,通过DatabaseHelper类创建数据库settings.db,并使用默认设置 
                        //对数据库表数据初始化
                        DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId);
                        SQLiteDatabase database = dbHelper.getWritableDatabase();
                        migrateLegacySettingsForUserLocked(dbHelper, database, userId);
                        //关键代码,生成和初始化xml文件
                        // 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);
                }
            }
        }

上面的代码首先是调用了makeKey()方法,所谓makeKey()就是和上文中的数据分类小章节中提到的System、Global和Secure三种key。然后调用getSettingsFile()方法获取到一个File对象的实例,如下:

        private File getSettingsFile(int key) {
            if (isGlobalSettingsKey(key)) {
                final int userId = getUserIdFromKey(key);
                return new File(Environment.getUserSystemDirectory(userId),
                        SETTINGS_FILE_GLOBAL);
            } else if (isSystemSettingsKey(key)) {
                final int userId = getUserIdFromKey(key);
                return new File(Environment.getUserSystemDirectory(userId),
                        SETTINGS_FILE_SYSTEM);
            } else if (isSecureSettingsKey(key)) {
                final int userId = getUserIdFromKey(key);
                return new File(Environment.getUserSystemDirectory(userId),
                        SETTINGS_FILE_SECURE);
            } else if (isSsaidSettingsKey(key)) {
                final int userId = getUserIdFromKey(key);
                return new File(Environment.getUserSystemDirectory(userId),
                        SETTINGS_FILE_SSAID);
            } else {
                throw new IllegalArgumentException("Invalid settings key:" + key);
            }
        }

上面的代码中对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
  • 在8.0以后,每位使用者所安装的每个 APP (package) 都会产生一组独立的 ID (SSAID),并将描述档案放置 “/data/system/users/0/settings_ssaid.xml"。

在migrateAllLegacySettingsIfNeeded()方法,实例化一股DatabaseHelper,DatabaseHelper是SQLiteOpenHelper的子类。

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

class DatabaseHelper extends SQLiteOpenHelper {

	private static final String DATABASE_NAME = "settings.db";

	public DatabaseHelper(Context context, int userHandle) {
      super(context, dbNameForUser(userHandle), null, DATABASE_VERSION);
      mContext = context;
      mUserHandle = userHandle;
  	}

	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();
     }
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
	   //创建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);");
	
		// 1、创建表,createSecureTable,Secure数据表
	   createSecureTable(db);
	
	   // 2、创建表,createGlobalTable,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);
	   }
	
		// 3、默认音量数据填充数据库
	   // Load initial volume levels into DB
	   loadVolumeLevels(db);
	
		// 4、默认数据填充数据库
	   // Load inital settings values
	   loadSettings(db);
	}
}

loadSettings()这个方法和loadVolumeLevels()方法类似,都是加载很多默认值写入到数据库中,这些默认值很大一部分被定义在文件frameworks/base/packages/SettingsProvider/res/values/defaults.xml中,也有一些来自其它地方。总之,loadVolumeLevels()和loadSettings()的作用就是在手机第一次启动时,把手机编好设置的默认值写入到数据库settings.db中。

Settings.db数据迁移到xml中

继续回到SettingsRegistry中的migrateAllLegacySettingsIfNeeded方法,在migrateAllLegacySettingsIfNeeded中接下来会执行migrateLegacySettingsForUserLocked方法

private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper, SQLiteDatabase database, int userId) {
     //1、处理System数据
     // Move over the system settings.
     final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); 
     //生成对应的SettingsState对象保存在mSettingsStates中,生成setting_system.xml
     ensureSettingsStateLocked(systemKey);
     SettingsState systemSettings = mSettingsStates.get(systemKey);
     //数据迁移,将settings.db的数据迁移到settingsStates.get(systemKey);
     migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);
     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);
     //生成对应的SettingsState对象保存在mSettingsStates中,生成settings_secure.xml
     ensureSettingsStateLocked(secureKey);
     SettingsState secureSettings = mSettingsStates.get(secureKey);
     migrateLegacySettingsLocked(secureSettings, database, TABLE_SECURE);
     //数据迁移,将settings.db的数据迁移到settings_secure.xml中
     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);
         //生成对应的SettingsState对象保存在mSettingsStates中,生成settings_global.xml
         ensureSettingsStateLocked(globalKey);
         SettingsState globalSettings = mSettingsStates.get(globalKey);
         //数据迁移,将settings.db的数据迁移到settings_global.xml中
         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();
     }
     //数据迁移完成后,是否删除settings.db,或者做一个备份
     // Drop the database as now all is moved and persisted.
     if (DROP_DATABASE_ON_MIGRATION) {
         dbHelper.dropDatabase();
     } else {
         dbHelper.backupDatabase();
     }
  }

ensureSettingsStateLocked是一个非常重要的方法,实例化一个SettingsState对象,
这个对象指向文件data/system/users/0/xx.xml文件,然后将settingsState放置在对象mSettingsStates中。如果是工程版本的系统,吧数据库settings.db重命名为settings.db-backup,如果是非工程版本的系统,把数据库文件删除,也会删除日志settings.db-journal.

SettnigsProvider启动时会创建settings.db数据库,然后把所有的默认设置项写入到数据库,接着会把数据库中所有的设置项从数据库转移到xml文件中,随后便会对数据库执行删除操作。为什么会有这么一个过程,这些过程是否可以移除创建数据库这一步?其实这个过程是为了兼容之前的版本而设计,在SettingsProvider被Android重构后,SettingsProvider中数据库相关的代码Android已经停止更新。

  //ensureSettingsStateLocked是一个重要的方法,给每一个数据类型key,
  //创建一个对应的SettingsState对象并保存在mSettingsStates
  private void ensureSettingsStateLocked(int key) {
      if (mSettingsStates.get(key) == null) {
          final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key));
          SettingsState settingsState = new SettingsState(getContext(), mLock,
                  getSettingsFile(key), key, maxBytesPerPackage, mHandlerThread.getLooper());
          mSettingsStates.put(key, settingsState);
      }
  }

之后会执行migrateLegacySettingsLocked方法

private void migrateLegacySettingsLocked(SettingsState settingsState,
        SQLiteDatabase database, String table) {
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    queryBuilder.setTables(table);

    Cursor cursor = queryBuilder.query(database, ALL_COLUMNS,
            null, null, null, null, null);

    try {
        ......

        while (!cursor.isAfterLast()) {
            String name = cursor.getString(nameColumnIdx);
            String value = cursor.getString(valueColumnIdx);
            settingsState.insertSettingLocked(name, value,
                    SettingsState.SYSTEM_PACKAGE_NAME);
            cursor.moveToNext();
        }
    } finally {
        cursor.close();
    }
}

上面这个方法,查询数据库中System所有的设置,然后在while循环中把每个值的信息作为insertSettingLocked()的参数。

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

public boolean insertSettingLocked(String name, String value, String packageName) {

    Setting oldState = mSettings.get(name);
    String oldValue = (oldState != null) ? oldState.value : null;

    if (oldState != null) {
        ......
    } else {
        Setting state = new Setting(name, value, packageName);
        mSettings.put(name, state);
    }
    ......
}

上面的方法把每一个设置项封装到Setting中,接着把state放置到ArrayMap的实例mSettings中。

那么,从方法ensureSettingsStateLocked()到insertSettingsLocked()方法,这个过程表明,有一个对象SettingsState,指向文件/data/system/users/0/settings_system.xml,持有变量mSettings,而mSettings持有封装了设置项的name,value,packageName的对象Setting,也就是settings_system.xml文件中的所有设置项间接被SettingsState持有。

在migrateLegacySettingsForUserLocked中,migrateLegacySettingsLocked方法执行完毕后,调用systemSettings.persistSyncLocked(),systemSettings是SettingsState的实例,代表的是settings_system.xml的所有设置项,persistSyncLocked()方法就是把systemSettings持有的所有的设置项从内存中固化到文件settings_system.xml中。

封装SettingsProvider接口

SettingsProvider是向整个Android系统提供用户偏好设置的程序,framework有一个类Settings.java对使用SettingsProvider进行了封装。

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

public final class Settings {
    public static final String AUTHORITY = "settings";
    public static final class Global extends NameValueTable {
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/global");
        ......
    }
    
    public static final class Secure extends NameValueTable {
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/secure");
        ......
    }
    
    public static final class System extends NameValueTable {
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/system");
        ......
    }
    
    private static class NameValueCache {
        private final Uri mUri;
        private final HashMap mValues = new HashMap();
        public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
            ......
        }
        public boolean putStringForUser(ContentResolver cr, String name, String value,
                final int userHandle) {
            ......
        }
        private IContentProvider lazyGetProvider(ContentResolver cr) {
            IContentProvider cp = null;
            synchronized (NameValueCache.this) {
                cp = mContentProvider;
                if (cp == null) {
                    cp = mContentProvider = cr.acquireProvider(mUri.getAuthority());
                }
            }
            return cp;
        }
}

上面的代码中,分别声明了Global、Secure、System三个静态内部类,分别对应SettingsProvider中的Global、Secure、System三种数据类型。Global、Secure、System三个静态内部类会分别持有自己NameValueCache的实例变量,每个NameValueCache持有指向SettingsProvider中的SettingsProvider.java的AIDL远程调用IContentProvider,读者可以阅读《Android System Server大纲之ContentService和ContentProvider原理剖析》了解ConatentProvider的这个过程。因此,查询数据需要经过NameValueCache的getStringForUser()方法,插入数据需要经过putStringForUser()方法。同时,NameValueCache还持有一个变量mValues,用于保存查询过的设置项,以便下下次再次发起查询时,能够快速返回。

操作SettingsProvider

由于Settings.java对使用SettingsProvider进行了封装,所以,使用起来相当简单简洁。由于Global、Secure、System三种数据类型的使用是几乎相同,所以本文就只以Global为例对查询插入数据的过程进行分析。

查询数据

从SettingsProvider的Global中查询数据,查询是否是飞行模式使用方法如下:

String globalValue = Settings.Global.getString(getContentResolver(), Settings.Global.AIRPLANE_MODE_ON);

分析一下getString()方法

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

public static String getString(ContentResolver resolver, String name) {
    return getStringForUser(resolver, name, UserHandle.myUserId());
}

/** @hide */
public static String getStringForUser(ContentResolver resolver, String name,
        int userHandle) {
    //因为在Android系统的更新中,保存在Global、Secure、System三种类型的数据存放位置有变化
    //需要添加判断兼容老版本的使用方法。
    if (MOVED_TO_SECURE.contains(name)) {
        Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global"
                + " to android.provider.Settings.Secure, returning read-only value.");
        return Secure.getStringForUser(resolver, name, userHandle);
    }
    return sNameValueCache.getStringForUser(resolver, name, userHandle);
}

NameValueCache.getStringForUser()方法,如下

public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
    final boolean isSelf = (userHandle == UserHandle.myUserId());
    if (isSelf) {
        ......
            } else if (mValues.containsKey(name)) {
                return mValues.get(name);
        ......
    IContentProvider cp = lazyGetProvider(cr);

    // Try the fast path first, not using query().  If this
    // fails (alternate Settings provider that doesn't support
    // this interface?) then we fall back to the query/table
    // interface.
    if (mCallGetCommand != null) {
        try {
            ......
            Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
            if (b != null) {
                String value = b.getString(Settings.NameValueTable.VALUE);
                ......
                        mValues.put(name, value);
                    }
                return value;
            }
        } catch (RemoteException e) {
            // Not supported by the remote side?  Fall through
            // to query().
        }
    }
    Cursor c = null;
    try {
        c = cp.query(cr.getPackageName(), mUri, SELECT_VALUE, NAME_EQ_PLACEHOLDER,
                     new String[]{name}, null, null);
        String value = c.moveToNext() ? c.getString(0) : null;
        synchronized (NameValueCache.this) {
            mValues.put(name, value);
        }
        return value;
        ......
     if (c != null) c.close();
    }
}
}

NameValueCache是Settings.java中的内部类,

首先从缓存mValues变量中去找,如果没有查询到,就调用SettingsProvider的call()接口,如果call()接口也没有查询到,再调用query()接口。这里用的是call()接口,下文就以call()接口往下分析。cp.call()会调用到SettingsProvider的call()方法,读者可以阅读《Android System Server大纲之ContentService和ContentProvider原理剖析》了解ConatentProvider的这个过程。注意参数mCallGetCommand。

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

public Bundle call(String method, String name, Bundle args) {
    final int requestingUserId = getRequestingUserId(args);
    switch (method) {
        case Settings.CALL_METHOD_GET_GLOBAL: {
            Setting setting = getGlobalSetting(name);
            return packageValueForCallResult(setting, isTrackingGeneration(args));
        }

        case Settings.CALL_METHOD_GET_SECURE: {
            Setting setting = getSecureSetting(name, requestingUserId);
            return packageValueForCallResult(setting, isTrackingGeneration(args));
        }
        ......
    }

    return null;
}

上层传过来的参数的command是mCallGetCommand,即Settings.CALL_METHOD_GET_GLOBAL,所以调用getGlobalSettings(),getGlobalSetting()返回的Setting setting是封装了设置项name、value等信息的,查看getGlobalSetting()方法:

private Setting getGlobalSetting(String name) {
    // Get the value.
    synchronized (mLock) {
        return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL,
                UserHandle.USER_SYSTEM, name);
    }
}

通过mSettingsRegistry.getSettingLocked()继续寻找:

public Setting getSettingLocked(int type, int userId, String name) {
    final int key = makeKey(type, userId);

    SettingsState settingsState = peekSettingsStateLocked(key);
    return settingsState.getSettingLocked(name);
}

通过peekSettingsStateLocked(key)寻找SettingsState:

private SettingsState peekSettingsStateLocked(int key) {
    SettingsState settingsState = mSettingsStates.get(key);
    if (settingsState != null) {
        return settingsState;
    }

    ensureSettingsForUserLocked(getUserIdFromKey(key));
    return mSettingsStates.get(key);
}

获取到SettingsState settingsState,回到getSettingLocked(),调用settingsState.getSettingLocked(name):

public Setting getSettingLocked(String name) {
    if (TextUtils.isEmpty(name)) {
        return mNullSetting;
    }
    Setting setting = mSettings.get(name);
    if (setting != null) {
        return new Setting(setting);
    }
    return mNullSetting;
}

到getSettingsLocked()最终获取到Setting setting。

插入数据

从SettingsProvider的Global中插入数据,插入飞行模式的使用方法如下:

boolean isSuccess = Settings.System.putInt(getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);

和查询代码一样,代码非常简洁,整个查询过程几乎类似

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

public static boolean putInt(ContentResolver cr, String name, int value) {
    return putString(cr, name, Integer.toString(value));
}
public static boolean putString(ContentResolver resolver,
        String name, String value) {
    return putStringForUser(resolver, name, value, UserHandle.myUserId());
}
public static boolean putStringForUser(ContentResolver resolver,
        String name, String value, int userHandle) {
    ......
    return sNameValueCache.putStringForUser(resolver, name, value, userHandle);
}

和查询一样,继续看putStringForUser():

public boolean putStringForUser(ContentResolver cr, String name, String value,
        final int userHandle) {
    try {
        Bundle arg = new Bundle();
        arg.putString(Settings.NameValueTable.VALUE, value);
        arg.putInt(CALL_METHOD_USER_KEY, userHandle);
        IContentProvider cp = lazyGetProvider(cr);
        cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
    } catch (RemoteException e) {
        Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
        return false;
    }
    return true;
}

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

public Bundle call(String method, String name, Bundle args) {
    final int requestingUserId = getRequestingUserId(args);
    switch (method) {
        ......

        case Settings.CALL_METHOD_PUT_GLOBAL: {
            String value = getSettingValue(args);
            insertGlobalSetting(name, value, requestingUserId, false);
            break;
        }

        case Settings.CALL_METHOD_PUT_SECURE: {
            String value = getSettingValue(args);
            insertSecureSetting(name, value, requestingUserId, false);
            break;
        }

        ......
    }

    return null;
}

首先调用getSettingValue(args)获取对应的设置项,接着insertGlobalSetting()方法:

private boolean insertGlobalSetting(String name, String value, int requestingUserId,
        boolean forceNotify) {
    return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT,
            forceNotify);
}

直接调用了mutateGlobalSetting()方法:

private boolean mutateGlobalSetting(String name, String value, int requestingUserId,
        int operation, boolean forceNotify) {
    // Make sure the caller can change the settings - treated as secure.
    enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);

    // If this is a setting that is currently restricted for this user, do not allow
    // unrestricting changes.
    if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value)) {
        return false;
    }

    // Perform the mutation.
    synchronized (mLock) {
        switch (operation) {
            case MUTATION_OPERATION_INSERT: {
                return mSettingsRegistry
                        .insertSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
                                name, value, getCallingPackage(), forceNotify);
            }

            ......
        }
    }
    return false;
}

首先进行权限检查,然后调用mSettingsRegistry.insertSettingLocked()方法:

public boolean insertSettingLocked(int type, int userId, String name, String value,
        String packageName, boolean forceNotify) {
    final int key = makeKey(type, userId);

    SettingsState settingsState = peekSettingsStateLocked(key);
    final boolean success = settingsState.insertSettingLocked(name, value, packageName);

    if (forceNotify || success) {
        notifyForSettingsChange(key, name);
    }
    return success;
}

新增数据保存到数据库

以新增一个"intercept_back"来获取back键是否被禁用,数据类型是 System integer类型。

定义变量:

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

public static final class System extends NameValueTable {
..
...
		/**
		*Enable / disable back key interface
		*0 = enable
		*1 = disable
		*@hide
		*/
		public static final String INTERCEPT_BACK = "intercept_back";
        public static final String[] SETTINGS_TO_BACKUP = {
            STAY_ON_WHILE_PLUGGED_IN,   // moved to global
            WIFI_USE_STATIC_IP,
            WIFI_STATIC_IP,
            WIFI_STATIC_GATEWAY,
            WIFI_STATIC_NETMASK,
            ......
            SHOW_BATTERY_PERCENT,
			INTERCEPT_BACK  //add by wangjin in 2019/01/09
        };

}

注意:

在这一步一定要加/** @hide */(一定是/** */格式),不然编译会报以下错误:

Checking API: checkpublicapi-current
out/target/common/obj/PACKAGING/public_api.txt:20: error 5: Added public field android.Manifest.permission.BACKUP
out/target/common/obj/PACKAGING/public_api.txt:82: error 5: Added public field android.Manifest.permission.INVOKE_CARRIER_SETUP
out/target/common/obj/PACKAGING/public_api.txt:106: error 5: Added public field android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
out/target/common/obj/PACKAGING/public_api.txt:116: error 5: Added public field android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST
 
******************************
You have tried to change the API from what has been previously approved.
 
To make these errors go away, you have two choices:
   1) You can add "@hide" javadoc comments to the methods, etc. listed in the
      errors above.
 
   2) You can update current.txt by executing the following command:
         make update-api
 
      To submit the revised current.txt to the main Android repository,
      you will need approval.

设置默认值

vendor/mediatek/proprietary/packages/apps/SettingsProvider/res/values/defaults.xml  (mtk)

0

加载该值

private void loadSystemSettings(SQLiteDatabase db) {
   loadIntegerSetting(stmt,Settings.System.INTERCEPT_BACK, R.integer.intercept_back);
}

 

你可能感兴趣的:(Android之路,源码分析)