Per-user settings
Each user has its own Settings.System.* and Settings.Secure.* namespace now. In addition, this CL introduces the new Settings.Global.* namespace, which contains a number of previously-elsewhere named settings entities; these Global.* entities are common to all users. Because these elements have been moved from their prior existence in the other namespaces, attempts to access them under their old names and namespaces are detected and redirected (with appropriate compile-time and logging messages) to their new homes. The new Global.* namespace can only be written by system-level code, just like the existing Secure.* namespace. If an app attempts to write a key that was previously in the System.* namespace but has been moved to the Global.* namespace, then a warning is logged and no write is performed; the action is a no-op. (The app is explicitly not crashed, to avoid breaking well-behaved apps that can't know any better.) There is also now a hidden API for getting/setting settings entities associated with a user other than the caller's. Reading/writing data for a user other than yourself requires the signature-level INTERACT_ACROSS_USERS_FULL permission. Manipulating data for a different user cannot be done via the ContentProvider query() / insert() APIs; you must use the Settings.get/put APIs for that degree of control. In general, use of the get/set API is *strongly* preferred over query-type access to Settings. Bug 6985398 Change-Id: Ibee54ddff99fb847c8c2479c23b50f1e7524d724
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