Place Duo calls with PreCall

This CL consolidates common duo calling operations into precall, including checking reachability, falling back to carrier video call, placing the call to duo with startActivityForResult.

TEST=TAP
Bug: 78783816
Test: TAP
PiperOrigin-RevId: 195742478
Change-Id: I9fea1e4999f604e1f3a153b28079cd0db77b7393
diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java
index b99cef1..c2c753e 100644
--- a/java/com/android/dialer/app/calllog/CallLogAdapter.java
+++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java
@@ -64,7 +64,6 @@
 import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
 import com.android.dialer.calldetails.CallDetailsEntries;
 import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
-import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction;
 import com.android.dialer.calllogutils.PhoneCallDetails;
 import com.android.dialer.common.Assert;
@@ -407,29 +406,8 @@
               }
             }
             expandViewHolderActions(viewHolder);
-
-            if (isDuoCallButtonVisible(viewHolder.videoCallButtonView)) {
-              CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
-            }
           }
         }
-
-        private boolean isDuoCallButtonVisible(View videoCallButtonView) {
-          if (videoCallButtonView == null) {
-            return false;
-          }
-          if (videoCallButtonView.getVisibility() != View.VISIBLE) {
-            return false;
-          }
-          IntentProvider intentProvider = (IntentProvider) videoCallButtonView.getTag();
-          if (intentProvider == null) {
-            return false;
-          }
-          return DuoComponent.get(activity)
-              .getDuo()
-              .getIntentType(intentProvider.getIntent(activity))
-              .isPresent();
-        }
       };
 
   @Nullable
diff --git a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
index b7781f3..9b7741d 100644
--- a/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
+++ b/java/com/android/dialer/app/calllog/CallLogListItemViewHolder.java
@@ -19,7 +19,6 @@
 import android.Manifest.permission;
 import android.annotation.SuppressLint;
 import android.app.Activity;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -51,7 +50,6 @@
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.widget.Toast;
 import com.android.contacts.common.dialog.CallSubjectDialog;
 import com.android.dialer.app.DialtactsActivity;
 import com.android.dialer.app.R;
@@ -96,7 +94,6 @@
 import com.android.dialer.util.CallUtil;
 import com.android.dialer.util.DialerUtils;
 import com.android.dialer.util.UriUtils;
-import com.google.common.base.Optional;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -550,7 +547,8 @@
       case CallbackAction.DUO:
         if (showDuoPrimaryButton()) {
           CallIntentBuilder.increaseLightbringerCallButtonAppearInCollapsedCallLogItemCount();
-          primaryActionButtonView.setTag(IntentProvider.getDuoVideoIntentProvider(number));
+          primaryActionButtonView.setTag(
+              IntentProvider.getDuoVideoIntentProvider(number, isNonContactEntry(info)));
         } else {
           primaryActionButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number));
         }
@@ -684,14 +682,17 @@
 
         boolean identifiedSpamCall = isSpamFeatureEnabled && isSpam;
         if (duo.isReachable(context, number)) {
-          videoCallButtonView.setTag(IntentProvider.getDuoVideoIntentProvider(number));
+          videoCallButtonView.setTag(
+              IntentProvider.getDuoVideoIntentProvider(number, isNonContactEntry(info)));
           videoCallButtonView.setVisibility(View.VISIBLE);
+          CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
         } else if (duo.isActivated(context) && !identifiedSpamCall) {
           if (ConfigProviderBindings.get(context)
               .getBoolean("enable_call_log_duo_invite_button", false)) {
             inviteVideoButtonView.setTag(IntentProvider.getDuoInviteIntentProvider(number));
             inviteVideoButtonView.setVisibility(View.VISIBLE);
             Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_INVITE_SHOWN);
+            CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
           }
         } else if (duo.isEnabled(context) && !identifiedSpamCall) {
           if (!duo.isInstalled(context)) {
@@ -701,6 +702,7 @@
               setUpVideoButtonView.setVisibility(View.VISIBLE);
               Logger.get(context)
                   .logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_INSTALL_SHOWN);
+              CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
             }
           } else {
             if (ConfigProviderBindings.get(context)
@@ -709,6 +711,7 @@
               setUpVideoButtonView.setVisibility(View.VISIBLE);
               Logger.get(context)
                   .logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_ACTIVATE_SHOWN);
+              CallIntentBuilder.increaseLightbringerCallButtonAppearInExpandedCallLogItemCount();
             }
           }
         }
@@ -1024,20 +1027,13 @@
     if (intentProvider == null) {
       return;
     }
-
+    intentProvider.logInteraction(context);
     final Intent intent = intentProvider.getIntent(context);
     // See IntentProvider.getCallDetailIntentProvider() for why this may be null.
     if (intent == null) {
       return;
     }
