summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/location/SettingsInjector.java
diff options
context:
space:
mode:
authorTom O'Neill <tomo@google.com>2013-08-29 21:22:54 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-08-29 21:22:54 +0000
commit4f08ddb28c4569004f888ea2955667b27485adf8 (patch)
treee9a2a0dc5a73fef6f0565eec78df623217a42fdd /src/com/android/settings/location/SettingsInjector.java
parentad320aa8d350e859a87269c67d6afd2f5401cf61 (diff)
parentf06dc3216318672d5a30d58c0cfcb4142dbab2c2 (diff)
downloadpackages_apps_Settings-4f08ddb28c4569004f888ea2955667b27485adf8.zip
packages_apps_Settings-4f08ddb28c4569004f888ea2955667b27485adf8.tar.gz
packages_apps_Settings-4f08ddb28c4569004f888ea2955667b27485adf8.tar.bz2
Merge "Make sure we've finished loading settings before processing any reloads" into klp-dev
Diffstat (limited to 'src/com/android/settings/location/SettingsInjector.java')
-rw-r--r--src/com/android/settings/location/SettingsInjector.java125
1 files changed, 85 insertions, 40 deletions
diff --git a/src/com/android/settings/location/SettingsInjector.java b/src/com/android/settings/location/SettingsInjector.java
index 22e2413..69de713 100644
--- a/src/com/android/settings/location/SettingsInjector.java
+++ b/src/com/android/settings/location/SettingsInjector.java
@@ -60,6 +60,10 @@ import java.util.Set;
class SettingsInjector {
static final String TAG = "SettingsInjector";
+ /**
+ * If reading the status of a setting takes longer than this, we go ahead and start reading
+ * the next setting.
+ */
private static final long INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS = 1000;
/**
@@ -219,9 +223,6 @@ class SettingsInjector {
/**
* Gets a list of preferences that other apps have injected.
- *
- * TODO: extract InjectedLocationSettingGetter that returns an iterable over
- * InjectedSetting objects, so that this class can focus on UI
*/
public List<Preference> getInjectedSettings() {
Iterable<InjectedSetting> settings = getSettings();
@@ -273,62 +274,83 @@ class SettingsInjector {
private final class StatusLoadingHandler extends Handler {
/**
- * Settings whose status values need to be loaded. A set is used to prevent redundant loads
- * even if {@link #reloadStatusMessages()} is called many times in rapid succession (for
- * example, if we receive a lot of {@link
- * android.location.SettingInjectorService#ACTION_INJECTED_SETTING_CHANGED} broadcasts).
- * <p/>
- * We use a linked hash set to ensure that when {@link #reloadStatusMessages()} is called,
- * any settings that haven't been loaded yet will finish loading before any already-loaded
- * messages are loaded again.
+ * Settings whose status values need to be loaded. A set is used to prevent redundant loads.
*/
- private LinkedHashSet<Setting> mSettingsToLoad = new LinkedHashSet<Setting>();
+ private Set<Setting> mSettingsToLoad = new HashSet<Setting>();
/**
- * Whether we're in the middle of loading settings.
+ * Settings that are being loaded now and haven't timed out. In practice this should have
+ * zero or one elements.
*/
- private boolean mLoading;
+ private Set<Setting> mSettingsBeingLoaded = new HashSet<Setting>();
+
+ /**
+ * Settings that are being loaded but have timed out. If only one setting has timed out, we
+ * will go ahead and start loading the next setting so that one slow load won't delay the
+ * load of the other settings.
+ */
+ private Set<Setting> mTimedOutSettings = new HashSet<Setting>();
+
+ private boolean mReloadRequested;
@Override
public void handleMessage(Message msg) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "handleMessage start: " + msg + ", mSettingsToLoad: " + mSettingsToLoad);
+ Log.d(TAG, "handleMessage start: " + msg + ", " + this);
}
+ // Update state in response to message
switch (msg.what) {
case WHAT_RELOAD:
- mSettingsToLoad.addAll(mSettings);
- if (mLoading) {
- // Already waiting for a service to return its status, don't ask a new one
- return;
- }
- mLoading = true;
+ mReloadRequested = true;
+ break;
+ case WHAT_RECEIVED_STATUS:
+ final Setting receivedSetting = (Setting) msg.obj;
+ mSettingsBeingLoaded.remove(receivedSetting);
+ mTimedOutSettings.remove(receivedSetting);
+ removeMessages(WHAT_TIMEOUT, receivedSetting);
break;
case WHAT_TIMEOUT:
+ final Setting timedOutSetting = (Setting) msg.obj;
+ mSettingsBeingLoaded.remove(timedOutSetting);
+ mTimedOutSettings.add(timedOutSetting);
if (Log.isLoggable(TAG, Log.WARN)) {
- final Setting setting = (Setting) msg.obj;
- setting.timedOut = true;
- Log.w(TAG, "Timed out trying to get status for: " + setting);
+ Log.w(TAG, "Timed out trying to get status for: " + timedOutSetting);
}
break;
- case WHAT_RECEIVED_STATUS:
- final Setting setting = (Setting) msg.obj;
- if (setting.timedOut) {
- // We've already restarted retrieving the next setting, don't start another
- return;
- }
-
- // Received the setting without timeout, clear any previous timed out status
- setting.timedOut = false;
- break;
default:
- throw new IllegalArgumentException("Unexpected what: " + msg);
+ Log.wtf(TAG, "Unexpected what: " + msg);
+ }
+
+ // Decide whether to load addiitonal settings based on the new state. Start by seeing
+ // if we have headroom to load another setting.
+ if (mSettingsBeingLoaded.size() > 0 || mTimedOutSettings.size() > 1) {
+ // Don't load any more settings until one of the pending settings has completed.
+ // To reduce memory pressure, we want to be loading at most one setting (plus at
+ // most one timed-out setting) at a time. This means we'll be responsible for
+ // bringing in at most two services.
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "too many services already live for " + msg + ", " + this);
+ }
+ return;
+ }
+
+ if (mReloadRequested && mSettingsToLoad.isEmpty() && mSettingsBeingLoaded.isEmpty()
+ && mTimedOutSettings.isEmpty()) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "reloading because idle and reload requesteed " + msg + ", " + this);
+ }
+ // Reload requested, so must reload all settings
+ mSettingsToLoad.addAll(mSettings);
+ mReloadRequested = false;
}
// Remove the next setting to load from the queue, if any
Iterator<Setting> iter = mSettingsToLoad.iterator();
if (!iter.hasNext()) {
- mLoading = false;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "nothing left to do for " + msg + ", " + this);
+ }
return;
}
Setting setting = iter.next();
@@ -337,17 +359,28 @@ class SettingsInjector {
// Request the status value
Intent intent = setting.createUpdatingIntent();
mContext.startService(intent);
+ mSettingsBeingLoaded.add(setting);
// Ensure that if receiving the status value takes too long, we start loading the
// next value anyway
Message timeoutMsg = obtainMessage(WHAT_TIMEOUT, setting);
- removeMessages(WHAT_TIMEOUT);
sendMessageDelayed(timeoutMsg, INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS);
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "handleMessage end: " + msg + ", mSettingsToLoad: " + mSettingsToLoad);
+ Log.d(TAG, "handleMessage end " + msg + ", " + this
+ + ", started loading " + setting);
}
}
+
+ @Override
+ public String toString() {
+ return "StatusLoadingHandler{" +
+ "mSettingsToLoad=" + mSettingsToLoad +
+ ", mSettingsBeingLoaded=" + mSettingsBeingLoaded +
+ ", mTimedOutSettings=" + mTimedOutSettings +
+ ", mReloadRequested=" + mReloadRequested +
+ '}';
+ }
}
/**
@@ -357,7 +390,6 @@ class SettingsInjector {
public final InjectedSetting setting;
public final Preference preference;
- public boolean timedOut = false;
private Setting(InjectedSetting setting, Preference preference) {
this.setting = setting;
@@ -369,11 +401,24 @@ class SettingsInjector {
return "Setting{" +
"setting=" + setting +
", preference=" + preference +
- ", timedOut=" + timedOut +
'}';
}
/**
+ * Returns true if they both have the same {@link #setting} value. Ignores mutable
+ * preference so that it's safe to use in sets.
+ */
+ @Override
+ public boolean equals(Object o) {
+ return this == o || o instanceof Setting && setting.equals(((Setting) o).setting);
+ }
+
+ @Override
+ public int hashCode() {
+ return setting.hashCode();
+ }
+
+ /**
* Creates an Intent to ask the receiver for the current status for the setting, and display
* it when it replies.
*/