am 06efb530: Per-user settings

* commit '06efb530a479ea12398c1b3ee4b80e2ac85a1680':
  Per-user settings
This commit is contained in:
Christopher Tate
2012-09-06 16:41:38 -07:00
committed by Android Git Automerger
4 changed files with 2462 additions and 575 deletions

View File

@@ -18718,6 +18718,53 @@ package android.provider {
field public static final java.lang.String EXTRA_INPUT_METHOD_ID = "input_method_id";
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
ctor public Settings.Global();
method public static float getFloat(android.content.ContentResolver, java.lang.String, float);
method public static float getFloat(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
method public static int getInt(android.content.ContentResolver, java.lang.String, int);
method public static int getInt(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
method public static long getLong(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
method public static synchronized java.lang.String getString(android.content.ContentResolver, java.lang.String);
method public static android.net.Uri getUriFor(java.lang.String);
method public static boolean putFloat(android.content.ContentResolver, java.lang.String, float);
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
field public static final java.lang.String ADB_ENABLED = "adb_enabled";
field public static final java.lang.String AIRPLANE_MODE_ON = "airplane_mode_on";
field public static final java.lang.String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
field public static final java.lang.String AUTO_TIME = "auto_time";
field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth";
field public static final java.lang.String RADIO_CELL = "cell";
field public static final java.lang.String RADIO_NFC = "nfc";
field public static final java.lang.String RADIO_WIFI = "wifi";
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
field public static final java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
field public static final java.lang.String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms";
field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on";
field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
field public static final java.lang.String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
field public static final java.lang.String WIFI_ON = "wifi_on";
field public static final java.lang.String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
field public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
field public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
field public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
field public static final java.lang.String WIFI_WATCHDOG_ON = "wifi_watchdog_on";
}
public static class Settings.NameValueTable implements android.provider.BaseColumns {
ctor public Settings.NameValueTable();
method public static android.net.Uri getUriFor(android.net.Uri, java.lang.String);
@@ -18744,28 +18791,28 @@ package android.provider {
method public static final void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
field public static final java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
field public static final java.lang.String ADB_ENABLED = "adb_enabled";
field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled";
field public static final java.lang.String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins";
field public static final java.lang.String ALLOW_MOCK_LOCATION = "mock_location";
field public static final java.lang.String ANDROID_ID = "android_id";
field public static final deprecated java.lang.String BACKGROUND_DATA = "background_data";
field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
field public static final deprecated java.lang.String BLUETOOTH_ON = "bluetooth_on";
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DATA_ROAMING = "data_roaming";
field public static final deprecated java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEFAULT_INPUT_METHOD = "default_input_method";
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final deprecated java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services";
field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
field public static final java.lang.String HTTP_PROXY = "http_proxy";
field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final java.lang.String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
field public static final java.lang.String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
field public static final java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
field public static final java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
field public static final deprecated java.lang.String NETWORK_PREFERENCE = "network_preference";
field public static final java.lang.String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
field public static final java.lang.String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
field public static final java.lang.String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
@@ -18781,9 +18828,9 @@ package android.provider {
field public static final deprecated java.lang.String TTS_DEFAULT_VARIANT = "tts_default_variant";
field public static final java.lang.String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
field public static final deprecated java.lang.String TTS_USE_DEFAULTS = "tts_use_defaults";
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
field public static final java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
field public static final deprecated java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
field public static final deprecated java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
field public static final deprecated java.lang.String WIFI_MAX_DHCP_RETRY_COUNT = "wifi_max_dhcp_retry_count";
field public static final java.lang.String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS = "wifi_mobile_data_transition_wakelock_timeout_ms";
field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON = "wifi_networks_available_notification_on";
field public static final java.lang.String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
@@ -18816,7 +18863,7 @@ package android.provider {
method public static int getInt(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
method public static long getLong(android.content.ContentResolver, java.lang.String, long);
method public static long getLong(android.content.ContentResolver, java.lang.String) throws android.provider.Settings.SettingNotFoundException;
method public static boolean getShowGTalkServiceStatus(android.content.ContentResolver);
method public static deprecated boolean getShowGTalkServiceStatus(android.content.ContentResolver);
method public static synchronized java.lang.String getString(android.content.ContentResolver, java.lang.String);
method public static android.net.Uri getUriFor(java.lang.String);
method public static boolean putConfiguration(android.content.ContentResolver, android.content.res.Configuration);
@@ -18824,18 +18871,18 @@ package android.provider {
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
method public static void setShowGTalkServiceStatus(android.content.ContentResolver, boolean);
method public static deprecated void setShowGTalkServiceStatus(android.content.ContentResolver, boolean);
field public static final java.lang.String ACCELEROMETER_ROTATION = "accelerometer_rotation";
field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled";
field public static final java.lang.String AIRPLANE_MODE_ON = "airplane_mode_on";
field public static final java.lang.String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
field public static final deprecated java.lang.String AIRPLANE_MODE_ON = "airplane_mode_on";
field public static final deprecated java.lang.String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
field public static final java.lang.String ALARM_ALERT = "alarm_alert";
field public static final java.lang.String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
field public static final deprecated java.lang.String ANDROID_ID = "android_id";
field public static final java.lang.String ANIMATOR_DURATION_SCALE = "animator_duration_scale";
field public static final java.lang.String APPEND_FOR_LAST_AUDIBLE = "_last_audible";
field public static final java.lang.String AUTO_TIME = "auto_time";
field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
field public static final deprecated java.lang.String AUTO_TIME = "auto_time";
field public static final deprecated java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
field public static final java.lang.String BLUETOOTH_DISCOVERABILITY = "bluetooth_discoverability";
field public static final java.lang.String BLUETOOTH_DISCOVERABILITY_TIMEOUT = "bluetooth_discoverability_timeout";
field public static final deprecated java.lang.String BLUETOOTH_ON = "bluetooth_on";
@@ -18868,10 +18915,10 @@ package android.provider {
field public static final deprecated java.lang.String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
field public static final deprecated java.lang.String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
field public static final deprecated java.lang.String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth";
field public static final java.lang.String RADIO_CELL = "cell";
field public static final java.lang.String RADIO_NFC = "nfc";
field public static final java.lang.String RADIO_WIFI = "wifi";
field public static final deprecated java.lang.String RADIO_BLUETOOTH = "bluetooth";
field public static final deprecated java.lang.String RADIO_CELL = "cell";
field public static final deprecated java.lang.String RADIO_NFC = "nfc";
field public static final deprecated java.lang.String RADIO_WIFI = "wifi";
field public static final java.lang.String RINGTONE = "ringtone";
field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness";
field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
@@ -18884,7 +18931,7 @@ package android.provider {
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
@@ -18912,10 +18959,10 @@ package android.provider {
field public static final deprecated java.lang.String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY = "wifi_networks_available_repeat_delay";
field public static final deprecated java.lang.String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
field public static final deprecated java.lang.String WIFI_ON = "wifi_on";
field public static final java.lang.String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
field public static final int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
field public static final int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
field public static final int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
field public static final deprecated java.lang.String WIFI_SLEEP_POLICY = "wifi_sleep_policy";
field public static final deprecated int WIFI_SLEEP_POLICY_DEFAULT = 0; // 0x0
field public static final deprecated int WIFI_SLEEP_POLICY_NEVER = 2; // 0x2
field public static final deprecated int WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED = 1; // 0x1
field public static final java.lang.String WIFI_STATIC_DNS1 = "wifi_static_dns1";
field public static final java.lang.String WIFI_STATIC_DNS2 = "wifi_static_dns2";
field public static final java.lang.String WIFI_STATIC_GATEWAY = "wifi_static_gateway";

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,9 @@ import android.database.sqlite.SQLiteStatement;
import android.media.AudioManager;
import android.media.AudioService;
import android.net.ConnectivityManager;
import android.os.Environment;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
@@ -48,6 +50,7 @@ import com.android.internal.widget.LockPatternView;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
@@ -64,15 +67,21 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
private static final int DATABASE_VERSION = 82;
private static final int DATABASE_VERSION = 83;
private Context mContext;
private int mUserHandle;
private static final HashSet<String> mValidTables = new HashSet<String>();
private static final String TABLE_SYSTEM = "system";
private static final String TABLE_SECURE = "secure";
private static final String TABLE_GLOBAL = "global";
static {
mValidTables.add("system");
mValidTables.add("secure");
mValidTables.add(TABLE_SYSTEM);
mValidTables.add(TABLE_SECURE);
mValidTables.add(TABLE_GLOBAL);
mValidTables.add("bluetooth_devices");
mValidTables.add("bookmarks");
@@ -82,9 +91,23 @@ public class DatabaseHelper extends SQLiteOpenHelper {
mValidTables.add("old_favorites");
}
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
static String dbNameForUser(final int userHandle) {
// The owner gets the unadorned db name;
if (userHandle == UserHandle.USER_OWNER) {
return DATABASE_NAME;
} else {
// Place the database in the user-specific data tree so that it's
// cleaned up automatically when the user is deleted.
File databaseFile = new File(
Environment.getUserSystemDirectory(userHandle), DATABASE_NAME);
return databaseFile.getPath();
}
}
public DatabaseHelper(Context context, int userHandle) {
super(context, dbNameForUser(userHandle), null, DATABASE_VERSION);
mContext = context;
mUserHandle = userHandle;
setWriteAheadLoggingEnabled(true);
}
@@ -101,6 +124,15 @@ public class DatabaseHelper extends SQLiteOpenHelper {
db.execSQL("CREATE INDEX secureIndex1 ON secure (name);");
}
private void createGlobalTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE global (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT UNIQUE ON CONFLICT REPLACE," +
"value TEXT" +
");");
db.execSQL("CREATE INDEX globalIndex1 ON global (name);");
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE system (" +
@@ -112,6 +144,11 @@ public class DatabaseHelper extends SQLiteOpenHelper {
createSecureTable(db);
// Only create the global table for the singleton 'owner' user
if (mUserHandle == UserHandle.USER_OWNER) {
createGlobalTable(db);
}
db.execSQL("CREATE TABLE bluetooth_devices (" +
"_id INTEGER PRIMARY KEY," +
"name TEXT," +
@@ -271,7 +308,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS,
Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS,
};
moveFromSystemToSecure(db, settingsToMove);
moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_SECURE, settingsToMove);
upgradeVersion = 28;
}
@@ -637,7 +674,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
"lockscreen.lockedoutpermanently",
"lockscreen.password_salt"
};
moveFromSystemToSecure(db, settingsToMove);
moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_SECURE, settingsToMove);
upgradeVersion = 52;
}
@@ -687,7 +724,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
Secure.SET_INSTALL_LOCATION,
Secure.DEFAULT_INSTALL_LOCATION
};
moveFromSystemToSecure(db, settingsToMove);
moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_SECURE, settingsToMove);
db.beginTransaction();
SQLiteStatement stmt = null;
try {
@@ -1013,7 +1050,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
SQLiteStatement stmt = null;
Cursor c = null;
try {
c = db.query("secure", new String[] {"_id", "value"},
c = db.query(TABLE_SECURE, new String[] {"_id", "value"},
"name='lockscreen.disabled'",
null, null, null, null);
// only set default if it has not yet been set
@@ -1089,14 +1126,14 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// toggle touch exploration. Note that the user has already manually
// enabled the services and touch exploration which means the she has
// given consent to have these services work in touch exploration mode.
final boolean accessibilityEnabled = getIntValueFromTable(db, "secure",
final boolean accessibilityEnabled = getIntValueFromTable(db, TABLE_SECURE,
Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
final boolean touchExplorationEnabled = getIntValueFromTable(db, "secure",
final boolean touchExplorationEnabled = getIntValueFromTable(db, TABLE_SECURE,
Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
if (accessibilityEnabled && touchExplorationEnabled) {
String enabledServices = getStringValueFromTable(db, "secure",
String enabledServices = getStringValueFromTable(db, TABLE_SECURE,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
String touchExplorationGrantedServices = getStringValueFromTable(db, "secure",
String touchExplorationGrantedServices = getStringValueFromTable(db, TABLE_SECURE,
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, "");
if (TextUtils.isEmpty(touchExplorationGrantedServices)
&& !TextUtils.isEmpty(enabledServices)) {
@@ -1137,6 +1174,15 @@ public class DatabaseHelper extends SQLiteOpenHelper {
R.string.def_screensaver_component);
loadStringSetting(stmt, Settings.Secure.SCREENSAVER_COMPONENTS,
R.string.def_screensaver_component);
// Migrate now-global settings. Note that this happens before
// new users can be created.
createGlobalTable(db);
String[] settingsToMove = (String[]) SettingsProvider.sSystemGlobalKeys.toArray();
moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove);
settingsToMove = (String[]) SettingsProvider.sSecureGlobalKeys.toArray();
moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -1162,11 +1208,34 @@ public class DatabaseHelper extends SQLiteOpenHelper {
upgradeVersion = 82;
}
if (upgradeVersion == 82) {
// Move to per-user settings dbs
db.beginTransaction();
SQLiteStatement stmt = null;
try {
// Migrate now-global settings. Note that this happens before
// new users can be created.
createGlobalTable(db);
String[] settingsToMove = (String[]) SettingsProvider.sSystemGlobalKeys.toArray();
moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove);
settingsToMove = (String[]) SettingsProvider.sSecureGlobalKeys.toArray();
moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
if (stmt != null) stmt.close();
}
upgradeVersion = 83;
}
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
+ ", must wipe the settings provider");
db.execSQL("DROP TABLE IF EXISTS global");
db.execSQL("DROP TABLE IF EXISTS globalIndex1");
db.execSQL("DROP TABLE IF EXISTS system");
db.execSQL("DROP INDEX IF EXISTS systemIndex1");
db.execSQL("DROP TABLE IF EXISTS secure");
@@ -1187,18 +1256,19 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
}
private void moveFromSystemToSecure(SQLiteDatabase db, String [] settingsToMove) {
// Copy settings values from 'system' to 'secure' and delete them from 'system'
private void moveSettingsToNewTable(SQLiteDatabase db,
String sourceTable, String destTable,
String[] settingsToMove) {
// Copy settings values from the source table to the dest, and remove from the source
SQLiteStatement insertStmt = null;
SQLiteStatement deleteStmt = null;
db.beginTransaction();
try {
insertStmt =
db.compileStatement("INSERT INTO secure (name,value) SELECT name,value FROM "
+ "system WHERE name=?");
deleteStmt = db.compileStatement("DELETE FROM system WHERE name=?");
insertStmt = db.compileStatement("INSERT INTO "
+ destTable + " (name,value) SELECT name,value FROM "
+ sourceTable + " WHERE name=?");
deleteStmt = db.compileStatement("DELETE FROM " + sourceTable + " WHERE name=?");
for (String setting : settingsToMove) {
insertStmt.bindString(1, setting);
@@ -1220,7 +1290,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
private void upgradeLockPatternLocation(SQLiteDatabase db) {
Cursor c = db.query("system", new String[] {"_id", "value"}, "name='lock_pattern'",
Cursor c = db.query(TABLE_SYSTEM, new String[] {"_id", "value"}, "name='lock_pattern'",
null, null, null, null);
if (c.getCount() > 0) {
c.moveToFirst();
@@ -1237,7 +1307,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
}
c.close();
db.delete("system", "name='lock_pattern'", null);
db.delete(TABLE_SYSTEM, "name='lock_pattern'", null);
} else {
c.close();
}
@@ -1245,7 +1315,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private void upgradeScreenTimeoutFromNever(SQLiteDatabase db) {
// See if the timeout is -1 (for "Never").
Cursor c = db.query("system", new String[] { "_id", "value" }, "name=? AND value=?",
Cursor c = db.query(TABLE_SYSTEM, new String[] { "_id", "value" }, "name=? AND value=?",
new String[] { Settings.System.SCREEN_OFF_TIMEOUT, "-1" },
null, null, null);
@@ -1514,6 +1584,10 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private void loadSettings(SQLiteDatabase db) {
loadSystemSettings(db);
loadSecureSettings(db);
// The global table only exists for the 'owner' user
if (mUserHandle == UserHandle.USER_OWNER) {
loadGlobalSettings(db);
}
}
private void loadSystemSettings(SQLiteDatabase db) {
@@ -1524,10 +1598,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadBooleanSetting(stmt, Settings.System.DIM_SCREEN,
R.bool.def_dim_screen);
loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN,
("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
mContext.getResources().getBoolean(R.bool.def_stay_on_while_plugged_in))
? 1 : 0);
loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
R.integer.def_screen_off_timeout);
@@ -1546,21 +1616,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
// Set default tty mode
loadSetting(stmt, Settings.System.TTY_MODE, 0);
loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON,
R.bool.def_airplane_mode_on);
loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS,
R.string.def_airplane_mode_radios);
loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
R.string.airplane_mode_toggleable_radios);
loadBooleanSetting(stmt, Settings.System.AUTO_TIME,
R.bool.def_auto_time); // Sync time to NITZ
loadBooleanSetting(stmt, Settings.System.AUTO_TIME_ZONE,
R.bool.def_auto_time_zone); // Sync timezone to NITZ
loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
R.integer.def_screen_brightness);
@@ -1584,9 +1639,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadIntegerSetting(stmt, Settings.System.POINTER_SPEED,
R.integer.def_pointer_speed);
loadIntegerSetting(stmt, Settings.System.WIFI_SLEEP_POLICY,
R.integer.def_wifi_sleep_policy);
} finally {
if (stmt != null) stmt.close();
}
@@ -1641,39 +1693,12 @@ public class DatabaseHelper extends SQLiteOpenHelper {
stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+ " VALUES(?,?);");
loadBooleanSetting(stmt, Settings.Secure.BLUETOOTH_ON,
R.bool.def_bluetooth_on);
// Data roaming default, based on build
loadSetting(stmt, Settings.Secure.DATA_ROAMING,
"true".equalsIgnoreCase(
SystemProperties.get("ro.com.android.dataroaming",
"false")) ? 1 : 0);
// Mobile Data default, based on build
loadSetting(stmt, Settings.Secure.MOBILE_DATA,
"true".equalsIgnoreCase(
SystemProperties.get("ro.com.android.mobiledata",
"true")) ? 1 : 0);
loadBooleanSetting(stmt, Settings.Secure.INSTALL_NON_MARKET_APPS,
R.bool.def_install_non_market_apps);
loadBooleanSetting(stmt, Settings.Secure.PACKAGE_VERIFIER_ENABLE,
R.bool.def_package_verifier_enable);
loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
R.string.def_location_providers_allowed);
loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED,
R.bool.assisted_gps_enabled);
loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
R.integer.def_network_preference);
loadBooleanSetting(stmt, Settings.Secure.USB_MASS_STORAGE_ENABLED,
R.bool.def_usb_mass_storage_enabled);
loadBooleanSetting(stmt, Settings.Secure.WIFI_ON,
R.bool.def_wifi_on);
loadBooleanSetting(stmt, Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
@@ -1694,10 +1719,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type);
// Enable or disable Cell Broadcast SMS
loadSetting(stmt, Settings.Secure.CDMA_CELL_BROADCAST_SMS,
RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
// Don't do this. The SystemServer will initialize ADB_ENABLED from a
// persistent system property instead.
//loadSetting(stmt, Settings.Secure.ADB_ENABLED, 0);
@@ -1726,20 +1747,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
loadStringSetting(stmt, Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
R.string.def_accessibility_web_content_key_bindings);
final int maxBytes = mContext.getResources().getInteger(
R.integer.def_download_manager_max_bytes_over_mobile);
if (maxBytes > 0) {
loadSetting(stmt, Settings.Secure.DOWNLOAD_MAX_BYTES_OVER_MOBILE,
Integer.toString(maxBytes));
}
final int recommendedMaxBytes = mContext.getResources().getInteger(
R.integer.def_download_manager_recommended_max_bytes_over_mobile);
if (recommendedMaxBytes > 0) {
loadSetting(stmt, Settings.Secure.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
Integer.toString(recommendedMaxBytes));
}
loadIntegerSetting(stmt, Settings.Secure.LONG_PRESS_TIMEOUT,
R.integer.def_long_press_timeout_millis);
@@ -1759,15 +1766,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
R.bool.def_lockscreen_disabled);
}
loadBooleanSetting(stmt, Settings.Secure.DEVICE_PROVISIONED,
R.bool.def_device_provisioned);
loadBooleanSetting(stmt, Settings.Secure.NETSTATS_ENABLED,
R.bool.def_netstats_enabled);
loadIntegerSetting(stmt, Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
R.integer.def_max_dhcp_retries);
loadBooleanSetting(stmt, Settings.Secure.SCREENSAVER_ENABLED,
R.bool.def_screensaver_enabled);
loadBooleanSetting(stmt, Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
@@ -1791,6 +1789,97 @@ public class DatabaseHelper extends SQLiteOpenHelper {
R.string.def_backup_transport);
}
private void loadGlobalSettings(SQLiteDatabase db) {
SQLiteStatement stmt = null;
try {
stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
+ " VALUES(?,?);");
// --- Previously in 'system'
loadBooleanSetting(stmt, Settings.Global.AIRPLANE_MODE_ON,
R.bool.def_airplane_mode_on);
loadStringSetting(stmt, Settings.Global.AIRPLANE_MODE_RADIOS,
R.string.def_airplane_mode_radios);
loadStringSetting(stmt, Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
R.string.airplane_mode_toggleable_radios);
loadBooleanSetting(stmt, Settings.Global.ASSISTED_GPS_ENABLED,
R.bool.assisted_gps_enabled);
loadBooleanSetting(stmt, Settings.Global.AUTO_TIME,
R.bool.def_auto_time); // Sync time to NITZ
loadBooleanSetting(stmt, Settings.Global.AUTO_TIME_ZONE,
R.bool.def_auto_time_zone); // Sync timezone to NITZ
loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
("1".equals(SystemProperties.get("ro.kernel.qemu")) ||
mContext.getResources().getBoolean(R.bool.def_stay_on_while_plugged_in))
? 1 : 0);
loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY,
R.integer.def_wifi_sleep_policy);
// --- Previously in 'secure'
loadBooleanSetting(stmt, Settings.Global.BLUETOOTH_ON,
R.bool.def_bluetooth_on);
// Enable or disable Cell Broadcast SMS
loadSetting(stmt, Settings.Global.CDMA_CELL_BROADCAST_SMS,
RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
// Data roaming default, based on build
loadSetting(stmt, Settings.Global.DATA_ROAMING,
"true".equalsIgnoreCase(
SystemProperties.get("ro.com.android.dataroaming",
"false")) ? 1 : 0);
loadBooleanSetting(stmt, Settings.Global.DEVICE_PROVISIONED,
R.bool.def_device_provisioned);
final int maxBytes = mContext.getResources().getInteger(
R.integer.def_download_manager_max_bytes_over_mobile);
if (maxBytes > 0) {
loadSetting(stmt, Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE,
Integer.toString(maxBytes));
}
final int recommendedMaxBytes = mContext.getResources().getInteger(
R.integer.def_download_manager_recommended_max_bytes_over_mobile);
if (recommendedMaxBytes > 0) {
loadSetting(stmt, Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
Integer.toString(recommendedMaxBytes));
}
// Mobile Data default, based on build
loadSetting(stmt, Settings.Global.MOBILE_DATA,
"true".equalsIgnoreCase(
SystemProperties.get("ro.com.android.mobiledata",
"true")) ? 1 : 0);
loadBooleanSetting(stmt, Settings.Global.NETSTATS_ENABLED,
R.bool.def_netstats_enabled);
loadBooleanSetting(stmt, Settings.Global.INSTALL_NON_MARKET_APPS,
R.bool.def_install_non_market_apps);
loadIntegerSetting(stmt, Settings.Global.NETWORK_PREFERENCE,
R.integer.def_network_preference);
loadBooleanSetting(stmt, Settings.Global.USB_MASS_STORAGE_ENABLED,
R.bool.def_usb_mass_storage_enabled);
loadIntegerSetting(stmt, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
R.integer.def_max_dhcp_retries);
// --- New global settings start here
} finally {
if (stmt != null) stmt.close();
}
}
private void loadSetting(SQLiteStatement stmt, String key, Object value) {
stmt.bindString(1, key);
stmt.bindString(2, value.toString());
@@ -1817,7 +1906,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
}
private int getIntValueFromSystem(SQLiteDatabase db, String name, int defaultValue) {
return getIntValueFromTable(db, "system", name, defaultValue);
return getIntValueFromTable(db, TABLE_SYSTEM, name, defaultValue);
}
private int getIntValueFromTable(SQLiteDatabase db, String table, String name,

View File

@@ -18,53 +18,73 @@ package com.android.providers.settings;
import java.io.FileNotFoundException;
import java.security.SecureRandom;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import android.app.ActivityManagerNative;
import android.app.backup.BackupManager;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteStatement;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DrmStore;
import android.provider.MediaStore;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.LruCache;
import android.util.Slog;
import android.util.SparseArray;
public class SettingsProvider extends ContentProvider {
private static final String TAG = "SettingsProvider";
private static final boolean LOCAL_LOGV = false;
private static final String TABLE_SYSTEM = "system";
private static final String TABLE_SECURE = "secure";
private static final String TABLE_GLOBAL = "global";
private static final String TABLE_FAVORITES = "favorites";
private static final String TABLE_OLD_FAVORITES = "old_favorites";
private static final String[] COLUMN_VALUE = new String[] { "value" };
// Cache for settings, access-ordered for acting as LRU.
// Caches for each user's settings, access-ordered for acting as LRU.
// Guarded by themselves.
private static final int MAX_CACHE_ENTRIES = 200;
private static final SettingsCache sSystemCache = new SettingsCache("system");
private static final SettingsCache sSecureCache = new SettingsCache("secure");
private static final SparseArray<SettingsCache> sSystemCaches
= new SparseArray<SettingsCache>();
private static final SparseArray<SettingsCache> sSecureCaches
= new SparseArray<SettingsCache>();
private static final SettingsCache sGlobalCache = new SettingsCache(TABLE_GLOBAL);
// The count of how many known (handled by SettingsProvider)
// database mutations are currently being handled. Used by
// sFileObserver to not reload the database when it's ourselves
// database mutations are currently being handled for this user.
// Used by file observers to not reload the database when it's ourselves
// modifying it.
private static final AtomicInteger sKnownMutationsInFlight = new AtomicInteger(0);
private static final SparseArray<AtomicInteger> sKnownMutationsInFlight
= new SparseArray<AtomicInteger>();
// Over this size we don't reject loading or saving settings but
// we do consider them broken/malicious and don't keep them in
@@ -77,9 +97,130 @@ public class SettingsProvider extends ContentProvider {
// want to cache the existence of a key, but not store its value.
private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null);
protected DatabaseHelper mOpenHelper;
// Each defined user has their own settings
protected final SparseArray<DatabaseHelper> mOpenHelpers = new SparseArray<DatabaseHelper>();
//protected DatabaseHelper mOpenHelper;
private UserManager mUserManager;
private BackupManager mBackupManager;
/**
* Settings which need to be treated as global/shared in multi-user environments.
*/
static final HashSet<String> sSecureGlobalKeys;
static final HashSet<String> sSystemGlobalKeys;
static {
// Keys (name column) from the 'secure' table that are now in the owner user's 'global'
// table, shared across all users
// These must match Settings.Secure.MOVED_TO_GLOBAL
sSecureGlobalKeys = new HashSet<String>();
sSecureGlobalKeys.add(Settings.Secure.ASSISTED_GPS_ENABLED);
sSecureGlobalKeys.add(Settings.Secure.CDMA_CELL_BROADCAST_SMS);
sSecureGlobalKeys.add(Settings.Secure.CDMA_ROAMING_MODE);
sSecureGlobalKeys.add(Settings.Secure.CDMA_SUBSCRIPTION_MODE);
sSecureGlobalKeys.add(Settings.Secure.DATA_ACTIVITY_TIMEOUT_MOBILE);
sSecureGlobalKeys.add(Settings.Secure.DATA_ACTIVITY_TIMEOUT_WIFI);
sSecureGlobalKeys.add(Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED);
sSecureGlobalKeys.add(Settings.Secure.DISPLAY_DENSITY_FORCED);
sSecureGlobalKeys.add(Settings.Secure.DISPLAY_SIZE_FORCED);
sSecureGlobalKeys.add(Settings.Secure.DOWNLOAD_MAX_BYTES_OVER_MOBILE);
sSecureGlobalKeys.add(Settings.Secure.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
sSecureGlobalKeys.add(Settings.Secure.MOBILE_DATA);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_DEV_BUCKET_DURATION);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_DEV_DELETE_AGE);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_DEV_PERSIST_BYTES);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_DEV_ROTATE_AGE);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_ENABLED);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_GLOBAL_ALERT_BYTES);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_POLL_INTERVAL);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_REPORT_XT_OVER_DEV);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_SAMPLE_ENABLED);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_TIME_CACHE_MAX_AGE);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_BUCKET_DURATION);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_DELETE_AGE);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_PERSIST_BYTES);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_ROTATE_AGE);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_TAG_BUCKET_DURATION);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_TAG_DELETE_AGE);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_TAG_PERSIST_BYTES);
sSecureGlobalKeys.add(Settings.Secure.NETSTATS_UID_TAG_ROTATE_AGE);
sSecureGlobalKeys.add(Settings.Secure.NETWORK_PREFERENCE);
sSecureGlobalKeys.add(Settings.Secure.NITZ_UPDATE_DIFF);
sSecureGlobalKeys.add(Settings.Secure.NITZ_UPDATE_SPACING);
sSecureGlobalKeys.add(Settings.Secure.NTP_SERVER);
sSecureGlobalKeys.add(Settings.Secure.NTP_TIMEOUT);
sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT);
sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS);
sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT);
sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS);
sSecureGlobalKeys.add(Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT);
sSecureGlobalKeys.add(Settings.Secure.SAMPLING_PROFILER_MS);
sSecureGlobalKeys.add(Settings.Secure.SETUP_PREPAID_DATA_SERVICE_URL);
sSecureGlobalKeys.add(Settings.Secure.SETUP_PREPAID_DETECTION_REDIR_HOST);
sSecureGlobalKeys.add(Settings.Secure.SETUP_PREPAID_DETECTION_TARGET_URL);
sSecureGlobalKeys.add(Settings.Secure.TETHER_DUN_APN);
sSecureGlobalKeys.add(Settings.Secure.TETHER_DUN_REQUIRED);
sSecureGlobalKeys.add(Settings.Secure.TETHER_SUPPORTED);
sSecureGlobalKeys.add(Settings.Secure.THROTTLE_HELP_URI);
sSecureGlobalKeys.add(Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC);
sSecureGlobalKeys.add(Settings.Secure.THROTTLE_NOTIFICATION_TYPE);
sSecureGlobalKeys.add(Settings.Secure.THROTTLE_POLLING_SEC);
sSecureGlobalKeys.add(Settings.Secure.THROTTLE_RESET_DAY);
sSecureGlobalKeys.add(Settings.Secure.THROTTLE_THRESHOLD_BYTES);
sSecureGlobalKeys.add(Settings.Secure.THROTTLE_VALUE_KBITSPS);
sSecureGlobalKeys.add(Settings.Secure.USE_GOOGLE_MAIL);
sSecureGlobalKeys.add(Settings.Secure.WEB_AUTOFILL_QUERY_URL);
sSecureGlobalKeys.add(Settings.Secure.WIFI_COUNTRY_CODE);
sSecureGlobalKeys.add(Settings.Secure.WIFI_FRAMEWORK_SCAN_INTERVAL_MS);
sSecureGlobalKeys.add(Settings.Secure.WIFI_FREQUENCY_BAND);
sSecureGlobalKeys.add(Settings.Secure.WIFI_IDLE_MS);
sSecureGlobalKeys.add(Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT);
sSecureGlobalKeys.add(Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS);
sSecureGlobalKeys.add(Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON);
sSecureGlobalKeys.add(Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY);
sSecureGlobalKeys.add(Settings.Secure.WIFI_NUM_OPEN_NETWORKS_KEPT);
sSecureGlobalKeys.add(Settings.Secure.WIFI_ON);
sSecureGlobalKeys.add(Settings.Secure.WIFI_P2P_DEVICE_NAME);
sSecureGlobalKeys.add(Settings.Secure.WIFI_SAVED_STATE);
sSecureGlobalKeys.add(Settings.Secure.WIFI_SUPPLICANT_SCAN_INTERVAL_MS);
sSecureGlobalKeys.add(Settings.Secure.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED);
sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_NUM_ARP_PINGS);
sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_ON);
sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
sSecureGlobalKeys.add(Settings.Secure.WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS);
sSecureGlobalKeys.add(Settings.Secure.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
sSecureGlobalKeys.add(Settings.Secure.WTF_IS_FATAL);
// Keys from the 'system' table now moved to 'global'
// These must match Settings.System.MOVED_TO_GLOBAL
sSystemGlobalKeys = new HashSet<String>();
sSystemGlobalKeys.add(Settings.Secure.ADB_ENABLED);
sSystemGlobalKeys.add(Settings.Secure.BLUETOOTH_ON);
sSystemGlobalKeys.add(Settings.Secure.DATA_ROAMING);
sSystemGlobalKeys.add(Settings.Secure.DEVICE_PROVISIONED);
sSystemGlobalKeys.add(Settings.Secure.INSTALL_NON_MARKET_APPS);
sSystemGlobalKeys.add(Settings.Secure.USB_MASS_STORAGE_ENABLED);
sSystemGlobalKeys.add(Settings.System.AIRPLANE_MODE_ON);
sSystemGlobalKeys.add(Settings.System.AIRPLANE_MODE_RADIOS);
sSystemGlobalKeys.add(Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
sSystemGlobalKeys.add(Settings.System.AUTO_TIME);
sSystemGlobalKeys.add(Settings.System.AUTO_TIME_ZONE);
sSystemGlobalKeys.add(Settings.System.CAR_DOCK_SOUND);
sSystemGlobalKeys.add(Settings.System.CAR_UNDOCK_SOUND);
sSystemGlobalKeys.add(Settings.System.DESK_DOCK_SOUND);
sSystemGlobalKeys.add(Settings.System.DESK_UNDOCK_SOUND);
sSystemGlobalKeys.add(Settings.System.DOCK_SOUNDS_ENABLED);
sSystemGlobalKeys.add(Settings.System.LOCK_SOUND);
sSystemGlobalKeys.add(Settings.System.UNLOCK_SOUND);
sSystemGlobalKeys.add(Settings.System.LOW_BATTERY_SOUND);
sSystemGlobalKeys.add(Settings.System.POWER_SOUNDS_ENABLED);
sSystemGlobalKeys.add(Settings.System.WIFI_SLEEP_POLICY);
}
private boolean settingMovedToGlobal(final String name) {
return sSecureGlobalKeys.contains(name) || sSystemGlobalKeys.contains(name);
}
/**
* Decode a content URL into the table, projection, and arguments
* used to access the corresponding database rows.
@@ -107,7 +248,7 @@ public class SettingsProvider extends ContentProvider {
if (!DatabaseHelper.isValidTable(this.table)) {
throw new IllegalArgumentException("Bad root path: " + this.table);
}
if ("system".equals(this.table) || "secure".equals(this.table)) {
if (TABLE_SYSTEM.equals(this.table) || TABLE_SECURE.equals(this.table)) {
this.where = Settings.NameValueTable.NAME + "=?";
this.args = new String[] { url.getPathSegments().get(1) };
} else {
@@ -144,7 +285,9 @@ public class SettingsProvider extends ContentProvider {
throw new IllegalArgumentException("Invalid URI: " + tableUri);
}
String table = tableUri.getPathSegments().get(0);
if ("system".equals(table) || "secure".equals(table)) {
if (TABLE_SYSTEM.equals(table) ||
TABLE_SECURE.equals(table) ||
TABLE_GLOBAL.equals(table)) {
String name = values.getAsString(Settings.NameValueTable.NAME);
return Uri.withAppendedPath(tableUri, name);
} else {
@@ -159,18 +302,21 @@ public class SettingsProvider extends ContentProvider {
* contract class uses these to provide client-side caches.)
* @param uri to send notifications for
*/
private void sendNotify(Uri uri) {
private void sendNotify(Uri uri, int userHandle) {
// Update the system property *first*, so if someone is listening for
// a notification and then using the contract class to get their data,
// the system property will be updated and they'll get the new data.
boolean backedUpDataChanged = false;
String property = null, table = uri.getPathSegments().get(0);
if (table.equals("system")) {
property = Settings.System.SYS_PROP_SETTING_VERSION;
if (table.equals(TABLE_SYSTEM)) {
property = Settings.System.SYS_PROP_SETTING_VERSION + '_' + userHandle;
backedUpDataChanged = true;
} else if (table.equals("secure")) {
property = Settings.Secure.SYS_PROP_SETTING_VERSION;
} else if (table.equals(TABLE_SECURE)) {
property = Settings.Secure.SYS_PROP_SETTING_VERSION + '_' + userHandle;
backedUpDataChanged = true;
} else if (table.equals(TABLE_GLOBAL)) {
property = Settings.Global.SYS_PROP_SETTING_VERSION; // this one is global
backedUpDataChanged = true;
}
@@ -201,7 +347,7 @@ public class SettingsProvider extends ContentProvider {
* @throws SecurityException if the caller is forbidden to write.
*/
private void checkWritePermissions(SqlArguments args) {
if ("secure".equals(args.table) &&
if ((TABLE_SECURE.equals(args.table) || TABLE_GLOBAL.equals(args.table)) &&
getContext().checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
PackageManager.PERMISSION_GRANTED) {
@@ -218,70 +364,147 @@ public class SettingsProvider extends ContentProvider {
// normally the exclusive owner of the database. But we keep this
// enabled all the time to minimize development-vs-user
// differences in testing.
private static SettingsFileObserver sObserverInstance;
private static SparseArray<SettingsFileObserver> sObserverInstances
= new SparseArray<SettingsFileObserver>();
private class SettingsFileObserver extends FileObserver {
private final AtomicBoolean mIsDirty = new AtomicBoolean(false);
private final int mUserHandle;
private final String mPath;
public SettingsFileObserver(String path) {
public SettingsFileObserver(int userHandle, String path) {
super(path, FileObserver.CLOSE_WRITE |
FileObserver.CREATE | FileObserver.DELETE |
FileObserver.MOVED_TO | FileObserver.MODIFY);
mUserHandle = userHandle;
mPath = path;
}
public void onEvent(int event, String path) {
int modsInFlight = sKnownMutationsInFlight.get();
int modsInFlight = sKnownMutationsInFlight.get(mUserHandle).get();
if (modsInFlight > 0) {
// our own modification.
return;
}
Log.d(TAG, "external modification to " + mPath + "; event=" + event);
Log.d(TAG, "User " + mUserHandle + " external modification to " + mPath
+ "; event=" + event);
if (!mIsDirty.compareAndSet(false, true)) {
// already handled. (we get a few update events
// during an sqlite write)
return;
}
Log.d(TAG, "updating our caches for " + mPath);
fullyPopulateCaches();
Log.d(TAG, "User " + mUserHandle + " updating our caches for " + mPath);
fullyPopulateCaches(mUserHandle);
mIsDirty.set(false);
}
}
@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext());
mBackupManager = new BackupManager(getContext());
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
if (!ensureAndroidIdIsSet()) {
return false;
synchronized (this) {
establishDbTrackingLocked(UserHandle.USER_OWNER);
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
getContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_OWNER);
if (userHandle != UserHandle.USER_OWNER) {
onUserRemoved(userHandle);
}
}
}
}, userFilter);
if (!ensureAndroidIdIsSet()) {
return false;
}
}
// Watch for external modifications to the database file,
// keeping our cache in sync.
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
sObserverInstance = new SettingsFileObserver(db.getPath());
sObserverInstance.startWatching();
startAsyncCachePopulation();
return true;
}
private void startAsyncCachePopulation() {
new Thread("populate-settings-caches") {
public void run() {
fullyPopulateCaches();
void onUserRemoved(int userHandle) {
// the db file itself will be deleted automatically, but we need to tear down
// our caches and other internal bookkeeping. Creation/deletion of a user's
// settings db infrastructure is synchronized on 'this'
synchronized (this) {
FileObserver observer = sObserverInstances.get(userHandle);
if (observer != null) {
observer.stopWatching();
sObserverInstances.delete(userHandle);
}
}.start();
mOpenHelpers.delete(userHandle);
sSystemCaches.delete(userHandle);
sSecureCaches.delete(userHandle);
sKnownMutationsInFlight.delete(userHandle);
String property = Settings.System.SYS_PROP_SETTING_VERSION + '_' + userHandle;
SystemProperties.set(property, "");
property = Settings.Secure.SYS_PROP_SETTING_VERSION + '_' + userHandle;
SystemProperties.set(property, "");
}
}
private void fullyPopulateCaches() {
fullyPopulateCache("secure", sSecureCache);
fullyPopulateCache("system", sSystemCache);
private void establishDbTrackingLocked(int userHandle) {
if (LOCAL_LOGV) {
Slog.i(TAG, "Installing settings db helper and caches for user " + userHandle);
}
DatabaseHelper dbhelper = new DatabaseHelper(getContext(), userHandle);
mOpenHelpers.append(userHandle, dbhelper);
// Watch for external modifications to the database files,
// keeping our caches in sync.
sSystemCaches.append(userHandle, new SettingsCache(TABLE_SYSTEM));
sSecureCaches.append(userHandle, new SettingsCache(TABLE_SECURE));
sKnownMutationsInFlight.append(userHandle, new AtomicInteger(0));
SQLiteDatabase db = dbhelper.getWritableDatabase();
// Now we can start observing it for changes
SettingsFileObserver observer = new SettingsFileObserver(userHandle, db.getPath());
sObserverInstances.append(userHandle, observer);
observer.startWatching();
startAsyncCachePopulation(userHandle);
}
class CachePrefetchThread extends Thread {
private int mUserHandle;
CachePrefetchThread(int userHandle) {
super("populate-settings-caches");
mUserHandle = userHandle;
}
@Override
public void run() {
fullyPopulateCaches(mUserHandle);
}
}
private void startAsyncCachePopulation(int userHandle) {
new CachePrefetchThread(userHandle).start();
}
private void fullyPopulateCaches(final int userHandle) {
DatabaseHelper dbHelper = mOpenHelpers.get(userHandle);
// Only populate the globals cache once, for the owning user
if (userHandle == UserHandle.USER_OWNER) {
fullyPopulateCache(dbHelper, TABLE_GLOBAL, sGlobalCache);
}
fullyPopulateCache(dbHelper, TABLE_SECURE, sSecureCaches.get(userHandle));
fullyPopulateCache(dbHelper, TABLE_SYSTEM, sSystemCaches.get(userHandle));
}
// Slurp all values (if sane in number & size) into cache.
private void fullyPopulateCache(String table, SettingsCache cache) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
private void fullyPopulateCache(DatabaseHelper dbHelper, String table, SettingsCache cache) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor c = db.query(
table,
new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE },
@@ -337,23 +560,154 @@ public class SettingsProvider extends ContentProvider {
}
}
// Lazy-initialize the settings caches for non-primary users
private SettingsCache getOrConstructCache(int callingUser, SparseArray<SettingsCache> which) {
synchronized (this) {
getOrEstablishDatabaseLocked(callingUser); // ignore return value; we don't need it
return which.get(callingUser);
}
}
// Lazy initialize the database helper and caches for this user, if necessary
private DatabaseHelper getOrEstablishDatabaseLocked(int callingUser) {
long oldId = Binder.clearCallingIdentity();
try {
DatabaseHelper dbHelper = mOpenHelpers.get(callingUser);
if (null == dbHelper) {
establishDbTrackingLocked(callingUser);
dbHelper = mOpenHelpers.get(callingUser);
}
return dbHelper;
} finally {
Binder.restoreCallingIdentity(oldId);
}
}
public SettingsCache cacheForTable(final int callingUser, String tableName) {
if (TABLE_SYSTEM.equals(tableName)) {
return getOrConstructCache(callingUser, sSystemCaches);
}
if (TABLE_SECURE.equals(tableName)) {
return getOrConstructCache(callingUser, sSecureCaches);
}
if (TABLE_GLOBAL.equals(tableName)) {
return sGlobalCache;
}
return null;
}
/**
* Used for wiping a whole cache on deletes when we're not
* sure what exactly was deleted or changed.
*/
public void invalidateCache(final int callingUser, String tableName) {
SettingsCache cache = cacheForTable(callingUser, tableName);
if (cache == null) {
return;
}
synchronized (cache) {
cache.evictAll();
cache.mCacheFullyMatchesDisk = false;
}
}
/**
* Fast path that avoids the use of chatty remoted Cursors.
*/
@Override
public Bundle call(String method, String request, Bundle args) {
int callingUser = UserHandle.getCallingUserId();
if (args != null) {
int reqUser = args.getInt(Settings.CALL_METHOD_USER_KEY, callingUser);
if (reqUser != callingUser) {
getContext().enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"Not permitted to access settings for other users");
if (reqUser == UserHandle.USER_CURRENT) {
try {
reqUser = ActivityManagerNative.getDefault().getCurrentUser().id;
} catch (RemoteException e) {
// can't happen
}
if (LOCAL_LOGV) {
Slog.v(TAG, " USER_CURRENT resolved to " + reqUser);
}
}
if (reqUser < 0) {
throw new IllegalArgumentException("Bad user handle " + reqUser);
}
callingUser = reqUser;
if (LOCAL_LOGV) Slog.v(TAG, " fetching setting for user " + callingUser);
}
}
// Note: we assume that get/put operations for moved-to-global names have already
// been directed to the new location on the caller side (otherwise we'd fix them
// up here).
DatabaseHelper dbHelper;
SettingsCache cache;
// Get methods
if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
return lookupValue("system", sSystemCache, request);
if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
synchronized (this) {
dbHelper = getOrEstablishDatabaseLocked(callingUser);
cache = sSystemCaches.get(callingUser);
}
return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
}
if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
return lookupValue("secure", sSecureCache, request);
if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
synchronized (this) {
dbHelper = getOrEstablishDatabaseLocked(callingUser);
cache = sSecureCaches.get(callingUser);
}
return lookupValue(dbHelper, TABLE_SECURE, cache, request);
}
if (Settings.CALL_METHOD_GET_GLOBAL.equals(method)) {
if (LOCAL_LOGV) Slog.v(TAG, "call(global:" + request + ") for " + callingUser);
// fast path: owner db & cache are immutable after onCreate() so we need not
// guard on the attempt to look them up
return lookupValue(getOrEstablishDatabaseLocked(UserHandle.USER_OWNER), TABLE_GLOBAL,
sGlobalCache, 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);
if (newValue == null) {
throw new IllegalArgumentException("Bad value for " + method);
}
final ContentValues values = new ContentValues();
values.put(Settings.NameValueTable.NAME, request);
values.put(Settings.NameValueTable.VALUE, newValue);
if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
insert(Settings.System.CONTENT_URI, values);
} else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
insert(Settings.Secure.CONTENT_URI, values);
} else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
insert(Settings.Global.CONTENT_URI, values);
} else {
Slog.w(TAG, "call() with invalid method: " + method);
}
return null;
}
// Looks up value 'key' in 'table' and returns either a single-pair Bundle,
// possibly with a null value, or null on failure.
private Bundle lookupValue(String table, SettingsCache cache, String key) {
private Bundle lookupValue(DatabaseHelper dbHelper, String table,
final SettingsCache cache, String key) {
if (cache == null) {
Slog.e(TAG, "cache is null for user " + UserHandle.getCallingUserId() + " : key=" + key);
return null;
}
synchronized (cache) {
Bundle value = cache.get(key);
if (value != null) {
@@ -372,7 +726,7 @@ public class SettingsProvider extends ContentProvider {
}
}
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = db.query(table, COLUMN_VALUE, "name=?", new String[]{key},
@@ -393,8 +747,14 @@ public class SettingsProvider extends ContentProvider {
@Override
public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {
final int callingUser = UserHandle.getCallingUserId();
if (LOCAL_LOGV) Slog.v(TAG, "query() for user " + callingUser);
SqlArguments args = new SqlArguments(url, where, whereArgs);
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
DatabaseHelper dbH;
synchronized (this) {
dbH = getOrEstablishDatabaseLocked(callingUser);
}
SQLiteDatabase db = dbH.getReadableDatabase();
// The favorites table was moved from this provider to a provider inside Home
// Home still need to query this table to upgrade from pre-cupcake builds
@@ -437,15 +797,22 @@ public class SettingsProvider extends ContentProvider {
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
final int callingUser = UserHandle.getCallingUserId();
if (LOCAL_LOGV) Slog.v(TAG, "bulkInsert() for user " + callingUser);
SqlArguments args = new SqlArguments(uri);
if (TABLE_FAVORITES.equals(args.table)) {
return 0;
}
checkWritePermissions(args);
SettingsCache cache = SettingsCache.forTable(args.table);
SettingsCache cache = cacheForTable(callingUser, args.table);
sKnownMutationsInFlight.incrementAndGet();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
mutationCount.incrementAndGet();
DatabaseHelper dbH;
synchronized (this) {
dbH = getOrEstablishDatabaseLocked(callingUser);
}
SQLiteDatabase db = dbH.getWritableDatabase();
db.beginTransaction();
try {
int numValues = values.length;
@@ -457,10 +824,10 @@ public class SettingsProvider extends ContentProvider {
db.setTransactionSuccessful();
} finally {
db.endTransaction();
sKnownMutationsInFlight.decrementAndGet();
mutationCount.decrementAndGet();
}
sendNotify(uri);
sendNotify(uri, callingUser);
return values.length;
}
@@ -538,6 +905,22 @@ public class SettingsProvider extends ContentProvider {
@Override
public Uri insert(Uri url, ContentValues initialValues) {
return insertForUser(url, initialValues, UserHandle.getCallingUserId());
}
// Settings.put*ForUser() always winds up here, so this is where we apply
// policy around permission to write settings for other users.
private Uri insertForUser(Uri url, ContentValues initialValues, int desiredUserHandle) {
final int callingUser = UserHandle.getCallingUserId();
if (callingUser != desiredUserHandle) {
getContext().enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"Not permitted to access settings for other users");
}
if (LOCAL_LOGV) Slog.v(TAG, "insert(" + url + ") for user " + desiredUserHandle
+ " by " + callingUser);
SqlArguments args = new SqlArguments(url);
if (TABLE_FAVORITES.equals(args.table)) {
return null;
@@ -551,28 +934,41 @@ public class SettingsProvider extends ContentProvider {
if (!parseProviderList(url, initialValues)) return null;
}
SettingsCache cache = SettingsCache.forTable(args.table);
// The global table is stored under the owner, always
if (TABLE_GLOBAL.equals(args.table)) {
desiredUserHandle = UserHandle.USER_OWNER;
}
SettingsCache cache = cacheForTable(desiredUserHandle, args.table);
String value = initialValues.getAsString(Settings.NameValueTable.VALUE);
if (SettingsCache.isRedundantSetValue(cache, name, value)) {
return Uri.withAppendedPath(url, name);
}
sKnownMutationsInFlight.incrementAndGet();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final AtomicInteger mutationCount = sKnownMutationsInFlight.get(desiredUserHandle);
mutationCount.incrementAndGet();
DatabaseHelper dbH;
synchronized (this) {
dbH = getOrEstablishDatabaseLocked(callingUser);
}
SQLiteDatabase db = dbH.getWritableDatabase();
final long rowId = db.insert(args.table, null, initialValues);
sKnownMutationsInFlight.decrementAndGet();
mutationCount.decrementAndGet();
if (rowId <= 0) return null;
SettingsCache.populate(cache, initialValues); // before we notify
if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + initialValues);
// Note that we use the original url here, not the potentially-rewritten table name
url = getUriFor(url, initialValues, rowId);
sendNotify(url);
sendNotify(url, desiredUserHandle);
return url;
}
@Override
public int delete(Uri url, String where, String[] whereArgs) {
final int callingUser = UserHandle.getCallingUserId();
if (LOCAL_LOGV) Slog.v(TAG, "delete() for user " + callingUser);
SqlArguments args = new SqlArguments(url, where, whereArgs);
if (TABLE_FAVORITES.equals(args.table)) {
return 0;
@@ -581,36 +977,53 @@ public class SettingsProvider extends ContentProvider {
}
checkWritePermissions(args);
sKnownMutationsInFlight.incrementAndGet();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count = db.delete(args.table, args.where, args.args);
sKnownMutationsInFlight.decrementAndGet();
if (count > 0) {
SettingsCache.invalidate(args.table); // before we notify
sendNotify(url);
final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
mutationCount.incrementAndGet();
DatabaseHelper dbH;
synchronized (this) {
dbH = getOrEstablishDatabaseLocked(callingUser);
}
startAsyncCachePopulation();
SQLiteDatabase db = dbH.getWritableDatabase();
int count = db.delete(args.table, args.where, args.args);
mutationCount.decrementAndGet();
if (count > 0) {
invalidateCache(callingUser, args.table); // before we notify
sendNotify(url, callingUser);
}
startAsyncCachePopulation(callingUser);
if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted");
return count;
}
@Override
public int update(Uri url, ContentValues initialValues, String where, String[] whereArgs) {
// NOTE: update() is never called by the front-end Settings 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.
final int callingUser = UserHandle.getCallingUserId();
if (LOCAL_LOGV) Slog.v(TAG, "update() for user " + callingUser);
SqlArguments args = new SqlArguments(url, where, whereArgs);
if (TABLE_FAVORITES.equals(args.table)) {
return 0;
}
checkWritePermissions(args);
sKnownMutationsInFlight.incrementAndGet();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count = db.update(args.table, initialValues, args.where, args.args);
sKnownMutationsInFlight.decrementAndGet();
if (count > 0) {
SettingsCache.invalidate(args.table); // before we notify
sendNotify(url);
final AtomicInteger mutationCount = sKnownMutationsInFlight.get(callingUser);
mutationCount.incrementAndGet();
DatabaseHelper dbH;
synchronized (this) {
dbH = getOrEstablishDatabaseLocked(callingUser);
}
startAsyncCachePopulation();
SQLiteDatabase db = dbH.getWritableDatabase();
int count = db.update(args.table, initialValues, args.where, args.args);
mutationCount.decrementAndGet();
if (count > 0) {
invalidateCache(callingUser, args.table); // before we notify
sendNotify(url, callingUser);
}
startAsyncCachePopulation(callingUser);
if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues);
return count;
}
@@ -772,16 +1185,6 @@ public class SettingsProvider extends ContentProvider {
return bundle;
}
public static SettingsCache forTable(String tableName) {
if ("system".equals(tableName)) {
return SettingsProvider.sSystemCache;
}
if ("secure".equals(tableName)) {
return SettingsProvider.sSecureCache;
}
return null;
}
/**
* Populates a key in a given (possibly-null) cache.
*/
@@ -808,21 +1211,6 @@ public class SettingsProvider extends ContentProvider {
}
}
/**
* Used for wiping a whole cache on deletes when we're not
* sure what exactly was deleted or changed.
*/
public static void invalidate(String tableName) {
SettingsCache cache = SettingsCache.forTable(tableName);
if (cache == null) {
return;
}
synchronized (cache) {
cache.evictAll();
cache.mCacheFullyMatchesDisk = false;
}
}
/**
* For suppressing duplicate/redundant settings inserts early,
* checking our cache first (but without faulting it in),