-
-    // We check to see if we are starting a Duo intent. The reason is Duo
-    // intents need to be started using startActivityForResult instead of the usual startActivity
-    Optional<Duo.IntentType> duoIntentType =
-        DuoComponent.get(context).getDuo().getIntentType(intent);
-    if (duoIntentType.isPresent()) {
-      startDuoActivity(intent, duoIntentType.get());
-    } else if (OldCallDetailsActivity.isLaunchIntent(intent)) {
+    if (OldCallDetailsActivity.isLaunchIntent(intent)) {
       PerformanceReport.recordClick(UiAction.Type.OPEN_CALL_DETAIL);
       ((Activity) context)
           .startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_CALL_DETAILS);
@@ -1046,9 +1042,6 @@
           && intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, -1)
               == VideoProfile.STATE_BIDIRECTIONAL) {
         Logger.get(context).logImpression(DialerImpression.Type.IMS_VIDEO_REQUESTED_FROM_CALL_LOG);
-      } else if (intent.filterEquals(
-          DuoComponent.get(context).getDuo().getInstallDuoIntent().orNull())) {
-        Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_INSTALL);
       }
 
       DialerUtils.startActivityWithErrorToast(context, intent);
@@ -1062,32 +1055,6 @@
     return false;
   }
 
-  private void startDuoActivity(Intent intent, Duo.IntentType intentType) {
-    switch (intentType) {
-      case CALL:
-        Logger.get(context)
-            .logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FROM_CALL_LOG);
-        if (isNonContactEntry(info)) {
-          Logger.get(context)
-              .logImpression(
-                  DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_VIDEO_REQUESTED_FROM_CALL_LOG);
-        }
-        break;
-      case INVITE:
-        Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_INVITE);
-        break;
-      case ACTIVATE:
-        Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_ACTIVATE);
-        break;
-    }
-    try {
-      Activity activity = (Activity) context;
-      activity.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
-    } catch (ActivityNotFoundException e) {
-      Toast.makeText(context, R.string.activity_not_available, Toast.LENGTH_SHORT).show();
-    }
-  }
-
   private DialerContact buildContact() {
     DialerContact.Builder contact = DialerContact.newBuilder();
     contact.setPhotoId(info.photoId);
diff --git a/java/com/android/dialer/app/calllog/IntentProvider.java b/java/com/android/dialer/app/calllog/IntentProvider.java
index 1bc726f..21f3418 100644
--- a/java/com/android/dialer/app/calllog/IntentProvider.java
+++ b/java/com/android/dialer/app/calllog/IntentProvider.java
@@ -32,6 +32,8 @@
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.dialercontact.DialerContact;
 import com.android.dialer.duo.DuoComponent;
+import com.android.dialer.logging.DialerImpression;
+import com.android.dialer.logging.Logger;
 import com.android.dialer.precall.PreCall;
 import com.android.dialer.util.IntentUtil;
 import java.util.ArrayList;
@@ -93,11 +95,26 @@
     };
   }
 
-  public static IntentProvider getDuoVideoIntentProvider(String number) {
+  public static IntentProvider getDuoVideoIntentProvider(String number, boolean isNonContact) {
     return new IntentProvider() {
       @Override
       public Intent getIntent(Context context) {
-        return DuoComponent.get(context).getDuo().getCallIntent(number).orNull();
+        return PreCall.getIntent(
+            context,
+            new CallIntentBuilder(number, CallInitiationType.Type.CALL_LOG)
+                .setIsDuoCall(true)
+                .setIsVideoCall(true));
+      }
+
+      @Override
+      public void logInteraction(Context context) {
+        Logger.get(context)
+            .logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FROM_CALL_LOG);
+        if (isNonContact) {
+          Logger.get(context)
+              .logImpression(
+                  DialerImpression.Type.LIGHTBRINGER_NON_CONTACT_VIDEO_REQUESTED_FROM_CALL_LOG);
+        }
       }
     };
   }
@@ -108,6 +125,11 @@
       public Intent getIntent(Context context) {
         return DuoComponent.get(context).getDuo().getInstallDuoIntent().orNull();
       }
+
+      @Override
+      public void logInteraction(Context context) {
+        Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_INSTALL);
+      }
     };
   }
 
@@ -117,6 +139,11 @@
       public Intent getIntent(Context context) {
         return DuoComponent.get(context).getDuo().getActivateIntent().orNull();
       }
+
+      @Override
+      public void logInteraction(Context context) {
+        Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_SET_UP_ACTIVATE);
+      }
     };
   }
 
@@ -126,6 +153,11 @@
       public Intent getIntent(Context context) {
         return DuoComponent.get(context).getDuo().getInviteIntent(number).orNull();
       }
+
+      @Override
+      public void logInteraction(Context context) {
+        Logger.get(context).logImpression(DialerImpression.Type.DUO_CALL_LOG_INVITE);
+      }
     };
   }
 
@@ -244,4 +276,6 @@
   }
 
   public abstract Intent getIntent(Context context);
+
+  public void logInteraction(Context context) {}
 }
diff --git a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
index 80a4191..86a2676 100644
--- a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
+++ b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java
@@ -19,7 +19,6 @@
 import android.Manifest.permission;
 import android.annotation.SuppressLint;
 import android.app.Activity;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
