Merge "Replace switches on resources with if-else" into ub-contactsdialer-i-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5674fc5..053d0e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -439,11 +439,6 @@
 
     <!-- The message in a confirmation dialog shown when the user selects a
         contact aggregation suggestion in Contact editor. [CHAR LIMIT=512]-->
-    <string name="aggregation_suggestion_join_dialog_message">Link
-        the current contact with the selected contact?</string>
-
-    <!-- The message in a confirmation dialog shown when the user selects a
-        contact aggregation suggestion in Contact editor. [CHAR LIMIT=512]-->
     <string name="aggregation_suggestion_edit_dialog_message">Switch to editing
         the selected contact? Information you entered so far will be copied.</string>
 
diff --git a/src/com/android/contacts/CallUtil.java b/src/com/android/contacts/CallUtil.java
index d83b63d..ddde01c 100644
--- a/src/com/android/contacts/CallUtil.java
+++ b/src/com/android/contacts/CallUtil.java
@@ -116,13 +116,6 @@
     }
 
     /**
-     * @return Uri that directly dials a user's voicemail inbox.
-     */
-    public static Uri getVoicemailUri() {
-        return Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null);
-    }
-
-    /**
      * Determines if video calling is available, and if so whether presence checking is available
      * as well.
      *
@@ -166,23 +159,12 @@
             return VIDEO_CALLING_DISABLED;
         } catch (SecurityException e) {
             FeedbackHelper.sendFeedback(context, TAG,
-                    "Security exception when querying intent activities", e);
+                    "Security exception when getting call capable phone accounts", e);
             return VIDEO_CALLING_DISABLED;
         }
     }
 
     /**
-     * Determines if one of the call capable phone accounts defined supports video calling.
-     *
-     * @param context The context.
-     * @return {@code true} if one of the call capable phone accounts supports video calling,
-     *      {@code false} otherwise.
-     */
-    public static boolean isVideoEnabled(Context context) {
-        return (getVideoCallingAvailability(context) & VIDEO_CALLING_ENABLED) != 0;
-    }
-
-    /**
      * Determines if one of the call capable phone accounts defined supports calling with a subject
      * specified.
      *
@@ -201,13 +183,20 @@
             return false;
         }
 
-        List<PhoneAccountHandle> accountHandles = telecommMgr.getCallCapablePhoneAccounts();
-        for (PhoneAccountHandle accountHandle : accountHandles) {
-            PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle);
-            if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT)) {
-                return true;
+        try {
+            List<PhoneAccountHandle> accountHandles = telecommMgr.getCallCapablePhoneAccounts();
+            for (PhoneAccountHandle accountHandle : accountHandles) {
+                PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle);
+                if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT)) {
+                    return true;
+                }
             }
+            return false;
+        } catch (SecurityException e) {
+            FeedbackHelper.sendFeedback(context, TAG,
+                    "Security exception when getting call capable phone accounts", e);
+            return false;
         }
-        return false;
+
     }
 }
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index abb8463..326ff93 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -468,7 +468,12 @@
      */
     public void changePhoto(int photoMode) {
         mPhotoMode = photoMode;
-        PhotoSourceDialogFragment.show(this, mPhotoMode);
+        // This method is called from an onClick handler in the PhotoEditorView. It's possible for
+        // onClick methods to run after onSaveInstanceState is called for the activity, so check
+        // if it's safe to commit transactions before trying.
+        if (isSafeToCommitTransactions()) {
+            PhotoSourceDialogFragment.show(this, mPhotoMode);
+        }
     }
 
     public Toolbar getToolbar() {
diff --git a/src/com/android/contacts/compat/telecom/TelecomManagerCompat.java b/src/com/android/contacts/compat/telecom/TelecomManagerCompat.java
index fbbc589..881c1a4 100644
--- a/src/com/android/contacts/compat/telecom/TelecomManagerCompat.java
+++ b/src/com/android/contacts/compat/telecom/TelecomManagerCompat.java
@@ -17,25 +17,15 @@
 
 import android.app.Activity;
 import android.content.Intent;
-import android.net.Uri;
 import android.support.annotation.Nullable;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 
 import com.android.contacts.compat.CompatUtils;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Compatibility class for {@link android.telecom.TelecomManager}.
  */
 public class TelecomManagerCompat {
-    public static final String TELECOM_MANAGER_CLASS = "android.telecom.TelecomManager";
     /**
      * Places a new outgoing call to the provided address using the system telecom service with
      * the specified intent.
@@ -55,226 +45,4 @@
         }
         activity.startActivityForResult(intent, 0);
     }
-
-    /**
-     * Get the URI for running an adn query.
-     *
-     * @param telecomManager the {@link TelecomManager} used for method calls, if possible.
-     * @param accountHandle The handle for the account to derive an adn query URI for or
-     * {@code null} to return a URI which will use the default account.
-     * @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount}
-     * for the the content retrieve.
-     */
-    public static Uri getAdnUriForPhoneAccount(@Nullable TelecomManager telecomManager,
-            PhoneAccountHandle accountHandle) {
-        if (telecomManager != null && (CompatUtils.isMarshmallowCompatible()
-                || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS, "getAdnUriForPhoneAccount",
-                        PhoneAccountHandle.class))) {
-            return telecomManager.getAdnUriForPhoneAccount(accountHandle);
-        }
-        return Uri.parse("content://icc/adn");
-    }
-
-    /**
-     * Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone
-     * calls. The returned list includes only those accounts which have been explicitly enabled
-     * by the user.
-     *
-     * @param telecomManager the {@link TelecomManager} used for method calls, if possible.
-     * @return A list of PhoneAccountHandle objects.
-     */
-    public static List<PhoneAccountHandle> getCallCapablePhoneAccounts(
-            @Nullable TelecomManager telecomManager) {
-        if (telecomManager != null && (CompatUtils.isMarshmallowCompatible()
-                || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS,
-                        "getCallCapablePhoneAccounts"))) {
-            return telecomManager.getCallCapablePhoneAccounts();
-        }
-        return new ArrayList<>();
-    }
-
-    /**
-     * Used to determine the currently selected default dialer package.
-     *
-     * @param telecomManager the {@link TelecomManager} used for method calls, if possible.
-     * @return package name for the default dialer package or null if no package has been
-     *         selected as the default dialer.
-     */
-    @Nullable
-    public static String getDefaultDialerPackage(@Nullable TelecomManager telecomManager) {
-        if (telecomManager != null && CompatUtils.isDefaultDialerCompatible()) {
-            return telecomManager.getDefaultDialerPackage();
-        }
-        return null;
-    }
-
-    /**
-     * Return the {@link PhoneAccount} which will be used to place outgoing calls to addresses with
-     * the specified {@code uriScheme}. This PhoneAccount will always be a member of the
-     * list which is returned from invoking {@link TelecomManager#getCallCapablePhoneAccounts()}.
-     * The specific account returned depends on the following priorities:
-     *
-     * 1. If the user-selected default PhoneAccount supports the specified scheme, it will
-     * be returned.
-     * 2. If there exists only one PhoneAccount that supports the specified scheme, it
-     * will be returned.
-     *
-     * If no PhoneAccount fits the criteria above, this method will return {@code null}.
-     *
-     * @param telecomManager the {@link TelecomManager} used for method calls, if possible.
-     * @param uriScheme The URI scheme.
-     * @return The {@link PhoneAccountHandle} corresponding to the account to be used.
-     */
-    @Nullable
-    public static PhoneAccountHandle getDefaultOutgoingPhoneAccount(
-            @Nullable TelecomManager telecomManager, @Nullable String uriScheme) {
-        if (telecomManager != null && (CompatUtils.isMarshmallowCompatible()
-                || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS,
-                        "getDefaultOutgoingPhoneAccount", String.class))) {
-            return telecomManager.getDefaultOutgoingPhoneAccount(uriScheme);
-        }
-        return null;
-    }
-
-    /**
-     * Return the line 1 phone number for given phone account.
-     *
-     * @param telecomManager the {@link TelecomManager} to use in the event that
-     *    {@link TelecomManager#getLine1Number(PhoneAccountHandle)} is available
-     * @param telephonyManager the {@link TelephonyManager} to use if TelecomManager#getLine1Number
-     *    is unavailable
-     * @param phoneAccountHandle the phoneAccountHandle upon which to check the line one number
-     * @return the line one number
-     */
-    @Nullable
-    public static String getLine1Number(@Nullable TelecomManager telecomManager,
-            @Nullable TelephonyManager telephonyManager,
-            @Nullable PhoneAccountHandle phoneAccountHandle) {
-        if (telecomManager != null && CompatUtils.isMarshmallowCompatible()) {
-            return telecomManager.getLine1Number(phoneAccountHandle);
-        }
-        if (telephonyManager != null) {
-            return telephonyManager.getLine1Number();
-        }
-        return null;
-    }
-
-    /**
-     * Return whether a given phone number is the configured voicemail number for a
-     * particular phone account.
-     *
-     * @param telecomManager the {@link TelecomManager} to use for checking the number.
-     * @param accountHandle The handle for the account to check the voicemail number against
-     * @param number The number to look up.
-     */
-    public static boolean isVoiceMailNumber(@Nullable TelecomManager telecomManager,
-            @Nullable PhoneAccountHandle accountHandle, @Nullable String number) {
-        if (telecomManager != null && (CompatUtils.isMarshmallowCompatible()
-                || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS, "isVoiceMailNumber",
-                        PhoneAccountHandle.class, String.class))) {
-            return telecomManager.isVoiceMailNumber(accountHandle, number);
-        }
-        return PhoneNumberUtils.isVoiceMailNumber(number);
-    }
-
-    /**
-     * Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes
-     * resources which can be used in a user interface.
-     *
-     * @param telecomManager the {@link TelecomManager} used for method calls, if possible.
-     * @param account The {@link PhoneAccountHandle}.
-     * @return The {@link PhoneAccount} object or null if it doesn't exist.
-     */
-    @Nullable
-    public static PhoneAccount getPhoneAccount(@Nullable TelecomManager telecomManager,
-            @Nullable PhoneAccountHandle accountHandle) {
-        if (telecomManager != null && (CompatUtils.isMethodAvailable(
-                TELECOM_MANAGER_CLASS, "getPhoneAccount", PhoneAccountHandle.class))) {
-            return telecomManager.getPhoneAccount(accountHandle);
-        }
-        return null;
-    }
-
-    /**
-     * Return the voicemail number for a given phone account.
-     *
-     * @param telecomManager The {@link TelecomManager} object to use for retrieving the voicemail
-     * number if accountHandle is specified.
-     * @param telephonyManager The {@link TelephonyManager} object to use for retrieving the
-     * voicemail number if accountHandle is null.
-     * @param accountHandle The handle for the phone account.
-     * @return The voicemail number for the phone account, and {@code null} if one has not been
-     *         configured.
-     */
-    @Nullable
-    public static String getVoiceMailNumber(@Nullable TelecomManager telecomManager,
-            @Nullable TelephonyManager telephonyManager,
-            @Nullable PhoneAccountHandle accountHandle) {
-        if (telecomManager != null && (CompatUtils.isMethodAvailable(
-                TELECOM_MANAGER_CLASS, "getVoiceMailNumber", PhoneAccountHandle.class))) {
-            return telecomManager.getVoiceMailNumber(accountHandle);
-        } else if (telephonyManager != null){
-            return telephonyManager.getVoiceMailNumber();
-        }
-        return null;
-    }
-
-    /**
-     * Processes the specified dial string as an MMI code.
-     * MMI codes are any sequence of characters entered into the dialpad that contain a "*" or "#".
-     * Some of these sequences launch special behavior through handled by Telephony.
-     *
-     * @param telecomManager The {@link TelecomManager} object to use for handling MMI.
-     * @param dialString The digits to dial.
-     * @return {@code true} if the digits were processed as an MMI code, {@code false} otherwise.
-     */
-    public static boolean handleMmi(@Nullable TelecomManager telecomManager,
-            @Nullable String dialString, @Nullable PhoneAccountHandle accountHandle) {
-        if (telecomManager == null || TextUtils.isEmpty(dialString)) {
-            return false;
-        }
-        if (CompatUtils.isMarshmallowCompatible()) {
-            return telecomManager.handleMmi(dialString, accountHandle);
-        }
-
-        Object handleMmiResult = CompatUtils.invokeMethod(
-                telecomManager,
-                "handleMmi",
-                new Class<?>[] {PhoneAccountHandle.class, String.class},
-                new Object[] {accountHandle, dialString});
-        if (handleMmiResult != null) {
-            return (boolean) handleMmiResult;
-        }
-
-        return telecomManager.handleMmi(dialString);
-    }
-
-    /**
-     * Silences the ringer if a ringing call exists. Noop if {@link TelecomManager#silenceRinger()}
-     * is unavailable.
-     *
-     * @param telecomManager the TelecomManager to use to silence the ringer.
-     */
-    public static void silenceRinger(@Nullable TelecomManager telecomManager) {
-        if (telecomManager != null && (CompatUtils.isMarshmallowCompatible() || CompatUtils
-                .isMethodAvailable(TELECOM_MANAGER_CLASS, "silenceRinger"))) {
-            telecomManager.silenceRinger();
-        }
-    }
-
-    /**
-     * Returns the current SIM call manager. Apps must be prepared for this method to return null,
-     * indicating that there currently exists no registered SIM call manager.
-     *
-     * @param telecomManager the {@link TelecomManager} to use to fetch the SIM call manager.
-     * @return The phone account handle of the current sim call manager.
-     */
-    @Nullable
-    public static PhoneAccountHandle getSimCallManager(TelecomManager telecomManager) {
-        if (telecomManager != null && (CompatUtils.isMarshmallowCompatible() || CompatUtils
-                .isMethodAvailable(TELECOM_MANAGER_CLASS, "getSimCallManager"))) {
-            return telecomManager.getSimCallManager();
-        }
-        return null;
-    }
 }
