Merge change 22654 into eclair

* changes:
  Move some Contacts fields to new CommonDataKinds rows
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b0f5d4c..ff3cae5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -57,7 +57,15 @@
     <!-- Menu item/button name -->
     <string name="read_unread_action">Read/Unread</string>
     <!-- Menu item/button name -->
+    <string name="read_action">Mark Read</string>
+    <!-- Menu item/button name -->
+    <string name="unread_action">Mark Unread</string>
+    <!-- Menu item/button name -->
     <string name="favorite_action">Favorite</string>
+    <!-- Menu item/button name -->
+    <string name="set_star_action">Mark Star</string>
+    <!-- Menu item/button name -->
+    <string name="remove_star_action">Remove Star</string>
     <!-- Menu item -->
     <string name="refresh_action">Refresh</string>
     <!-- Menu item -->
@@ -73,6 +81,8 @@
     <!-- Menu item -->
     <string name="remove_account_action">Remove account</string>
     <!-- Menu item -->
+    <string name="folders_action">Folders</string>
+    <!-- Menu item -->
     <string name="accounts_action">Accounts</string>
     <!-- Menu item -->
     <string name="mark_as_read_action">Mark as read</string>
@@ -480,9 +490,6 @@
     <!-- Name of Microsoft Exchange account type; used by AccountManager -->
     <string name="exchange_name">Exchange</string>
 
-    <!-- Title for notification that Exchange push isn't working properly -->
-    <string name="notification_ping_loop_title">Exchange notice</string>
-    <!-- Warning that Exchange push is not working properly and has been turned off in favor of a 5 minute check interval -->
-    <string name="notification_ping_loop_text">Exchange push has been disabled.</string>
-
+	<!-- Message that appears if the AccountManager cannot create the system Account -->
+	<string name="system_account_create_failed">The AccountManager could not create the Account; please try again.</string>
 </resources>
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index 4f70c44..46bcda5 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -683,8 +683,10 @@
                     int pingStatus = SyncManager.pingStatus(mailboxId);
                     String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
                     if (pingStatus == SyncManager.PING_STATUS_OK) {
+
                         String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN);
                         if ((syncKey == null) || syncKey.equals("0")) {
+                            // We can't push until the initial sync is done
                             pushCount--;
                             continue;
                         }
diff --git a/src/com/android/exchange/SyncManager.java b/src/com/android/exchange/SyncManager.java
index baab1c2..a226e2e 100644
--- a/src/com/android/exchange/SyncManager.java
+++ b/src/com/android/exchange/SyncManager.java
@@ -156,26 +156,28 @@
     // Keeps track of which services require a wake lock (by mailbox id)
     private HashMap<Long, Boolean> mWakeLocks = new HashMap<Long, Boolean>();
     // Keeps track of PendingIntents for mailbox alarms (by mailbox id)
-    static private HashMap<Long, PendingIntent> sPendingIntents =
-        new HashMap<Long, PendingIntent>();
+    private HashMap<Long, PendingIntent> mPendingIntents = new HashMap<Long, PendingIntent>();
     // The actual WakeLock obtained by SyncManager
     private WakeLock mWakeLock = null;
     private static final AccountList EMPTY_ACCOUNT_LIST = new AccountList();
 
     // Observers that we use to look for changed mail-related data
+    private Handler mHandler = new Handler();
     private AccountObserver mAccountObserver;
     private MailboxObserver mMailboxObserver;
     private SyncedMessageObserver mSyncedMessageObserver;
     private MessageObserver mMessageObserver;
     private EasSyncStatusObserver mSyncStatusObserver;
     private EasAccountsUpdatedListener mAccountsUpdatedListener;
-    private Handler mHandler = new Handler();
 
     private ContentResolver mResolver;
 
     // The singleton SyncManager object, with its thread and stop flag
     protected static SyncManager INSTANCE;
     private static Thread sServiceThread = null;
+    // Cached unique device id
+    private static String sDeviceId = null;
+
     private boolean mStop = false;
 
     // The reason for SyncManager's next wakeup call
@@ -184,9 +186,6 @@
     // Receiver of connectivity broadcasts
     private ConnectivityReceiver mConnectivityReceiver = null;
 
-    // Cached unique device id
-    private String mDeviceId = null;
-
     // The callback sent in from the UI using setCallback
     private IEmailServiceCallback mCallback;
     private RemoteCallbackList<IEmailServiceCallback> mCallbackList =
@@ -333,8 +332,6 @@
 
         public AccountObserver(Handler handler) {
             super(handler);
-            Context context = getContext();
-
             // At startup, we want to see what EAS accounts exist and cache them
             Cursor c = getContentResolver().query(Account.CONTENT_URI, Account.CONTENT_PROJECTION,
                     null, null, null);
@@ -345,6 +342,7 @@
             }
 
             // Create the account mailbox for any account that doesn't have one