@@ -35,7 +34,6 @@
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.Toolbar;
 import android.view.View;
-import android.widget.Toast;
 import com.android.dialer.assisteddialing.ui.AssistedDialingSettingActivity;
 import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
 import com.android.dialer.callintent.CallInitiationType;
@@ -48,9 +46,6 @@
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
 import com.android.dialer.common.concurrent.UiListener;
 import com.android.dialer.common.database.Selection;
-import com.android.dialer.constants.ActivityRequestCodes;
-import com.android.dialer.duo.Duo;
-import com.android.dialer.duo.DuoComponent;
 import com.android.dialer.enrichedcall.EnrichedCallComponent;
 import com.android.dialer.enrichedcall.EnrichedCallManager;
 import com.android.dialer.enrichedcall.historyquery.proto.HistoryResult;
@@ -63,7 +58,6 @@
 import com.android.dialer.precall.PreCall;
 import com.android.dialer.rtt.RttTranscriptActivity;
 import com.android.dialer.rtt.RttTranscriptUtil;
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import com.google.i18n.phonenumbers.NumberParseException;
@@ -327,19 +321,11 @@
     public void placeDuoVideoCall(String phoneNumber) {
       Logger.get(getActivity())
           .logImpression(DialerImpression.Type.CALL_DETAILS_LIGHTBRINGER_CALL_BACK);
-      Duo duo = DuoComponent.get(getActivity()).getDuo();
-      Optional<Intent> intentOptional = duo.getCallIntent(phoneNumber);
-      if (!duo.isReachable(getActivity(), phoneNumber) || !intentOptional.isPresent()) {
-        placeImsVideoCall(phoneNumber);
-        return;
-      }
-
-      try {
-        getActivity()
-            .startActivityForResult(intentOptional.get(), ActivityRequestCodes.DIALTACTS_DUO);
-      } catch (ActivityNotFoundException e) {
-        Toast.makeText(getActivity(), R.string.activity_not_available, Toast.LENGTH_SHORT).show();
-      }
+      PreCall.start(
+          getActivity(),
+          new CallIntentBuilder(phoneNumber, CallInitiationType.Type.CALL_DETAILS)
+              .setIsDuoCall(true)
+              .setIsVideoCall(true));
     }
 
     @Override
diff --git a/java/com/android/dialer/callintent/CallIntentBuilder.java b/java/com/android/dialer/callintent/CallIntentBuilder.java
index 92efd39..613fdf6 100644
--- a/java/com/android/dialer/callintent/CallIntentBuilder.java
+++ b/java/com/android/dialer/callintent/CallIntentBuilder.java
@@ -43,6 +43,7 @@
   private final CallSpecificAppData callSpecificAppData;
   @Nullable private PhoneAccountHandle phoneAccountHandle;
   private boolean isVideoCall;
+  private boolean isDuoCall;
   private String callSubject;
   private boolean allowAssistedDial;
 
@@ -109,6 +110,7 @@
     callSpecificAppData = data;
     phoneAccountHandle = parcel.readParcelable(classLoader);
     isVideoCall = parcel.readInt() != 0;
+    isDuoCall = parcel.readInt() != 0;
     callSubject = parcel.readString();
     allowAssistedDial = parcel.readInt() != 0;
     inCallUiIntentExtras.putAll(parcel.readBundle(classLoader));
@@ -152,6 +154,15 @@
     return isVideoCall;
   }
 
+  public CallIntentBuilder setIsDuoCall(boolean isDuoCall) {
+    this.isDuoCall = isDuoCall;
+    return this;
+  }
+
+  public boolean isDuoCall() {
+    return isDuoCall;
+  }
+
   /** Default false. Should only be set to true if the number has a lookup URI. */
   public CallIntentBuilder setAllowAssistedDial(boolean allowAssistedDial) {
     this.allowAssistedDial = allowAssistedDial;
@@ -267,6 +278,7 @@
     dest.writeByteArray(callSpecificAppData.toByteArray());
     dest.writeParcelable(phoneAccountHandle, flags);
     dest.writeInt(isVideoCall ? 1 : 0);
+    dest.writeInt(isDuoCall ? 1 : 0);
     dest.writeString(callSubject);
     dest.writeInt(allowAssistedDial ? 1 : 0);
     dest.writeBundle(inCallUiIntentExtras);
diff --git a/java/com/android/dialer/calllogutils/CallLogRowActions.java b/java/com/android/dialer/calllogutils/CallLogRowActions.java
index d23a15f..65f3c5f 100644
--- a/java/com/android/dialer/calllogutils/CallLogRowActions.java
+++ b/java/com/android/dialer/calllogutils/CallLogRowActions.java
@@ -20,6 +20,7 @@
 import com.android.dialer.callintent.CallInitiationType;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.calllog.model.CoalescedRow;
+import com.android.dialer.duo.DuoComponent;
 import com.android.dialer.precall.PreCall;
 
 /** Actions which can be performed on a call log row. */
@@ -37,6 +38,10 @@
         activity,
         new CallIntentBuilder(
                 row.getNumber().getNormalizedNumber(), CallInitiationType.Type.CALL_LOG)
-            .setIsVideoCall((row.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO));
+            .setIsVideoCall((row.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO)
+            .setIsDuoCall(
+                DuoComponent.get(activity)
+                    .getDuo()
+                    .isDuoAccount(row.getPhoneAccountComponentName())));
   }
 }
