Merge "DO NOT MERGE Add flag to control dynamic shortcuts experment" into ub-contactsdialer-g-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 12f6866..723d04b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.contacts"
-    android:versionCode="20002"
-    android:versionName="2.0.2">
+    android:versionCode="10602"
+    android:versionName="1.6.2">
 
     <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25" />
 
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index 09281ee..729d75f 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -497,9 +497,11 @@
                     return true;
                 }
             });
-            menuItem.setIcon(displayableAccount.getIcon());
-            // Get rid of the default menu item overlay and show original account icons.
-            menuItem.getIcon().setColorFilter(Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP);
+            if (displayableAccount.getIcon() != null) {
+                menuItem.setIcon(displayableAccount.getIcon());
+                // Get rid of the default menu item overlay and show original account icons.
+                menuItem.getIcon().setColorFilter(Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP);
+            }
             // Create a dummy action view to attach extra hidden content description to the menuItem
             // for Talkback. We want Talkback to read out the account type but not have it be part
             // of the menuItem title.
diff --git a/src/com/android/contacts/common/model/AccountTypeManager.java b/src/com/android/contacts/common/model/AccountTypeManager.java
index c9f68d4..a083219 100644
--- a/src/com/android/contacts/common/model/AccountTypeManager.java
+++ b/src/com/android/contacts/common/model/AccountTypeManager.java
@@ -595,9 +595,10 @@
                                     && accountWithDataSet.dataSet == null) {
                                 foundWritableGoogleAccount = true;
                             }
-                        }
-                        if (accountType.isGroupMembershipEditable()) {
-                            groupWritableAccounts.add(accountWithDataSet);
+
+                            if (accountType.isGroupMembershipEditable()) {
+                                groupWritableAccounts.add(accountWithDataSet);
+                            }
                         }
                     }
                 }
@@ -632,9 +633,10 @@
             }
             if (localAccountType.areContactsWritable()) {
                 contactWritableAccounts.add(localAccount);
-            }
-            if (localAccountType.isGroupMembershipEditable()) {
-                groupWritableAccounts.add(localAccount);
+
+                if (localAccountType.isGroupMembershipEditable()) {
+                    groupWritableAccounts.add(localAccount);
+                }
             }
         }
 
diff --git a/src/com/android/contacts/common/model/account/ExternalAccountType.java b/src/com/android/contacts/common/model/account/ExternalAccountType.java
index 1298fb3..fc45e0b 100644
--- a/src/com/android/contacts/common/model/account/ExternalAccountType.java
+++ b/src/com/android/contacts/common/model/account/ExternalAccountType.java
@@ -25,6 +25,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.text.TextUtils;
@@ -101,6 +102,7 @@
     private String mAccountTypeIconAttribute;
     private boolean mHasContactsMetadata;
     private boolean mHasEditSchema;
+    private boolean mGroupMembershipEditable;
 
     public ExternalAccountType(Context context, String resPackageName, boolean isExtension) {
         this(context, resPackageName, isExtension, null);
@@ -172,6 +174,9 @@
         iconRes = resolveExternalResId(context, mAccountTypeIconAttribute,
                 syncAdapterPackageName, ATTR_ACCOUNT_ICON);
 
+        final DataKind dataKind = getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE);
+        mGroupMembershipEditable = dataKind != null && dataKind.editable;
+
         // If we reach this point, the account type has been successfully initialized.
         mIsInitialized = true;
     }
@@ -293,6 +298,11 @@
         return mExtensionPackageNames;
     }
 
