Add way to migrate CM specific settings to CMSettingsProvider
issue-id: CYNGNOS-829 Change-Id: I08743ebf9ffd3846ae501ed41e396b1556dc41cf
This commit is contained in:
parent
8fc6affd38
commit
05d0129478
|
@ -27,7 +27,7 @@ LOCAL_PACKAGE_NAME := CMSettingsProvider
|
|||
LOCAL_CERTIFICATE := platform
|
||||
LOCAL_PRIVILEGED_MODULE := true
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := \
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
org.cyanogenmod.platform.sdk
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
|
|
@ -31,12 +31,22 @@
|
|||
android:allowClearUserData="false"
|
||||
android:enabled="true">
|
||||
|
||||
<provider android:name="CMSettingsProvider" android:authorities="cmsettings"
|
||||
<provider android:name="CMSettingsProvider"
|
||||
android:authorities="cmsettings"
|
||||
android:multiprocess="false"
|
||||
android:exported="true"
|
||||
android:writePermission="cyanogenmod.permission.WRITE_SETTINGS"
|
||||
android:singleUser="true"
|
||||
android:initOrder="100" />
|
||||
|
||||
<receiver android:name="PreBootReceiver" android:enabled="true">
|
||||
<!-- This broadcast is sent after the core system has finished
|
||||
booting, before the home app is launched or BOOT_COMPLETED
|
||||
is sent. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PRE_BOOT_COMPLETED"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2014-2015 The CyanogenMod Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
<!-- Defaults for System -->
|
||||
|
||||
<!-- Default for CMSettings.System.QS_QUICK_PULLDOWN
|
||||
0. Off
|
||||
1. Right
|
||||
2. Left -->
|
||||
<integer name="def_qs_quick_pulldown">0</integer>
|
||||
|
||||
<!-- Defaults for Secure -->
|
||||
|
||||
<!-- Default for CMSettings.Secure.ADVANCED_MODE -->
|
||||
<bool name="def_advanced_mode">true</bool>
|
||||
|
||||
<!-- Default for CMSettings.Secure.DEFAULT_THEME_COMPONENTS -->
|
||||
<string name="def_theme_components"></string>
|
||||
|
||||
<!-- Default for CMSettings.Secure.DEFAULT_THEME_PACKAGE -->
|
||||
<string name="def_theme_package"></string>
|
||||
|
||||
<!-- Defaults for CMSettings.Secure.DEV_FORCE_SHOW_NAVBAR -->
|
||||
<integer name="def_force_show_navbar">0</integer>
|
||||
|
||||
<!-- Default for CMSettings.Secure.QS_TILES
|
||||
Comma-delimited, quick settings tiles. See QSConstants.java for a list of all available tiles -->
|
||||
<string name="def_qs_tiles">wifi,bt,cell,airplane,rotation,flashlight,location,cast,visualizer,hotspot,live_display</string>
|
||||
|
||||
<!-- Default for CMSettings.Secure.STATS_COLLECTION -->
|
||||
<bool name="def_stats_collection">false</bool>
|
||||
|
||||
<!-- Defaults for Global -->
|
||||
|
||||
<!-- Default for CMSettings.Global.DEVICE_NAME
|
||||
$1=MODEL -->
|
||||
<string name="def_device_name">%1$s</string>
|
||||
|
||||
<!-- Default for CMSettings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
|
||||
1==on -->
|
||||
<integer name="def_heads_up_enabled">1</integer>
|
||||
|
||||
</resources>
|
|
@ -16,12 +16,16 @@
|
|||
|
||||
package org.cyanogenmod.cmsettings;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import cyanogenmod.providers.CMSettings;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
@ -34,7 +38,7 @@ public class CMDatabaseHelper extends SQLiteOpenHelper{
|
|||
private static final boolean LOCAL_LOGV = false;
|
||||
|
||||
private static final String DATABASE_NAME = "cmsettings.db";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
private static final int DATABASE_VERSION = 2;
|
||||
|
||||
static class CMTableNames {
|
||||
static final String TABLE_SYSTEM = "system";
|
||||
|
@ -50,6 +54,11 @@ public class CMDatabaseHelper extends SQLiteOpenHelper{
|
|||
|
||||
private static final String CREATE_INDEX_SQL_FORMAT = "CREATE INDEX %sIndex%d ON %s (name);";
|
||||
|
||||
private static final String DROP_TABLE_SQL_FORMAT = "DROP TABLE IF EXISTS %s;";
|
||||
|
||||
private static final String DROP_INDEX_SQL_FORMAT = "DROP INDEX IF EXISTS %sIndex%d;";
|
||||
|
||||
private Context mContext;
|
||||
private int mUserHandle;
|
||||
|
||||
/**
|
||||
|
@ -77,11 +86,13 @@ public class CMDatabaseHelper extends SQLiteOpenHelper{
|
|||
*/
|
||||
public CMDatabaseHelper(Context context, int userId) {
|
||||
super(context, dbNameForUser(userId), null, DATABASE_VERSION);
|
||||
mContext = context;
|
||||
mUserHandle = userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates System, Secure, and Global tables in the specified {@link SQLiteDatabase}
|
||||
* Creates System, Secure, and Global tables in the specified {@link SQLiteDatabase} and loads
|
||||
* default values into the created tables.
|
||||
* @param db The database.
|
||||
*/
|
||||
@Override
|
||||
|
@ -96,9 +107,11 @@ public class CMDatabaseHelper extends SQLiteOpenHelper{
|
|||
createDbTable(db, CMTableNames.TABLE_GLOBAL);
|
||||
}
|
||||
|
||||
loadSettings(db);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
||||
if (LOCAL_LOGV) Log.v(TAG, "Successfully created tables for cm settings db");
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Successfully created tables for cm settings db");
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
@ -106,11 +119,11 @@ public class CMDatabaseHelper extends SQLiteOpenHelper{
|
|||
|
||||
/**
|
||||
* Creates a table and index for the specified database and table name
|
||||
* @param db
|
||||
* @param tableName
|
||||
* @param db The {@link SQLiteDatabase} to create the table and index in.
|
||||
* @param tableName The name of the database table to create.
|
||||
*/
|
||||
private void createDbTable(SQLiteDatabase db, String tableName) {
|
||||
if (LOCAL_LOGV) Log.v(TAG, "Creating table and index for: " + tableName);
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Creating table and index for: " + tableName);
|
||||
|
||||
String createTableSql = String.format(CREATE_TABLE_SQL_FORMAT, tableName);
|
||||
db.execSQL(createTableSql);
|
||||
|
@ -121,6 +134,154 @@ public class CMDatabaseHelper extends SQLiteOpenHelper{
|
|||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
int upgradeVersion = oldVersion;
|
||||
|
||||
if (upgradeVersion < 2) {
|
||||
db.beginTransaction();
|
||||
try {
|
||||
loadSettings(db);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
|
||||
upgradeVersion = 2;
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
// *** Remember to update DATABASE_VERSION above!
|
||||
|
||||
if (upgradeVersion < newVersion) {
|
||||
Log.w(TAG, "Got stuck trying to upgrade db. Old version: " + oldVersion
|
||||
+ ", version stuck at: " + upgradeVersion + ", new version: "
|
||||
+ newVersion + ". Must wipe the cm settings provider.");
|
||||
|
||||
dropDbTable(db, CMTableNames.TABLE_SYSTEM);
|
||||
dropDbTable(db, CMTableNames.TABLE_SECURE);
|
||||
|
||||
if (mUserHandle == UserHandle.USER_OWNER) {
|
||||
dropDbTable(db, CMTableNames.TABLE_GLOBAL);
|
||||
}
|
||||
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the table and index for the specified database and table name
|
||||
* @param db The {@link SQLiteDatabase} to drop the table and index in.
|
||||
* @param tableName The name of the database table to drop.
|
||||
*/
|
||||
private void dropDbTable(SQLiteDatabase db, String tableName) {
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Dropping table and index for: " + tableName);
|
||||
|
||||
String dropTableSql = String.format(DROP_TABLE_SQL_FORMAT, tableName);
|
||||
db.execSQL(dropTableSql);
|
||||
|
||||
String dropIndexSql = String.format(DROP_INDEX_SQL_FORMAT, tableName, 1);
|
||||
db.execSQL(dropIndexSql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads default values for specific settings into the database.
|
||||
* @param db The {@link SQLiteDatabase} to insert into.
|
||||
*/
|
||||
private void loadSettings(SQLiteDatabase db) {
|
||||
// System
|
||||
loadIntegerSetting(db, CMTableNames.TABLE_SYSTEM, CMSettings.System.QS_QUICK_PULLDOWN,
|
||||
R.integer.def_qs_quick_pulldown);
|
||||
|
||||
// Secure
|
||||
loadBooleanSetting(db, CMTableNames.TABLE_SECURE, CMSettings.Secure.ADVANCED_MODE,
|
||||
R.bool.def_advanced_mode);
|
||||
|
||||
loadStringSetting(db, CMTableNames.TABLE_SECURE, CMSettings.Secure.DEFAULT_THEME_COMPONENTS,
|
||||
R.string.def_theme_components);
|
||||
|
||||
loadStringSetting(db, CMTableNames.TABLE_SECURE, CMSettings.Secure.DEFAULT_THEME_PACKAGE,
|
||||
R.string.def_theme_package);
|
||||
|
||||
loadIntegerSetting(db, CMTableNames.TABLE_SECURE, CMSettings.Secure.DEV_FORCE_SHOW_NAVBAR,
|
||||
R.integer.def_force_show_navbar);
|
||||
|
||||
loadStringSetting(db, CMTableNames.TABLE_SECURE, CMSettings.Secure.QS_TILES,
|
||||
R.string.def_qs_tiles);
|
||||
|
||||
loadBooleanSetting(db, CMTableNames.TABLE_SECURE, CMSettings.Secure.STATS_COLLECTION,
|
||||
R.bool.def_stats_collection);
|
||||
|
||||
// Global
|
||||
if (mUserHandle == UserHandle.USER_OWNER) {
|
||||
loadSettingsForTable(db, CMTableNames.TABLE_GLOBAL, CMSettings.Global.DEVICE_NAME,
|
||||
getDefaultDeviceName());
|
||||
|
||||
loadIntegerSetting(db, CMTableNames.TABLE_GLOBAL,
|
||||
CMSettings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
|
||||
R.integer.def_heads_up_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a string resource into a database table. If a conflict occurs, that value is not
|
||||
* inserted into the database table.
|
||||
* @param db The {@link SQLiteDatabase} to insert into.
|
||||
* @param tableName The name of the table to insert into.
|
||||
* @param name The name of the value to insert into the table.
|
||||
* @param resId The name of the string resource.
|
||||
*/
|
||||
private void loadStringSetting(SQLiteDatabase db, String tableName, String name, int resId) {
|
||||
loadSettingsForTable(db, tableName, name, mContext.getResources().getString(resId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a boolean resource into a database table. If a conflict occurs, that value is not
|
||||
* inserted into the database table.
|
||||
* @param db The {@link SQLiteDatabase} to insert into.
|
||||
* @param tableName The name of the table to insert into.
|
||||
* @param name The name of the value to insert into the table.
|
||||
* @param resId The name of the boolean resource.
|
||||
*/
|
||||
private void loadBooleanSetting(SQLiteDatabase db, String tableName, String name, int resId) {
|
||||
loadSettingsForTable(db, tableName, name,
|
||||
mContext.getResources().getBoolean(resId) ? "1" : "0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an integer resource into a database table. If a conflict occurs, that value is not
|
||||
* inserted into the database table.
|
||||
* @param db The {@link SQLiteDatabase} to insert into.
|
||||
* @param tableName The name of the table to insert into.
|
||||
* @param name The name of the value to insert into the table.
|
||||
* @param resId The name of the integer resource.
|
||||
*/
|
||||
private void loadIntegerSetting(SQLiteDatabase db, String tableName, String name, int resId) {
|
||||
loadSettingsForTable(db, tableName, name,
|
||||
Integer.toString(mContext.getResources().getInteger(resId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a name/value pair into a database table. If a conflict occurs, that value is not
|
||||
* inserted into the database table.
|
||||
* @param db The {@link SQLiteDatabase} to insert into.
|
||||
* @param tableName The name of the table to insert into.
|
||||
* @param name The name of the value to insert into the table.
|
||||
* @param value The value to insert into the table.
|
||||
*/
|
||||
private void loadSettingsForTable(SQLiteDatabase db, String tableName, String name,
|
||||
String value) {
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Loading key: " + name + ", value: " + value);
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(Settings.NameValueTable.NAME, name);
|
||||
contentValues.put(Settings.NameValueTable.VALUE, value);
|
||||
|
||||
db.insertWithOnConflict(tableName, null, contentValues, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Gets the default device name
|
||||
*/
|
||||
private String getDefaultDeviceName() {
|
||||
return mContext.getResources().getString(R.string.def_device_name, Build.MODEL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,26 +16,41 @@
|
|||
|
||||
package org.cyanogenmod.cmsettings;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.UriMatcher;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.AbstractCursor;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import cyanogenmod.providers.CMSettings;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The CMSettingsProvider serves as a {@link ContentProvider} for CM specific settings
|
||||
*/
|
||||
|
@ -45,6 +60,10 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
|
||||
private static final boolean USER_CHECK_THROWS = true;
|
||||
|
||||
private static final String PREF_HAS_MIGRATED_CM_SETTINGS = "has_migrated_cm_settings";
|
||||
|
||||
private static final Bundle NULL_SETTING = Bundle.forPair("value", null);
|
||||
|
||||
// Each defined user has their own settings
|
||||
protected final SparseArray<CMDatabaseHelper> mDbHelpers = new SparseArray<CMDatabaseHelper>();
|
||||
|
||||
|
@ -52,6 +71,13 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
private static final int SECURE = 2;
|
||||
private static final int GLOBAL = 3;
|
||||
|
||||
private static final int SYSTEM_ITEM_NAME = 4;
|
||||
private static final int SECURE_ITEM_NAME = 5;
|
||||
private static final int GLOBAL_ITEM_NAME = 6;
|
||||
|
||||
private static final String ITEM_MATCHER = "/*";
|
||||
private static final String NAME_SELECTION = Settings.NameValueTable.NAME + " = ?";
|
||||
|
||||
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
|
||||
static {
|
||||
|
@ -61,11 +87,17 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
SECURE);
|
||||
sUriMatcher.addURI(CMSettings.AUTHORITY, CMDatabaseHelper.CMTableNames.TABLE_GLOBAL,
|
||||
GLOBAL);
|
||||
// TODO add other paths for getting specific items
|
||||
sUriMatcher.addURI(CMSettings.AUTHORITY, CMDatabaseHelper.CMTableNames.TABLE_SYSTEM +
|
||||
ITEM_MATCHER, SYSTEM_ITEM_NAME);
|
||||
sUriMatcher.addURI(CMSettings.AUTHORITY, CMDatabaseHelper.CMTableNames.TABLE_SECURE +
|
||||
ITEM_MATCHER, SECURE_ITEM_NAME);
|
||||
sUriMatcher.addURI(CMSettings.AUTHORITY, CMDatabaseHelper.CMTableNames.TABLE_GLOBAL +
|
||||
ITEM_MATCHER, GLOBAL_ITEM_NAME);
|
||||
}
|
||||
|
||||
private UserManager mUserManager;
|
||||
private Uri.Builder mUriBuilder;
|
||||
private SharedPreferences mSharedPrefs;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
|
@ -79,36 +111,349 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
mUriBuilder.scheme(ContentResolver.SCHEME_CONTENT);
|
||||
mUriBuilder.authority(CMSettings.AUTHORITY);
|
||||
|
||||
// TODO Add migration for cm settings
|
||||
mSharedPrefs = getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE);
|
||||
|
||||
IntentFilter userFilter = new IntentFilter();
|
||||
userFilter.addAction(Intent.ACTION_USER_REMOVED);
|
||||
getContext().registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
|
||||
UserHandle.USER_OWNER);
|
||||
String action = intent.getAction();
|
||||
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Received intent: " + action + " for user: " + userId);
|
||||
|
||||
if (action.equals(Intent.ACTION_USER_REMOVED)) {
|
||||
onUserRemoved(userId);
|
||||
}
|
||||
}
|
||||
}, userFilter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// region Migration Methods
|
||||
|
||||
/**
|
||||
* Migrates CM settings for all existing users if this has not been run before.
|
||||
*/
|
||||
private void migrateCMSettingsForExistingUsersIfNeeded() {
|
||||
boolean hasMigratedCMSettings = mSharedPrefs.getBoolean(PREF_HAS_MIGRATED_CM_SETTINGS,
|
||||
false);
|
||||
|
||||
if (!hasMigratedCMSettings) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
for (UserInfo user : mUserManager.getUsers()) {
|
||||
migrateCMSettingsForUser(user.id);
|
||||
}
|
||||
|
||||
mSharedPrefs.edit().putBoolean(PREF_HAS_MIGRATED_CM_SETTINGS, true).commit();
|
||||
|
||||
// TODO: Add this as part of a boot message to the UI
|
||||
long timeDiffMillis = System.currentTimeMillis() - startTime;
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Migration finished in " + timeDiffMillis + " milliseconds");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates CM settings for a specific user.
|
||||
* @param userId The id of the user to run CM settings migration for.
|
||||
*/
|
||||
private void migrateCMSettingsForUser(int userId) {
|
||||
synchronized (this) {
|
||||
if (LOCAL_LOGV) Log.d(TAG, "CM settings will be migrated for user id: " + userId);
|
||||
|
||||
// Migrate system settings
|
||||
HashMap<String, String> systemToCmSettingsMap = new HashMap<String, String>();
|
||||
systemToCmSettingsMap.put(Settings.System.QS_QUICK_PULLDOWN,
|
||||
CMSettings.System.QS_QUICK_PULLDOWN);
|
||||
|
||||
int rowsMigrated = migrateCMSettingsForTable(userId,
|
||||
CMDatabaseHelper.CMTableNames.TABLE_SYSTEM, systemToCmSettingsMap);
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Migrated " + rowsMigrated + " to CM system table");
|
||||
|
||||
// Migrate secure settings
|
||||
HashMap<String, String> secureToCmSettingsMap = new HashMap<String, String>();
|
||||
secureToCmSettingsMap.put(Settings.Secure.ADVANCED_MODE,
|
||||
CMSettings.Secure.ADVANCED_MODE);
|
||||
secureToCmSettingsMap.put(Settings.Secure.BUTTON_BACKLIGHT_TIMEOUT,
|
||||
CMSettings.Secure.BUTTON_BACKLIGHT_TIMEOUT);
|
||||
secureToCmSettingsMap.put(Settings.Secure.BUTTON_BRIGHTNESS,
|
||||
CMSettings.Secure.BUTTON_BRIGHTNESS);
|
||||
secureToCmSettingsMap.put(Settings.Secure.DEFAULT_THEME_COMPONENTS,
|
||||
CMSettings.Secure.DEFAULT_THEME_COMPONENTS);
|
||||
secureToCmSettingsMap.put(Settings.Secure.DEFAULT_THEME_PACKAGE,
|
||||
CMSettings.Secure.DEFAULT_THEME_PACKAGE);
|
||||
secureToCmSettingsMap.put(Settings.Secure.DEV_FORCE_SHOW_NAVBAR,
|
||||
CMSettings.Secure.DEV_FORCE_SHOW_NAVBAR);
|
||||
secureToCmSettingsMap.put(
|
||||
Configuration.THEME_PKG_CONFIGURATION_PERSISTENCE_PROPERTY,
|
||||
CMSettings.Secure.NAME_THEME_CONFIG);
|
||||
secureToCmSettingsMap.put(Settings.Secure.KEYBOARD_BRIGHTNESS,
|
||||
CMSettings.Secure.KEYBOARD_BRIGHTNESS);
|
||||
secureToCmSettingsMap.put(Settings.Secure.POWER_MENU_ACTIONS,
|
||||
CMSettings.Secure.POWER_MENU_ACTIONS);
|
||||
secureToCmSettingsMap.put(Settings.Secure.STATS_COLLECTION,
|
||||
CMSettings.Secure.STATS_COLLECTION);
|
||||
secureToCmSettingsMap.put(Settings.Secure.QS_SHOW_BRIGHTNESS_SLIDER,
|
||||
CMSettings.Secure.QS_SHOW_BRIGHTNESS_SLIDER);
|
||||
secureToCmSettingsMap.put(Settings.Secure.QS_TILES,
|
||||
CMSettings.Secure.QS_TILES);
|
||||
secureToCmSettingsMap.put(Settings.Secure.QS_USE_MAIN_TILES,
|
||||
CMSettings.Secure.QS_USE_MAIN_TILES);
|
||||
secureToCmSettingsMap.put(Settings.Secure.VOLUME_LINK_NOTIFICATION,
|
||||
CMSettings.Secure.VOLUME_LINK_NOTIFICATION);
|
||||
|
||||
int navRingTargetsLength = Settings.Secure.NAVIGATION_RING_TARGETS.length;
|
||||
int cmNavRingTargetsLength = CMSettings.Secure.NAVIGATION_RING_TARGETS.length;
|
||||
int minNavRingTargetsLength = navRingTargetsLength <= cmNavRingTargetsLength ?
|
||||
navRingTargetsLength : cmNavRingTargetsLength;
|
||||
|
||||
for (int i = 0; i < minNavRingTargetsLength; i++) {
|
||||
systemToCmSettingsMap.put(Settings.Secure.NAVIGATION_RING_TARGETS[i],
|
||||
CMSettings.Secure.NAVIGATION_RING_TARGETS[i]);
|
||||
}
|
||||
|
||||
rowsMigrated = migrateCMSettingsForTable(userId,
|
||||
CMDatabaseHelper.CMTableNames.TABLE_SECURE, secureToCmSettingsMap);
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Migrated " + rowsMigrated + " to CM secure table");
|
||||
|
||||
// Migrate global settings
|
||||
if (userId == UserHandle.USER_OWNER) {
|
||||
HashMap<String, String> globalToCmSettingsMap = new HashMap<String, String>();
|
||||
globalToCmSettingsMap.put(Settings.Global.DEVICE_NAME,
|
||||
CMSettings.Global.DEVICE_NAME);
|
||||
globalToCmSettingsMap.put(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
|
||||
CMSettings.Global.HEADS_UP_NOTIFICATIONS_ENABLED);
|
||||
|
||||
rowsMigrated = migrateCMSettingsForTable(userId,
|
||||
CMDatabaseHelper.CMTableNames.TABLE_GLOBAL, globalToCmSettingsMap);
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Migrated " + rowsMigrated + " to CM global table");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates CM settings for a specific table and user id.
|
||||
* @param userId The id of the user to run CM settings migration for.
|
||||
* @param tableName The name of the table to run CM settings migration on.
|
||||
* @param settingsMap A mapping between key names in {@link Settings} and {@link CMSettings}
|
||||
* @return Number of rows migrated.
|
||||
*/
|
||||
private int migrateCMSettingsForTable(int userId, String tableName, HashMap<String,
|
||||
String> settingsMap) {
|
||||
ContentResolver contentResolver = getContext().getContentResolver();
|
||||
Set<Map.Entry<String, String>> entrySet = settingsMap.entrySet();
|
||||
ContentValues[] contentValues = new ContentValues[settingsMap.size()];
|
||||
|
||||
int migrateSettingsCount = 0;
|
||||
for (Map.Entry<String, String> keyPair : entrySet) {
|
||||
String settingsKey = keyPair.getKey();
|
||||
String cmSettingsKey = keyPair.getValue();
|
||||
String settingsValue = null;
|
||||
|
||||
if (tableName.equals(CMDatabaseHelper.CMTableNames.TABLE_SYSTEM)) {
|
||||
settingsValue = Settings.System.getStringForUser(contentResolver, settingsKey,
|
||||
userId);
|
||||
}
|
||||
else if (tableName.equals(CMDatabaseHelper.CMTableNames.TABLE_SECURE)) {
|
||||
settingsValue = Settings.Secure.getStringForUser(contentResolver, settingsKey,
|
||||
userId);
|
||||
}
|
||||
else if (tableName.equals(CMDatabaseHelper.CMTableNames.TABLE_GLOBAL)) {
|
||||
settingsValue = Settings.Global.getStringForUser(contentResolver, settingsKey,
|
||||
userId);
|
||||
}
|
||||
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Table: " + tableName + ", Key: " + settingsKey + ", Value: "
|
||||
+ settingsValue);
|
||||
|
||||
ContentValues contentValue = new ContentValues();
|
||||
contentValue.put(Settings.NameValueTable.NAME, cmSettingsKey);
|
||||
contentValue.put(Settings.NameValueTable.VALUE, settingsValue);
|
||||
contentValues[migrateSettingsCount++] = contentValue;
|
||||
}
|
||||
|
||||
int rowsInserted = 0;
|
||||
if (contentValues.length > 0) {
|
||||
Uri uri = mUriBuilder.build();
|
||||
uri = uri.buildUpon().appendPath(tableName).build();
|
||||
rowsInserted = bulkInsertForUser(userId, uri, contentValues);
|
||||
}
|
||||
|
||||
return rowsInserted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs cleanup for the removed user.
|
||||
* @param userId The id of the user that is removed.
|
||||
*/
|
||||
private void onUserRemoved(int userId) {
|
||||
synchronized (this) {
|
||||
// the db file itself will be deleted automatically, but we need to tear down
|
||||
// our helpers and other internal bookkeeping.
|
||||
|
||||
mDbHelpers.delete(userId);
|
||||
|
||||
if (LOCAL_LOGV) Log.d(TAG, "User " + userId + " is removed");
|
||||
}
|
||||
}
|
||||
|
||||
// endregion Migration Methods
|
||||
|
||||
// region Content Provider Methods
|
||||
|
||||
@Override
|
||||
public Bundle call(String method, String request, Bundle args) {
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Call method: " + method);
|
||||
|
||||
int callingUserId = UserHandle.getCallingUserId();
|
||||
if (args != null) {
|
||||
int reqUser = args.getInt(CMSettings.CALL_METHOD_USER_KEY, callingUserId);
|
||||
if (reqUser != callingUserId) {
|
||||
callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
|
||||
Binder.getCallingUid(), reqUser, false, true,
|
||||
"get/set setting for user", null);
|
||||
if (LOCAL_LOGV) Log.v(TAG, " access setting for user " + callingUserId);
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate methods
|
||||
if (CMSettings.CALL_METHOD_MIGRATE_SETTINGS.equals(method)) {
|
||||
migrateCMSettingsForExistingUsersIfNeeded();
|
||||
|
||||
return null;
|
||||
} else if (CMSettings.CALL_METHOD_MIGRATE_SETTINGS_FOR_USER.equals(method)) {
|
||||
migrateCMSettingsForUser(callingUserId);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get methods
|
||||
if (CMSettings.CALL_METHOD_GET_SYSTEM.equals(method)) {
|
||||
return lookupSingleValue(callingUserId, CMSettings.System.CONTENT_URI, request);
|
||||
}
|
||||
else if (CMSettings.CALL_METHOD_GET_SECURE.equals(method)) {
|
||||
return lookupSingleValue(callingUserId, CMSettings.Secure.CONTENT_URI, request);
|
||||
}
|
||||
else if (CMSettings.CALL_METHOD_GET_GLOBAL.equals(method)) {
|
||||
return lookupSingleValue(callingUserId, CMSettings.Global.CONTENT_URI, request);
|
||||
}
|
||||
|
||||
// Put methods - new value is in the args bundle under the key named by
|
||||
// the Settings.NameValueTable.VALUE static.
|
||||
final String newValue = (args == null)
|
||||
? null : args.getString(Settings.NameValueTable.VALUE);
|
||||
|
||||
// Framework can't do automatic permission checking for calls, so we need
|
||||
// to do it here.
|
||||
if (getContext().checkCallingOrSelfPermission(
|
||||
cyanogenmod.platform.Manifest.permission.WRITE_SETTINGS) !=
|
||||
PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException(
|
||||
String.format("Permission denial: writing to settings requires %1$s",
|
||||
cyanogenmod.platform.Manifest.permission.WRITE_SETTINGS));
|
||||
}
|
||||
|
||||
// Put methods
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(Settings.NameValueTable.NAME, request);
|
||||
values.put(Settings.NameValueTable.VALUE, newValue);
|
||||
|
||||
if (CMSettings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
|
||||
insertForUser(callingUserId, CMSettings.System.CONTENT_URI, values);
|
||||
}
|
||||
else if (CMSettings.CALL_METHOD_PUT_SECURE.equals(method)) {
|
||||
insertForUser(callingUserId, CMSettings.Secure.CONTENT_URI, values);
|
||||
}
|
||||
else if (CMSettings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
|
||||
insertForUser(callingUserId, CMSettings.Global.CONTENT_URI, values);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a single value for a specific user, uri, and key.
|
||||
* @param userId The id of the user to perform the lookup for.
|
||||
* @param uri The uri for which table to perform the lookup in.
|
||||
* @param key The key to perform the lookup with.
|
||||
* @return A single value stored in a {@link Bundle}.
|
||||
*/
|
||||
private Bundle lookupSingleValue(int userId, Uri uri, String key) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = queryForUser(userId, uri, new String[]{ Settings.NameValueTable.VALUE },
|
||||
Settings.NameValueTable.NAME + " = ?", new String[]{ key }, null);
|
||||
|
||||
if (cursor != null && cursor.getCount() == 1) {
|
||||
cursor.moveToFirst();
|
||||
String value = cursor.getString(0);
|
||||
return value == null ? NULL_SETTING : Bundle.forPair(Settings.NameValueTable.VALUE,
|
||||
value);
|
||||
}
|
||||
} catch (SQLiteException e) {
|
||||
Log.w(TAG, "settings lookup error", e);
|
||||
return null;
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
return NULL_SETTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
return queryForUser(UserHandle.getCallingUserId(), uri, projection, selection,
|
||||
selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a query for a specific user.
|
||||
* @param userId The id of the user to perform the query for.
|
||||
* @param uri The uri for which table to perform the query on. Optionally, the uri can end in
|
||||
* the name of a specific element to query for.
|
||||
* @param projection The columns that are returned in the {@link Cursor}.
|
||||
* @param selection The column names that the selection criteria applies to.
|
||||
* @param selectionArgs The column values that the selection criteria applies to.
|
||||
* @param sortOrder The ordering of how the values should be returned in the {@link Cursor}.
|
||||
* @return {@link Cursor} of the results from the query.
|
||||
*/
|
||||
private Cursor queryForUser(int userId, Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
||||
String tableName = getTableNameFromUri(uri);
|
||||
int code = sUriMatcher.match(uri);
|
||||
String tableName = getTableNameFromUriMatchCode(code);
|
||||
checkWritePermissions(tableName);
|
||||
|
||||
int callingUserId = UserHandle.getCallingUserId();
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName,
|
||||
callingUserId));
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName, userId));
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(tableName);
|
||||
|
||||
Cursor returnCursor = queryBuilder.query(db, projection, selection, selectionArgs, null,
|
||||
null, sortOrder);
|
||||
Cursor returnCursor;
|
||||
if (isItemUri(code)) {
|
||||
// The uri is looking for an element with a specific name
|
||||
returnCursor = queryBuilder.query(db, projection, NAME_SELECTION,
|
||||
new String[] { uri.getLastPathSegment() }, null, null, sortOrder);
|
||||
} else {
|
||||
returnCursor = queryBuilder.query(db, projection, selection, selectionArgs, null,
|
||||
null, sortOrder);
|
||||
}
|
||||
|
||||
// the default Cursor interface does not support per-user observation
|
||||
try {
|
||||
AbstractCursor abstractCursor = (AbstractCursor) returnCursor;
|
||||
abstractCursor.setNotificationUri(getContext().getContentResolver(), uri,
|
||||
callingUserId);
|
||||
abstractCursor.setNotificationUri(getContext().getContentResolver(), uri, userId);
|
||||
} catch (ClassCastException e) {
|
||||
// details of the concrete Cursor implementation have changed and this code has
|
||||
// not been updated to match -- complain and fail hard.
|
||||
|
@ -121,8 +466,14 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
int code = sUriMatcher.match(uri);
|
||||
String tableName = getTableNameFromUriMatchCode(code);
|
||||
|
||||
if (isItemUri(code)) {
|
||||
return "vnd.android.cursor.item/" + tableName;
|
||||
} else {
|
||||
return "vnd.android.cursor.dir/" + tableName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -166,8 +517,6 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
|
||||
if (rowId >= 0) {
|
||||
numRowsAffected++;
|
||||
|
||||
if (LOCAL_LOGV) Log.d(TAG, tableName + " <- " + values);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -179,7 +528,6 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
}
|
||||
|
||||
if (numRowsAffected > 0) {
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
notifyChange(uri, tableName, userId);
|
||||
if (LOCAL_LOGV) Log.d(TAG, tableName + ": " + numRowsAffected + " row(s) inserted");
|
||||
}
|
||||
|
@ -189,6 +537,18 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
return insertForUser(UserHandle.getCallingUserId(), uri, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs insert for a specific user.
|
||||
* @param userId The user id to perform the insert for.
|
||||
* @param uri The content:// URI of the insertion request.
|
||||
* @param values A sets of column_name/value pairs to add to the database.
|
||||
* This must not be {@code null}.
|
||||
* @return
|
||||
*/
|
||||
private Uri insertForUser(int userId, Uri uri, ContentValues values) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
@ -200,17 +560,16 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
String tableName = getTableNameFromUri(uri);
|
||||
checkWritePermissions(tableName);
|
||||
|
||||
int callingUserId = UserHandle.getCallingUserId();
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName,
|
||||
callingUserId));
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName, userId));
|
||||
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
long rowId = db.insert(tableName, null, values);
|
||||
|
||||
Uri returnUri = null;
|
||||
if (rowId > -1) {
|
||||
returnUri = ContentUris.withAppendedId(uri, rowId);
|
||||
notifyChange(returnUri, tableName, callingUserId);
|
||||
String name = values.getAsString(Settings.NameValueTable.NAME);
|
||||
returnUri = Uri.withAppendedPath(uri, name);
|
||||
notifyChange(returnUri, tableName, userId);
|
||||
if (LOCAL_LOGV) Log.d(TAG, "Inserted row id: " + rowId + " into tableName: " +
|
||||
tableName);
|
||||
}
|
||||
|
@ -235,8 +594,8 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
int callingUserId = UserHandle.getCallingUserId();
|
||||
CMDatabaseHelper dbHelper = getOrEstablishDatabase(getUserIdForTable(tableName,
|
||||
callingUserId));
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
|
||||
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||
numRowsAffected = db.delete(tableName, selection, selectionArgs);
|
||||
|
||||
if (numRowsAffected > 0) {
|
||||
|
@ -250,6 +609,11 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
// NOTE: update() is never called by the front-end CMSettings API, and updates that
|
||||
// wind up affecting rows in Secure that are globally shared will not have the
|
||||
// intended effect (the update will be invisible to the rest of the system).
|
||||
// This should have no practical effect, since writes to the Secure db can only
|
||||
// be done by system code, and that code should be using the correct API up front.
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Uri cannot be null");
|
||||
}
|
||||
|
@ -269,13 +633,15 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
int numRowsAffected = db.update(tableName, values, selection, selectionArgs);
|
||||
|
||||
if (numRowsAffected > 0) {
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
notifyChange(uri, tableName, callingUserId);
|
||||
if (LOCAL_LOGV) Log.d(TAG, tableName + ": " + numRowsAffected + " row(s) updated");
|
||||
}
|
||||
|
||||
return numRowsAffected;
|
||||
}
|
||||
|
||||
// endregion Content Provider Methods
|
||||
|
||||
/**
|
||||
* Tries to get a {@link CMDatabaseHelper} for the specified user and if it does not exist, a
|
||||
* new instance of {@link CMDatabaseHelper} is created for the specified user and returned.
|
||||
|
@ -356,6 +722,26 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the matched uri code refers to an item in a table
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
private boolean isItemUri(int code) {
|
||||
switch (code) {
|
||||
case SYSTEM:
|
||||
case SECURE:
|
||||
case GLOBAL:
|
||||
return false;
|
||||
case SYSTEM_ITEM_NAME:
|
||||
case SECURE_ITEM_NAME:
|
||||
case GLOBAL_ITEM_NAME:
|
||||
return true;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid uri match code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilizes an {@link UriMatcher} to check for a valid combination of scheme, authority, and
|
||||
* path and returns the corresponding table name
|
||||
|
@ -365,15 +751,27 @@ public class CMSettingsProvider extends ContentProvider {
|
|||
private String getTableNameFromUri(Uri uri) {
|
||||
int code = sUriMatcher.match(uri);
|
||||
|
||||
return getTableNameFromUriMatchCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the corresponding table name for the matched uri code
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
private String getTableNameFromUriMatchCode(int code) {
|
||||
switch (code) {
|
||||
case SYSTEM:
|
||||
case SYSTEM_ITEM_NAME:
|
||||
return CMDatabaseHelper.CMTableNames.TABLE_SYSTEM;
|
||||
case SECURE:
|
||||
case SECURE_ITEM_NAME:
|
||||
return CMDatabaseHelper.CMTableNames.TABLE_SECURE;
|
||||
case GLOBAL:
|
||||
case GLOBAL_ITEM_NAME:
|
||||
return CMDatabaseHelper.CMTableNames.TABLE_GLOBAL;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid uri: " + uri);
|
||||
throw new IllegalArgumentException("Invalid uri match code: " + code);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* Copyright (c) 2015, The CyanogenMod Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.cyanogenmod.cmsettings;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.IContentProvider;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import cyanogenmod.providers.CMSettings;
|
||||
|
||||
public class PreBootReceiver extends BroadcastReceiver{
|
||||
private static final String TAG = "CMSettingsReceiver";
|
||||
private static final boolean LOCAL_LOGV = false;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (LOCAL_LOGV) {
|
||||
Log.d(TAG, "Received pre boot intent. Attempting to migrate CM settings.");
|
||||
}
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
IContentProvider contentProvider = contentResolver.acquireProvider(
|
||||
CMSettings.AUTHORITY);
|
||||
|
||||
try{
|
||||
contentProvider.call(contentResolver.getPackageName(),
|
||||
CMSettings.CALL_METHOD_MIGRATE_SETTINGS, null, null);
|
||||
|
||||
context.getPackageManager().setComponentEnabledSetting(
|
||||
new ComponentName(context, getClass()),
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP);
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Failed to trigger settings migration due to RemoteException");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,10 @@
|
|||
|
||||
<uses-permission android:name="cyanogenmod.permission.WRITE_SETTINGS"/>
|
||||
<uses-permission android:name="cyanogenmod.permission.WRITE_SECURE_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<uses-permission android:name="android.permission.MANAGE_USERS" />
|
||||
|
||||
<instrumentation
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
|
|
|
@ -18,11 +18,19 @@ package org.cyanogenmod.cmsettings.tests;
|
|||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.IContentProvider;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.util.Log;
|
||||
import android.text.TextUtils;
|
||||
import cyanogenmod.providers.CMSettings;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -31,129 +39,219 @@ import java.util.Map;
|
|||
public class CMSettingsProviderTest extends AndroidTestCase {
|
||||
private static final String TAG = "CMSettingsProviderTest";
|
||||
|
||||
private static final LinkedHashMap<String, String> mMap = new LinkedHashMap<String, String>();
|
||||
private static final LinkedHashMap<String, String> sMap = new LinkedHashMap<String, String>();
|
||||
|
||||
static {
|
||||
mMap.put("testKey1", "value1");
|
||||
mMap.put("testKey2", "value2");
|
||||
mMap.put("testKey3", "value3");
|
||||
sMap.put("testKey1", "value1");
|
||||
sMap.put("testKey2", "value2");
|
||||
sMap.put("testKey3", "value3");
|
||||
}
|
||||
|
||||
private static final String[] PROJECTIONS = new String[] { "name", "value" };
|
||||
private static final String[] PROJECTIONS = new String[] { Settings.NameValueTable.NAME,
|
||||
Settings.NameValueTable.VALUE };
|
||||
|
||||
private ContentResolver mContentResolver;
|
||||
private UserManager mUserManager;
|
||||
private UserInfo mGuest;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
if (mGuest != null) {
|
||||
mUserManager.removeUser(mGuest.id);
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testMigrateCMSettingsForOtherUser() {
|
||||
// Make sure there's an owner
|
||||
assertTrue(findUser(mUserManager, UserHandle.USER_OWNER));
|
||||
|
||||
mGuest = mUserManager.createGuest(mContext, "GuestUser1");
|
||||
assertNotNull(mGuest);
|
||||
|
||||
testMigrateSettingsForUser(mGuest.id);
|
||||
}
|
||||
|
||||
private void testMigrateSettingsForUser(int userId) {
|
||||
// Setup values in Settings
|
||||
final String expectedPullDownValue = "testQuickPullDownValue";
|
||||
Settings.System.putStringForUser(mContentResolver, Settings.System.QS_QUICK_PULLDOWN,
|
||||
expectedPullDownValue, userId);
|
||||
|
||||
final int expectedKeyboardBrightness = 4;
|
||||
Settings.Secure.putIntForUser(mContentResolver, Settings.Secure.KEYBOARD_BRIGHTNESS,
|
||||
expectedKeyboardBrightness, userId);
|
||||
|
||||
Bundle arg = new Bundle();
|
||||
arg.putInt(CMSettings.CALL_METHOD_USER_KEY, userId);
|
||||
IContentProvider contentProvider = mContentResolver.acquireProvider(
|
||||
CMSettings.AUTHORITY);
|
||||
|
||||
try{
|
||||
// Trigger migrate settings for guest
|
||||
contentProvider.call(mContentResolver.getPackageName(),
|
||||
CMSettings.CALL_METHOD_MIGRATE_SETTINGS_FOR_USER, null, arg);
|
||||
} catch (RemoteException ex) {
|
||||
fail("Failed to trigger settings migration due to RemoteException");
|
||||
}
|
||||
|
||||
// Check values
|
||||
final String actualPullDownValue = CMSettings.System.getStringForUser(mContentResolver,
|
||||
CMSettings.System.QS_QUICK_PULLDOWN, userId);
|
||||
assertEquals(expectedPullDownValue, actualPullDownValue);
|
||||
|
||||
final int actualKeyboardBrightness = CMSettings.Secure.getIntForUser(mContentResolver,
|
||||
CMSettings.Secure.KEYBOARD_BRIGHTNESS, -1, userId);
|
||||
assertEquals(expectedKeyboardBrightness, actualKeyboardBrightness);
|
||||
}
|
||||
|
||||
private boolean findUser(UserManager userManager, int userHandle) {
|
||||
for (UserInfo user : userManager.getUsers()) {
|
||||
if (user.id == userHandle) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testBulkInsertSuccess() {
|
||||
Log.d(TAG, "Starting bulk insert test");
|
||||
|
||||
ContentValues[] contentValues = new ContentValues[mMap.size()];
|
||||
ContentValues[] contentValues = new ContentValues[sMap.size()];
|
||||
String[] keyValues = new String[sMap.size()];
|
||||
int count = 0;
|
||||
for (Map.Entry<String, String> kVPair : mMap.entrySet()) {
|
||||
for (Map.Entry<String, String> kVPair : sMap.entrySet()) {
|
||||
ContentValues contentValue = new ContentValues();
|
||||
contentValue.put(PROJECTIONS[0], kVPair.getKey());
|
||||
contentValue.put(PROJECTIONS[1], kVPair.getValue());
|
||||
|
||||
final String key = kVPair.getKey();
|
||||
contentValue.put(Settings.NameValueTable.NAME, key);
|
||||
keyValues[count] = key;
|
||||
|
||||
contentValue.put(Settings.NameValueTable.VALUE, kVPair.getValue());
|
||||
contentValues[count++] = contentValue;
|
||||
}
|
||||
|
||||
testBulkInsertForUri(CMSettings.System.CONTENT_URI, contentValues);
|
||||
testBulkInsertForUri(CMSettings.Secure.CONTENT_URI, contentValues);
|
||||
testBulkInsertForUri(CMSettings.Global.CONTENT_URI, contentValues);
|
||||
|
||||
Log.d(TAG, "Finished bulk insert test");
|
||||
testBulkInsertForUri(CMSettings.System.CONTENT_URI, contentValues, keyValues);
|
||||
testBulkInsertForUri(CMSettings.Secure.CONTENT_URI, contentValues, keyValues);
|
||||
testBulkInsertForUri(CMSettings.Global.CONTENT_URI, contentValues, keyValues);
|
||||
}
|
||||
|
||||
private void testBulkInsertForUri(Uri uri, ContentValues[] contentValues) {
|
||||
private void testBulkInsertForUri(Uri uri, ContentValues[] contentValues, String[] keyValues) {
|
||||
int rowsInserted = mContentResolver.bulkInsert(uri, contentValues);
|
||||
assertEquals(mMap.size(), rowsInserted);
|
||||
assertEquals(sMap.size(), rowsInserted);
|
||||
|
||||
Cursor queryCursor = mContentResolver.query(uri, PROJECTIONS, null, null, null);
|
||||
final String placeholderSymbol = "?";
|
||||
String[] placeholders = new String[contentValues.length];
|
||||
for (int i = 0; i < placeholders.length; i++) {
|
||||
placeholders[i] = placeholderSymbol;
|
||||
}
|
||||
|
||||
final String placeholdersString = TextUtils.join(",", placeholders);
|
||||
|
||||
Cursor queryCursor = mContentResolver.query(uri, PROJECTIONS,
|
||||
Settings.NameValueTable.NAME + " IN (" + placeholdersString + ")", keyValues,
|
||||
null);
|
||||
assertEquals(contentValues.length, queryCursor.getCount());
|
||||
try {
|
||||
while (queryCursor.moveToNext()) {
|
||||
assertEquals(PROJECTIONS.length, queryCursor.getColumnCount());
|
||||
|
||||
String actualKey = queryCursor.getString(0);
|
||||
assertTrue(mMap.containsKey(actualKey));
|
||||
assertTrue(sMap.containsKey(actualKey));
|
||||
|
||||
assertEquals(mMap.get(actualKey), queryCursor.getString(1));
|
||||
assertEquals(sMap.get(actualKey), queryCursor.getString(1));
|
||||
}
|
||||
|
||||
Log.d(TAG, "Test successful");
|
||||
}
|
||||
finally {
|
||||
queryCursor.close();
|
||||
}
|
||||
|
||||
// TODO: Find a better way to cleanup database/use ProviderTestCase2 without process crash
|
||||
for (String key : mMap.keySet()) {
|
||||
mContentResolver.delete(uri, PROJECTIONS[0] + " = ?", new String[]{ key });
|
||||
for (String key : sMap.keySet()) {
|
||||
mContentResolver.delete(uri, Settings.NameValueTable.NAME + " = ?",
|
||||
new String[]{ key });
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testInsertUpdateDeleteSuccess() {
|
||||
Log.d(TAG, "Starting insert/update/delete test");
|
||||
|
||||
testInsertUpdateDeleteForUri(CMSettings.System.CONTENT_URI);
|
||||
testInsertUpdateDeleteForUri(CMSettings.Secure.CONTENT_URI);
|
||||
testInsertUpdateDeleteForUri(CMSettings.Global.CONTENT_URI);
|
||||
|
||||
Log.d(TAG, "Finished insert/update/delete test");
|
||||
}
|
||||
|
||||
private void testInsertUpdateDeleteForUri(Uri uri) {
|
||||
String key1 = "testKey1";
|
||||
String key = "key";
|
||||
String value1 = "value1";
|
||||
String value2 = "value2";
|
||||
|
||||
// test insert
|
||||
ContentValues contentValue = new ContentValues();
|
||||
contentValue.put(PROJECTIONS[0], key1);
|
||||
contentValue.put(PROJECTIONS[1], value1);
|
||||
contentValue.put(Settings.NameValueTable.NAME, key);
|
||||
contentValue.put(Settings.NameValueTable.VALUE, value1);
|
||||
|
||||
mContentResolver.insert(uri, contentValue);
|
||||
Uri expectedUri = uri.withAppendedPath(uri, key);
|
||||
Uri returnUri = mContentResolver.insert(uri, contentValue);
|
||||
assertEquals(expectedUri, returnUri);
|
||||
|
||||
// check insert
|
||||
Cursor queryCursor = mContentResolver.query(uri, PROJECTIONS, null, null, null);
|
||||
assertEquals(1, queryCursor.getCount());
|
||||
Cursor queryCursor = null;
|
||||
try {
|
||||
// check insert
|
||||
queryCursor = mContentResolver.query(uri, PROJECTIONS, Settings.NameValueTable.NAME +
|
||||
" = ?", new String[]{ key }, null);
|
||||
assertEquals(1, queryCursor.getCount());
|
||||
|
||||
queryCursor.moveToNext();
|
||||
assertEquals(PROJECTIONS.length, queryCursor.getColumnCount());
|
||||
assertExpectedKeyValuePair(queryCursor, key, value1);
|
||||
|
||||
String actualKey = queryCursor.getString(0);
|
||||
assertEquals(key1, actualKey);
|
||||
assertEquals(value1, queryCursor.getString(1));
|
||||
// check insert with returned uri
|
||||
queryCursor = mContentResolver.query(returnUri, PROJECTIONS, null, null, null);
|
||||
assertEquals(1, queryCursor.getCount());
|
||||
|
||||
// test update
|
||||
contentValue.clear();
|
||||
contentValue.put(PROJECTIONS[1], value2);
|
||||
assertExpectedKeyValuePair(queryCursor, key, value1);
|
||||
|
||||
int rowsAffected = mContentResolver.update(uri, contentValue, PROJECTIONS[0] + " = ?",
|
||||
new String[]{key1});
|
||||
assertEquals(1, rowsAffected);
|
||||
// test update
|
||||
contentValue.clear();
|
||||
contentValue.put(Settings.NameValueTable.VALUE, value2);
|
||||
|
||||
// check update
|
||||
queryCursor = mContentResolver.query(uri, PROJECTIONS, null, null, null);
|
||||
assertEquals(1, queryCursor.getCount());
|
||||
int rowsAffected = mContentResolver.update(uri, contentValue,
|
||||
Settings.NameValueTable.NAME + " = ?", new String[]{ key });
|
||||
assertEquals(1, rowsAffected);
|
||||
|
||||
queryCursor.moveToNext();
|
||||
assertEquals(PROJECTIONS.length, queryCursor.getColumnCount());
|
||||
// check update
|
||||
queryCursor = mContentResolver.query(uri, PROJECTIONS, Settings.NameValueTable.NAME +
|
||||
" = ?", new String[]{ key }, null);
|
||||
assertEquals(1, queryCursor.getCount());
|
||||
|
||||
actualKey = queryCursor.getString(0);
|
||||
assertEquals(key1, actualKey);
|
||||
assertEquals(value2, queryCursor.getString(1));
|
||||
assertExpectedKeyValuePair(queryCursor, key, value2);
|
||||
|
||||
// test delete
|
||||
rowsAffected = mContentResolver.delete(uri, PROJECTIONS[0] + " = ?", new String[]{key1});
|
||||
assertEquals(1, rowsAffected);
|
||||
// test delete
|
||||
rowsAffected = mContentResolver.delete(uri, Settings.NameValueTable.NAME + " = ?",
|
||||
new String[]{ key });
|
||||
assertEquals(1, rowsAffected);
|
||||
|
||||
// check delete
|
||||
queryCursor = mContentResolver.query(uri, PROJECTIONS, null, null, null);
|
||||
assertEquals(0, queryCursor.getCount());
|
||||
// check delete
|
||||
queryCursor = mContentResolver.query(uri, PROJECTIONS, Settings.NameValueTable.NAME +
|
||||
" = ?", new String[]{ key }, null);
|
||||
assertEquals(0, queryCursor.getCount());
|
||||
} finally {
|
||||
if (queryCursor != null) {
|
||||
queryCursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertExpectedKeyValuePair(Cursor cursor, String expectedKey,
|
||||
String expectedValue) {
|
||||
cursor.moveToNext();
|
||||
assertEquals(PROJECTIONS.length, cursor.getColumnCount());
|
||||
|
||||
String actualKey = cursor.getString(0);
|
||||
assertEquals(expectedKey, actualKey);
|
||||
assertEquals(expectedValue, cursor.getString(1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.content.ContentResolver;
|
|||
import android.content.IContentProvider;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
|
@ -44,10 +45,57 @@ public final class CMSettings {
|
|||
}
|
||||
}
|
||||
|
||||
// region Call Methods
|
||||
|
||||
/**
|
||||
* @hide - User handle argument extra to the fast-path call()-based requests
|
||||
*/
|
||||
public static final String CALL_METHOD_USER_KEY = "_user";
|
||||
|
||||
/**
|
||||
* @hide - Private call() method on SettingsProvider to read from 'system' table.
|
||||
*/
|
||||
public static final String CALL_METHOD_GET_SYSTEM = "GET_system";
|
||||
|
||||
/**
|
||||
* @hide - Private call() method on SettingsProvider to read from 'secure' table.
|
||||
*/
|
||||
public static final String CALL_METHOD_GET_SECURE = "GET_secure";
|
||||
|
||||
/**
|
||||
* @hide - Private call() method on SettingsProvider to read from 'global' table.
|
||||
*/
|
||||
public static final String CALL_METHOD_GET_GLOBAL = "GET_global";
|
||||
|
||||
/**
|
||||
* @hide - Private call() method to write to 'system' table
|
||||
*/
|
||||
public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
|
||||
|
||||
/**
|
||||
* @hide - Private call() method to write to 'secure' table
|
||||
*/
|
||||
public static final String CALL_METHOD_PUT_SECURE = "PUT_secure";
|
||||
|
||||
/**
|
||||
* @hide - Private call() method to write to 'global' table
|
||||
*/
|
||||
public static final String CALL_METHOD_PUT_GLOBAL= "PUT_global";
|
||||
|
||||
/**
|
||||
* @hide - Private call() method on CMSettingsProvider to migrate CM settings
|
||||
*/
|
||||
public static final String CALL_METHOD_MIGRATE_SETTINGS = "migrate_settings";
|
||||
|
||||
/**
|
||||
* @hide - Private call() method on CMSettingsProvider to migrate CM settings for a user
|
||||
*/
|
||||
public static final String CALL_METHOD_MIGRATE_SETTINGS_FOR_USER = "migrate_settings_for_user";
|
||||
|
||||
// endregion
|
||||
|
||||
// Thread-safe.
|
||||
private static class NameValueCache {
|
||||
// TODO Add call options for fast path at insert and get
|
||||
|
||||
private final String mVersionSystemProperty;
|
||||
private final Uri mUri;
|
||||
|
||||
|
@ -62,9 +110,17 @@ public final class CMSettings {
|
|||
// Initially null; set lazily and held forever. Synchronized on 'this'.
|
||||
private IContentProvider mContentProvider = null;
|
||||
|
||||
public NameValueCache(String versionSystemProperty, Uri uri) {
|
||||
// The method we'll call (or null, to not use) on the provider
|
||||
// for the fast path of retrieving settings.
|
||||
private final String mCallGetCommand;
|
||||
private final String mCallSetCommand;
|
||||
|
||||
public NameValueCache(String versionSystemProperty, Uri uri,
|
||||
String getCommand, String setCommand) {
|
||||
mVersionSystemProperty = versionSystemProperty;
|
||||
mUri = uri;
|
||||
mCallGetCommand = getCommand;
|
||||
mCallSetCommand = setCommand;
|
||||
}
|
||||
|
||||
private IContentProvider lazyGetProvider(ContentResolver cr) {
|
||||
|
@ -79,7 +135,30 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets a a string value with the specified name from the name/value cache if possible. If
|
||||
* Puts a string name/value pair into the content provider for the specified user.
|
||||
* @param cr The content resolver to use.
|
||||
* @param name The name of the key to put into the content provider.
|
||||
* @param value The value to put into the content provider.
|
||||
* @param userId The user id to use for the content provider.
|
||||
* @return Whether the put was successful.
|
||||
*/
|
||||
public boolean putStringForUser(ContentResolver cr, String name, String value,
|
||||
final int userId) {
|
||||
try {
|
||||
Bundle arg = new Bundle();
|
||||
arg.putString(Settings.NameValueTable.VALUE, value);
|
||||
arg.putInt(CALL_METHOD_USER_KEY, userId);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string value with the specified name from the name/value cache if possible. If
|
||||
* not, it will use the content resolver and perform a query.
|
||||
* @param cr Content resolver to use if name/value cache does not contain the name or if
|
||||
* the cache version is older than the current version.
|
||||
|
@ -90,6 +169,7 @@ public final class CMSettings {
|
|||
public String getStringForUser(ContentResolver cr, String name, final int userId) {
|
||||
final boolean isSelf = (userId == UserHandle.myUserId());
|
||||
if (isSelf) {
|
||||
if (LOCAL_LOGV) Log.d(TAG, "get setting for self");
|
||||
long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0);
|
||||
|
||||
// Our own user's settings data uses a client-side cache
|
||||
|
@ -115,6 +195,40 @@ public final class CMSettings {
|
|||
|
||||
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 args = null;
|
||||
if (!isSelf) {
|
||||
args = new Bundle();
|
||||
args.putInt(CALL_METHOD_USER_KEY, userId);
|
||||
}
|
||||
Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
|
||||
if (b != null) {
|
||||
String value = b.getPairValue();
|
||||
// Don't update our cache for reads of other users' data
|
||||
if (isSelf) {
|
||||
synchronized (this) {
|
||||
mValues.put(name, value);
|
||||
}
|
||||
} else {
|
||||
if (LOCAL_LOGV) Log.i(TAG, "call-query of user " + userId
|
||||
+ " by " + UserHandle.myUserId()
|
||||
+ " so not updating cache");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
// If the response Bundle is null, we fall through
|
||||
// to the query interface below.
|
||||
} 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,
|
||||
|
@ -143,9 +257,8 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* System settings, containing miscellaneous CM system preferences. This
|
||||
* table holds simple name/value pairs. There are convenience
|
||||
* functions for accessing individual settings entries.
|
||||
* System settings, containing miscellaneous CM system preferences. This table holds simple
|
||||
* name/value pairs. There are convenience functions for accessing individual settings entries.
|
||||
*/
|
||||
public static final class System extends Settings.NameValueTable {
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/system");
|
||||
|
@ -154,10 +267,22 @@ public final class CMSettings {
|
|||
|
||||
private static final NameValueCache sNameValueCache = new NameValueCache(
|
||||
SYS_PROP_CM_SETTING_VERSION,
|
||||
CONTENT_URI);
|
||||
CONTENT_URI,
|
||||
CALL_METHOD_GET_SYSTEM,
|
||||
CALL_METHOD_PUT_SYSTEM);
|
||||
|
||||
// region Methods
|
||||
|
||||
/**
|
||||
* Construct the content URI for a particular name/value pair, useful for monitoring changes
|
||||
* with a ContentObserver.
|
||||
* @param name to look up in the table
|
||||
* @return the corresponding content URI
|
||||
*/
|
||||
public static Uri getUriFor(String name) {
|
||||
return Settings.NameValueTable.getUriFor(CONTENT_URI, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a name in the database.
|
||||
* @param resolver to access the database with
|
||||
|
@ -170,8 +295,8 @@ public final class CMSettings {
|
|||
|
||||
/** @hide */
|
||||
public static String getStringForUser(ContentResolver resolver, String name,
|
||||
int userHandle) {
|
||||
return sNameValueCache.getStringForUser(resolver, name, userHandle);
|
||||
int userId) {
|
||||
return sNameValueCache.getStringForUser(resolver, name, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -182,11 +307,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putString(ContentResolver resolver, String name, String value) {
|
||||
return putString(resolver, CONTENT_URI, name, value);
|
||||
return putStringForUser(resolver, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putStringForUser(ContentResolver resolver, String name, String value,
|
||||
int userId) {
|
||||
return sNameValueCache.putStringForUser(resolver, name, value, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as an integer. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to an integer
|
||||
* for you. The default value will be returned if the setting is
|
||||
|
@ -200,7 +331,12 @@ public final class CMSettings {
|
|||
* or not a valid integer.
|
||||
*/
|
||||
public static int getInt(ContentResolver cr, String name, int def) {
|
||||
String v = getString(cr, name);
|
||||
return getIntForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static int getIntForUser(ContentResolver cr, String name, int def, int userId) {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return v != null ? Integer.parseInt(v) : def;
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -209,7 +345,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as an integer. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to an integer
|
||||
* for you.
|
||||
|
@ -228,7 +364,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static int getInt(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getString(cr, name);
|
||||
return getIntForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static int getIntForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return Integer.parseInt(v);
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -250,11 +392,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putInt(ContentResolver cr, String name, int value) {
|
||||
return putString(cr, name, Integer.toString(value));
|
||||
return putIntForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putIntForUser(ContentResolver cr, String name, int value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Integer.toString(value), userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a {@code long}. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a {@code long}
|
||||
* for you. The default value will be returned if the setting is
|
||||
|
@ -268,7 +416,13 @@ public final class CMSettings {
|
|||
* or not a valid {@code long}.
|
||||
*/
|
||||
public static long getLong(ContentResolver cr, String name, long def) {
|
||||
String valString = getString(cr, name);
|
||||
return getLongForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static long getLongForUser(ContentResolver cr, String name, long def,
|
||||
int userId) {
|
||||
String valString = getStringForUser(cr, name, userId);
|
||||
long value;
|
||||
try {
|
||||
value = valString != null ? Long.parseLong(valString) : def;
|
||||
|
@ -279,7 +433,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a {@code long}. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a {@code long}
|
||||
* for you.
|
||||
|
@ -297,7 +451,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static long getLong(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String valString = getString(cr, name);
|
||||
return getLongForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static long getLongForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String valString = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return Long.parseLong(valString);
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -306,7 +466,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for updating a secure settings value as a long
|
||||
* Convenience function for updating a single settings value as a long
|
||||
* integer. This will either create a new entry in the table if the
|
||||
* given name does not exist, or modify the value of the existing row
|
||||
* with that name. Note that internally setting values are always
|
||||
|
@ -319,11 +479,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putLong(ContentResolver cr, String name, long value) {
|
||||
return putString(cr, name, Long.toString(value));
|
||||
return putLongForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putLongForUser(ContentResolver cr, String name, long value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Long.toString(value), userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a floating point number. Note that internally setting values are
|
||||
* always stored as strings; this function converts the string to an
|
||||
* float for you. The default value will be returned if the setting
|
||||
|
@ -337,7 +503,13 @@ public final class CMSettings {
|
|||
* or not a valid float.
|
||||
*/
|
||||
public static float getFloat(ContentResolver cr, String name, float def) {
|
||||
String v = getString(cr, name);
|
||||
return getFloatForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static float getFloatForUser(ContentResolver cr, String name, float def,
|
||||
int userId) {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return v != null ? Float.parseFloat(v) : def;
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -346,7 +518,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single system settings value
|
||||
* as a float. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a float
|
||||
* for you.
|
||||
|
@ -365,7 +537,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static float getFloat(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getString(cr, name);
|
||||
return getFloatForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static float getFloatForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
if (v == null) {
|
||||
throw new CMSettingNotFoundException(name);
|
||||
}
|
||||
|
@ -390,7 +568,13 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putFloat(ContentResolver cr, String name, float value) {
|
||||
return putString(cr, name, Float.toString(value));
|
||||
return putFloatForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putFloatForUser(ContentResolver cr, String name, float value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Float.toString(value), userId);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
@ -408,8 +592,8 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Secure settings, containing miscellaneous CM secure preferences. This
|
||||
* table holds simple name/value pairs. There are convenience
|
||||
* Secure settings, containing miscellaneous CM secure preferences. This
|
||||
* table holds simple name/value pairs. There are convenience
|
||||
* functions for accessing individual settings entries.
|
||||
*/
|
||||
public static final class Secure extends Settings.NameValueTable {
|
||||
|
@ -419,7 +603,21 @@ public final class CMSettings {
|
|||
|
||||
private static final NameValueCache sNameValueCache = new NameValueCache(
|
||||
SYS_PROP_CM_SETTING_VERSION,
|
||||
CONTENT_URI);
|
||||
CONTENT_URI,
|
||||
CALL_METHOD_GET_SECURE,
|
||||
CALL_METHOD_PUT_SECURE);
|
||||
|
||||
// region Methods
|
||||
|
||||
/**
|
||||
* Construct the content URI for a particular name/value pair, useful for monitoring changes
|
||||
* with a ContentObserver.
|
||||
* @param name to look up in the table
|
||||
* @return the corresponding content URI
|
||||
*/
|
||||
public static Uri getUriFor(String name) {
|
||||
return Settings.NameValueTable.getUriFor(CONTENT_URI, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a name in the database.
|
||||
|
@ -433,8 +631,8 @@ public final class CMSettings {
|
|||
|
||||
/** @hide */
|
||||
public static String getStringForUser(ContentResolver resolver, String name,
|
||||
int userHandle) {
|
||||
return sNameValueCache.getStringForUser(resolver, name, userHandle);
|
||||
int userId) {
|
||||
return sNameValueCache.getStringForUser(resolver, name, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -445,11 +643,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putString(ContentResolver resolver, String name, String value) {
|
||||
return putString(resolver, CONTENT_URI, name, value);
|
||||
return putStringForUser(resolver, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putStringForUser(ContentResolver resolver, String name, String value,
|
||||
int userId) {
|
||||
return sNameValueCache.putStringForUser(resolver, name, value, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as an integer. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to an integer
|
||||
* for you. The default value will be returned if the setting is
|
||||
|
@ -463,7 +667,12 @@ public final class CMSettings {
|
|||
* or not a valid integer.
|
||||
*/
|
||||
public static int getInt(ContentResolver cr, String name, int def) {
|
||||
String v = getString(cr, name);
|
||||
return getIntForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static int getIntForUser(ContentResolver cr, String name, int def, int userId) {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return v != null ? Integer.parseInt(v) : def;
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -472,7 +681,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as an integer. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to an integer
|
||||
* for you.
|
||||
|
@ -491,7 +700,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static int getInt(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getString(cr, name);
|
||||
return getIntForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static int getIntForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return Integer.parseInt(v);
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -513,11 +728,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putInt(ContentResolver cr, String name, int value) {
|
||||
return putString(cr, name, Integer.toString(value));
|
||||
return putIntForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putIntForUser(ContentResolver cr, String name, int value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Integer.toString(value), userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a {@code long}. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a {@code long}
|
||||
* for you. The default value will be returned if the setting is
|
||||
|
@ -531,7 +752,13 @@ public final class CMSettings {
|
|||
* or not a valid {@code long}.
|
||||
*/
|
||||
public static long getLong(ContentResolver cr, String name, long def) {
|
||||
String valString = getString(cr, name);
|
||||
return getLongForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static long getLongForUser(ContentResolver cr, String name, long def,
|
||||
int userId) {
|
||||
String valString = getStringForUser(cr, name, userId);
|
||||
long value;
|
||||
try {
|
||||
value = valString != null ? Long.parseLong(valString) : def;
|
||||
|
@ -542,7 +769,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a {@code long}. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a {@code long}
|
||||
* for you.
|
||||
|
@ -560,7 +787,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static long getLong(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String valString = getString(cr, name);
|
||||
return getLongForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static long getLongForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String valString = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return Long.parseLong(valString);
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -569,7 +802,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for updating a secure settings value as a long
|
||||
* Convenience function for updating a single settings value as a long
|
||||
* integer. This will either create a new entry in the table if the
|
||||
* given name does not exist, or modify the value of the existing row
|
||||
* with that name. Note that internally setting values are always
|
||||
|
@ -582,11 +815,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putLong(ContentResolver cr, String name, long value) {
|
||||
return putString(cr, name, Long.toString(value));
|
||||
return putLongForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putLongForUser(ContentResolver cr, String name, long value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Long.toString(value), userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a floating point number. Note that internally setting values are
|
||||
* always stored as strings; this function converts the string to an
|
||||
* float for you. The default value will be returned if the setting
|
||||
|
@ -600,7 +839,13 @@ public final class CMSettings {
|
|||
* or not a valid float.
|
||||
*/
|
||||
public static float getFloat(ContentResolver cr, String name, float def) {
|
||||
String v = getString(cr, name);
|
||||
return getFloatForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static float getFloatForUser(ContentResolver cr, String name, float def,
|
||||
int userId) {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return v != null ? Float.parseFloat(v) : def;
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -609,7 +854,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single system settings value
|
||||
* as a float. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a float
|
||||
* for you.
|
||||
|
@ -628,7 +873,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static float getFloat(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getString(cr, name);
|
||||
return getFloatForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static float getFloatForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
if (v == null) {
|
||||
throw new CMSettingNotFoundException(name);
|
||||
}
|
||||
|
@ -653,7 +904,13 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putFloat(ContentResolver cr, String name, float value) {
|
||||
return putString(cr, name, Float.toString(value));
|
||||
return putFloatForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putFloatForUser(ContentResolver cr, String name, float value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Float.toString(value), userId);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
@ -770,8 +1027,8 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Global settings, containing miscellaneous CM global preferences. This
|
||||
* table holds simple name/value pairs. There are convenience
|
||||
* Global settings, containing miscellaneous CM global preferences. This
|
||||
* table holds simple name/value pairs. There are convenience
|
||||
* functions for accessing individual settings entries.
|
||||
*/
|
||||
public static final class Global extends Settings.NameValueTable {
|
||||
|
@ -781,10 +1038,22 @@ public final class CMSettings {
|
|||
|
||||
private static final NameValueCache sNameValueCache = new NameValueCache(
|
||||
SYS_PROP_CM_SETTING_VERSION,
|
||||
CONTENT_URI);
|
||||
CONTENT_URI,
|
||||
CALL_METHOD_GET_GLOBAL,
|
||||
CALL_METHOD_PUT_GLOBAL);
|
||||
|
||||
// region Methods
|
||||
|
||||
/**
|
||||
* Construct the content URI for a particular name/value pair, useful for monitoring changes
|
||||
* with a ContentObserver.
|
||||
* @param name to look up in the table
|
||||
* @return the corresponding content URI
|
||||
*/
|
||||
public static Uri getUriFor(String name) {
|
||||
return Settings.NameValueTable.getUriFor(CONTENT_URI, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a name in the database.
|
||||
* @param resolver to access the database with
|
||||
|
@ -797,8 +1066,8 @@ public final class CMSettings {
|
|||
|
||||
/** @hide */
|
||||
public static String getStringForUser(ContentResolver resolver, String name,
|
||||
int userHandle) {
|
||||
return sNameValueCache.getStringForUser(resolver, name, userHandle);
|
||||
int userId) {
|
||||
return sNameValueCache.getStringForUser(resolver, name, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -809,11 +1078,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putString(ContentResolver resolver, String name, String value) {
|
||||
return putString(resolver, CONTENT_URI, name, value);
|
||||
return putStringForUser(resolver, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putStringForUser(ContentResolver resolver, String name, String value,
|
||||
int userId) {
|
||||
return sNameValueCache.putStringForUser(resolver, name, value, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as an integer. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to an integer
|
||||
* for you. The default value will be returned if the setting is
|
||||
|
@ -827,7 +1102,12 @@ public final class CMSettings {
|
|||
* or not a valid integer.
|
||||
*/
|
||||
public static int getInt(ContentResolver cr, String name, int def) {
|
||||
String v = getString(cr, name);
|
||||
return getIntForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static int getIntForUser(ContentResolver cr, String name, int def, int userId) {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return v != null ? Integer.parseInt(v) : def;
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -836,7 +1116,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as an integer. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to an integer
|
||||
* for you.
|
||||
|
@ -855,7 +1135,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static int getInt(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getString(cr, name);
|
||||
return getIntForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static int getIntForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return Integer.parseInt(v);
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -877,11 +1163,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putInt(ContentResolver cr, String name, int value) {
|
||||
return putString(cr, name, Integer.toString(value));
|
||||
return putIntForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putIntForUser(ContentResolver cr, String name, int value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Integer.toString(value), userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a {@code long}. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a {@code long}
|
||||
* for you. The default value will be returned if the setting is
|
||||
|
@ -895,7 +1187,13 @@ public final class CMSettings {
|
|||
* or not a valid {@code long}.
|
||||
*/
|
||||
public static long getLong(ContentResolver cr, String name, long def) {
|
||||
String valString = getString(cr, name);
|
||||
return getLongForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static long getLongForUser(ContentResolver cr, String name, long def,
|
||||
int userId) {
|
||||
String valString = getStringForUser(cr, name, userId);
|
||||
long value;
|
||||
try {
|
||||
value = valString != null ? Long.parseLong(valString) : def;
|
||||
|
@ -906,7 +1204,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a {@code long}. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a {@code long}
|
||||
* for you.
|
||||
|
@ -924,7 +1222,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static long getLong(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String valString = getString(cr, name);
|
||||
return getLongForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static long getLongForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String valString = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return Long.parseLong(valString);
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -933,7 +1237,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for updating a secure settings value as a long
|
||||
* Convenience function for updating a single settings value as a long
|
||||
* integer. This will either create a new entry in the table if the
|
||||
* given name does not exist, or modify the value of the existing row
|
||||
* with that name. Note that internally setting values are always
|
||||
|
@ -946,11 +1250,17 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putLong(ContentResolver cr, String name, long value) {
|
||||
return putString(cr, name, Long.toString(value));
|
||||
return putLongForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putLongForUser(ContentResolver cr, String name, long value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Long.toString(value), userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single settings value
|
||||
* as a floating point number. Note that internally setting values are
|
||||
* always stored as strings; this function converts the string to an
|
||||
* float for you. The default value will be returned if the setting
|
||||
|
@ -964,7 +1274,13 @@ public final class CMSettings {
|
|||
* or not a valid float.
|
||||
*/
|
||||
public static float getFloat(ContentResolver cr, String name, float def) {
|
||||
String v = getString(cr, name);
|
||||
return getFloatForUser(cr, name, def, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static float getFloatForUser(ContentResolver cr, String name, float def,
|
||||
int userId) {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
try {
|
||||
return v != null ? Float.parseFloat(v) : def;
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -973,7 +1289,7 @@ public final class CMSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience function for retrieving a single secure settings value
|
||||
* Convenience function for retrieving a single system settings value
|
||||
* as a float. Note that internally setting values are always
|
||||
* stored as strings; this function converts the string to a float
|
||||
* for you.
|
||||
|
@ -992,7 +1308,13 @@ public final class CMSettings {
|
|||
*/
|
||||
public static float getFloat(ContentResolver cr, String name)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getString(cr, name);
|
||||
return getFloatForUser(cr, name, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static float getFloatForUser(ContentResolver cr, String name, int userId)
|
||||
throws CMSettingNotFoundException {
|
||||
String v = getStringForUser(cr, name, userId);
|
||||
if (v == null) {
|
||||
throw new CMSettingNotFoundException(name);
|
||||
}
|
||||
|
@ -1017,7 +1339,13 @@ public final class CMSettings {
|
|||
* @return true if the value was set, false on database errors
|
||||
*/
|
||||
public static boolean putFloat(ContentResolver cr, String name, float value) {
|
||||
return putString(cr, name, Float.toString(value));
|
||||
return putFloatForUser(cr, name, value, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean putFloatForUser(ContentResolver cr, String name, float value,
|
||||
int userId) {
|
||||
return putStringForUser(cr, name, Float.toString(value), userId);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
@ -1032,7 +1360,7 @@ public final class CMSettings {
|
|||
public static final String DEVICE_NAME = "device_name";
|
||||
|
||||
/**
|
||||
* Defines global heads up toggle. One of HEADS_UP_OFF, HEADS_UP_ON.
|
||||
* Defines global heads up toggle. One of HEADS_UP_OFF, HEADS_UP_ON.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<uses-permission android:name="cyanogenmod.permission.PUBLISH_CUSTOM_TILE" />
|
||||
<uses-permission android:name="cyanogenmod.permission.WRITE_SETTINGS"/>
|
||||
<uses-permission android:name="cyanogenmod.permission.WRITE_SECURE_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<uses-permission android:name="cyanogenmod.permission.MODIFY_NETWORK_SETTINGS" />
|
||||
<uses-permission android:name="cyanogenmod.permission.MODIFY_SOUND_SETTINGS" />
|
||||
<uses-permission android:name="cyanogenmod.permission.MANAGE_ALARMS" />
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
package org.cyanogenmod.tests.providers;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
@ -24,10 +28,20 @@ import cyanogenmod.providers.CMSettings;
|
|||
|
||||
public class CMSettingsTest extends AndroidTestCase{
|
||||
private ContentResolver mContentResolver;
|
||||
private CMSettingsTestObserver mTestObserver;
|
||||
|
||||
private static boolean sIsOnChangedCalled = false;
|
||||
private static Uri sExpectedUriChange = null;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
mContentResolver = getContext().getContentResolver();
|
||||
mTestObserver = new CMSettingsTestObserver(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
mContentResolver.unregisterContentObserver(mTestObserver);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
|
@ -43,6 +57,12 @@ public class CMSettingsTest extends AndroidTestCase{
|
|||
String actualValue = CMSettings.System.getString(mContentResolver, key);
|
||||
assertEquals(expectedValue, actualValue);
|
||||
|
||||
// setup observer
|
||||
sIsOnChangedCalled = false;
|
||||
sExpectedUriChange = CMSettings.System.getUriFor(key);
|
||||
mContentResolver.registerContentObserver(sExpectedUriChange, false, mTestObserver,
|
||||
UserHandle.USER_ALL);
|
||||
|
||||
// replace
|
||||
final String expectedReplaceValue = "systemTestValue2";
|
||||
isPutSuccessful = CMSettings.System.putString(mContentResolver, key, expectedReplaceValue);
|
||||
|
@ -53,9 +73,13 @@ public class CMSettingsTest extends AndroidTestCase{
|
|||
assertEquals(expectedReplaceValue, actualValue);
|
||||
|
||||
// delete to clean up
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.System.CONTENT_URI, Settings.NameValueTable.NAME + " = ?",
|
||||
new String[]{ key });
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.System.CONTENT_URI,
|
||||
Settings.NameValueTable.NAME + " = ?", new String[]{ key });
|
||||
assertEquals(1, rowsAffected);
|
||||
|
||||
if (!sIsOnChangedCalled) {
|
||||
fail("On change was never called or was called with the wrong uri");
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
|
@ -71,6 +95,12 @@ public class CMSettingsTest extends AndroidTestCase{
|
|||
String actualValue = CMSettings.Secure.getString(mContentResolver, key);
|
||||
assertEquals(expectedValue, actualValue);
|
||||
|
||||
// setup observer
|
||||
sIsOnChangedCalled = false;
|
||||
sExpectedUriChange = CMSettings.Secure.getUriFor(key);
|
||||
mContentResolver.registerContentObserver(sExpectedUriChange, false, mTestObserver,
|
||||
UserHandle.USER_ALL);
|
||||
|
||||
// replace
|
||||
final String expectedReplaceValue = "secureTestValue2";
|
||||
isPutSuccessful = CMSettings.Secure.putString(mContentResolver, key, expectedReplaceValue);
|
||||
|
@ -81,9 +111,13 @@ public class CMSettingsTest extends AndroidTestCase{
|
|||
assertEquals(expectedReplaceValue, actualValue);
|
||||
|
||||
// delete to clean up
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.Secure.CONTENT_URI, Settings.NameValueTable.NAME + " = ?",
|
||||
new String[]{ key });
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.Secure.CONTENT_URI,
|
||||
Settings.NameValueTable.NAME + " = ?", new String[]{ key });
|
||||
assertEquals(1, rowsAffected);
|
||||
|
||||
if (!sIsOnChangedCalled) {
|
||||
fail("On change was never called or was called with the wrong uri");
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
|
@ -99,6 +133,12 @@ public class CMSettingsTest extends AndroidTestCase{
|
|||
String actualValue = CMSettings.Global.getString(mContentResolver, key);
|
||||
assertEquals(expectedValue, actualValue);
|
||||
|
||||
// setup observer
|
||||
sIsOnChangedCalled = false;
|
||||
sExpectedUriChange = CMSettings.Global.getUriFor(key);
|
||||
mContentResolver.registerContentObserver(sExpectedUriChange, false, mTestObserver,
|
||||
UserHandle.USER_OWNER);
|
||||
|
||||
// replace
|
||||
final String expectedReplaceValue = "globalTestValue2";
|
||||
isPutSuccessful = CMSettings.Global.putString(mContentResolver, key, expectedReplaceValue);
|
||||
|
@ -109,9 +149,27 @@ public class CMSettingsTest extends AndroidTestCase{
|
|||
assertEquals(expectedReplaceValue, actualValue);
|
||||
|
||||
// delete to clean up
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.Global.CONTENT_URI, Settings.NameValueTable.NAME + " = ?",
|
||||
new String[]{ key });
|
||||
int rowsAffected = mContentResolver.delete(CMSettings.Global.CONTENT_URI,
|
||||
Settings.NameValueTable.NAME + " = ?", new String[]{ key });
|
||||
assertEquals(1, rowsAffected);
|
||||
|
||||
if (!sIsOnChangedCalled) {
|
||||
fail("On change was never called or was called with the wrong uri");
|
||||
}
|
||||
}
|
||||
|
||||
private class CMSettingsTestObserver extends ContentObserver {
|
||||
|
||||
public CMSettingsTestObserver(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
if (sExpectedUriChange.equals(uri)) {
|
||||
sIsOnChangedCalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Add tests for other users
|
||||
|
|
Loading…
Reference in New Issue