diff --git a/java/com/android/dialer/duo/Duo.java b/java/com/android/dialer/duo/Duo.java
index 06a3db0..85fe9fb 100644
--- a/java/com/android/dialer/duo/Duo.java
+++ b/java/com/android/dialer/duo/Duo.java
@@ -27,6 +27,8 @@
 import android.telecom.PhoneAccountHandle;
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.util.List;
 
 /** Interface for Duo video call integration. */
@@ -61,7 +63,8 @@
 
   /** Starts a task to update the reachability of the parameter numbers asynchronously. */
   @MainThread
-  void updateReachability(@NonNull Context context, @NonNull List<String> numbers);
+  ListenableFuture<ImmutableMap<String, ReachabilityData>> updateReachability(
+      @NonNull Context context, @NonNull List<String> numbers);
 
   /**
    * Clears the current reachability data and starts a task to load the latest reachability data
@@ -95,19 +98,6 @@
    */
   Optional<Intent> getInviteIntent(String number);
 
-  /** Return value of {@link #getIntentType(Intent)} */
-  enum IntentType {
-    /** The intent is returned by {@link #getCallIntent(String)} */
-    CALL,
-    /** The intent is returned by {@link #getActivateIntent()} */
-    ACTIVATE,
-    /** The intent is returned by {@link #getInviteIntent(String)} */
-    INVITE
-  }
-
-  /** Classifies a Duo intent. Absent if the intent is not a Duo intent. */
-  Optional<IntentType> getIntentType(Intent intent);
-
   Optional<Intent> getInstallDuoIntent();
 
   /** Requests upgrading the parameter ongoing call to a Duo video call. */
diff --git a/java/com/android/dialer/duo/PlaceDuoCallNotifier.java b/java/com/android/dialer/duo/PlaceDuoCallNotifier.java
deleted file mode 100644
index 8fde981..0000000
--- a/java/com/android/dialer/duo/PlaceDuoCallNotifier.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 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.dialer.duo;
-
-import android.content.Context;
-import android.content.Intent;
-import android.support.v4.content.LocalBroadcastManager;
-import com.android.dialer.common.LogUtil;
-
-/** Notifies that a Duo video call should be started. */
-public final class PlaceDuoCallNotifier {
-
-  private PlaceDuoCallNotifier() {}
-
-  /**
-   * Broadcasts an intent notifying that a Duo call should be started.
-   *
-   * <p>See {@link PlaceDuoCallReceiver} for how the intent is handled.
-   *
-   * @param phoneNumber The number to start a Duo call. It can be of any format.
-   */
-  public static void notify(Context context, String phoneNumber) {
-    LogUtil.enterBlock("PlaceDuoCallNotifier.notify");
-
-    Intent intent = new Intent();
-    intent.setAction(PlaceDuoCallReceiver.ACTION_START_DUO_CALL);
-    intent.putExtra(PlaceDuoCallReceiver.EXTRA_PHONE_NUMBER, phoneNumber);
-
-    LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
-  }
-}
diff --git a/java/com/android/dialer/duo/PlaceDuoCallReceiver.java b/java/com/android/dialer/duo/PlaceDuoCallReceiver.java
deleted file mode 100644
index f504aef..0000000
--- a/java/com/android/dialer/duo/PlaceDuoCallReceiver.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2018 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.dialer.duo;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import com.android.dialer.common.Assert;
-import com.android.dialer.common.LogUtil;
-import com.android.dialer.constants.ActivityRequestCodes;
-
-/** A {@link BroadcastReceiver} that starts a Duo video call. */
-public final class PlaceDuoCallReceiver extends BroadcastReceiver {
-
-  static final String ACTION_START_DUO_CALL = "start_duo_call";
-  static final String EXTRA_PHONE_NUMBER = "phone_number";
-
-  /**
-   * {@link Activity} needed to launch Duo.
-   *
-   * <p>A Duo call can only be placed via {@link Activity#startActivityForResult(Intent, int)}.
-   */
-  private final Activity activity;
-
-  /** Returns an {@link IntentFilter} containing all actions accepted by this broadcast receiver. */
-  public static IntentFilter getIntentFilter() {
-    IntentFilter intentFilter = new IntentFilter();
-    intentFilter.addAction(ACTION_START_DUO_CALL);
-    return intentFilter;
-  }
-
-  public PlaceDuoCallReceiver(Activity activity) {
-    this.activity = activity;
-  }
-
-  @Override
-  public void onReceive(Context context, Intent intent) {
-    LogUtil.enterBlock("PlaceDuoCallReceiver.onReceive");
-
-    String action = intent.getAction();
-
-    switch (Assert.isNotNull(action)) {
-      case ACTION_START_DUO_CALL:
-        startDuoCall(context, intent);
-        break;
-      default:
-        throw new IllegalStateException("Unsupported action: " + action);
-    }
-  }
-
-  private void startDuoCall(Context context, Intent intent) {
-    LogUtil.enterBlock("PlaceDuoCallReceiver.startDuoCall");
-
-    Assert.checkArgument(intent.hasExtra(EXTRA_PHONE_NUMBER));
-    String phoneNumber = intent.getStringExtra(EXTRA_PHONE_NUMBER);
-
-    Duo duo = DuoComponent.get(context).getDuo();
-    activity.startActivityForResult(
-        duo.getCallIntent(phoneNumber).orNull(), ActivityRequestCodes.DIALTACTS_DUO);
-  }
-}
diff --git a/java/com/android/dialer/duo/stub/DuoStub.java b/java/com/android/dialer/duo/stub/DuoStub.java
index b086714..2131d16 100644
--- a/java/com/android/dialer/duo/stub/DuoStub.java
+++ b/java/com/android/dialer/duo/stub/DuoStub.java
@@ -29,6 +29,9 @@
 import com.android.dialer.duo.Duo;
 import com.android.dialer.duo.DuoListener;
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.util.List;
 import javax.inject.Inject;
 