diff --git a/src/com/android/contacts/database/SimContactDaoImpl.java b/src/com/android/contacts/database/SimContactDaoImpl.java
index 8d47824..4eb5fd3 100644
--- a/src/com/android/contacts/database/SimContactDaoImpl.java
+++ b/src/com/android/contacts/database/SimContactDaoImpl.java
@@ -89,9 +89,15 @@
     private final TelephonyManager mTelephonyManager;
 
     public SimContactDaoImpl(Context context) {
+        this(context, context.getContentResolver(),
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
+    }
+
+    public SimContactDaoImpl(Context context, ContentResolver resolver,
+            TelephonyManager telephonyManager) {
         mContext = context;
-        mResolver = context.getContentResolver();
-        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        mResolver = resolver;
+        mTelephonyManager = telephonyManager;
     }
 
     public Context getContext() {
diff --git a/src/com/android/contacts/editor/JoinSuggestedContactDialogFragment.java b/src/com/android/contacts/editor/JoinSuggestedContactDialogFragment.java
deleted file mode 100644
index be3313b..0000000
--- a/src/com/android/contacts/editor/JoinSuggestedContactDialogFragment.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2015 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.editor;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.os.Bundle;
-
-import com.android.contacts.R;
-
-public class JoinSuggestedContactDialogFragment extends DialogFragment {
-
-    private static final String ARG_RAW_CONTACT_IDS = "rawContactIds";
-
-    public static void show(ContactEditorFragment fragment, long[] rawContactIds) {
-        final Bundle args = new Bundle();
-        args.putLongArray(ARG_RAW_CONTACT_IDS, rawContactIds);
-
-        final JoinSuggestedContactDialogFragment dialog = new JoinSuggestedContactDialogFragment();
-        dialog.setArguments(args);
-        dialog.setTargetFragment(fragment, 0);
-        dialog.show(fragment.getFragmentManager(), "join");
-    }
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        return new AlertDialog.Builder(getActivity())
-                .setIconAttribute(android.R.attr.alertDialogIcon)
-                .setMessage(R.string.aggregation_suggestion_join_dialog_message)
-                .setPositiveButton(android.R.string.yes,
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int whichButton) {
-                                ContactEditorFragment targetFragment =
-                                        (ContactEditorFragment) getTargetFragment();
-                                long rawContactIds[] =
-                                        getArguments().getLongArray(ARG_RAW_CONTACT_IDS);
-                                targetFragment.doJoinSuggestedContact(rawContactIds);
-                            }
-                        }
-                )
-                .setNegativeButton(android.R.string.no, null)
-                .create();
-    }
-}
diff --git a/tests/src/com/android/contacts/activities/SimImportActivityTest.java b/tests/src/com/android/contacts/activities/SimImportActivityTest.java
index 8ebe69b..64b86e2 100644
--- a/tests/src/com/android/contacts/activities/SimImportActivityTest.java
+++ b/tests/src/com/android/contacts/activities/SimImportActivityTest.java
@@ -1,18 +1,37 @@
+/*
+ * 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.
+ */
 package com.android.contacts.activities;
 
 import static com.android.contacts.tests.ContactsMatchers.DataCursor.hasMimeType;
 import static com.android.contacts.tests.ContactsMatchers.hasRowMatching;
 import static com.android.contacts.tests.ContactsMatchers.hasValueForColumn;