+            Context context = getContext();
             for (Account account: mAccounts) {
                 int cnt = Mailbox.count(context, Mailbox.CONTENT_URI, "accountKey=" + account.mId,
                         null);
@@ -619,13 +617,11 @@
      * it's available
      */
     static public synchronized String getDeviceId() throws IOException {
-        if (INSTANCE == null) {
+        if (sDeviceId != null) {
+            return sDeviceId;
+        } else  if (INSTANCE == null) {
             throw new IOException("No SyncManager instance");
         }
-        // If we've already got the id, return it
-        if (INSTANCE.mDeviceId != null) {
-            return INSTANCE.mDeviceId;
-        }
 
         // Otherwise, we'll read the id file or create one if it's not found
         try {
@@ -660,65 +656,27 @@
             Log.d(TAG, "onCreate called on running SyncManager");
         } else {
             INSTANCE = this;
-            mAccountObserver = new AccountObserver(mHandler);
-            mMailboxObserver = new MailboxObserver(mHandler);
-            mSyncedMessageObserver = new SyncedMessageObserver(mHandler);
-            mMessageObserver = new MessageObserver(mHandler);
-            mSyncStatusObserver = new EasSyncStatusObserver();
             try {
-                mDeviceId = getDeviceId();
+                sDeviceId = getDeviceId();
             } catch (IOException e) {
                 // We can't run in this situation
                 throw new RuntimeException();
             }
+            mResolver = getContentResolver();
+            mAccountObserver = new AccountObserver(mHandler);
+            mResolver.registerContentObserver(Account.CONTENT_URI, false, mAccountObserver);
+            mMailboxObserver = new MailboxObserver(mHandler);
+            mSyncedMessageObserver = new SyncedMessageObserver(mHandler);
+            mMessageObserver = new MessageObserver(mHandler);
+            mSyncStatusObserver = new EasSyncStatusObserver();
         }
 
-        mResolver = getContentResolver();
-        mResolver.registerContentObserver(Account.CONTENT_URI, false, mAccountObserver);
-
         maybeStartSyncManagerThread();
     }
 
     @Override
     public void onDestroy() {
         log("!!! SyncManager onDestroy");
-
-        // Cover the case in which we never really started (no EAS accounts)
-        if (INSTANCE == null) {
-            return;
-        }
-
-        stopServices();
-        // Stop receivers and content observers
-        if (mConnectivityReceiver != null) {
-            unregisterReceiver(mConnectivityReceiver);
-        }
-        ContentResolver resolver = getContentResolver();
-        resolver.unregisterContentObserver(mAccountObserver);
-        resolver.unregisterContentObserver(mMailboxObserver);
-        resolver.unregisterContentObserver(mSyncedMessageObserver);
-        resolver.unregisterContentObserver(mMessageObserver);
-
-        // Don't leak the Intent associated with this listener
-        if (mAccountsUpdatedListener != null) {
-            AccountManager.get(this).removeOnAccountsUpdatedListener(mAccountsUpdatedListener);
-            mAccountsUpdatedListener = null;
-        }
-
-        // Clear pending alarms
-        clearAlarms();
-
-        // Release our wake lock, if we have one
-        synchronized (mWakeLocks) {
-            if (mWakeLock != null) {
-                mWakeLock.release();
-                mWakeLock = null;
-            }
-        }
-
-        sPendingIntents.clear();
-
-        INSTANCE = null;
     }
 
     void maybeStartSyncManagerThread() {
@@ -858,26 +816,26 @@
     }
 
     private void clearAlarm(long id) {
-        synchronized (sPendingIntents) {
-            PendingIntent pi = sPendingIntents.get(id);
+        synchronized (mPendingIntents) {
+            PendingIntent pi = mPendingIntents.get(id);
             if (pi != null) {
                 AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
                 alarmManager.cancel(pi);
                 //log("+Alarm cleared for " + alarmOwner(id));
-                sPendingIntents.remove(id);
+                mPendingIntents.remove(id);
             }
         }
     }
 
     private void setAlarm(long id, long millis) {
-        synchronized (sPendingIntents) {
-            PendingIntent pi = sPendingIntents.get(id);
+        synchronized (mPendingIntents) {
+            PendingIntent pi = mPendingIntents.get(id);
             if (pi == null) {
                 Intent i = new Intent(this, MailboxAlarmReceiver.class);
                 i.putExtra("mailbox", id);
                 i.setData(Uri.parse("Box" + id));
                 pi = PendingIntent.getBroadcast(this, 0, i, 0);
-                sPendingIntents.put(id, pi);
+                mPendingIntents.put(id, pi);
 
                 AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
                 alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millis, pi);
@@ -888,11 +846,11 @@
 
     private void clearAlarms() {
         AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
-        synchronized (sPendingIntents) {
-            for (PendingIntent pi : sPendingIntents.values()) {
+        synchronized (mPendingIntents) {
+            for (PendingIntent pi : mPendingIntents.values()) {
                 alarmManager.cancel(pi);
             }
-            sPendingIntents.clear();
+            mPendingIntents.clear();
         }
     }
 
@@ -1188,11 +1146,44 @@
             stopServices();
             log("Shutdown requested");
         } finally {
+            // Lots of cleanup here
+            // Stop our running syncs
+            stopServices();
+
+            // Stop receivers and content observers
+            if (mConnectivityReceiver != null) {
+                unregisterReceiver(mConnectivityReceiver);
+            }
+            ContentResolver resolver = getContentResolver();
+            resolver.unregisterContentObserver(mAccountObserver);
+            resolver.unregisterContentObserver(mMailboxObserver);
+            resolver.unregisterContentObserver(mSyncedMessageObserver);
+            resolver.unregisterContentObserver(mMessageObserver);
+
+            // Don't leak the Intent associated with this listener
+            if (mAccountsUpdatedListener != null) {
+                AccountManager.get(this).removeOnAccountsUpdatedListener(mAccountsUpdatedListener);
+                mAccountsUpdatedListener = null;
+            }
+
+            // Clear pending alarms and associated Intents
+            clearAlarms();
+
+            // Release our wake lock, if we have one
+            synchronized (mWakeLocks) {
+                if (mWakeLock != null) {
+                    mWakeLock.release();
+                    mWakeLock = null;
+                }
+            }
+
             log("Goodbye");
         }
 
-        startService(new Intent(this, SyncManager.class));
-        throw new RuntimeException("EAS SyncManager crash; please restart me...");
+        if (!mStop) {
+            // If this wasn't intentional, try to restart the service
+            throw new RuntimeException("EAS SyncManager crash; please restart me...");
+       }
     }
 
     private void releaseMailbox(long mailboxId) {
diff --git a/src/com/android/exchange/adapter/AbstractSyncParser.java b/src/com/android/exchange/adapter/AbstractSyncParser.java
index 66d8aba..c8f9ad1 100644
--- a/src/com/android/exchange/adapter/AbstractSyncParser.java
+++ b/src/com/android/exchange/adapter/AbstractSyncParser.java
@@ -93,6 +93,10 @@
         if (nextTag(START_DOCUMENT) != Tags.SYNC_SYNC) {
             throw new EasParserException();
         }
+
+        boolean mailboxUpdated = false;
+        ContentValues cv = new ContentValues();
+
         // Loop here through the remaining xml
         while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
             if (tag == Tags.SYNC_COLLECTION || tag == Tags.SYNC_COLLECTIONS) {
@@ -132,7 +136,11 @@
                 }
                 String newKey = getValue();
                 userLog("Parsed key for ", mMailbox.mDisplayName, ": ", newKey);
-                mAdapter.setSyncKey(newKey, true);
+                if (!newKey.equals(mMailbox.mSyncKey)) {
+                    mAdapter.setSyncKey(newKey, true);
+                    cv.put(MailboxColumns.SYNC_KEY, newKey);
+                    mailboxUpdated = true;
+                }
                 // If we were pushing (i.e. auto-start), now we'll become ping-triggered
                 if (mMailbox.mSyncInterval == Mailbox.CHECK_INTERVAL_PUSH) {
                     mMailbox.mSyncInterval = Mailbox.CHECK_INTERVAL_PING;
@@ -145,28 +153,23 @@
         // Commit any changes
         commit();
 
-        // If the sync interval has changed, or if no commands were parsed save the change
+        // If the sync interval has changed, we need to save it
         if (mMailbox.mSyncInterval != interval) {
-            synchronized (mService.getSynchronizer()) {
-                if (!mService.isStopped()) {
-                    // Make sure we save away the new syncFrequency
-                    ContentValues cv = new ContentValues();
-                    cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
-                    mMailbox.update(mContext, cv);
-                }
-            }
-        // If this box has backed off of push, and there were changes, try to change back to
-        // ping; it seems to help at times
+            cv.put(MailboxColumns.SYNC_INTERVAL, mMailbox.mSyncInterval);
+            mailboxUpdated = true;
+        // If there are changes, and we were bounced from push/ping, try again
         } else if (mService.mChangeCount > 0 &&
                 mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH &&
                 mMailbox.mSyncInterval > 0) {
-            synchronized (mService.getSynchronizer()) {
+            userLog("Changes found to ping loop mailbox ", mMailbox.mDisplayName, ": will ping.");
+            cv.put(MailboxColumns.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
+            mailboxUpdated = true;
+        }
+
+        if (mailboxUpdated) {
+             synchronized (mService.getSynchronizer()) {
                 if (!mService.isStopped()) {
-                    ContentValues cv = new ContentValues();
-                    cv.put(MailboxColumns.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PING);
-                    mMailbox.update(mContext, cv);
-                    userLog("Changes found to ping loop mailbox ", mMailbox.mDisplayName,
-                            ": switch back to ping.");
+                     mMailbox.update(mContext, cv);
                 }
             }
         }