+    @Override
+    public boolean isGroupMembershipEditable() {
+        return mGroupMembershipEditable;
+    }
+
     /**
      * Inflate this {@link AccountType} from the given parser. This may only
      * load details matching the publicly-defined schema.
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index 5c3c565..f43d974 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -63,6 +63,8 @@
 import com.android.contacts.common.model.account.AccountDisplayInfoFactory;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.DeviceLocalAccountType;
+import com.android.contacts.common.model.account.SimAccountType;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.common.util.AccountsListAdapter;
 import com.android.contacts.common.util.MaterialColorMapUtils;
@@ -165,7 +167,8 @@
 
             final TextView text2 = (TextView) resultView.findViewById(android.R.id.text2);
             final String accountName = rawContactDelta.getAccountName();
-            if (TextUtils.isEmpty(accountName)) {
+            if (TextUtils.isEmpty(accountName) || accountType instanceof DeviceLocalAccountType
+                    || accountType instanceof SimAccountType) {
                 text2.setVisibility(View.GONE);
             } else {
                 // Truncate email addresses in the middle so we don't lose the domain
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index 947f35e..519abff 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -380,7 +380,9 @@
     }
 
     protected void rebuildValues() {
-        setValues(mKind, mEntry, mState, mReadOnly, mViewIdGenerator);
+        if (mKind != null) {
+            setValues(mKind, mEntry, mState, mReadOnly, mViewIdGenerator);
+        }
     }
 
     /**
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 6d8c5ad..16d7c9c 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -31,7 +31,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.contacts.common.model.account.AccountDisplayInfo;
@@ -247,9 +246,13 @@
                 mName.setValues(
                         type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME),
                         primary, state, false, vig);
-                mPhoneticName.setValues(
-                        type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME),
-                        primary, state, false, vig);
+                final DataKind phoneticNameKind =
+                        type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME);
+                if (phoneticNameKind != null) {
+                    mPhoneticName.setValues(phoneticNameKind, primary, state, false, vig);
+                } else {
+                    mPhoneticName.setVisibility(View.GONE);
+                }
                 // It is useful to use Nickname outside of a KindSectionView so that we can treat it
                 // as a part of StructuredName's fake KindSectionView, even though it uses a
                 // different CP2 mime-type. We do a bit of extra work below to make this possible.
diff --git a/tests/Android.mk b/tests/Android.mk
index 48a00f4..b895035 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -8,7 +8,9 @@
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res)
+
+res_dirs := res ../res-icons
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
 
 LOCAL_PACKAGE_NAME := ContactsTests
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 65c04bc..65be28a 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -36,6 +36,8 @@
     <uses-permission android:name="android.permission.READ_SOCIAL_STREAM" />
 
     <application
+        android:icon="@mipmap/ic_contacts_launcher_square"
+        android:roundIcon="@mipmap/ic_contacts_launcher"
         android:label="@string/applicationLabel">
 
         <uses-library android:name="android.test.runner" />
@@ -49,7 +51,13 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".allintents.ResultActivity"/>
+        <activity android:name=".allintents.ResultActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.item/vnd.contactstest.profile" />
+            </intent-filter>
+        </activity>
 
         <activity android:name=".quickcontact.QuickContactTestsActivity"/>
 
@@ -69,6 +77,17 @@
                 android:name="android.accounts.AccountAuthenticator"
                 android:resource="@xml/test_basic_authenticator" />
         </service>
+        <service android:name=".testauth.TestSyncService$Basic" android:exported="true" >
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter"/>
+            </intent-filter>
+            <meta-data
+                android:name="android.content.SyncAdapter"
+                android:resource="@xml/test_basic_syncadapter" />
+            <meta-data
+                android:name="android.provider.CONTACTS_STRUCTURE"
+                android:resource="@xml/contacts_contactsdatakind" />
+        </service>
 
         <service android:name=".QueryService" />
         <service android:name=".PhoneNumberTestService" />
diff --git a/tests/res/drawable/ic_contact_picture.png b/tests/res/drawable/ic_contact_picture.png
deleted file mode 100644
index 6876777..0000000
--- a/tests/res/drawable/ic_contact_picture.png
+++ /dev/null
Binary files differ
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 1528112..76cf4e4 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -104,7 +104,5 @@
     <string name="attribution_flicker">Flicker</string>
     <string name="attribution_twitter">Twitter</string>
 
-    <string name="authenticator_basic_label">Test adapter</string>
-
     <string name="test_string">TEST STRING</string>
 </resources>
diff --git a/tests/res/xml/contacts_contactsdatakind.xml b/tests/res/xml/contacts_contactsdatakind.xml
new file mode 100644
index 0000000..c289e6e
--- /dev/null
+++ b/tests/res/xml/contacts_contactsdatakind.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+
+<!--
+    contacts.xml to build a "fallback account type" equivalent with
+    an additional test ContactsDataKind
+-->
+
+<ContactsAccountType
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    accountType="com.android.contacts.tests.testauth.basic"
+    accountTypeLabel="@string/applicationLabel"
+    accountTypeIcon="@mipmap/ic_contacts_launcher_square"
+    >
+    <EditSchema
+        >
+        <DataKind kind="name"
+            maxOccurs="1"
+            supportsDisplayName="true"
+            supportsPrefix="true"
+            supportsMiddleName="true"
+            supportsSuffix="true"
+            supportsPhoneticFamilyName="true"
+            supportsPhoneticMiddleName="true"
+            supportsPhoneticGivenName="true"
+            >
+        </DataKind>
+        <DataKind kind="photo" maxOccurs="1" />
+        <DataKind kind="phone" >
+            <Type type="mobile" />
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="fax_work" />
+            <Type type="fax_home" />
+            <Type type="pager" />
+            <Type type="other" />
+            <Type type="custom"/>
+            <Type type="callback" />
+            <Type type="car" />
+            <Type type="company_main" />
+            <Type type="isdn" />
+            <Type type="main" />
+            <Type type="other_fax" />
+            <Type type="radio" />
+            <Type type="telex" />
+            <Type type="tty_tdd" />
+            <Type type="work_mobile"/>
+            <Type type="work_pager" />
+            <Type type="assistant" />
+            <Type type="mms" />
+        </DataKind>
+        <DataKind kind="email" >
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="other" />
+            <Type type="mobile" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="nickname" maxOccurs="1" />
+        <DataKind kind="im" >
+            <Type type="aim" />
+            <Type type="msn" />
+            <Type type="yahoo" />
+            <Type type="skype" />
+            <Type type="qq" />
+            <Type type="google_talk" />
+            <Type type="icq" />
+            <Type type="jabber" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="postal" needsStructured="false" >
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="other" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="organization" maxOccurs="1" />
+        <DataKind kind="website" />
+        <DataKind kind="sip_address" maxOccurs="1" />
+        <DataKind kind="note" maxOccurs="1" />
+        <DataKind kind="group_membership" maxOccurs="1" />
+    </EditSchema>
+
+    <ContactsDataKind
+        android:mimeType="vnd.android.cursor.item/vnd.contactstest.profile"
+        android:icon="@mipmap/ic_contacts_launcher_square"
+        android:summaryColumn="data2"
+        android:detailColumn="data3" />
+
+</ContactsAccountType>
diff --git a/tests/res/xml/test_basic_authenticator.xml b/tests/res/xml/test_basic_authenticator.xml
index ecd100a..83bd135 100644
--- a/tests/res/xml/test_basic_authenticator.xml
+++ b/tests/res/xml/test_basic_authenticator.xml
@@ -18,8 +18,8 @@
 -->
 
 <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:accountType="com.android.contacts.tests.authtest.basic"
-    android:icon="@drawable/ic_contact_picture"
-    android:smallIcon="@drawable/ic_contact_picture"
-    android:label="@string/authenticator_basic_label"
+    android:accountType="com.android.contacts.tests.testauth.basic"
+    android:icon="@mipmap/ic_contacts_launcher_square"
+    android:smallIcon="@mipmap/ic_contacts_launcher_square"
+    android:label="@string/applicationLabel"
 />
diff --git a/tests/res/xml/test_basic_syncadapter.xml b/tests/res/xml/test_basic_syncadapter.xml
index fecc0eb..b897185 100644
--- a/tests/res/xml/test_basic_syncadapter.xml
+++ b/tests/res/xml/test_basic_syncadapter.xml
@@ -19,7 +19,7 @@
 
 <sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
     android:contentAuthority="com.android.contacts"
-    android:accountType="com.android.contacts.tests.authtest.basic"
+    android:accountType="com.android.contacts.tests.testauth.basic"
     android:supportsUploading="true"
     android:userVisible="true"
 />
diff --git a/tests/src/com/android/contacts/common/tests/testauth/TestSyncAdapter.java b/tests/src/com/android/contacts/common/tests/testauth/TestSyncAdapter.java
deleted file mode 100644
index a7c0f83..0000000
--- a/tests/src/com/android/contacts/common/tests/testauth/TestSyncAdapter.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-package com.android.contacts.common.tests.testauth;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.AbstractThreadedSyncAdapter;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SyncResult;
-import android.os.Bundle;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-
-/**
- * Simple (minimal) sync adapter.
- *
- */
-public class TestSyncAdapter extends AbstractThreadedSyncAdapter {
-    private final AccountManager mAccountManager;
-
-    private final Context mContext;
-
-    public TestSyncAdapter(Context context, boolean autoInitialize) {
-        super(context, autoInitialize);
-        mContext = context.getApplicationContext();
-        mAccountManager = AccountManager.get(mContext);
-    }
-
-    /**
-     * Doesn't actually sync, but sweep up all existing local-only contacts.
-     */
-    @Override
-    public void onPerformSync(Account account, Bundle extras, String authority,
-            ContentProviderClient provider, SyncResult syncResult) {
-        Log.v(TestauthConstants.LOG_TAG, "TestSyncAdapter.onPerformSync() account=" + account);
-
-        // First, claim all local-only contacts, if any.
-        ContentResolver cr = mContext.getContentResolver();
-        ContentValues values = new ContentValues();
-        values.put(RawContacts.ACCOUNT_NAME, account.name);
-        values.put(RawContacts.ACCOUNT_TYPE, account.type);
-        final int count = cr.update(RawContacts.CONTENT_URI, values,
-                RawContacts.ACCOUNT_NAME + " IS NULL AND " + RawContacts.ACCOUNT_TYPE + " IS NULL",
-                null);
-        if (count > 0) {
-            Log.v(TestauthConstants.LOG_TAG, "Claimed " + count + " local raw contacts");
-        }
-
-        // TODO: Clear isDirty flag
-        // TODO: Remove isDeleted raw contacts
-    }
-}
diff --git a/tests/src/com/android/contacts/tests/FakeDeviceAccountTypeFactory.java b/tests/src/com/android/contacts/tests/FakeDeviceAccountTypeFactory.java
index 6a500ec..65f8014 100644
--- a/tests/src/com/android/contacts/tests/FakeDeviceAccountTypeFactory.java
+++ b/tests/src/com/android/contacts/tests/FakeDeviceAccountTypeFactory.java
@@ -16,12 +16,10 @@
 package com.android.contacts.tests;
 
 import com.android.contacts.common.model.account.AccountType;