-
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.allOf;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.content.BroadcastReceiver;
+import android.content.ContentProviderClient;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.database.Cursor;
 import android.os.Build;
@@ -20,52 +39,60 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Data;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.filters.SdkSuppress;
-import android.support.test.filters.Suppress;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.Until;
+import android.support.v4.content.LocalBroadcastManager;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
 
+import com.android.contacts.SimImportService;
 import com.android.contacts.database.SimContactDao;
+import com.android.contacts.database.SimContactDaoImpl;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.SimCard;
 import com.android.contacts.model.SimContact;
 import com.android.contacts.model.account.AccountWithDataSet;
+import com.android.contacts.test.mocks.ForwardingContentProvider;
+import com.android.contacts.test.mocks.MockContentProvider;
 import com.android.contacts.tests.AccountsTestHelper;
 import com.android.contacts.tests.ContactsMatchers;
 import com.android.contacts.tests.FakeSimContactDao;
 import com.android.contacts.tests.StringableCursor;
-
+import com.google.common.base.Function;
 import com.google.common.base.Functions;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
 
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * UI Tests for {@link SimImportActivity}
  *
  * These should probably be converted to espresso tests because espresso does a better job of
  * waiting for the app to be idle once espresso library is added
  */
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
 @TargetApi(Build.VERSION_CODES.M)
 public class SimImportActivityTest {
 
-    public static final int TIMEOUT = 1000;
+    public static final int TIMEOUT = 3000;
     private Context mContext;
     private UiDevice mDevice;
     private Instrumentation mInstrumentation;
     private FakeSimContactDao mDao;
     private AccountsTestHelper mAccountHelper;
+    private Activity mActivity;
 
     @Before
     public void setUp() throws Exception {
@@ -87,6 +114,10 @@
         SimContactDao.setFactoryForTest(SimContactDao.DEFAULT_FACTORY);
         mAccountHelper.cleanup();
         AccountTypeManager.setInstanceForTest(null);
+        if (mActivity != null) {
+            mActivity.finish();
+            mInstrumentation.waitForIdleSync();
+        }
     }
 
     @Test
@@ -96,15 +127,17 @@
                         new SimContact(2, "Sim Two", null),
                         new SimContact(3, null, "5550103")
                 );
