diff options
author | Ben Murdoch <benm@google.com> | 2012-05-14 16:39:50 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2012-05-16 18:05:38 +0100 |
commit | 234eadcf7d0dbf2d24f92c24f40343d518f6fe3a (patch) | |
tree | 3c06d8f77a653ef05940c049c549826ebca0569d | |
parent | 54217b39d7d097f2f4fe9fac928a1c3bf1b9f13f (diff) | |
download | packages_apps_browser-234eadcf7d0dbf2d24f92c24f40343d518f6fe3a.zip packages_apps_browser-234eadcf7d0dbf2d24f92c24f40343d518f6fe3a.tar.gz packages_apps_browser-234eadcf7d0dbf2d24f92c24f40343d518f6fe3a.tar.bz2 |
Don't wait on ContactsProvider
Right now during the initial WebSettings sync to native we wait
for the autofill profile to be loaded from disk so that it can
be synced. If there's no profile set, then we try to infer a
profile from the user's Me contact profile. Querying the Me
contact can be slow and in some extreme cases can cause the
settings sync on the UI thread to block long enough to show
an ANR.
Instead signal the threads (via the CountdownLatch) waiting on
the initial import before we do the Me profile lookup. Note that
we still may block the UI thread if the lookup of an already saved
profile takes an exceptionally long time. This is so that when a
user has saved a profile, we'll never resort to showing them the
"setup autofill" message. (But all ANR reports to date have shown
that we were querying the Me contact at the time of ANR).
Bug: 6371781
Change-Id: Ibb0d5e285ec3587d9f9bad3e69b79890850c2f6d
-rw-r--r-- | src/com/android/browser/AutofillHandler.java | 61 | ||||
-rw-r--r-- | src/com/android/browser/BrowserSettings.java | 2 |
2 files changed, 43 insertions, 20 deletions
diff --git a/src/com/android/browser/AutofillHandler.java b/src/com/android/browser/AutofillHandler.java index 99ee6a0..5b0320d 100644 --- a/src/com/android/browser/AutofillHandler.java +++ b/src/com/android/browser/AutofillHandler.java @@ -26,6 +26,7 @@ import android.os.AsyncTask; import android.os.Message; import android.preference.PreferenceManager; import android.provider.ContactsContract; +import android.util.Log; import android.webkit.WebSettingsClassic.AutoFillProfile; import java.util.concurrent.CountDownLatch; @@ -43,6 +44,8 @@ public class AutofillHandler { private CountDownLatch mLoaded = new CountDownLatch(1); private Context mContext; + private static final String LOGTAG = "AutofillHandler"; + public AutofillHandler(Context context) { mContext = context.getApplicationContext(); } @@ -62,18 +65,24 @@ public class AutofillHandler { new LoadFromDb().start(); } - public void waitForLoad() { + private void waitForLoad() { try { mLoaded.await(); - } catch (InterruptedException e) {} + } catch (InterruptedException e) { + Log.w(LOGTAG, "Caught exception while waiting for AutofillProfile to load."); + } } private class LoadFromDb extends Thread { @Override public void run() { - SharedPreferences p = - PreferenceManager.getDefaultSharedPreferences(mContext); + // Note the lack of synchronization over mAutoFillActiveProfileId and + // mAutoFillProfile here. This is because we control all other access + // to these members through the public functions of this class, and they + // all wait for this thread via the mLoaded CountDownLatch. + + SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(mContext); // Read the last active AutoFill profile id. mAutoFillActiveProfileId = p.getInt( @@ -82,7 +91,9 @@ public class AutofillHandler { // Load the autofill profile data from the database. We use a database separate // to the browser preference DB to make it easier to support multiple profiles - // and switching between them. + // and switching between them. Note that this may block startup if this DB lookup + // is extremely slow. We do this to ensure that if there's a profile set, the + // user never sees the "setup Autofill" option. AutoFillProfileDatabase autoFillDb = AutoFillProfileDatabase.getInstance(mContext); Cursor c = autoFillDb.getProfile(mAutoFillActiveProfileId); @@ -116,12 +127,19 @@ public class AutofillHandler { c.close(); autoFillDb.close(); + // At this point we've loaded the profile if there was one, so let any thread + // waiting on initialization continue. + mLoaded.countDown(); + + // Synchronization note: strictly speaking, it's possible that mAutoFillProfile + // may get a value after we check below, but that's OK. This check is only an + // optimisation, and we do a proper synchronized check further down when it comes + // to actually setting the inferred profile. if (mAutoFillProfile == null) { - // We did not load a profile from disk. Try to populate one with the user's + // We did not load a profile from disk. Try to infer one from the user's // "me" contact. final Uri profileUri = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, ContactsContract.Contacts.Data.CONTENT_DIRECTORY); - String name = getContactField(profileUri, ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); @@ -143,13 +161,16 @@ public class AutofillHandler { // When querying structured postal data, it often all comes back as a string // inside the "street" field. - mAutoFillProfile = new AutoFillProfile( - 1, name, email, company, null, null, null, null, - null, null, phone); + synchronized(AutofillHandler.this) { + // Only use this profile if one hasn't been set inbetween the + // inital import and this thread getting to this point. + if (mAutoFillProfile == null) { + setAutoFillProfile(new AutoFillProfile(1, name, email, company, + null, null, null, null, null, null, phone), null); + } + } } } - - mLoaded.countDown(); } private String getContactField(Uri uri, String field, String itemType) { @@ -174,7 +195,8 @@ public class AutofillHandler { } } - public void setAutoFillProfile(AutoFillProfile profile, Message msg) { + public synchronized void setAutoFillProfile(AutoFillProfile profile, Message msg) { + waitForLoad(); int profileId = NO_AUTOFILL_PROFILE_SET; if (profile != null) { profileId = profile.getUniqueId(); @@ -193,11 +215,12 @@ public class AutofillHandler { setActiveAutoFillProfileId(profileId); } - public AutoFillProfile getAutoFillProfile() { + public synchronized AutoFillProfile getAutoFillProfile() { + waitForLoad(); return mAutoFillProfile; } - private void setActiveAutoFillProfileId(int activeProfileId) { + private synchronized void setActiveAutoFillProfileId(int activeProfileId) { mAutoFillActiveProfileId = activeProfileId; Editor ed = PreferenceManager. getDefaultSharedPreferences(mContext).edit(); @@ -234,9 +257,11 @@ public class AutofillHandler { @Override protected Void doInBackground(AutoFillProfile... values) { mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext); - assert mAutoFillActiveProfileId != NO_AUTOFILL_PROFILE_SET; - AutoFillProfile newProfile = values[0]; - mAutoFillProfileDb.addOrUpdateProfile(mAutoFillActiveProfileId, newProfile); + synchronized (AutofillHandler.this) { + assert mAutoFillActiveProfileId != NO_AUTOFILL_PROFILE_SET; + AutoFillProfile newProfile = values[0]; + mAutoFillProfileDb.addOrUpdateProfile(mAutoFillActiveProfileId, newProfile); + } return null; } } diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java index 76dc48f..0b7e87a 100644 --- a/src/com/android/browser/BrowserSettings.java +++ b/src/com/android/browser/BrowserSettings.java @@ -510,12 +510,10 @@ public class BrowserSettings implements OnSharedPreferenceChangeListener, } public AutoFillProfile getAutoFillProfile() { - mAutofillHandler.waitForLoad(); return mAutofillHandler.getAutoFillProfile(); } public void setAutoFillProfile(AutoFillProfile profile, Message msg) { - mAutofillHandler.waitForLoad(); mAutofillHandler.setAutoFillProfile(profile, msg); // Auto-fill will reuse the same profile ID when making edits to the profile, // so we need to force a settings sync (otherwise the SharedPreferences |