@@ -72,10 +75,12 @@
   }
 
   @Override
-  public void updateReachability(@NonNull Context context, @NonNull List<String> numbers) {
+  public ListenableFuture<ImmutableMap<String, ReachabilityData>> updateReachability(
+      @NonNull Context context, @NonNull List<String> numbers) {
     Assert.isMainThread();
     Assert.isNotNull(context);
     Assert.isNotNull(numbers);
+    return Futures.immediateFuture(ImmutableMap.of());
   }
 
   @Override
@@ -115,11 +120,6 @@
   }
 
   @Override
-  public Optional<IntentType> getIntentType(Intent intent) {
-    return Optional.absent();
-  }
-
-  @Override
   public Optional<Intent> getInstallDuoIntent() {
     return null;
   }
diff --git a/java/com/android/dialer/historyitemactions/DuoCallModule.java b/java/com/android/dialer/historyitemactions/DuoCallModule.java
deleted file mode 100644
index e6f31e2..0000000
--- a/java/com/android/dialer/historyitemactions/DuoCallModule.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 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.dialer.historyitemactions;
-
-import android.Manifest.permission;
-import android.content.Context;
-import android.support.annotation.RequiresPermission;
-import com.android.dialer.duo.PlaceDuoCallNotifier;
-
-/** {@link HistoryItemActionModule} for making a Duo call. */
-public class DuoCallModule implements HistoryItemActionModule {
-
-  private final Context context;
-  private final String phoneNumber;
-
-  /**
-   * Creates a module for making a Duo call.
-   *
-   * @param phoneNumber The number to start a Duo call. It can be of any format.
-   */
-  public DuoCallModule(Context context, String phoneNumber) {
-    this.context = context;
-    this.phoneNumber = phoneNumber;
-  }
-
-  @Override
-  public int getStringId() {
-    return R.string.video_call;
-  }
-
-  @Override
-  public int getDrawableId() {
-    return R.drawable.quantum_ic_videocam_vd_white_24;
-  }
-
-  @Override
-  @RequiresPermission(permission.READ_PHONE_STATE)
-  public boolean onClick() {
-    PlaceDuoCallNotifier.notify(context, phoneNumber);
-    return true; // Close the bottom sheet.
-  }
-}
diff --git a/java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java b/java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java
index 5da56a1..e1c6c96 100644
--- a/java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java
+++ b/java/com/android/dialer/historyitemactions/HistoryItemActionModulesBuilder.java
@@ -149,18 +149,14 @@
     // Do not set PhoneAccountHandle so that regular PreCall logic will be used. The account used to
     // place or receive the call should be ignored for carrier video calls.
     // TODO(a bug): figure out the correct video call behavior
-    HistoryItemActionModule carrierVideoCallModule =
-        IntentModule.newCallModule(
-            context,
-            new CallIntentBuilder(moduleInfo.getNormalizedNumber(), getCallInitiationType())
-                .setAllowAssistedDial(moduleInfo.getCanSupportAssistedDialing())
-                .setIsVideoCall(true));
-    HistoryItemActionModule duoVideoCallModule =
-        new DuoCallModule(context, moduleInfo.getNormalizedNumber());
+    CallIntentBuilder callIntentBuilder =
+        new CallIntentBuilder(moduleInfo.getNormalizedNumber(), getCallInitiationType())
+            .setAllowAssistedDial(moduleInfo.getCanSupportAssistedDialing())
+            .setIsVideoCall(true);
 
     // If the module info is for a video call, add an appropriate video call module.
     if ((moduleInfo.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
-      modules.add(isDuoCall() && canPlaceDuoCall() ? duoVideoCallModule : carrierVideoCallModule);
+      modules.add(IntentModule.newCallModule(context, callIntentBuilder.setIsDuoCall(isDuoCall())));
       return this;
     }
 
@@ -169,9 +165,9 @@
     //
     // The carrier video call module takes precedence over the Duo module.
     if (canPlaceCarrierVideoCall()) {
-      modules.add(carrierVideoCallModule);
+      modules.add(IntentModule.newCallModule(context, callIntentBuilder));
     } else if (canPlaceDuoCall()) {
-      modules.add(duoVideoCallModule);
+      modules.add(IntentModule.newCallModule(context, callIntentBuilder.setIsDuoCall(true)));
     }
     return this;
   }
diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java
index 3f660f5..2046b04 100644
--- a/java/com/android/dialer/main/impl/MainActivity.java
+++ b/java/com/android/dialer/main/impl/MainActivity.java
@@ -24,7 +24,6 @@
 import com.android.dialer.calllog.config.CallLogConfigComponent;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
-import com.android.dialer.duo.PlaceDuoCallReceiver;
 import com.android.dialer.interactions.PhoneNumberInteraction.DisambigDialogDismissedListener;
 import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode;
 import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorListener;
@@ -48,9 +47,6 @@
    */
   private ShowBlockReportSpamDialogReceiver showBlockReportSpamDialogReceiver;
 
-  /** {@link android.content.BroadcastReceiver} that starts a Duo call. */
-  private PlaceDuoCallReceiver placeDuoCallReceiver;
-
   public static Intent getShowCallLogIntent(Context context) {
     return getShowTabIntent(context, TabIndex.CALL_LOG);
   }
@@ -83,7 +79,6 @@
     activePeer.onActivityCreate(savedInstanceState);
 
     showBlockReportSpamDialogReceiver = new ShowBlockReportSpamDialogReceiver(getFragmentManager());
-    placeDuoCallReceiver = new PlaceDuoCallReceiver(/* activity = */ this);
   }
 
   protected MainActivityPeer getNewPeer() {
@@ -109,8 +104,6 @@
     LocalBroadcastManager.getInstance(this)
         .registerReceiver(
             showBlockReportSpamDialogReceiver, ShowBlockReportSpamDialogReceiver.getIntentFilter());
-    LocalBroadcastManager.getInstance(this)
-        .registerReceiver(placeDuoCallReceiver, PlaceDuoCallReceiver.getIntentFilter());
   }
 
   @Override
@@ -125,7 +118,6 @@
     activePeer.onActivityPause();
 
     LocalBroadcastManager.getInstance(this).unregisterReceiver(showBlockReportSpamDialogReceiver);
-    LocalBroadcastManager.getInstance(this).unregisterReceiver(placeDuoCallReceiver);
   }
 
   @Override
diff --git a/java/com/android/dialer/precall/PreCallCoordinator.java b/java/com/android/dialer/precall/PreCallCoordinator.java
index 9c24e0d..4d4859a 100644
--- a/java/com/android/dialer/precall/PreCallCoordinator.java
+++ b/java/com/android/dialer/precall/PreCallCoordinator.java
@@ -68,8 +68,8 @@
   @NonNull
   PendingAction startPendingAction();
 
-  <Output> void listen(
-      ListenableFuture<Output> future,
-      Consumer<Output> successListener,
+  <OutputT> void listen(
+      ListenableFuture<OutputT> future,
+      Consumer<OutputT> successListener,
       Consumer<Throwable> failureListener);
 }