-        mInstrumentation.startActivitySync(new Intent(mContext, SimImportActivity.class)
+        mActivity = mInstrumentation.startActivitySync(new Intent(mContext, SimImportActivity.class)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
 
         mDevice.waitForIdle();
 
+        assertTrue(mDevice.wait(Until.hasObject(By.text("Sim One")), TIMEOUT));
+
         assertTrue(mDevice.hasObject(By.text("Sim One")));
         assertTrue(mDevice.hasObject(By.text("Sim Two")));
         assertTrue(mDevice.hasObject(By.text("5550103")));
-}
+    }
 
     @Test
     public void shouldHaveEmptyState() {
@@ -115,24 +148,24 @@
 
         mDevice.waitForIdle();
 
-        assertTrue(mDevice.hasObject(By.textStartsWith("No contacts")));
+        assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("No contacts")), TIMEOUT));
     }
 
     @Test
     public void smokeRotateInEmptyState() {
         mDao.addSim(someSimCard());
 
-        final Activity activity = mInstrumentation.startActivitySync(
+        mActivity = mInstrumentation.startActivitySync(
                 new Intent(mContext, SimImportActivity.class)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
 
         assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("No contacts")), TIMEOUT));
 
-        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+        mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
 
         mDevice.waitForIdle();
 
