Fix a few sync-related bugs:

* Don't leak the receiver registered by AccountManager for updates
* Only restart dead threads if they aren't logged in the syncErrorMap
  (to prevent looping behavior as seen in bug #2072456

Change-Id: Id1b4c53ef8b721bf8bfa8426627fd76831864c70
diff --git a/src/com/android/exchange/AbstractSyncService.java b/src/com/android/exchange/AbstractSyncService.java
index 59dc58b..15cede8 100644
--- a/src/com/android/exchange/AbstractSyncService.java
+++ b/src/com/android/exchange/AbstractSyncService.java
@@ -203,7 +203,12 @@
         }
     }
 
-    public void userLog(Exception e) {
+    public void userLog(String str, Exception e) {
+        if (Eas.USER_LOG) {
+            Log.e(TAG, str, e);
+        } else {
+            Log.e(TAG, str + e);
+        }
         if (Eas.FILE_LOG) {
             FileLogger.log(e);
         }
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index 9d0e21b..62e770b 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -57,7 +57,6 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.os.RemoteException;
-import android.util.Log;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -1024,11 +1023,10 @@
             }
         } catch (IOException e) {
             String message = e.getMessage();
-            userLog("Caught IOException: ", ((message == null) ? "" : message));
+            userLog("Caught IOException: ", ((message == null) ? "No message" : message));
             mExitStatus = EXIT_IO_ERROR;
         } catch (Exception e) {
-            Log.e(TAG, "Uncaught exception in EasSyncService" + e);
-            userLog(e);
+            userLog("Uncaught exception in EasSyncService", e);
         } finally {
             if (!mStop) {
                 userLog(mMailbox.mDisplayName, ": sync finished");
diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java
index ab3e23f..ae31c8e 100644
--- a/src/com/android/exchange/SyncManager.java
+++ b/src/com/android/exchange/SyncManager.java
@@ -63,7 +63,6 @@
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -621,7 +620,7 @@
      */
     static public synchronized String getDeviceId() throws IOException {
         if (INSTANCE == null) {
-            throw new IOException();
+            throw new IOException("No SyncManager instance");
         }
         // If we've already got the id, return it
         if (INSTANCE.mDeviceId != null) {
@@ -645,14 +644,9 @@
                 w.close();
                 return id;
             }
-        } catch (FileNotFoundException e) {
-            // We'll just use the default below
-            Log.e(TAG, "Can't get device name!");
         } catch (IOException e) {
-            // We'll just use the default below
-            Log.e(TAG, "Can't get device name!");
         }
-        throw new IOException();
+        throw new IOException("Can't get device name");
     }
 
     @Override
@@ -705,6 +699,9 @@
         resolver.unregisterContentObserver(mSyncedMessageObserver);
         resolver.unregisterContentObserver(mMessageObserver);
 
+        // Don't leak the Intent associated with this listener
+        AccountManager.get(this).removeOnAccountsUpdatedListener(mAccountsUpdatedListener);
+
         // Clear pending alarms
         clearAlarms();
 
@@ -1286,7 +1283,8 @@
                     }
                 } else {
                     Thread thread = service.mThread;
-                    if (thread != null && !thread.isAlive()) {
+                    // Look for threads that have died but aren't in an error state
+                    if (thread != null && !thread.isAlive() && !mSyncErrorMap.containsKey(mid)) {
                         releaseMailbox(mid);
                         // Restart this if necessary
                         if (nextWait > 3*SECONDS) {
diff --git a/src/com/android/exchange/adapter/AbstractSyncParser.java b/src/com/android/exchange/adapter/AbstractSyncParser.java
index 2a323cf..d598c22 100644
--- a/src/com/android/exchange/adapter/AbstractSyncParser.java
+++ b/src/com/android/exchange/adapter/AbstractSyncParser.java
@@ -87,7 +87,7 @@
 
         // If we're not at the top of the xml tree, throw an exception
         if (nextTag(START_DOCUMENT) != Tags.SYNC_SYNC) {
-            throw new IOException();
+            throw new EasParserException();
         }
         // Loop here through the remaining xml
         while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index b4fd00b..379a80f 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -103,7 +103,7 @@
         boolean res = false;
         boolean resetFolders = false;
         if (nextTag(START_DOCUMENT) != Tags.FOLDER_FOLDER_SYNC)
-            throw new IOException();
+            throw new EasParserException();
         while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
             if (tag == Tags.FOLDER_STATUS) {
                 status = getValueInt();
@@ -122,7 +122,7 @@
                         // Other errors are at the server, so let's throw an error that will
                         // cause this sync to be retried at a later time
                         mService.errorLog("Throwing IOException; will retry later");
-                        throw new IOException();
+                        throw new EasParserException("Folder status error");
                     }
                 }
             } else if (tag == Tags.FOLDER_SYNC_KEY) {
diff --git a/src/com/android/exchange/adapter/Parser.java b/src/com/android/exchange/adapter/Parser.java
index f9a005f..ffe8039 100644
--- a/src/com/android/exchange/adapter/Parser.java
+++ b/src/com/android/exchange/adapter/Parser.java
@@ -116,6 +116,14 @@
 
     public class EasParserException extends IOException {
         private static final long serialVersionUID = 1L;
+
+        EasParserException() {
+            super("WBXML format error");
+        }
+
+        EasParserException(String reason) {
+            super(reason);
+        }
     }
 
     public boolean parse() throws IOException, EasException {
diff --git a/src/com/android/exchange/adapter/Serializer.java b/src/com/android/exchange/adapter/Serializer.java
index 0d9e669..faaade4 100644
--- a/src/com/android/exchange/adapter/Serializer.java
+++ b/src/com/android/exchange/adapter/Serializer.java
@@ -57,7 +57,7 @@
 
     public void done() throws IOException {
         if (depth != 0) {
-            throw new IOException();
+            throw new IOException("Done received with unclosed tags");
         }
         writeInteger(out, 0);
         out.write(buf.toByteArray());