Only consider writable accounts for preferred SIM

Some apps will add their own account to contacts and cause PhoneLookup to return multiple accounts. Since Contacts cannot write to these accounts the preferred SIM settings on them does not make sense.

In this CL a reduced version of AccountTypeManager is used to determine if the account is writable. External accounts (which capabilities are determined by parsing their sync adapter) are not supported.

Bug: 70689051
Test: CallingAccountSelectorTest
PiperOrigin-RevId: 180693656
Change-Id: I24e93860cd576777c6d3861f65f75baa234dac87
diff --git a/java/com/android/dialer/precall/impl/CallingAccountSelector.java b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
index a61a3b1..d407296 100644
--- a/java/com/android/dialer/precall/impl/CallingAccountSelector.java
+++ b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -28,8 +29,10 @@
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.PhoneLookup;
 import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsContract.RawContacts;
 import android.support.annotation.MainThread;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -59,6 +62,7 @@
 import com.android.dialer.preferredsim.suggestion.SuggestionProvider.Suggestion;
 import com.android.dialer.telecom.TelecomUtil;
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -296,7 +300,7 @@
       if (!isPreferredSimEnabled(context)) {
         return result;
       }
-      result.dataId = getDataId(context.getContentResolver(), phoneNumber);
+      result.dataId = getDataId(context, phoneNumber);
       if (result.dataId.isPresent()) {
         result.phoneAccountHandle = getPreferredAccount(context, result.dataId.get());
       }
@@ -313,24 +317,33 @@
   @WorkerThread
   @NonNull
   private static Optional<String> getDataId(
-      @NonNull ContentResolver contentResolver, @Nullable String phoneNumber) {
+      @NonNull Context context, @Nullable String phoneNumber) {
     Assert.isWorkerThread();
     if (VERSION.SDK_INT < VERSION_CODES.N) {
       return Optional.absent();
     }
     try (Cursor cursor =
-        contentResolver.query(
-            Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)),
-            new String[] {PhoneLookup.DATA_ID},
-            null,
-            null,
-            null)) {
+        context
+            .getContentResolver()
+            .query(
+                Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)),
+                new String[] {PhoneLookup.DATA_ID},
+                null,
+                null,
+                null)) {
       if (cursor == null) {
         return Optional.absent();
       }
+      ImmutableSet<String> validAccountTypes = PreferredAccountUtil.getValidAccountTypes(context);
       Set<String> result = new ArraySet<>();
       while (cursor.moveToNext()) {
-        result.add(cursor.getString(0));
+        Optional<String> accountType =
+            getAccountType(context.getContentResolver(), cursor.getLong(0));
+        if (accountType.isPresent() && validAccountTypes.contains(accountType.get())) {
+          result.add(cursor.getString(0));
+        } else {
+          LogUtil.i("CallingAccountSelector.getDataId", "ignoring non-writable " + accountType);
+        }
       }
       // TODO(twyen): if there are multiples attempt to grab from the contact that initiated the
       // call.
@@ -344,6 +357,44 @@
   }
 
   @WorkerThread
+  private static Optional<String> getAccountType(ContentResolver contentResolver, long dataId) {
+    Assert.isWorkerThread();
+    Optional<Long> rawContactId = getRawContactId(contentResolver, dataId);
+    if (!rawContactId.isPresent()) {
+      return Optional.absent();
+    }
+    try (Cursor cursor =
+        contentResolver.query(
+            ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId.get()),
+            new String[] {RawContacts.ACCOUNT_TYPE},
+            null,
+            null,
+            null)) {
+      if (cursor == null || !cursor.moveToFirst()) {
+        return Optional.absent();
+      }
+      return Optional.of(cursor.getString(0));
+    }
+  }
+
+  @WorkerThread
+  private static Optional<Long> getRawContactId(ContentResolver contentResolver, long dataId) {
+    Assert.isWorkerThread();
+    try (Cursor cursor =
+        contentResolver.query(
+            ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
+            new String[] {Data.RAW_CONTACT_ID},
+            null,
+            null,
+            null)) {
+      if (cursor == null || !cursor.moveToFirst()) {
+        return Optional.absent();
+      }
+      return Optional.of(cursor.getLong(0));
+    }
+  }
+
+  @WorkerThread
   @NonNull
   private static Optional<PhoneAccountHandle> getPreferredAccount(
       @NonNull Context context, @NonNull String dataId) {
diff --git a/java/com/android/dialer/precall/impl/PreferredAccountUtil.java b/java/com/android/dialer/precall/impl/PreferredAccountUtil.java
index a41cb6e..6505888 100644
--- a/java/com/android/dialer/precall/impl/PreferredAccountUtil.java
+++ b/java/com/android/dialer/precall/impl/PreferredAccountUtil.java
@@ -30,7 +30,9 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import com.android.dialer.common.LogUtil;
+import com.android.dialer.configprovider.ConfigProviderComponent;
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * Utilities for looking up and validating preferred {@link PhoneAccountHandle}. Contacts should
@@ -91,4 +93,24 @@
     }
     return false;
   }
+
+  /**
+   * Return a set of {@link android.accounts.Account#type} that is known to have writable contacts.
+   * This is a light weight implementation of {@link
+   * com.android.contacts.common.model.AccountTypeManager#getAccountTypes(boolean)}. External
+   * accounts are not supported.
+   */
+  public static ImmutableSet<String> getValidAccountTypes(Context context) {
+    return ImmutableSet.copyOf(
+        ConfigProviderComponent.get(context)
+            .getConfigProvider()
+            .getString(
+                "preferred_sim_valid_account_types",
+                "com.google;"
+                    + "com.osp.app.signin;"
+                    + "com.android.exchange;"
+                    + "com.google.android.exchange;"
+                    + "com.google.android.gm.exchange")
+            .split(";"));
+  }
 }