Read carrier and device config for enabling Duo

Test: Manually verified:
  * Duo fallback is enabled for Pixel running OPD1.170608.001
  * Duo fallback is disabled for Nexus 5X running OPP3.170518.004

Bug: 37773868
Change-Id: I8b165566bf9cfe79ae8039cd8d2ec6eff9148376
diff --git a/src/com/android/contacts/CallUtil.java b/src/com/android/contacts/CallUtil.java
index 7172766..bba1faa 100644
--- a/src/com/android/contacts/CallUtil.java
+++ b/src/com/android/contacts/CallUtil.java
@@ -19,18 +19,23 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.contacts.compat.CompatUtils;
 import com.android.contacts.compat.PhoneAccountSdkCompat;
 import com.android.contacts.util.PermissionsUtil;
 import com.android.contacts.util.PhoneNumberHelper;
 import com.android.contactsbind.FeedbackHelper;
+import com.android.contactsbind.experiments.Flags;
 import com.android.phone.common.PhoneConstants;
 
 import java.util.List;
@@ -61,6 +66,14 @@
      */
     public static final int VIDEO_CALLING_PRESENCE = 2;
 
+    /** {@link PhoneAccount#EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK} */
+    private static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK =
+            "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
+
+    /** {@link CarrierConfigManager#CONFIG_ALLOW_VIDEO_CALLING_FALLBACK} */
+    private static final String CONFIG_ALLOW_VIDEO_CALLING_FALLBACK =
+            "allow_video_calling_fallback_bool";
+
     /**
      * Return an Intent for making a phone call. Scheme (e.g. tel, sip) will be determined
      * automatically.
@@ -210,4 +223,66 @@
         }
 
     }
+
+    /**
+     * Determines if we're able to use Tachyon as a fallback for video calling.
+     *
+     * @param context The context.
+     * @return {@code true} if there exists a call capable phone account which supports using a
+     * fallback for video calling, the carrier configuration supports a fallback, and the
+     * experiment for using a fallback is enabled. Otherwise {@code false} is returned.
+     */
+    public static boolean isTachyonEnabled(Context context) {
+        // Need to be able to read phone state, and be on at least N to check PhoneAccount extras.
+        if (!PermissionsUtil.hasPermission(context, android.Manifest.permission.READ_PHONE_STATE)
+                || !CompatUtils.isNCompatible()) {
+            return false;
+        }
+        TelecomManager telecommMgr = (TelecomManager)
+                context.getSystemService(Context.TELECOM_SERVICE);
+        if (telecommMgr == null) {
+            return false;
+        }
+        try {
+            List<PhoneAccountHandle> accountHandles = telecommMgr.getCallCapablePhoneAccounts();
+            for (PhoneAccountHandle accountHandle : accountHandles) {
+                PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle);
+                if (account == null) {
+                    continue;
+                }
+                // Check availability for the device config.
+                final Bundle accountExtras = account.getExtras();
+                final boolean deviceEnabled = accountExtras != null && accountExtras.getBoolean(
+                        EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK);
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Device video fallback config: " + deviceEnabled);
+                }
+
+                // Check availability from carrier config.
+                final PersistableBundle carrierConfig = context.getSystemService(
+                        CarrierConfigManager.class).getConfig();
+                final boolean carrierEnabled =
+                        carrierConfig != null && carrierConfig.getBoolean(
+                                CONFIG_ALLOW_VIDEO_CALLING_FALLBACK);
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Carrier video fallback config: " + carrierEnabled);
+                }
+
+                // Check experiment value.
+                final boolean experimentEnabled = Flags.getInstance().getBoolean(
+                        Experiments.QUICK_CONTACT_VIDEO_CALL);
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Experiment video fallback config: " + experimentEnabled);
+                }
+
+                // All three checks above must be true to enable Tachyon calling.
+                return deviceEnabled && carrierEnabled && experimentEnabled;
+            }
+            return false;
+        } catch (SecurityException e) {
+            FeedbackHelper.sendFeedback(context, TAG,
+                    "Security exception when getting call capable phone accounts", e);
+            return false;
+        }
+    }
 }
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 3c088b4..3a69f23 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -105,7 +105,6 @@
 import com.android.contacts.ContactsActivity;
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.DynamicShortcuts;
-import com.android.contacts.Experiments;
 import com.android.contacts.NfcHandler;
 import com.android.contacts.R;
 import com.android.contacts.ShortcutIntentBuilder;
@@ -176,7 +175,6 @@
 import com.android.contacts.widget.MultiShrinkScroller.MultiShrinkScrollerListener;
 import com.android.contacts.widget.QuickContactImageView;
 import com.android.contactsbind.HelpUtils;
-import com.android.contactsbind.experiments.Flags;
 
 import com.google.common.collect.Lists;
 
@@ -1457,6 +1455,7 @@
         Trace.beginSection("Build data items map");
 
         final Map<String, List<DataItem>> dataItemsMap = new HashMap<>();
+        final boolean tachyonEnabled = CallUtil.isTachyonEnabled(this);
 
         for (RawContact rawContact : data.getRawContacts()) {
             for (DataItem dataItem : rawContact.getDataItems()) {
@@ -1466,6 +1465,7 @@
                 if (mimeType == null) continue;
 
                 if (!MIMETYPE_TACHYON.equals(mimeType)) {
+                    // Only validate non-Tachyon mimetypes.
                     final AccountType accountType = rawContact.getAccountType(this);
                     final DataKind dataKind = AccountTypeManager.getInstance(this)
                             .getKindOrFallback(accountType, mimeType);
@@ -1477,6 +1477,9 @@
                             dataKind));
 
                     if (isMimeExcluded(mimeType) || !hasData) continue;
+                } else if (!tachyonEnabled) {
+                    // If tachyon isn't enabled, skip its mimetypes.
+                    continue;
                 }
 
                 List<DataItem> dataItemListByType = dataItemsMap.get(mimeType);
@@ -1820,7 +1823,7 @@
                     thirdIntent.putExtra(EXTRA_ACTION_TYPE, ActionType.VIDEOCALL);
                     thirdContentDescription =
                             res.getString(R.string.description_video_call);
-                } else if (Flags.getInstance().getBoolean(Experiments.QUICK_CONTACT_VIDEO_CALL)
+                } else if (CallUtil.isTachyonEnabled(context)
                         && ((PhoneDataItem) dataItem).isTachyonReachable()) {
                     thirdIcon = res.getDrawable(R.drawable.quantum_ic_videocam_vd_theme_24);
                     thirdAction = Entry.ACTION_INTENT;
@@ -1915,8 +1918,8 @@
                     aboutCardName.value = res.getString(R.string.about_card_title);
                 }
             }
-        } else if (Flags.getInstance().getBoolean(Experiments.QUICK_CONTACT_VIDEO_CALL)
-                && MIMETYPE_TACHYON.equals(dataItem.getMimeType())) {
+        } else if (CallUtil.isTachyonEnabled(context) && MIMETYPE_TACHYON.equals(
+                dataItem.getMimeType())) {
             // Skip these actions. They will be placed by the phone number.
             return null;
         } else {