-        assertTrue(mDevice.hasObject(By.textStartsWith("No contacts")));
+        assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("No contacts")), TIMEOUT));
     }
 
     @Test
@@ -140,42 +173,79 @@
         mDao.addSim(someSimCard(), new SimContact(1, "Name One", "5550101"),
                 new SimContact(2, "Name Two", "5550102"));
 
-        final Activity activity = mInstrumentation.startActivitySync(
+        mActivity = mInstrumentation.startActivitySync(
                 new Intent(mContext, SimImportActivity.class)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
 
         assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("Name One")), TIMEOUT));
 
-        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+        mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
 
         mDevice.waitForIdle();
 
-        assertTrue(mDevice.hasObject(By.textStartsWith("Name One")));
+        assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("Name One")), TIMEOUT));
     }
 
-
-    // TODO: fix this test. This doesn't work because AccountTypeManager returns a stale account
-    // list (it doesn't contain the accounts added during the current test run).
-    // Could use MockAccountTypeManager but probably ought to look at improving how
-    // AccountTypeManager updates it's account list.
-    @Suppress
+    /**
+     * Tests a complete import flow
+     *
+     * <p>Test case outline:</p>
+     * <ul>
+     * <li>Load SIM contacts
+     * <li>Change to a specific target account
+     * <li>Deselect 3 specific SIM contacts
+     * <li>Rotate the screen to landscape
+     * <li>Rotate the screen back to portrait
+     * <li>Press the import button
+     * <li>Wait for import to complete
+     * <li>Query contacts in target account and verify that they match selected contacts
+     * <li>Start import activity again
+     * <li>Switch to target account
+     * <li>Verify that previously imported contacts are disabled and not checked
+     * </ul>
+     *
+     * <p>This mocks out the IccProvider and stubs the canReadSimContacts method to make it work on
+     * an emulator but otherwise uses real dependency.
+     * </p>
+     */
     @Test