diff --git a/java/com/android/dialer/precall/impl/DuoAction.java b/java/com/android/dialer/precall/impl/DuoAction.java
new file mode 100644
index 0000000..c05fbe1
--- /dev/null
+++ b/java/com/android/dialer/precall/impl/DuoAction.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 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.dialer.precall.impl;
+
+import android.content.Context;
+import android.content.Intent;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.Annotations.Ui;
+import com.android.dialer.duo.Duo.ReachabilityData;
+import com.android.dialer.duo.DuoComponent;
+import com.android.dialer.precall.PreCallAction;
+import com.android.dialer.precall.PreCallCoordinator;
+import com.android.dialer.precall.PreCallCoordinator.PendingAction;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import javax.inject.Inject;
+
+/**
+ * Checks if a duo call is actually callable, and request an activity for {@link
+ * android.app.Activity#startActivityForResult(Intent, int)}
+ */
+public class DuoAction implements PreCallAction {
+
+  private final ListeningExecutorService uiExecutor;
+
+  @Inject
+  DuoAction(@Ui ListeningExecutorService uiExecutor) {
+    this.uiExecutor = uiExecutor;
+  }
+
+  @Override
+  public boolean requiresUi(Context context, CallIntentBuilder builder) {
+    // Duo call must be started with startActivityForResult() which needs a activity.
+    return builder.isDuoCall();
+  }
+
+  @Override
+  public void runWithoutUi(Context context, CallIntentBuilder builder) {}
+
+  @Override
+  public void runWithUi(PreCallCoordinator coordinator) {
+    if (!requiresUi(coordinator.getActivity(), coordinator.getBuilder())) {
+      return;
+    }
+    String number = coordinator.getBuilder().getUri().getSchemeSpecificPart();
+    ListenableFuture<ImmutableMap<String, ReachabilityData>> reachabilities =
+        DuoComponent.get(coordinator.getActivity())
+            .getDuo()
+            .updateReachability(coordinator.getActivity(), ImmutableList.of(number));
+    PendingAction pendingAction = coordinator.startPendingAction();
+
+    Futures.addCallback(
+        reachabilities,
+        new FutureCallback<ImmutableMap<String, ReachabilityData>>() {
+          @Override
+          public void onSuccess(ImmutableMap<String, ReachabilityData> result) {
+            if (!result.containsKey(number) || !result.get(number).videoCallable()) {
+              LogUtil.w(
+                  "DuoAction.runWithUi",
+                  number + " number no longer duo reachable, falling back to carrier video call");
+              coordinator.getBuilder().setIsDuoCall(false);
+            }
+            pendingAction.finish();
+          }
+
+          @Override
+          public void onFailure(Throwable throwable) {
+            LogUtil.e("DuoAction.runWithUi", "reachability query failed", throwable);
+            coordinator.getBuilder().setIsDuoCall(false);
+            pendingAction.finish();
+          }
+        },
+        uiExecutor);
+  }
+
+  @Override
+  public void onDiscard() {}
+}
diff --git a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
index f2ff0e3..240549c 100644
--- a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
+++ b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
@@ -26,6 +26,7 @@
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
 import com.android.dialer.common.concurrent.UiListener;
+import com.android.dialer.duo.DuoComponent;
 import com.android.dialer.function.Consumer;
 import com.android.dialer.logging.DialerImpression.Type;
 import com.android.dialer.logging.Logger;
@@ -33,6 +34,7 @@
 import com.android.dialer.precall.PreCallComponent;
 import com.android.dialer.precall.PreCallCoordinator;
 import com.android.dialer.telecom.TelecomUtil;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -101,7 +103,7 @@
     LogUtil.enterBlock("PreCallCoordinatorImpl.runNextAction");
     Assert.checkArgument(currentAction == null);
     if (currentActionIndex >= actions.size()) {
-      TelecomUtil.placeCall(activity, builder.build());
+      placeCall();
       activity.finish();
       return;
     }
@@ -177,4 +179,20 @@
         output -> successListener.accept((OutputT) output),
         failureListener::accept);
   }
+
+  private void placeCall() {
+    if (builder.isDuoCall()) {
+      Optional<Intent> intent =
+          DuoComponent.get(activity)
+              .getDuo()
+              .getCallIntent(builder.getUri().getSchemeSpecificPart());
+      if (intent.isPresent()) {
+        activity.startActivityForResult(intent.get(), 0);
+        return;
+      } else {
+        LogUtil.e("PreCallCoordinatorImpl.placeCall", "duo.getCallIntent() returned absent");
+      }
+    }
+    TelecomUtil.placeCall(activity, builder.build());
+  }
 }
diff --git a/java/com/android/dialer/precall/impl/PreCallModule.java b/java/com/android/dialer/precall/impl/PreCallModule.java
index 9820e2b..455453e 100644
--- a/java/com/android/dialer/precall/impl/PreCallModule.java
+++ b/java/com/android/dialer/precall/impl/PreCallModule.java
@@ -37,12 +37,13 @@
   @Provides
   @Singleton
   public static ImmutableList<PreCallAction> provideActions(
-      CallingAccountSelector callingAccountSelector) {
+      DuoAction duoAction, CallingAccountSelector callingAccountSelector) {
     return ImmutableList.of(
         new PermissionCheckAction(),
         new MalformedNumberRectifier(
             ImmutableList.of(new UkRegionPrefixInInternationalFormatHandler())),
         callingAccountSelector,
+        duoAction,
         new AssistedDialAction());
   }
 }
diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
index f1eed91..abb3aec 100644
--- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
+++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
@@ -51,9 +51,7 @@
 import com.android.dialer.common.FragmentUtils;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.ThreadUtil;
-import com.android.dialer.constants.ActivityRequestCodes;
 import com.android.dialer.dialercontact.DialerContact;
-import com.android.dialer.duo.DuoComponent;
 import com.android.dialer.enrichedcall.EnrichedCallComponent;
 import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener;
 import com.android.dialer.logging.DialerImpression;
@@ -549,8 +547,11 @@
   public void placeDuoCall(String phoneNumber) {
     Logger.get(getContext())
         .logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FROM_SEARCH);
-    Intent intent = DuoComponent.get(getContext()).getDuo().getCallIntent(phoneNumber).orNull();
-    getActivity().startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
+    PreCall.start(
+        getContext(),
+        new CallIntentBuilder(phoneNumber, CallInitiationType.Type.REGULAR_SEARCH)
+            .setIsVideoCall(true)
+            .setIsDuoCall(true));
     FragmentUtils.getParentUnsafe(this, SearchFragmentListener.class).onCallPlacedFromSearch();
   }
 
