am 84f3e50c: (-s ours) Import revised translations.  DO NOT MERGE

Merge commit '84f3e50c011372ba004ebc349def9558d6dda3c5' into eclair

* commit '84f3e50c011372ba004ebc349def9558d6dda3c5':
  Import revised translations.  DO NOT MERGE
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c485245..a66809f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -6,7 +6,10 @@
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <uses-permission android:name="android.permission.READ_SYNC_STATS" />
+    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
     <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.cp" />
     <uses-permission android:name="android.permission.SUBSCRIBED_FEEDS_READ" />
@@ -19,5 +22,12 @@
                 android:syncable="true" android:multiprocess="false"
                 android:readPermission="android.permission.READ_CONTACTS"
                 android:writePermission="android.permission.WRITE_CONTACTS" />
+        <service android:name="ContactsSyncAdapterService" android:exported="true">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter" />
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                       android:resource="@xml/syncadapter" />
+        </service>
     </application>
 </manifest>
diff --git a/res/drawable-hdpi/app_icon.png b/res/drawable-hdpi/app_icon.png
new file mode 100755
index 0000000..0bcbca4
--- /dev/null
+++ b/res/drawable-hdpi/app_icon.png
Binary files differ
diff --git a/res/drawable/app_icon.png b/res/drawable-mdpi/app_icon.png
similarity index 100%
rename from res/drawable/app_icon.png
rename to res/drawable-mdpi/app_icon.png
Binary files differ
diff --git a/res/xml/syncadapter.xml b/res/xml/syncadapter.xml
new file mode 100644
index 0000000..d46be9a
--- /dev/null
+++ b/res/xml/syncadapter.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * 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.
+ */
+-->
+
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the SyncAdapter. -->
+
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+    android:contentAuthority="contacts"
+    android:accountType="com.google"
+/>
diff --git a/src/com/android/providers/contacts/ContactsSyncAdapter.java b/src/com/android/providers/contacts/ContactsSyncAdapter.java
index 0a5090b..c15f2da 100644
--- a/src/com/android/providers/contacts/ContactsSyncAdapter.java
+++ b/src/com/android/providers/contacts/ContactsSyncAdapter.java
@@ -19,8 +19,6 @@
 import com.google.android.collect.Sets;
 import com.google.android.gdata.client.AndroidGDataClient;
 import com.google.android.gdata.client.AndroidXmlParserFactory;
-import com.google.android.googlelogin.GoogleLoginServiceBlockingHelper;
-import com.google.android.googlelogin.GoogleLoginServiceNotFoundException;
 import com.google.android.providers.AbstractGDataSyncAdapter;
 import com.google.wireless.gdata.client.GDataServiceClient;
 import com.google.wireless.gdata.client.QueryParams;
@@ -72,6 +70,8 @@
 import android.text.TextUtils;
 import android.util.Config;
 import android.util.Log;
+import android.accounts.AccountManager;
+import android.accounts.Account;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -126,7 +126,7 @@
     private boolean mPerformedGetServerDiffs;
 
     // Only valid during a sync. If set then this sync was a forced sync request
-    private boolean mSyncForced;
+    private boolean mIsManualSync;
 
     private int mPhotoDownloads;
     private int mPhotoUploads;
@@ -221,7 +221,7 @@
         throw new UnsupportedOperationException("this should never be used");
     }
 