-import com.android.contacts.common.model.account.FallbackAccountType;
 import com.android.contacts.common.util.DeviceLocalAccountTypeFactory;
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Set;
 
 public class FakeDeviceAccountTypeFactory implements DeviceLocalAccountTypeFactory {
 
diff --git a/tests/src/com/android/contacts/common/tests/testauth/TestAuthenticationService.java b/tests/src/com/android/contacts/tests/testauth/TestAuthenticationService.java
similarity index 96%
rename from tests/src/com/android/contacts/common/tests/testauth/TestAuthenticationService.java
rename to tests/src/com/android/contacts/tests/testauth/TestAuthenticationService.java
index 93d1f4a..84f3f0f 100644
--- a/tests/src/com/android/contacts/common/tests/testauth/TestAuthenticationService.java
+++ b/tests/src/com/android/contacts/tests/testauth/TestAuthenticationService.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.contacts.common.tests.testauth;
+package com.android.contacts.tests.testauth;
 
 import android.app.Service;
 import android.content.Intent;
diff --git a/tests/src/com/android/contacts/common/tests/testauth/TestAuthenticator.java b/tests/src/com/android/contacts/tests/testauth/TestAuthenticator.java
similarity index 98%
rename from tests/src/com/android/contacts/common/tests/testauth/TestAuthenticator.java
rename to tests/src/com/android/contacts/tests/testauth/TestAuthenticator.java
index 2f676c7..97e2e4d 100644
--- a/tests/src/com/android/contacts/common/tests/testauth/TestAuthenticator.java
+++ b/tests/src/com/android/contacts/tests/testauth/TestAuthenticator.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.contacts.common.tests.testauth;
+package com.android.contacts.tests.testauth;
 
 import android.accounts.AbstractAccountAuthenticator;
 import android.accounts.Account;
diff --git a/tests/src/com/android/contacts/tests/testauth/TestSyncAdapter.java b/tests/src/com/android/contacts/tests/testauth/TestSyncAdapter.java
new file mode 100644
index 0000000..076baea
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/testauth/TestSyncAdapter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+package com.android.contacts.tests.testauth;
+
+import android.accounts.Account;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SyncResult;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Simple (minimal) sync adapter.
+ *
+ */
+public class TestSyncAdapter extends AbstractThreadedSyncAdapter {
+
+    private static final String TEXT_CONTENT_ITEM_TYPE =
+            "vnd.android.cursor.item/vnd.contactstest.profile";
+
+    private final Context mContext;
+
+    public TestSyncAdapter(Context context, boolean autoInitialize) {
+        super(context, autoInitialize);
+        mContext = context.getApplicationContext();
+    }
+
+    /**
+     * Doesn't actually sync, but sweep up all existing local-only contacts.
+     */
+    @Override
+    public void onPerformSync(Account account, Bundle extras, String authority,
+            ContentProviderClient provider, SyncResult syncResult) {
+        Log.v(TestauthConstants.LOG_TAG, "TestSyncAdapter.onPerformSync() account=" + account);
+
+        final ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        final Cursor cursor = contentResolver.query(RawContacts.CONTENT_URI,
+                new String[] { RawContacts._ID },
+                RawContacts.ACCOUNT_NAME + " IS NULL AND " + RawContacts.ACCOUNT_TYPE + " IS NULL",
+                null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final String rawContactId = Long.toString(cursor.getLong(0));
+
+                // Claim all local-only contacts for the test account
+                ops.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI)
+                        .withValue(RawContacts.ACCOUNT_NAME, account.name)
+                        .withValue(RawContacts.ACCOUNT_TYPE, account.type)
+                        .withSelection(RawContacts._ID+"=?", new String[] { rawContactId })
+                        .build());
+
+                // Create custom QuickContact action data rows
+                final Uri dataUri = Data.CONTENT_URI.buildUpon()
+                        .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+                        .build();
+                ops.add(ContentProviderOperation.newInsert(dataUri)
+                        .withValue(Data.RAW_CONTACT_ID, rawContactId)
+                        .withValue(Data.MIMETYPE, TEXT_CONTENT_ITEM_TYPE)
+                        .withValue(Data.DATA3, "Contacts test action")
+                        .withValue(Data.DATA5, "view")
+                        .build());
+            }
+        } finally {
+            cursor.close();
+        }
+        if (ops.isEmpty()) return;
+
+        // TODO: Clear isDirty flag
+        // TODO: Remove isDeleted raw contacts
+
+        Log.v(TestauthConstants.LOG_TAG, "Claiming " + ops.size() + " local raw contacts");
+        for (ContentProviderOperation op : ops) {
+            Log.v(TestauthConstants.LOG_TAG, op.toString());
+        }
+        try {
+            contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+        } catch (Exception e ) {
+            Log.e(TestauthConstants.LOG_TAG, "Failed to claim local raw contacts", e);
+        }
+    }
+}
diff --git a/tests/src/com/android/contacts/common/tests/testauth/TestSyncService.java b/tests/src/com/android/contacts/tests/testauth/TestSyncService.java
similarity index 95%
rename from tests/src/com/android/contacts/common/tests/testauth/TestSyncService.java
rename to tests/src/com/android/contacts/tests/testauth/TestSyncService.java
index 3354cb4..9928777 100644
--- a/tests/src/com/android/contacts/common/tests/testauth/TestSyncService.java
+++ b/tests/src/com/android/contacts/tests/testauth/TestSyncService.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.android.contacts.common.tests.testauth;
+package com.android.contacts.tests.testauth;
 
 import android.app.Service;
 import android.content.Intent;
diff --git a/tests/src/com/android/contacts/common/tests/testauth/TestauthConstants.java b/tests/src/com/android/contacts/tests/testauth/TestauthConstants.java
similarity index 92%
rename from tests/src/com/android/contacts/common/tests/testauth/TestauthConstants.java
rename to tests/src/com/android/contacts/tests/testauth/TestauthConstants.java
index 3ce7f5a..717ed35 100644
--- a/tests/src/com/android/contacts/common/tests/testauth/TestauthConstants.java
+++ b/tests/src/com/android/contacts/tests/testauth/TestauthConstants.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.contacts.common.tests.testauth;
+package com.android.contacts.tests.testauth;
 
 class TestauthConstants {
     public static final String LOG_TAG = "Testauth";