diff --git a/java/com/android/dialer/speeddial/DisambigDialog.java b/java/com/android/dialer/speeddial/DisambigDialog.java
index 98fc992..2dd43fa 100644
--- a/java/com/android/dialer/speeddial/DisambigDialog.java
+++ b/java/com/android/dialer/speeddial/DisambigDialog.java
@@ -18,7 +18,6 @@
 
 import android.app.Dialog;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
@@ -40,8 +39,6 @@
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.DefaultFutureCallback;
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.constants.ActivityRequestCodes;
-import com.android.dialer.duo.DuoComponent;
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
 import com.android.dialer.precall.PreCall;
@@ -184,16 +181,13 @@
       Logger.get(getContext())
           .logImpression(
               DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT_DISAMBIG);
-      Intent intent =
-          DuoComponent.get(getContext()).getDuo().getCallIntent(channel.number()).orNull();
-      getActivity().startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
-      return;
     }
 
     PreCall.start(
         getContext(),
         new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
-            .setIsVideoCall(true));
+            .setIsVideoCall(true)
+            .setIsDuoCall(channel.technology() == Channel.DUO));
     dismiss();
   }
 
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index aa306d2..fac9a13 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -41,7 +41,6 @@
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
 import com.android.dialer.common.concurrent.SupportUiListener;
 import com.android.dialer.constants.ActivityRequestCodes;
-import com.android.dialer.duo.DuoComponent;
 import com.android.dialer.historyitemactions.DividerModule;
 import com.android.dialer.historyitemactions.HistoryItemActionBottomSheet;
 import com.android.dialer.historyitemactions.HistoryItemActionModule;
@@ -243,16 +242,13 @@
       if (channel.technology() == Channel.DUO) {
         Logger.get(activity)
             .logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT);
-        Intent intent =
-            DuoComponent.get(activity).getDuo().getCallIntent(channel.number()).orNull();
-        activity.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
-        return;
       }
 
       PreCall.start(
           activity,
           new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
-              .setIsVideoCall(channel.isVideoTechnology()));
+              .setIsVideoCall(channel.isVideoTechnology())
+              .setIsDuoCall(channel.technology() == Channel.DUO));
     }
 
     @Override
@@ -341,15 +337,12 @@
         Logger.get(getContext())
             .logImpression(
                 DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_SUGGESTED_CONTACT);
-        Intent intent =
-            DuoComponent.get(getContext()).getDuo().getCallIntent(channel.number()).orNull();
-        getActivity().startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
-        return;
       }
       PreCall.start(
           getContext(),
           new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
-              .setIsVideoCall(channel.isVideoTechnology()));
+              .setIsVideoCall(channel.isVideoTechnology())
+              .setIsDuoCall(channel.technology() == Channel.DUO));
     }
 
     private final class StarContactModule implements HistoryItemActionModule {
@@ -426,15 +419,12 @@
       if (channel.technology() == Channel.DUO) {
         Logger.get(activity)
             .logImpression(DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT);
-        Intent intent =
-            DuoComponent.get(activity).getDuo().getCallIntent(channel.number()).orNull();
-        activity.startActivityForResult(intent, ActivityRequestCodes.DIALTACTS_DUO);
-        return;
       }
       PreCall.start(
           activity,
           new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
-              .setIsVideoCall(channel.isVideoTechnology()));
+              .setIsVideoCall(channel.isVideoTechnology())
+              .setIsDuoCall(channel.technology() == Channel.DUO));
     }
 
     @Override
diff --git a/java/com/android/incallui/videotech/duo/DuoVideoTech.java b/java/com/android/incallui/videotech/duo/DuoVideoTech.java
index fdaed07..ac74e54 100644
--- a/java/com/android/incallui/videotech/duo/DuoVideoTech.java
+++ b/java/com/android/incallui/videotech/duo/DuoVideoTech.java
@@ -23,6 +23,7 @@
 import android.telecom.PhoneAccountHandle;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
 import com.android.dialer.configprovider.ConfigProviderBindings;
 import com.android.dialer.duo.Duo;
 import com.android.dialer.duo.DuoListener;
@@ -33,6 +34,8 @@
 import com.android.incallui.videotech.utils.SessionModificationState;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
 
 public class DuoVideoTech implements VideoTech, DuoListener {
   private final Duo duo;
@@ -77,7 +80,10 @@
     if (!isRemoteUpgradeAvailabilityQueried) {
       LogUtil.v("DuoVideoTech.isAvailable", "reachability unknown, starting remote query");
       isRemoteUpgradeAvailabilityQueried = true;
-      duo.updateReachability(context, ImmutableList.of(callingNumber));
+      Futures.addCallback(
+          duo.updateReachability(context, ImmutableList.of(callingNumber)),
+          new DefaultFutureCallback<>(),
+          MoreExecutors.directExecutor());
     }
 
     return false;