-    public void selectionsAreImportedAndDisabledOnSubsequentViews() throws Exception {
+    public void selectionsAreImportedAndDisabledOnSubsequentImports() throws Exception {
         // Clear out the instance so that it will have the most recent accounts when reloaded
         AccountTypeManager.setInstanceForTest(null);
 
         final AccountWithDataSet targetAccount = mAccountHelper.addTestAccount(
                 mAccountHelper.generateAccountName("SimImportActivity_target_"));
 
-        mDao.addSim(someSimCard(),
-                new SimContact(1, "Import One", "5550101"),
-                new SimContact(2, "Skip Two", "5550102"),
-                new SimContact(3, "Import Three", "5550103"),
-                new SimContact(4, "Skip Four", "5550104"),
-                new SimContact(5, "Skip Five", "5550105"),
-                new SimContact(6, "Import Six", "5550106"));
+        final MockContentProvider iccProvider = new MockContentProvider();
+        iccProvider.expect(MockContentProvider.Query.forAnyUri())
+                .withDefaultProjection(new String[] {SimContactDaoImpl._ID, SimContactDaoImpl.NAME,
+                        SimContactDaoImpl.NUMBER, SimContactDaoImpl.EMAILS })
+                .anyNumberOfTimes()
+                .returnRow(toCursorRow(new SimContact(1, "Import One", "5550101")))
+                .returnRow(toCursorRow(new SimContact(2, "Skip Two", "5550102")))
+                .returnRow(toCursorRow(new SimContact(3, "Import Three", "5550103")))
+                .returnRow(toCursorRow(new SimContact(4, "Skip Four", "5550104")))
+                .returnRow(toCursorRow(new SimContact(5, "Skip Five", "5550105")))
+                .returnRow(toCursorRow(new SimContact(6, "Import Six", "5550106")));
+        final MockContentResolver mockResolver = new MockContentResolver();
+        mockResolver.addProvider("icc", iccProvider);
+        final ContentProviderClient contactsProviderClient = mContext.getContentResolver()
+                .acquireContentProviderClient(ContactsContract.AUTHORITY);
+        mockResolver.addProvider(ContactsContract.AUTHORITY, new ForwardingContentProvider(
+                contactsProviderClient));
 
-        final Activity activity = mInstrumentation.startActivitySync(
+        SimContactDao.setFactoryForTest(new Function<Context, SimContactDao>() {
+            @Override
+            public SimContactDao apply(Context input) {
+                final SimContactDaoImpl spy = spy(new SimContactDaoImpl(
+                        mContext, mockResolver,
+                        (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)));
+                when(spy.canReadSimContacts()).thenReturn(true);
+                return spy;
+            }
+        });
+
+        mActivity = mInstrumentation.startActivitySync(
                 new Intent(mContext, SimImportActivity.class)
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
 
@@ -183,21 +253,28 @@
 
         mDevice.findObject(By.desc("Show more")).clickAndWait(Until.newWindow(), TIMEOUT);
         mDevice.findObject(By.textStartsWith("SimImportActivity_target_")).click();
-        mDevice.waitForIdle();
+
+        assertTrue(mDevice.wait(Until.hasObject(By.text("Skip Two")), TIMEOUT));
 
         mDevice.findObject(By.text("Skip Two")).click();
+        mDevice.findObject(By.text("Skip Four")).click();
         mDevice.findObject(By.text("Skip Five")).click();
+        mDevice.waitForIdle();
 
         assertTrue(mDevice.hasObject(By.text("Skip Two").checked(false)));
         assertTrue(mDevice.hasObject(By.text("Skip Five").checked(false)));
 
-        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+        mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
         mDevice.wait(Until.hasObject(By.text("Import One")), TIMEOUT);
-        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
+        mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
         mDevice.wait(Until.hasObject(By.text("Import One")), TIMEOUT);
 
+        ListenableFuture<?> nextImportFuture = nextImportCompleteBroadcast();
+
         mDevice.findObject(By.text("IMPORT").clickable(true)).click();
-        mDevice.waitForIdle();
+
+        // Block until import completes
+        nextImportFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
 
         final Cursor cursor = new StringableCursor(
                 mContext.getContentResolver().query(Data.CONTENT_URI, null,
@@ -229,37 +306,44 @@
         cursor.close();
 
 
-        mInstrumentation.startActivitySync(
+        mActivity = mInstrumentation.startActivitySync(
                 new Intent(mContext, SimImportActivity.class)
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
 
         assertTrue(mDevice.wait(Until.hasObject(By.text("Import One")), TIMEOUT));
 
-        mDevice.findObject(By.descStartsWith("Saving to")).clickAndWait(Until.newWindow(), TIMEOUT);
+        mDevice.findObject(By.descStartsWith("Show more")).clickAndWait(Until.newWindow(), TIMEOUT);
         mDevice.findObject(By.textContains(targetAccount.name)).click();
         mDevice.waitForIdle();
 
-        assertTrue(mDevice.hasObject(By.text("Import One").checked(false).enabled(false)));
+        assertTrue(mDevice.wait(Until.hasObject(By.text("Import One").checked(false).enabled(false)), TIMEOUT));
         assertTrue(mDevice.hasObject(By.text("Import Three").checked(false).enabled(false)));
         assertTrue(mDevice.hasObject(By.text("Import Six").checked(false).enabled(false)));
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            contactsProviderClient.close();
+        }
+    }
+
+    private ListenableFuture<Intent> nextImportCompleteBroadcast() {
+        final SettableFuture<Intent> result = SettableFuture.create();
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                result.set(intent);
+                LocalBroadcastManager.getInstance(mContext).unregisterReceiver(this);
+            }
+        };
+        LocalBroadcastManager.getInstance(mContext).registerReceiver(receiver, new IntentFilter(
+                SimImportService.BROADCAST_SIM_IMPORT_COMPLETE));
+        return result;
+    }
+
+    private Object[] toCursorRow(SimContact contact) {
+        return new Object[] { contact.getId(), contact.getName(), contact.getPhone(), null };
     }
 
     private SimCard someSimCard() {
         return new SimCard("id", 1, "Carrier", "SIM", "18005550101", "us");
     }
-
-    private Matcher<SimContact> withContactId(final long id) {
-        return new BaseMatcher<SimContact>() {
-            @Override
-            public boolean matches(Object o) {
-                return (o instanceof SimContact) && ((SimContact) o).getId() == id;
-            }
-
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("Expected SimContact with id=" + id);
-            }
-        };
-    }
 }
diff --git a/tests/src/com/android/contacts/test/mocks/ForwardingContentProvider.java b/tests/src/com/android/contacts/test/mocks/ForwardingContentProvider.java
new file mode 100644
index 0000000..b6ca983
--- /dev/null
+++ b/tests/src/com/android/contacts/test/mocks/ForwardingContentProvider.java
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+package com.android.contacts.test.mocks;
+
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.support.annotation.Nullable;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * Forwards calls to a {@link ContentProviderClient}
+ *
+ * <p>This allows mixing use of the system content providers in a
+ * {@link android.test.mock.MockContentResolver}
+ * </p>
+ */
+public class ForwardingContentProvider extends android.test.mock.MockContentProvider {
+
+    private final ContentProviderClient mClient;
+
+    public ForwardingContentProvider(ContentProviderClient client) {
+        mClient = client;
+    }
+
+    @Override
+    public synchronized Cursor query(Uri url, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        try {
+            return mClient.query(url, projection, selection, selectionArgs, sortOrder);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Nullable
+    @Override
+    public synchronized Cursor query(Uri url, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
+        try {
+            return mClient.query(url, projection, selection, selectionArgs, sortOrder,
+                    cancellationSignal);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public synchronized String getType(Uri url) {
+        try {
+            return mClient.getType(url);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public synchronized String[] getStreamTypes(Uri url, String mimeTypeFilter) {
+        try {
+            return mClient.getStreamTypes(url, mimeTypeFilter);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public synchronized Uri insert(Uri url, ContentValues initialValues) {
+        try {
+            return mClient.insert(url, initialValues);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public synchronized int bulkInsert(Uri url, ContentValues[] initialValues) {
+        try {
+            return mClient.bulkInsert(url, initialValues);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public synchronized int delete(Uri url, String selection, String[] selectionArgs) {
+        try {
+            return mClient.delete(url, selection, selectionArgs);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public synchronized int update(Uri url, ContentValues values,
+            String selection, String[] selectionArgs) {
+        try {
+            return mClient.update(url, values, selection, selectionArgs);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Nullable
+    @Override
+    public synchronized ParcelFileDescriptor openFile(Uri url, String mode) {
+        try {
+            return mClient.openFile(url, mode);
+        } catch (RemoteException|FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Nullable
+    @Override
+    public synchronized ParcelFileDescriptor openFile(Uri url, String mode,
+            CancellationSignal signal) {
+        try {
+            return mClient.openFile(url, mode, signal);
+        } catch (RemoteException|FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Nullable
+    @Override
+    public synchronized AssetFileDescriptor openAssetFile(Uri url, String mode) {
+        try {
+            return mClient.openAssetFile(url, mode);
+        } catch (RemoteException|FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Nullable
+    @Override
+    public synchronized AssetFileDescriptor openAssetFile(Uri url, String mode,
+            CancellationSignal signal) {
+        try {
+            return mClient.openAssetFile(url, mode, signal);
+        } catch (RemoteException|FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public synchronized AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri, String mimeType,
+            Bundle opts) {
+        try {
+            return mClient.openTypedAssetFileDescriptor(uri, mimeType, opts);
+        } catch (RemoteException|FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public synchronized AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri, String mimeType,
+            Bundle opts, CancellationSignal signal) {
+        try {
+            return mClient.openTypedAssetFileDescriptor(uri, mimeType, opts, signal);
+        } catch (RemoteException|FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public synchronized ContentProviderResult[] applyBatch(
+            ArrayList<ContentProviderOperation> operations) {
+        try {
+            return mClient.applyBatch(operations);
+        } catch (RemoteException|OperationApplicationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Nullable
+    @Override
+    public synchronized Bundle call(String method, String arg, Bundle extras) {
+        try {
+            return mClient.call(method, arg, extras);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}