am 06efb530: Per-user settings
* commit '06efb530a479ea12398c1b3ee4b80e2ac85a1680': Per-user settings
This commit is contained in:
@@ -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
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user