Fix ANR deadlock

When the Mms app starts up, Conversation starts a background thread
to load all the conversations into a cache. It had a lock on the cache.
Meanwhile, the ComposeMessageActivity started up and tried to load
its conversation, but was blocked and eventually ANR'd. Make the
sync lock cover a much smaller scope and don't hold the lock during
the slow operation of looking up the contact info. Also, remove the
unused SuggestionsProvider. It was causing the Mms app to spin up
needlessly. Fixes bug 2220565.

Change-Id: I0f8260338a5368dc4b8ef11249361ca0716a2887
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3e21e63..f9b433c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -248,9 +248,5 @@
         <meta-data android:name="android.app.default_searchable"
             android:value=".ui.SearchActivity" />
 
-        <!-- Search Suggestions Provider -->
-        <provider android:name="SuggestionsProvider"
-            android:authorities="com.android.mms.SuggestionsProvider" />
-
     </application>
 </manifest>
diff --git a/src/com/android/mms/SuggestionsProvider.java b/src/com/android/mms/SuggestionsProvider.java
deleted file mode 100644
index 2af3332..0000000
--- a/src/com/android/mms/SuggestionsProvider.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.mms;
-
-import android.content.SearchRecentSuggestionsProvider;
-
-/**
- * Simple extension / instantiation of SearchRecentSuggestionsProvider, for use with mms.
- */
-public class SuggestionsProvider extends SearchRecentSuggestionsProvider {
-
-    final static String AUTHORITY = "com.android.mms.SuggestionsProvider";
-    final static int MODE = DATABASE_MODE_QUERIES + DATABASE_MODE_2LINES;
-
-    public SuggestionsProvider() {
-        super();
-        setupSuggestions(AUTHORITY, MODE);
-    }
-}
diff --git a/src/com/android/mms/data/Conversation.java b/src/com/android/mms/data/Conversation.java
index d70559e..a44e0a9 100644
--- a/src/com/android/mms/data/Conversation.java
+++ b/src/com/android/mms/data/Conversation.java
@@ -516,9 +516,13 @@
             conv.mHasUnreadMessages = (c.getInt(READ) == 0);
             conv.mHasError = (c.getInt(ERROR) != 0);
             conv.mHasAttachment = (c.getInt(HAS_ATTACHMENT) != 0);
-
-            String recipientIds = c.getString(RECIPIENT_IDS);
-            conv.mRecipients = ContactList.getByIds(recipientIds, allowQuery);
+        }
+        // Fill in as much of the conversation as we can before doing the slow stuff of looking
+        // up the contacts associated with this conversation.
+        String recipientIds = c.getString(RECIPIENT_IDS);
+        ContactList recipients = ContactList.getByIds(recipientIds, allowQuery);;
+        synchronized (conv) {
+            conv.mRecipients = recipients;
         }
     }
 
@@ -656,55 +660,71 @@
      * Are we in the process of loading and caching all the threads?.
      */
    public static boolean loadingThreads() {
-        return mLoadingThreads;
+       synchronized (Cache.getInstance()) {
+           return mLoadingThreads;
+       }
     }
 
-    private static void cacheAllThreads(Context context) {
-        synchronized (Cache.getInstance()) {
-            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
-                LogTag.debug("[Conversation] cacheAllThreads");
-            }
-            mLoadingThreads = true;
+   private static void cacheAllThreads(Context context) {
+       if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
+           LogTag.debug("[Conversation] cacheAllThreads");
+       }
+       synchronized (Cache.getInstance()) {
+           if (mLoadingThreads) {
+              return;
+           }
+           mLoadingThreads = true;
+       }
 
-            // Keep track of what threads are now on disk so we
-            // can discard anything removed from the cache.
-            HashSet<Long> threadsOnDisk = new HashSet<Long>();
+       // Keep track of what threads are now on disk so we
+       // can discard anything removed from the cache.
+       HashSet<Long> threadsOnDisk = new HashSet<Long>();
 
-            // Query for all conversations.
-            Cursor c = context.getContentResolver().query(sAllThreadsUri,
-                    ALL_THREADS_PROJECTION, null, null, null);
-            try {
-                while (c.moveToNext()) {
-                    long threadId = c.getLong(ID);
-                    threadsOnDisk.add(threadId);
+       // Query for all conversations.
+       Cursor c = context.getContentResolver().query(sAllThreadsUri,
+               ALL_THREADS_PROJECTION, null, null, null);
+       try {
+           if (c != null) {
+               while (c.moveToNext()) {
+                   long threadId = c.getLong(ID);
+                   threadsOnDisk.add(threadId);
 
-                    // Try to find this thread ID in the cache.
-                    Conversation conv = Cache.get(threadId);
+                   // Try to find this thread ID in the cache.
+                   Conversation conv;
+                   synchronized (Cache.getInstance()) {
+                       conv = Cache.get(threadId);
+                   }
 
-                    if (conv == null) {
-                        // Make a new Conversation and put it in
-                        // the cache if necessary.
-                        conv = new Conversation(context, c, true);
-                        try {
-                            Cache.put(conv);
-                        } catch (IllegalStateException e) {
-                            LogTag.error("Tried to add duplicate Conversation to Cache");
-                        }
-                    } else {
-                        // Or update in place so people with references
-                        // to conversations get updated too.
-                        fillFromCursor(context, conv, c, true);
-                    }
-                }
-            } finally {
-                c.close();
-                mLoadingThreads = false;
-            }
+                   if (conv == null) {
+                       // Make a new Conversation and put it in
+                       // the cache if necessary.
+                       conv = new Conversation(context, c, true);
+                       try {
+                           synchronized (Cache.getInstance()) {
+                               Cache.put(conv);
+                           }
+                       } catch (IllegalStateException e) {
+                           LogTag.error("Tried to add duplicate Conversation to Cache");
+                       }
+                   } else {
+                       // Or update in place so people with references
+                       // to conversations get updated too.
+                       fillFromCursor(context, conv, c, true);
+                   }
+               }
+           }
+       } finally {
+           if (c != null) {
+               c.close();
+           }
+           synchronized (Cache.getInstance()) {
+               mLoadingThreads = false;
+           }
+       }
 
-            // Purge the cache of threads that no longer exist on disk.
-            Cache.keepOnly(threadsOnDisk);
-        }
-    }
+       // Purge the cache of threads that no longer exist on disk.
+       Cache.keepOnly(threadsOnDisk);
+   }
 
     private boolean loadFromThreadId(long threadId) {
         Cursor c = mContext.getContentResolver().query(sAllThreadsUri, ALL_THREADS_PROJECTION,