-    protected String getFeedUrl(String account) {
+    protected String getFeedUrl(Account account) {
         throw new UnsupportedOperationException("this should never be used");
     }
 
@@ -281,7 +281,7 @@
      * Look at the groups sync settings and the overall sync preference to determine which
      * feeds to sync and add them to the feedsToSync list.
      */
-    public static void addContactsFeedsToSync(ContentResolver cr, String account,
+    public static void addContactsFeedsToSync(ContentResolver cr, Account account,
             Collection<String> feedsToSync) {
         boolean shouldSyncEverything = getShouldSyncEverything(cr, account);
         if (shouldSyncEverything) {
@@ -290,7 +290,8 @@
         }
 
         Cursor cursor = cr.query(Contacts.Groups.CONTENT_URI, new String[]{Groups._SYNC_ID},
-                "_sync_account=? AND should_sync>0", new String[]{account}, null);
+                "_sync_account=? AND _sync_account_type=? AND should_sync>0",
+                new String[]{account.name, account.type}, null);
         try {
             while (cursor.moveToNext()) {
                 feedsToSync.add(getContactsFeedForGroup(account, cursor.getString(0)));
@@ -300,20 +301,22 @@
         }
     }
 
-    private static boolean getShouldSyncEverything(ContentResolver cr, String account) {
-        String value = Contacts.Settings.getSetting(cr, account, Contacts.Settings.SYNC_EVERYTHING);
+    private static boolean getShouldSyncEverything(ContentResolver cr, Account account) {
+        // TODO(fredq) should be using account instead of null
+        String value = Contacts.Settings.getSetting(cr, null, Contacts.Settings.SYNC_EVERYTHING);
         return !TextUtils.isEmpty(value) && !"0".equals(value);
     }
 
     private void getServerPhotos(SyncContext context, String feedUrl, int maxDownloads,
             GDataSyncData syncData, SyncResult syncResult) {
         final ContentResolver cr = getContext().getContentResolver();
+        final Account account = getAccount();
         Cursor cursor = cr.query(
                 Photos.CONTENT_URI,
                 new String[]{Photos._SYNC_ID, Photos._SYNC_VERSION, Photos.PERSON_ID,
                         Photos.DOWNLOAD_REQUIRED, Photos._ID}, ""
-                + "_sync_account=? AND download_required != 0",
-                new String[]{getAccount()}, null);
+                + "_sync_account=? AND _sync_account_type=? AND download_required != 0",
+                new String[]{account.name, account.type}, null);
         try {
             int numFetched = 0;
             while (cursor.moveToNext()) {
@@ -425,7 +428,7 @@
         GDataServiceClient client = getGDataServiceClient();
         String authToken = getAuthToken();
         ContentResolver cr = getContext().getContentResolver();
-        final String account = getAccount();
+        final Account account = getAccount();
 
         Cursor c = clientDiffs.query(Photos.CONTENT_URI, null /* all columns */,
                 null /* no where */, null /* no where args */, null /* default sort order */);
@@ -508,14 +511,8 @@
                                 }
                             }
                             syncResult.stats.numAuthExceptions++;
-                            try {
-                                GoogleLoginServiceBlockingHelper.invalidateAuthToken(getContext(),
-                                        authToken);
-                            } catch (GoogleLoginServiceNotFoundException e1) {
-                                if (Config.LOGD) {
-                                    Log.d(TAG, "could not invalidate auth token", e1);
-                                }
-                            }
+                            AccountManager.get(getContext()).invalidateAuthToken(
+                                    "com.google", authToken);
                             return;
 
                         case HttpException.SC_CONFLICT:
@@ -580,7 +577,7 @@
     }
 
     static protected String cursorToEntryImpl(ContentResolver cr, Cursor c, Entry entry,
-            String account) throws ParseException {
+            Account account) throws ParseException {
         cursorToBaseEntry(entry, account, c);
         String createUrl = null;
         if (entry instanceof ContactEntry) {
@@ -611,7 +608,7 @@
         entry.setSystemGroup(null);
     }
 
-    private static void cursorToContactEntry(String account, ContentResolver cr, Cursor c,
+    private static void cursorToContactEntry(Account account, ContentResolver cr, Cursor c,
             ContactEntry entry)
             throws ParseException {
         entry.setTitle(c.getString(c.getColumnIndexOrThrow(People.NAME)));
@@ -638,11 +635,11 @@
         return false;
     }
 
-    protected static void deletedCursorToEntryImpl(Cursor c, Entry entry, String account) {
+    protected static void deletedCursorToEntryImpl(Cursor c, Entry entry, Account account) {
         cursorToBaseEntry(entry, account, c);
     }
 
-    private static void cursorToBaseEntry(Entry entry, String account, Cursor c) {
+    private static void cursorToBaseEntry(Entry entry, Account account, Cursor c) {
         String feedUrl;
         if (entry instanceof ContactEntry) {
             feedUrl = getContactsFeedForAccount(account);
@@ -662,7 +659,8 @@
         }
     }
 
-    private static void addPhonesToContactEntry(ContentResolver cr, long personId, ContactEntry entry)
+    private static void addPhonesToContactEntry(ContentResolver cr, long personId,
+                                                ContactEntry entry)
             throws ParseException {
         Cursor c = cr.query(Phones.CONTENT_URI, null, "person=" + personId, null, null);
         int numberIndex = c.getColumnIndexOrThrow(People.Phones.NUMBER);
@@ -750,7 +748,7 @@
         }
     }
 
-    private static void addGroupMembershipToContactEntry(String account, ContentResolver cr,
+    private static void addGroupMembershipToContactEntry(Account account, ContentResolver cr,
             long personId, ContactEntry entry) throws ParseException {
         Cursor c = cr.query(GroupMembership.RAW_CONTENT_URI, null,
                 "person=" + personId, null, null);
@@ -841,7 +839,8 @@
      * actions on the ContentProvider to represent the entry.
      */
     protected void updateProvider(Feed feed, Long syncLocalId,
-            Entry baseEntry, ContentProvider provider, Object syncInfo) throws ParseException {
+            Entry baseEntry, ContentProvider provider, Object syncInfo,
+            GDataSyncData.FeedData feedSyncData) throws ParseException {
 
         // This is a hack to delete these incorrectly created contacts named "Starred in Android"
         if (baseEntry instanceof ContactEntry
@@ -860,7 +859,7 @@
         updateProviderImpl(getAccount(), syncLocalId, baseEntry, provider);
     }
 
-    protected static void updateProviderImpl(String account, Long syncLocalId,
+    protected static void updateProviderImpl(Account account, Long syncLocalId,
             Entry entry, ContentProvider provider) throws ParseException {
         // If this is a deleted entry then add it to the DELETED_CONTENT_URI
         ContentValues deletedValues = null;
@@ -875,7 +874,8 @@
             if (!TextUtils.isEmpty(editUri)) {
                 deletedValues.put(SyncConstValue._SYNC_VERSION, lastItemFromUri(editUri));
             }
-            deletedValues.put(SyncConstValue._SYNC_ACCOUNT, account);
+            deletedValues.put(SyncConstValue._SYNC_ACCOUNT, account.name);
+            deletedValues.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.type);
         }
 
         if (entry instanceof ContactEntry) {
@@ -897,7 +897,7 @@
         throw new IllegalArgumentException("unknown entry type, " + entry.getClass().getName());
     }
 
-    protected static void updateProviderWithContactEntry(String account, Long syncLocalId,
+    protected static void updateProviderWithContactEntry(Account account, Long syncLocalId,
             ContactEntry entry, ContentProvider provider) throws ParseException {
         final String name = entry.getTitle();
         final String notes = entry.getContent();
@@ -910,7 +910,8 @@
         values.put(People.NAME, name);
         values.put(People.NOTES, notes);
         values.put(People.PHONETIC_NAME, yomiName);
-        values.put(SyncConstValue._SYNC_ACCOUNT, account);
+        values.put(SyncConstValue._SYNC_ACCOUNT, account.name);
+        values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.type);
         values.put(SyncConstValue._SYNC_ID, personSyncId);
         values.put(SyncConstValue._SYNC_DIRTY, "0");
         values.put(SyncConstValue._SYNC_LOCAL_ID, syncLocalId);
@@ -924,7 +925,8 @@
         values.clear();
         values.put(Photos.PERSON_ID, ContentUris.parseId(personUri));
         values.put(Photos.EXISTS_ON_SERVER, photoExistsOnServer ? 1 : 0);
-        values.put(SyncConstValue._SYNC_ACCOUNT, account);
+        values.put(SyncConstValue._SYNC_ACCOUNT, account.name);
+        values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.type);
         values.put(SyncConstValue._SYNC_ID, personSyncId);
         values.put(SyncConstValue._SYNC_DIRTY, 0);
         values.put(SyncConstValue._SYNC_LOCAL_ID, syncLocalId);
@@ -1001,7 +1003,8 @@
                 continue;
             }
             values.clear();
-            values.put(GroupMembership.GROUP_SYNC_ACCOUNT, account);
+            values.put(GroupMembership.GROUP_SYNC_ACCOUNT, account.name);
+            values.put(GroupMembership.GROUP_SYNC_ACCOUNT_TYPE, account.type);
             values.put(GroupMembership.GROUP_SYNC_ID,
                     lastItemFromUri(groupMembershipInfo.getGroup()));
             Uri uri = Uri.withAppendedPath(personUri, GroupMembership.CONTENT_DIRECTORY);
@@ -1053,13 +1056,14 @@
         }
     }
 
-    protected static void updateProviderWithGroupEntry(String account, Long syncLocalId,
+    protected static void updateProviderWithGroupEntry(Account account, Long syncLocalId,
             GroupEntry entry, ContentProvider provider) throws ParseException {
         ContentValues values = new ContentValues();
         values.put(Groups.NAME, entry.getTitle());
         values.put(Groups.NOTES, entry.getContent());
         values.put(Groups.SYSTEM_ID, entry.getSystemGroup());
-        values.put(Groups._SYNC_ACCOUNT, account);
+        values.put(Groups._SYNC_ACCOUNT, account.name);
+        values.put(Groups._SYNC_ACCOUNT_TYPE, account.type);
         values.put(Groups._SYNC_ID, lastItemFromUri(entry.getId()));
         values.put(Groups._SYNC_DIRTY, 0);
         values.put(Groups._SYNC_LOCAL_ID, syncLocalId);
@@ -1116,17 +1120,19 @@
      * that we don't expect.
      */
     @Override
-    public void onAccountsChanged(String[] accountsArray) {
+    public void onAccountsChanged(Account[] accountsArray) {
         if (!"yes".equals(SystemProperties.get("ro.config.sync"))) {
             return;
         }
 
         ContentResolver cr = getContext().getContentResolver();
-        for (String account : accountsArray) {
-            String value = Contacts.Settings.getSetting(cr, account,
+        for (Account account : accountsArray) {
+            // TODO(fredq) should be using account instead of null
+            String value = Contacts.Settings.getSetting(cr, null,
                     Contacts.Settings.SYNC_EVERYTHING);
             if (value == null) {
-                Contacts.Settings.setSetting(cr, account, Contacts.Settings.SYNC_EVERYTHING, "1");
+                // TODO(fredq) should be using account instead of null
+                Contacts.Settings.setSetting(cr, null, Contacts.Settings.SYNC_EVERYTHING, "1");
             }
             updateSubscribedFeeds(cr, account);
         }
@@ -1137,8 +1143,8 @@
      *  @param account The account
      *  @return The contacts feed url for a specific account.
      */
-    public static String getContactsFeedForAccount(String account) {
-        String url = CONTACTS_FEED_URL + account + "/base2_property-android";
+    public static String getContactsFeedForAccount(Account account) {
+        String url = CONTACTS_FEED_URL + account.name + "/base2_property-android";
         return rewriteUrlforAccount(account, url);
     }
 
@@ -1148,7 +1154,7 @@
      *  @param groupSyncId The group id
      *  @return The contacts feed url for a specific account and group.
      */
-    public static String getContactsFeedForGroup(String account, String groupSyncId) {
+    public static String getContactsFeedForGroup(Account account, String groupSyncId) {
         String groupId = getCanonicalGroupsFeedForAccount(account);
         try {
             groupId = URLEncoder.encode(groupId, "utf-8");
@@ -1163,8 +1169,8 @@
      *  @param account The account
      *  @return The groups feed url for a specific account.
      */
-    public static String getGroupsFeedForAccount(String account) {
-        String url = GROUPS_FEED_URL + account + "/base2_property-android";
+    public static String getGroupsFeedForAccount(Account account) {
+        String url = GROUPS_FEED_URL + account.name + "/base2_property-android";
         return rewriteUrlforAccount(account, url);
     }
 
@@ -1177,8 +1183,8 @@
      *  @param account The account
      *  @return The groups feed url for a specific account.
      */
-    public static String getCanonicalGroupsFeedForAccount(String account) {
-        return GROUPS_FEED_URL + account + "/base";
+    public static String getCanonicalGroupsFeedForAccount(Account account) {
+        return GROUPS_FEED_URL + account.name + "/base";
     }
 
     /**
@@ -1186,8 +1192,8 @@
      *  @param account The account
      *  @return The photo feed url for a specific account.
      */
-    public static String getPhotosFeedForAccount(String account) {
-        String url = PHOTO_FEED_URL + account;
+    public static String getPhotosFeedForAccount(Account account) {
+        String url = PHOTO_FEED_URL + account.name;
         return rewriteUrlforAccount(account, url);
     }
 
@@ -1196,7 +1202,7 @@
     }
 
     @Override
-    protected void updateQueryParameters(QueryParams params) {
+    protected void updateQueryParameters(QueryParams params, GDataSyncData.FeedData feedSyncData) {
         // we want to get the events ordered by last modified, so we can
         // recover in case we cannot process the entire feed.
         params.setParamValue("orderby", "lastmodified");
@@ -1210,13 +1216,13 @@
     }
 
     @Override
-    public void onSyncStarting(SyncContext context, String account, boolean forced,
+    public void onSyncStarting(SyncContext context, Account account, boolean manualSync,
             SyncResult result) {
         mPerformedGetServerDiffs = false;
-        mSyncForced = forced;
+        mIsManualSync = manualSync;
         mPhotoDownloads = 0;
         mPhotoUploads = 0;
-        super.onSyncStarting(context, account, forced, result);
+        super.onSyncStarting(context, account, manualSync, result);
     }
 
     @Override
@@ -1224,20 +1230,19 @@
         final ContentResolver cr = getContext().getContentResolver();
 
         if (success && mPerformedGetServerDiffs && !mSyncCanceled) {
+            final Account account = getAccount();
             Cursor cursor = cr.query(
                     Photos.CONTENT_URI,
                     new String[]{Photos._SYNC_ID, Photos._SYNC_VERSION, Photos.PERSON_ID,
                             Photos.DOWNLOAD_REQUIRED}, ""
-                    + "_sync_account=? AND download_required != 0",
-                    new String[]{getAccount()}, null);
+                    + "_sync_account=? AND _sync_account_type=? AND download_required != 0",
+                    new String[]{account.name, account.type}, null);
             try {
                 if (cursor.getCount() != 0) {
                     Bundle extras = new Bundle();
-                    extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, getAccount());
-                    extras.putBoolean(ContentResolver.SYNC_EXTRAS_FORCE, mSyncForced);
-                    extras.putString("feed",
-                            ContactsSyncAdapter.getPhotosFeedForAccount(getAccount()));
-                    getContext().getContentResolver().startSync(Contacts.CONTENT_URI, extras);
+                    extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, mIsManualSync);
+                    extras.putString("feed", ContactsSyncAdapter.getPhotosFeedForAccount(account));
+                    ContentResolver.requestSync(account, Contacts.AUTHORITY, extras);
                 }
             } finally {
                 cursor.close();
@@ -1247,15 +1252,16 @@
         super.onSyncEnding(context, success);
     }
 
-    public static void updateSubscribedFeeds(ContentResolver cr, String account) {
+    public static void updateSubscribedFeeds(ContentResolver cr, Account account) {
         Set<String> feedsToSync = Sets.newHashSet();
         feedsToSync.add(getGroupsFeedForAccount(account));
         addContactsFeedsToSync(cr, account, feedsToSync);
 
         Cursor c = SubscribedFeeds.Feeds.query(cr, sSubscriptionProjection,
-                SubscribedFeeds.Feeds.AUTHORITY + "=? AND "
-                        + SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?",
-                new String[]{Contacts.AUTHORITY, account}, null);
+                SubscribedFeeds.Feeds.AUTHORITY + "=?"
+                        + " AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?"
+                        + " AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?",
+                new String[]{Contacts.AUTHORITY, account.name, account.type}, null);
         try {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.v(TAG, "scanning over subscriptions with authority "
@@ -1282,9 +1288,8 @@
 
             // request a sync of this feed
             Bundle extras = new Bundle();
-            extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, account);
             extras.putString("feed", feed);
-            cr.startSync(Contacts.CONTENT_URI, extras);
+            ContentResolver.requestSync(account, Contacts.AUTHORITY, extras);
         }
     }
 }
diff --git a/src/com/android/providers/contacts/ContactsSyncAdapterService.java b/src/com/android/providers/contacts/ContactsSyncAdapterService.java
new file mode 100644
index 0000000..ee95153
--- /dev/null
+++ b/src/com/android/providers/contacts/ContactsSyncAdapterService.java
@@ -0,0 +1,29 @@
+package com.android.providers.contacts;
+
+import android.app.Service;
+import android.os.IBinder;
+import android.content.Intent;
+import android.content.ContentProviderClient;
+import android.content.ContentProvider;
+import android.content.SyncableContentProvider;
+import android.provider.Contacts;
+
+public class ContactsSyncAdapterService extends Service {
+    private ContentProviderClient mContentProviderClient = null;
+
+    public void onCreate() {
+        mContentProviderClient =
+                getContentResolver().acquireContentProviderClient(Contacts.CONTENT_URI);
+    }
+
+    public void onDestroy() {
+        mContentProviderClient.release();
+    }
+
+    public IBinder onBind(Intent intent) {
+        ContentProvider contentProvider = mContentProviderClient.getLocalContentProvider();
+        if (contentProvider == null) throw new IllegalStateException();
+        SyncableContentProvider syncableContentProvider = (SyncableContentProvider)contentProvider;
+        return syncableContentProvider.getTempProviderSyncAdapter().getISyncAdapter().asBinder();
+    }
+}
diff --git a/src/com/android/providers/contacts/GoogleContactsProvider.java b/src/com/android/providers/contacts/GoogleContactsProvider.java
index 3f7bef5..77bce2c 100644
--- a/src/com/android/providers/contacts/GoogleContactsProvider.java
+++ b/src/com/android/providers/contacts/GoogleContactsProvider.java
@@ -27,13 +27,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SyncAdapter;
 import android.content.SyncContext;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.SystemClock;
 import android.provider.Contacts;
 import android.text.TextUtils;
+import android.accounts.Account;
 
 import java.util.Iterator;
 import java.util.Map;
@@ -64,23 +64,25 @@
             + "         OUTER JOIN groups AS g "
             + "         ON (gm.group_id=g._id "
             + "           OR (gm.group_sync_id=g._sync_id "
-            + "               AND gm.group_sync_account=g._sync_account))) "
+            + "               AND gm.group_sync_account=g._sync_account "
+            + "               AND gm.group_sync_account_type=g._sync_account_type))) "
             + "       GROUP BY person) "
             + "   WHERE max_should_sync=0)"
             + "  OR _id NOT IN (SELECT person FROM groupmembership))"
             + " AND _sync_dirty=0 "
-            + " AND _sync_account=?";
+            + " AND _sync_account=? "
+            + " AND _sync_account_type=?";
 
-    private SyncAdapter mSyncAdapter = null;
     private AlarmManager mAlarmService = null;
 
     @Override
     public boolean onCreate() {
+        setTempProviderSyncAdapter(new ContactsSyncAdapter(getContext(), this));
         BroadcastReceiver receiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (ACTION_PURGE_CONTACTS.equals(intent.getAction())) {
-                    purgeContacts(intent.getStringExtra("account"));
+                    purgeContacts((Account) intent.getParcelableExtra("account"));
                 }
             }
         };
@@ -89,17 +91,7 @@
     }
 
     @Override
-    public synchronized SyncAdapter getSyncAdapter() {
-        if (mSyncAdapter != null) {
-            return mSyncAdapter;
-        }
-        
-        mSyncAdapter = new ContactsSyncAdapter(getContext(), this);
-        return mSyncAdapter;
-    }
-
-    @Override
-    protected void onLocalChangesForAccount(final ContentResolver resolver, String account,
+    protected void onLocalChangesForAccount(final ContentResolver resolver, Account account,
             boolean groupsModified) {
         ContactsSyncAdapter.updateSubscribedFeeds(resolver, account);
         if (groupsModified) {
@@ -111,7 +103,7 @@
      * Delete any non-sync_dirty contacts associated with the given account
      * that are not in any of the synced groups.
      */
-    private void schedulePurge(String account) {
+    private void schedulePurge(Account account) {
         if (isTemporary()) {
             throw new IllegalStateException("this must not be called on temp providers");
         }
@@ -137,18 +129,19 @@
         purgeContacts(getSyncingAccount());
     }
 
-    private void purgeContacts(String account) {
+    private void purgeContacts(Account account) {
         if (isTemporary()) {
             throw new IllegalStateException("this must not be called on temp providers");
         }
         SQLiteDatabase db = getDatabase();
         db.beginTransaction();
         try {
+            // TODO(fredq) should be using account instead of null
             final String value = Contacts.Settings.getSetting(getContext().getContentResolver(),
-                    account, Contacts.Settings.SYNC_EVERYTHING);
+                    null, Contacts.Settings.SYNC_EVERYTHING);
             final boolean shouldSyncEverything = !TextUtils.isEmpty(value) && !"0".equals(value);
             if (!shouldSyncEverything) {
-                db.execSQL(PURGE_UNSYNCED_CONTACTS_SQL, new String[]{account});
+                db.execSQL(PURGE_UNSYNCED_CONTACTS_SQL, new String[]{account.name, account.type});
             }
 
             // remove any feeds in the SyncData that aren't in the current sync set.
@@ -179,12 +172,13 @@
         }
     }
 
-    private AbstractGDataSyncAdapter.GDataSyncData readSyncData(String account) {
+    private AbstractGDataSyncAdapter.GDataSyncData readSyncData(Account account) {
         if (!getDatabase().inTransaction()) {
             throw new IllegalStateException("you can only call this from within a transaction");
         }
-        Cursor c = getDatabase().query("_sync_state", new String[]{"data"}, "_sync_account=?",
-                new String[]{account}, null, null, null);
+        Cursor c = getDatabase().query("_sync_state", new String[]{"data"},
+                "_sync_account=? AND _sync_account_type=?",
+                new String[]{account.name, account.type}, null, null, null);
         try {
             byte[] data = null;
             if (c.moveToNext()) data = c.getBlob(0);
@@ -194,15 +188,17 @@
         }
     }
 
-    private void writeSyncData(String account, AbstractGDataSyncAdapter.GDataSyncData syncData) {
+    private void writeSyncData(Account account, AbstractGDataSyncAdapter.GDataSyncData syncData) {
         final SQLiteDatabase db = getDatabase();
         if (!db.inTransaction()) {
             throw new IllegalStateException("you can only call this from within a transaction");
         }
-        db.delete("_sync_state", "_sync_account=?", new String[]{account});
+        db.delete("_sync_state", "_sync_account=? AND _sync_account_type=?",
+                new String[]{account.name, account.type});
         ContentValues values = new ContentValues();
         values.put("data", ContactsSyncAdapter.newBytesFromGDataSyncData(syncData));
-        values.put("_sync_account", account);
+        values.put("_sync_account", account.name);
+        values.put("_sync_account_type", account.type);
         db.insert("_sync_state", "_sync_account", values);
     }
 }
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 8d207ad..c06a5e6 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -8,6 +8,7 @@
 
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />    
diff --git a/tests/src/com/android/providers/contacts/ContactsSyncAdapterTest.java b/tests/src/com/android/providers/contacts/ContactsSyncAdapterTest.java
index dc0204e..018907a 100644
--- a/tests/src/com/android/providers/contacts/ContactsSyncAdapterTest.java
+++ b/tests/src/com/android/providers/contacts/ContactsSyncAdapterTest.java
@@ -30,13 +30,14 @@
 import android.provider.SyncConstValue;
 import android.test.ProviderTestCase;
 import android.util.Log;
+import android.accounts.Account;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
 public class ContactsSyncAdapterTest extends ProviderTestCase<ContactsProvider> {
-    private static final String ACCOUNT = "testaccount@example.com";
+    private static final Account ACCOUNT = new Account("testaccount@example.com", "example.type");
 
     private MockSyncContext mMockSyncContext = new MockSyncContext();
     private ContactsSyncAdapter mSyncAdapter = null;
@@ -173,7 +174,7 @@
         }
     }
 
-    private static void addGroupMembership(ContactEntry p, String account, String groupId) {
+    private static void addGroupMembership(ContactEntry p, Account account, String groupId) {
         GroupMembershipInfo groupInfo = new GroupMembershipInfo();
         final String serverId =
                 ContactsSyncAdapter.getCanonicalGroupsFeedForAccount(account) + "/" + groupId;
@@ -420,11 +421,12 @@
         assertEquals(expectedServerVersion, getString(cursor, Photos._SYNC_VERSION));
     }
 
-    private Uri addPerson(ContactsProvider provider, String name,
-            String account, String syncId) {
+    private Uri addPerson(ContactsProvider provider, Account account,
+            String name, String syncId) {
         ContentValues values = new ContentValues();
         values.put(People.NAME, name);
-        values.put(People._SYNC_ACCOUNT, account);
+        values.put(People._SYNC_ACCOUNT, account.name);
+        values.put(People._SYNC_ACCOUNT_TYPE, account.type);
         values.put(People._SYNC_ID, syncId);
         return provider.insert(People.CONTENT_URI, values);
     }
@@ -452,7 +454,10 @@
                 feedFromEntry(entry) + "/" + getString(cursor, SyncConstValue._SYNC_ID));
         assertEquals(dumpRow(cursor), entry.getEditUri(),
                 entry.getId() + "/" + getString(cursor, SyncConstValue._SYNC_VERSION));
-        assertEquals(dumpRow(cursor), ACCOUNT, getString(cursor, SyncConstValue._SYNC_ACCOUNT));
+        assertEquals(dumpRow(cursor), ACCOUNT.name,
+                getString(cursor, SyncConstValue._SYNC_ACCOUNT));
+        assertEquals(dumpRow(cursor), ACCOUNT.type,
+                getString(cursor, SyncConstValue._SYNC_ACCOUNT_TYPE));
         assertEquals(dumpRow(cursor), entry.getTitle(), getString(cursor, Groups.NAME));
         assertEquals(dumpRow(cursor), entry.getSystemGroup(), getString(cursor, Groups.SYSTEM_ID));
         assertEquals(dumpRow(cursor), entry.getContent(), getString(cursor, Groups.NOTES));
@@ -531,7 +536,10 @@
                 feedFromEntry(entry) + "/" + getString(cursor, SyncConstValue._SYNC_ID));
         assertEquals(dumpRow(cursor), entry.getEditUri(),
                 entry.getId() + "/" + getString(cursor, SyncConstValue._SYNC_VERSION));
-        assertEquals(dumpRow(cursor), ACCOUNT, getString(cursor, SyncConstValue._SYNC_ACCOUNT));
+        assertEquals(dumpRow(cursor), ACCOUNT.name,
+                getString(cursor, SyncConstValue._SYNC_ACCOUNT));
+        assertEquals(dumpRow(cursor), ACCOUNT.type,
+                getString(cursor, SyncConstValue._SYNC_ACCOUNT_TYPE));
         assertEquals(dumpRow(cursor), entry.getTitle(), getString(cursor, People.NAME));
         assertEquals(dumpRow(cursor), entry.getContent(), getString(cursor, People.NOTES));
         if (syncLocalId != null) {
@@ -562,7 +570,10 @@
                 feedFromEntry(entry) + "/" + getString(cursor, SyncConstValue._SYNC_ID));
         assertEquals(dumpRow(cursor), entry.getEditUri(),
                 entry.getId() + "/" + getString(cursor, SyncConstValue._SYNC_VERSION));
-        assertEquals(dumpRow(cursor), ACCOUNT, getString(cursor, SyncConstValue._SYNC_ACCOUNT));
+        assertEquals(dumpRow(cursor), ACCOUNT.name,
+                getString(cursor, SyncConstValue._SYNC_ACCOUNT));
+        assertEquals(dumpRow(cursor), ACCOUNT.type,
+                getString(cursor, SyncConstValue._SYNC_ACCOUNT_TYPE));
         if (syncLocalId != null) {
             assertEquals(dumpRow(cursor),
                     syncLocalId.toString(), getString(cursor, SyncConstValue._SYNC_LOCAL_ID));
@@ -801,7 +812,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        mSyncAdapter = (ContactsSyncAdapter)getProvider().getSyncAdapter();
+        mSyncAdapter = (ContactsSyncAdapter)getProvider().getTempProviderSyncAdapter();
         getProvider().onSyncStart(mMockSyncContext, ACCOUNT);
     }
 
diff --git a/tests/src/com/android/providers/contacts/SyncContactsTest.java b/tests/src/com/android/providers/contacts/SyncContactsTest.java
index e4adf5b..7456bfa 100644
--- a/tests/src/com/android/providers/contacts/SyncContactsTest.java
+++ b/tests/src/com/android/providers/contacts/SyncContactsTest.java
@@ -1,20 +1,23 @@
 package com.android.providers.contacts;
 
-import com.google.android.collect.Maps;
-import com.google.android.googlelogin.GoogleLoginServiceBlockingHelper;
-import com.google.android.googlelogin.GoogleLoginServiceNotFoundException;
-
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.provider.Contacts;
 import android.test.RenamingDelegatingContext;
 import android.test.SyncBaseInstrumentation;
-import android.util.Log;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.OperationCanceledException;
+import android.accounts.AuthenticatorException;
 
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.io.IOException;
+
+import com.google.android.collect.Maps;
+import com.google.android.googlelogin.GoogleLoginServiceConstants;
 
 public class SyncContactsTest extends SyncBaseInstrumentation {
     private Context mTargetContext;
@@ -64,11 +67,20 @@
 
     private String getAccount() {
         try {
-            return GoogleLoginServiceBlockingHelper.getAccount(mTargetContext, false);
-        } catch (GoogleLoginServiceNotFoundException e) {
-            Log.e(this.getClass().getName(), "Could not find Google login service");
-            return null;
+            String[] features = new String[]{GoogleLoginServiceConstants.FEATURE_HOSTED_OR_GOOGLE};
+            Account[] accounts = AccountManager.get(mTargetContext).getAccountsByTypeAndFeatures(
+                    GoogleLoginServiceConstants.ACCOUNT_TYPE, features, null, null).getResult();
+            if (accounts.length > 0) {
+                return accounts[0].name;
+            }
+        } catch (OperationCanceledException e) {
+            // handle below
+        } catch (IOException e) {
+            // handle below
+        } catch (AuthenticatorException e) {
+            // handle below
         }
+        return null;
     }
 
     /**