diff options
author | Christopher Tate <ctate@google.com> | 2015-02-17 12:15:25 -0800 |
---|---|---|
committer | Christopher Tate <ctate@google.com> | 2015-03-16 16:24:28 -0700 |
commit | 6597e3435f8abfedbb9a4f1bfb10cc17ea7f38bf (patch) | |
tree | 206ce9f7b2f09470356ebbb6bca7a16eec243707 /packages | |
parent | dc16e24afcffef742bc48ac1047ad48e928d3489 (diff) | |
download | frameworks_base-6597e3435f8abfedbb9a4f1bfb10cc17ea7f38bf.zip frameworks_base-6597e3435f8abfedbb9a4f1bfb10cc17ea7f38bf.tar.gz frameworks_base-6597e3435f8abfedbb9a4f1bfb10cc17ea7f38bf.tar.bz2 |
Notification listener backup & restore
We now back up & restore the set of enabled notification listeners. Post-
restore, a listener that had been enabled on the ancestral device will be
enabled on the current device as soon as it's installed, matching the
user's previous configuration. After this has happened the enable/disable
state for that app is not "sticky"; disabling it again will work as
expected.
The infrastructure for accomplishing this is general: it can be leveraged
by any ManagedServices derivative. There's a bit of extra wiring in the
settings provider to support the restore-time information flow as well.
This is because ManagedServices -- like many other parts of the system --
monitors writes to the settings provider and does work in response to new
writes of the elements that it cares about. Unfortunately this means that
there is no way to use the BackupAgent's restoreFinished() hook to post-
process the restored data: by the time it is run, the ManagedService's
observers have already executed and culled any unknown components from
the description that was just pushed into settings.
As of this patch, the settings provider's restore logic knows that a
particular settings element will require a message to interested observers
about the restore-driven change. The message is delivered as a broadcast,
and is sent after the new value has been committed to the settings db.
Adding other system ManagedService handling that parallels this will only
require adding a new corresponding entry to the table of individual settings
for which the relevant "this settings element is being restored" broadcast
is sent, found in SettingsHelper.
(It isn't sent for all settings elements because very few settings elements
have semantics that require it; 3rd party code won't be running yet during
platform restore anyway; and sending up to hundreds of broadcasts during
setup & restore is far from ideal.)
Bug 19254153
Change-Id: Ib8268c6cb273862a3ee089d2764f3bff4a299103
Diffstat (limited to 'packages')
-rw-r--r-- | packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java | 12 | ||||
-rw-r--r-- | packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java | 124 |
2 files changed, 110 insertions, 26 deletions
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index eac83d8..7f826ef 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -793,7 +793,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { } // Figure out the white list and redirects to the global table. - String[] whitelist = null; + final String[] whitelist; if (contentUri.equals(Settings.Secure.CONTENT_URI)) { whitelist = Settings.Secure.SETTINGS_TO_BACKUP; } else if (contentUri.equals(Settings.System.CONTENT_URI)) { @@ -809,6 +809,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { Map<String, String> cachedEntries = new HashMap<String, String>(); ContentValues contentValues = new ContentValues(2); SettingsHelper settingsHelper = mSettingsHelper; + ContentResolver cr = getContentResolver(); final int whiteListSize = whitelist.length; for (int i = 0; i < whiteListSize; i++) { @@ -841,14 +842,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { final Uri destination = (movedToGlobal != null && movedToGlobal.contains(key)) ? Settings.Global.CONTENT_URI : contentUri; - - // The helper doesn't care what namespace the keys are in - if (settingsHelper.restoreValue(key, value)) { - contentValues.clear(); - contentValues.put(Settings.NameValueTable.NAME, key); - contentValues.put(Settings.NameValueTable.VALUE, value); - getContentResolver().insert(destination, contentValues); - } + settingsHelper.restoreValue(this, cr, contentValues, destination, key, value); if (DEBUG) { Log.d(TAG, "Restored setting: " + destination + " : "+ key + "=" + value); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 4144c80..1cad610 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -19,7 +19,10 @@ package com.android.providers.settings; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.backup.IBackupManager; +import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.location.LocationManager; import android.media.AudioManager; @@ -28,10 +31,12 @@ import android.net.Uri; import android.os.IPowerManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.util.ArraySet; import java.util.Locale; @@ -41,6 +46,48 @@ public class SettingsHelper { private AudioManager mAudioManager; private TelephonyManager mTelephonyManager; + /** + * A few settings elements are special in that a restore of those values needs to + * be post-processed by relevant parts of the OS. A restore of any settings element + * mentioned in this table will therefore cause the system to send a broadcast with + * the {@link Intent#ACTION_SETTING_RESTORED} action, with extras naming the + * affected setting and supplying its pre-restore value for comparison. + * + * @see Intent#ACTION_SETTING_RESTORED + * @see System#SETTINGS_TO_BACKUP + * @see Secure#SETTINGS_TO_BACKUP + * @see Global#SETTINGS_TO_BACKUP + * + * {@hide} + */ + private static final ArraySet<String> sBroadcastOnRestore; + static { + sBroadcastOnRestore = new ArraySet<String>(1); + sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + } + + private interface SettingsLookup { + public String lookup(ContentResolver resolver, String name, int userHandle); + } + + private static SettingsLookup sSystemLookup = new SettingsLookup() { + public String lookup(ContentResolver resolver, String name, int userHandle) { + return Settings.System.getStringForUser(resolver, name, userHandle); + } + }; + + private static SettingsLookup sSecureLookup = new SettingsLookup() { + public String lookup(ContentResolver resolver, String name, int userHandle) { + return Settings.Secure.getStringForUser(resolver, name, userHandle); + } + }; + + private static SettingsLookup sGlobalLookup = new SettingsLookup() { + public String lookup(ContentResolver resolver, String name, int userHandle) { + return Settings.Global.getStringForUser(resolver, name, userHandle); + } + }; + public SettingsHelper(Context context) { mContext = context; mAudioManager = (AudioManager) context @@ -58,24 +105,67 @@ public class SettingsHelper { * some cases the data will be written by the call to the appropriate API, * and in some cases the property value needs to be modified before setting. */ - public boolean restoreValue(String name, String value) { - if (Settings.System.SCREEN_BRIGHTNESS.equals(name)) { - setBrightness(Integer.parseInt(value)); - } else if (Settings.System.SOUND_EFFECTS_ENABLED.equals(name)) { - setSoundEffects(Integer.parseInt(value) == 1); - } else if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { - setGpsLocation(value); - return false; - } else if (Settings.Secure.BACKUP_AUTO_RESTORE.equals(name)) { - setAutoRestore(Integer.parseInt(value) == 1); - } else if (isAlreadyConfiguredCriticalAccessibilitySetting(name)) { - return false; - } else if (Settings.System.RINGTONE.equals(name) - || Settings.System.NOTIFICATION_SOUND.equals(name)) { - setRingtone(name, value); - return false; + public void restoreValue(Context context, ContentResolver cr, ContentValues contentValues, + Uri destination, String name, String value) { + // Will we need a post-restore broadcast for this element? + String oldValue = null; + boolean sendBroadcast = false; + final SettingsLookup table; + + if (destination.equals(Settings.Secure.CONTENT_URI)) { + table = sSecureLookup; + } else if (destination.equals(Settings.System.CONTENT_URI)) { + table = sSystemLookup; + } else { /* must be GLOBAL; this was preflighted by the caller */ + table = sGlobalLookup; + } + + if (sBroadcastOnRestore.contains(name)) { + oldValue = table.lookup(cr, name, UserHandle.USER_OWNER); + sendBroadcast = true; + } + + try { + if (Settings.System.SCREEN_BRIGHTNESS.equals(name)) { + setBrightness(Integer.parseInt(value)); + // fall through to the ordinary write to settings + } else if (Settings.System.SOUND_EFFECTS_ENABLED.equals(name)) { + setSoundEffects(Integer.parseInt(value) == 1); + // fall through to the ordinary write to settings + } else if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { + setGpsLocation(value); + return; + } else if (Settings.Secure.BACKUP_AUTO_RESTORE.equals(name)) { + setAutoRestore(Integer.parseInt(value) == 1); + } else if (isAlreadyConfiguredCriticalAccessibilitySetting(name)) { + return; + } else if (Settings.System.RINGTONE.equals(name) + || Settings.System.NOTIFICATION_SOUND.equals(name)) { + setRingtone(name, value); + return; + } + + // Default case: write the restored value to settings + contentValues.clear(); + contentValues.put(Settings.NameValueTable.NAME, name); + contentValues.put(Settings.NameValueTable.VALUE, value); + cr.insert(destination, contentValues); + } catch (Exception e) { + // If we fail to apply the setting, by definition nothing happened + sendBroadcast = false; + } finally { + // If this was an element of interest, send the "we just restored it" + // broadcast with the historical value now that the new value has + // been committed and observers kicked off. + if (sendBroadcast) { + Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED) + .setPackage("android").addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) + .putExtra(Intent.EXTRA_SETTING_NAME, name) + .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value) + .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue); + context.sendBroadcastAsUser(intent, UserHandle.OWNER, null); + } } - return true; } public String onBackupValue(String name, String value) { |