Merge changes I28244a72,Ic984f958,I5dc2bed7,I1be427b3,I0220a342, ...

* changes:
  Remove reference to RTT system setting.
  Fix the button style of RTT request dialog.
  Don't deadlock in DialerDatabaseHelper.
  Fix NPE for details number
  Per linguists' request, increase CHAR_LIMIT of "Carrier video" from 30 to 31.
  Fix permission
  Handle missed calls for new call log in old peer.
  Separate calls with the video feature from others when coalescing rows in the new call log.
  Support placing Duo calls in the new UI's bottom sheet.
  Turn off component generating step of RootComponentGenerato and @DialerCompoennt. Delete related tests.
  Implement dialog for responding RTT request.
diff --git a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java
index 680424a..bca4265 100644
--- a/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java
+++ b/java/com/android/dialer/app/calllog/PhoneCallDetailsHelper.java
@@ -279,7 +279,7 @@
       return;
     }
 
-    if (PhoneNumberUtils.isEmergencyNumber(details.number.toString())) {
+    if (PhoneNumberUtils.isEmergencyNumber(details.displayNumber)) {
       views.nameView.setText(R.string.emergency_number);
       views.nameView.setTextDirection(View.TEXT_DIRECTION_INHERIT);
       return;
diff --git a/java/com/android/dialer/calllog/database/Coalescer.java b/java/com/android/dialer/calllog/database/Coalescer.java
index ed09eea..6b1a9e1 100644
--- a/java/com/android/dialer/calllog/database/Coalescer.java
+++ b/java/com/android/dialer/calllog/database/Coalescer.java
@@ -18,6 +18,7 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.MatrixCursor;
+import android.provider.CallLog.Calls;
 import android.support.annotation.NonNull;
 import android.support.annotation.WorkerThread;
 import android.telecom.PhoneAccountHandle;
@@ -151,7 +152,6 @@
         TelecomUtil.composePhoneAccountHandle(
             row2.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME),
             row2.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_ID));
-
     if (!Objects.equals(phoneAccount1, phoneAccount2)) {
       return false;
     }
@@ -161,7 +161,7 @@
       return false;
     }
 
-    if (!meetsAssistedDialingCriteria(row1, row2)) {
+    if (!meetsCallFeatureCriteria(row1, row2)) {
       return false;
     }
 
@@ -185,20 +185,25 @@
   }
 
   /**
-   * Returns a boolean indicating whether or not FEATURES_ASSISTED_DIALING is mutually exclusive
-   * between two rows.
+   * Returns true if column {@link AnnotatedCallLog#FEATURES} of the two given rows indicate that
+   * they can be coalesced.
    */
-  private static boolean meetsAssistedDialingCriteria(ContentValues row1, ContentValues row2) {
-    int row1Assisted =
-        row1.getAsInteger(AnnotatedCallLog.FEATURES)
-            & TelephonyManagerCompat.FEATURES_ASSISTED_DIALING;
-    int row2Assisted =
-        row2.getAsInteger(AnnotatedCallLog.FEATURES)
-            & TelephonyManagerCompat.FEATURES_ASSISTED_DIALING;
+  private static boolean meetsCallFeatureCriteria(ContentValues row1, ContentValues row2) {
+    int row1Features = row1.getAsInteger(AnnotatedCallLog.FEATURES);
+    int row2Features = row2.getAsInteger(AnnotatedCallLog.FEATURES);
 
-    // FEATURES_ASSISTED_DIALING should not be combined with calls that are
-    // !FEATURES_ASSISTED_DIALING
-    return row1Assisted == row2Assisted;
+    // A row with FEATURES_ASSISTED_DIALING should not be combined with one without it.
+    if ((row1Features & TelephonyManagerCompat.FEATURES_ASSISTED_DIALING)
+        != (row2Features & TelephonyManagerCompat.FEATURES_ASSISTED_DIALING)) {
+      return false;
+    }
+
+    // A video call should not be combined with one that is not a video call.
+    if ((row1Features & Calls.FEATURES_VIDEO) != (row2Features & Calls.FEATURES_VIDEO)) {
+      return false;
+    }
+
+    return true;
   }
 
   /**
diff --git a/java/com/android/dialer/calllog/ui/menu/Modules.java b/java/com/android/dialer/calllog/ui/menu/Modules.java
index aeb69a7..e316f66 100644
--- a/java/com/android/dialer/calllog/ui/menu/Modules.java
+++ b/java/com/android/dialer/calllog/ui/menu/Modules.java
@@ -27,8 +27,10 @@
 import com.android.dialer.calllog.model.CoalescedRow;
 import com.android.dialer.calllogutils.CallLogEntryText;
 import com.android.dialer.calllogutils.NumberAttributesConverter;
+import com.android.dialer.duo.DuoConstants;
 import com.android.dialer.glidephotomanager.PhotoInfo;
 import com.android.dialer.historyitemactions.DividerModule;
+import com.android.dialer.historyitemactions.DuoCallModule;
 import com.android.dialer.historyitemactions.HistoryItemActionModule;
 import com.android.dialer.historyitemactions.IntentModule;
 import com.android.dialer.historyitemactions.SharedModules;
@@ -134,9 +136,15 @@
     // Add a video item if (1) the call log entry is for a video call, and (2) the call is not spam.
     if ((row.getFeatures() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO
         && !row.getNumberAttributes().getIsSpam()) {
+      boolean isDuoCall =
+          DuoConstants.PHONE_ACCOUNT_COMPONENT_NAME
+              .flattenToString()
+              .equals(row.getPhoneAccountComponentName());
       modules.add(
-          IntentModule.newVideoCallModule(
-              context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
+          isDuoCall
+              ? new DuoCallModule(context, normalizedNumber, CallInitiationType.Type.CALL_LOG)
+              : IntentModule.newCarrierVideoCallModule(
+                  context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
     }
 
     // TODO(zachh): Also show video option if the call log entry is for an audio call but video
diff --git a/java/com/android/dialer/calllogutils/res/values/strings.xml b/java/com/android/dialer/calllogutils/res/values/strings.xml
index bc19ce2..1c1f314 100644
--- a/java/com/android/dialer/calllogutils/res/values/strings.xml
+++ b/java/com/android/dialer/calllogutils/res/values/strings.xml
@@ -131,7 +131,7 @@
   <!-- String to be displayed to indicate in the call log that a call just now occurred. -->
   <string name="just_now">Just now</string>
 
-  <!-- Text to show in call log for a carrier video call. [CHAR LIMIT=30] -->
+  <!-- Text to show in call log for a carrier video call. [CHAR LIMIT=31] -->
   <string name="new_call_log_carrier_video">Carrier video</string>
 
   <!-- Text to show in call log for a duo video call. [CHAR LIMIT=30] -->
@@ -145,4 +145,4 @@
 
   <!-- String used to display calls from spam numbers in the call log.   [CHAR LIMIT=30] -->
   <string name="new_call_log_secondary_spam">Spam</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/java/com/android/dialer/constants/ActivityRequestCodes.java b/java/com/android/dialer/constants/ActivityRequestCodes.java
index 66c38fa..7fd619b 100644
--- a/java/com/android/dialer/constants/ActivityRequestCodes.java
+++ b/java/com/android/dialer/constants/ActivityRequestCodes.java
@@ -16,8 +16,6 @@
 
 package com.android.dialer.constants;
 
-import com.android.dialer.duo.Duo;
-
 /**
  * Class containing {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}
  * request codes.
@@ -32,7 +30,9 @@
   /** Request code for {@link com.android.dialer.callcomposer.CallComposerActivity} intent. */
   public static final int DIALTACTS_CALL_COMPOSER = 2;
 
-  /** Request code for {@link Duo#getIntent(android.content.Context, String)}. */
+  /**
+   * Request code for {@link com.android.dialer.duo.Duo#getIntent(android.content.Context, String)}.
+   */
   public static final int DIALTACTS_DUO = 3;
 
   /** Request code for {@link com.android.dialer.calldetails.OldCallDetailsActivity} intent. */
diff --git a/java/com/android/dialer/database/DialerDatabaseHelper.java b/java/com/android/dialer/database/DialerDatabaseHelper.java
index efff11e..b172d70 100644
--- a/java/com/android/dialer/database/DialerDatabaseHelper.java
+++ b/java/com/android/dialer/database/DialerDatabaseHelper.java
@@ -32,21 +32,23 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
-import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
 import android.text.TextUtils;
 import com.android.contacts.common.R;
 import com.android.contacts.common.util.StopWatch;
 import com.android.dialer.common.LogUtil;
-import com.android.dialer.common.concurrent.DialerExecutor.Worker;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.common.concurrent.DialerFutureSerializer;
 import com.android.dialer.common.database.Selection;
 import com.android.dialer.configprovider.ConfigProviderBindings;
 import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
 import com.android.dialer.smartdial.util.SmartDialNameMatcher;
 import com.android.dialer.smartdial.util.SmartDialPrefix;
 import com.android.dialer.util.PermissionsUtil;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Objects;
@@ -85,6 +87,8 @@
   private static final int MAX_ENTRIES = 20;
 
   private final Context context;
+  private final DialerFutureSerializer dialerFutureSerializer = new DialerFutureSerializer();
+
   private boolean isTestInstance = false;
 
   protected DialerDatabaseHelper(Context context, String databaseName, int dbVersion) {
@@ -344,11 +348,19 @@
    */
   public void startSmartDialUpdateThread(boolean forceUpdate) {
     if (PermissionsUtil.hasContactsReadPermissions(context)) {
-      DialerExecutorComponent.get(context)
-          .dialerExecutorFactory()
-          .createNonUiTaskBuilder(new UpdateSmartDialWorker())
-          .build()
-          .executeParallel(forceUpdate);
+      Futures.addCallback(
+          // Serialize calls to updateSmartDialDatabase. Use FutureSerializer instead of
+          // synchronizing on the method to prevent deadlocking thread pool. FutureSerializer
+          // provides the guarantee that the next AsyncCallable won't even be submitted until the
+          // ListenableFuture returned by the previous one completes. See a bug.
+          dialerFutureSerializer.submit(
+              () -> {
+                updateSmartDialDatabase(forceUpdate);
+                return null;
+              },
+              DialerExecutorComponent.get(context).backgroundExecutor()),
+          new DefaultFutureCallback<>(),
+          MoreExecutors.directExecutor());
     }
   }
 
@@ -657,7 +669,7 @@
    * @param forceUpdate If set to true, update the database by reloading all contacts.
    */
   @WorkerThread
-  public synchronized void updateSmartDialDatabase(boolean forceUpdate) {
+  public void updateSmartDialDatabase(boolean forceUpdate) {
     LogUtil.enterBlock("DialerDatabaseHelper.updateSmartDialDatabase");
 
     final SQLiteDatabase db = getWritableDatabase();
@@ -1296,14 +1308,4 @@
       return false;
     }
   }
-
-  private class UpdateSmartDialWorker implements Worker<Boolean, Void> {
-
-    @Nullable
-    @Override
-    public Void doInBackground(Boolean forceUpdate) throws Throwable {
-      updateSmartDialDatabase(forceUpdate);
-      return null;
-    }
-  }
 }
diff --git a/java/com/android/dialer/duo/PlaceDuoCallNotifier.java b/java/com/android/dialer/duo/PlaceDuoCallNotifier.java
new file mode 100644
index 0000000..8fde981
--- /dev/null
+++ b/java/com/android/dialer/duo/PlaceDuoCallNotifier.java
@@ -0,0 +1,45 @@
+/*
+ * 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
new file mode 100644
index 0000000..913132c
--- /dev/null
+++ b/java/com/android/dialer/duo/PlaceDuoCallReceiver.java
@@ -0,0 +1,77 @@
+/*
+ * 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.getIntent(context, phoneNumber), ActivityRequestCodes.DIALTACTS_DUO);
+  }
+}
diff --git a/java/com/android/dialer/historyitemactions/DuoCallModule.java b/java/com/android/dialer/historyitemactions/DuoCallModule.java
new file mode 100644
index 0000000..b0d6a11
--- /dev/null
+++ b/java/com/android/dialer/historyitemactions/DuoCallModule.java
@@ -0,0 +1,80 @@
+/*
+ * 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.callintent.CallInitiationType;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.duo.Duo;
+import com.android.dialer.duo.DuoComponent;
+import com.android.dialer.duo.PlaceDuoCallNotifier;
+import com.android.dialer.precall.PreCall;
+
+/** {@link HistoryItemActionModule} for making a Duo call. */
+public class DuoCallModule implements HistoryItemActionModule {
+
+  private final Context context;
+  private final String phoneNumber;
+  private final CallInitiationType.Type callInitiationType;
+
+  /**
+   * 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, CallInitiationType.Type callInitiationType) {
+    this.context = context;
+    this.phoneNumber = phoneNumber;
+    this.callInitiationType = callInitiationType;
+  }
+
+  @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() {
+    if (canPlaceDuoCall(context, phoneNumber)) {
+      PlaceDuoCallNotifier.notify(context, phoneNumber);
+    } else {
+      // If a Duo call can't be placed, fall back to an IMS video call.
+      PreCall.start(
+          context, new CallIntentBuilder(phoneNumber, callInitiationType).setIsVideoCall(true));
+    }
+
+    return true; // Close the bottom sheet.
+  }
+
+  private boolean canPlaceDuoCall(Context context, String phoneNumber) {
+    Duo duo = DuoComponent.get(context).getDuo();
+
+    return duo.isInstalled(context)
+        && duo.isEnabled(context)
+        && duo.isActivated(context)
+        && duo.isReachable(context, phoneNumber);
+  }
+}
diff --git a/java/com/android/dialer/historyitemactions/IntentModule.java b/java/com/android/dialer/historyitemactions/IntentModule.java
index efb10e8..a5236c5 100644
--- a/java/com/android/dialer/historyitemactions/IntentModule.java
+++ b/java/com/android/dialer/historyitemactions/IntentModule.java
@@ -77,7 +77,7 @@
         R.drawable.quantum_ic_call_white_24);
   }
 
-  public static IntentModule newVideoCallModule(
+  public static IntentModule newCarrierVideoCallModule(
       Context context,
       String number,
       @Nullable PhoneAccountHandle phoneAccountHandle,
diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java
index 2046b04..3f660f5 100644
--- a/java/com/android/dialer/main/impl/MainActivity.java
+++ b/java/com/android/dialer/main/impl/MainActivity.java
@@ -24,6 +24,7 @@
 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;
@@ -47,6 +48,9 @@
    */
   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);
   }
@@ -79,6 +83,7 @@
     activePeer.onActivityCreate(savedInstanceState);
 
     showBlockReportSpamDialogReceiver = new ShowBlockReportSpamDialogReceiver(getFragmentManager());
+    placeDuoCallReceiver = new PlaceDuoCallReceiver(/* activity = */ this);
   }
 
   protected MainActivityPeer getNewPeer() {
@@ -104,6 +109,8 @@
     LocalBroadcastManager.getInstance(this)
         .registerReceiver(
             showBlockReportSpamDialogReceiver, ShowBlockReportSpamDialogReceiver.getIntentFilter());
+    LocalBroadcastManager.getInstance(this)
+        .registerReceiver(placeDuoCallReceiver, PlaceDuoCallReceiver.getIntentFilter());
   }
 
   @Override
@@ -118,6 +125,7 @@
     activePeer.onActivityPause();
 
     LocalBroadcastManager.getInstance(this).unregisterReceiver(showBlockReportSpamDialogReceiver);
+    LocalBroadcastManager.getInstance(this).unregisterReceiver(placeDuoCallReceiver);
   }
 
   @Override
diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
index 6d78a51..c15d7c1 100644
--- a/java/com/android/dialer/main/impl/OldMainActivityPeer.java
+++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
@@ -68,10 +68,12 @@
 import com.android.dialer.calldetails.OldCallDetailsActivity;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.callintent.CallSpecificAppData;
+import com.android.dialer.calllog.CallLogComponent;
 import com.android.dialer.calllog.config.CallLogConfigComponent;
 import com.android.dialer.calllog.ui.NewCallLogFragment;
 import com.android.dialer.common.FragmentUtils.FragmentUtilListener;
 import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
 import com.android.dialer.common.concurrent.ThreadUtil;
 import com.android.dialer.common.concurrent.UiListener;
@@ -96,6 +98,7 @@
 import com.android.dialer.main.impl.bottomnav.BottomNavBar;
 import com.android.dialer.main.impl.bottomnav.BottomNavBar.OnBottomNavTabSelectedListener;
 import com.android.dialer.main.impl.bottomnav.BottomNavBar.TabIndex;
+import com.android.dialer.main.impl.bottomnav.MissedCallCountObserver;
 import com.android.dialer.main.impl.toolbar.MainToolbar;
 import com.android.dialer.metrics.Metrics;
 import com.android.dialer.metrics.MetricsComponent;
@@ -113,7 +116,9 @@
 import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker;
 import com.android.dialer.voicemailstatus.VoicemailStatusHelper;
 import com.android.voicemail.VoicemailComponent;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
 import java.util.Locale;
 import java.util.concurrent.TimeUnit;
 
@@ -184,7 +189,9 @@
   private BottomNavBar bottomNav;
   private MainBottomNavBarBottomNavTabListener bottomNavTabListener;
   private View snackbarContainer;
+  private MissedCallCountObserver missedCallCountObserver;
   private UiListener<String> getLastOutgoingCallListener;
+  private UiListener<Integer> missedCallObserverUiListener;
 
   public static Intent getShowTabIntent(Context context, @TabIndex int tabIndex) {
     Intent intent = new Intent(context, MainActivity.class);
@@ -219,6 +226,9 @@
     getLastOutgoingCallListener =
         DialerExecutorComponent.get(activity)
             .createUiListener(activity.getFragmentManager(), "Query last phone number");
+    missedCallObserverUiListener =
+        DialerExecutorComponent.get(activity)
+            .createUiListener(activity.getFragmentManager(), "Missed call observer");
   }
 
   private void initLayout(Bundle savedInstanceState) {
@@ -247,9 +257,13 @@
     boolean showVoicemailTab = canVoicemailTabBeShown(activity);
     bottomNav.showVoicemail(showVoicemailTab);
 
+    missedCallCountObserver =
+        new MissedCallCountObserver(
+            activity.getApplicationContext(), bottomNav, missedCallObserverUiListener);
+
     callLogFragmentListener =
         new MainCallLogFragmentListener(
-            activity, activity.getContentResolver(), bottomNav, toolbar);
+            activity, activity.getContentResolver(), bottomNav, toolbar, bottomNavTabListener);
     bottomNav.addOnTabSelectedListener(callLogFragmentListener);
 
     searchController =
@@ -471,6 +485,13 @@
      */
     bottomNavTabListener.ensureCorrectCallLogShown();
 
+    if (bottomNavTabListener.newCallLogFragmentActive()) {
+      missedCallCountObserver.onChange(false); // Set the initial value for the badge
+      activity
+          .getContentResolver()
+          .registerContentObserver(Calls.CONTENT_URI, true, missedCallCountObserver);
+    }
+
     // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
     ThreadUtil.postDelayedOnUiThread(
         () ->
@@ -489,6 +510,7 @@
   public void onActivityPause() {
     searchController.onActivityPause();
     LocalBroadcastManager.getInstance(activity).unregisterReceiver(disableNewCallLogReceiver);
+    activity.getContentResolver().unregisterContentObserver(missedCallCountObserver);
   }
 
   @Override
@@ -819,6 +841,7 @@
     private final Context context;
     private final BottomNavBar bottomNavBar;
     private final Toolbar toolbar;
+    private final MainBottomNavBarBottomNavTabListener bottomNavTabListener;
 
     private @TabIndex int currentTab = TabIndex.SPEED_DIAL;
     private long timeSelected = -1;
@@ -837,11 +860,13 @@
         Context context,
         ContentResolver contentResolver,
         BottomNavBar bottomNavBar,
-        Toolbar toolbar) {
+        Toolbar toolbar,
+        MainBottomNavBarBottomNavTabListener bottomNavTabListener) {
       callLogQueryHandler = new CallLogQueryHandler(context, contentResolver, this);
       this.context = context;
       this.bottomNavBar = bottomNavBar;
       this.toolbar = toolbar;
+      this.bottomNavTabListener = bottomNavTabListener;
     }
 
     private void registerVoicemailStatusContentObserver(Context context) {
@@ -953,8 +978,15 @@
     }
 
     private void markMissedCallsAsReadAndRemoveNotification() {
-      callLogQueryHandler.markMissedCallsAsRead();
-      CallLogNotificationsService.cancelAllMissedCalls(context);
+      if (bottomNavTabListener.newCallLogFragmentActive()) {
+        Futures.addCallback(
+            CallLogComponent.get(context).getClearMissedCalls().clearAll(),
+            new DefaultFutureCallback<>(),
+            MoreExecutors.directExecutor());
+      } else {
+        callLogQueryHandler.markMissedCallsAsRead();
+        CallLogNotificationsService.cancelAllMissedCalls(context);
+      }
     }
 
     private void setCurrentTab(@TabIndex int tabIndex) {
@@ -969,7 +1001,9 @@
       activityIsAlive = true;
       registerVoicemailStatusContentObserver(context);
       callLogQueryHandler.fetchVoicemailStatus();
-      callLogQueryHandler.fetchMissedCallsUnreadCount();
+      if (!bottomNavTabListener.newCallLogFragmentActive()) {
+        callLogQueryHandler.fetchMissedCallsUnreadCount();
+      }
       // Reset the tab on resume to restart the timer
       setCurrentTab(bottomNavBar.getSelectedTab());
     }
@@ -978,7 +1012,11 @@
     public void onActivityStop(boolean changingConfigurations, boolean keyguardLocked) {
       context.getContentResolver().unregisterContentObserver(voicemailStatusObserver);
       activityIsAlive = false;
-      if (viewedCallLogTabPastTimeThreshold() && !changingConfigurations && !keyguardLocked) {
+      // The new call log fragment handles this on its own.
+      if (!bottomNavTabListener.newCallLogFragmentActive()
+          && viewedCallLogTabPastTimeThreshold()
+          && !changingConfigurations
+          && !keyguardLocked) {
         markMissedCallsAsReadAndRemoveNotification();
       }
     }
@@ -1239,6 +1277,14 @@
       }
     }
 
+    boolean newCallLogFragmentActive() {
+      return supportFragmentManager.findFragmentByTag(CALL_LOG_TAG) != null
+          || (fragmentManager.findFragmentByTag(CALL_LOG_TAG) == null
+              && CallLogConfigComponent.get(activity)
+                  .callLogConfig()
+                  .isNewCallLogFragmentEnabled());
+    }
+
     @Override
     public void onContactsSelected() {
       LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.onContactsSelected");
diff --git a/java/com/android/dialer/main/impl/bottomnav/MissedCallCountObserver.java b/java/com/android/dialer/main/impl/bottomnav/MissedCallCountObserver.java
new file mode 100644
index 0000000..2760634
--- /dev/null
+++ b/java/com/android/dialer/main/impl/bottomnav/MissedCallCountObserver.java
@@ -0,0 +1,79 @@
+/*
+ * 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.main.impl.bottomnav;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.provider.CallLog.Calls;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.common.concurrent.UiListener;
+import com.android.dialer.main.impl.bottomnav.BottomNavBar.TabIndex;
+import com.android.dialer.util.PermissionsUtil;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Observes the call log and updates the badge count to show the number of unread missed calls.
+ *
+ * <p>Used only when the new call log fragment is enabled.
+ */
+public final class MissedCallCountObserver extends ContentObserver {
+  private final Context appContext;
+  private final BottomNavBar bottomNavBar;
+  private final UiListener<Integer> uiListener;
+
+  public MissedCallCountObserver(
+      Context appContext, BottomNavBar bottomNavBar, UiListener<Integer> uiListener) {
+    super(null);
+    this.appContext = appContext;
+    this.bottomNavBar = bottomNavBar;
+    this.uiListener = uiListener;
+  }
+
+  @SuppressLint("MissingPermission")
+  @Override
+  public void onChange(boolean selfChange) {
+    ListenableFuture<Integer> countFuture =
+        DialerExecutorComponent.get(appContext)
+            .backgroundExecutor()
+            .submit(
+                () -> {
+                  if (!PermissionsUtil.hasCallLogReadPermissions(appContext)) {
+                    return 0;
+                  }
+                  try (Cursor cursor =
+                      appContext
+                          .getContentResolver()
+                          .query(
+                              Calls.CONTENT_URI,
+                              new String[] {Calls._ID},
+                              Calls.IS_READ + " = ? AND " + Calls.TYPE + " = ?",
+                              new String[] {"0", Integer.toString(Calls.MISSED_TYPE)},
+                              /* sortOrder= */ null)) {
+                    return cursor == null ? 0 : cursor.getCount();
+                  }
+                });
+    uiListener.listen(
+        appContext,
+        countFuture,
+        count -> bottomNavBar.setNotificationCount(TabIndex.CALL_LOG, count == null ? 0 : count),
+        throwable -> {
+          throw new RuntimeException(throwable);
+        });
+  }
+}
diff --git a/java/com/android/dialer/oem/AndroidManifest.xml b/java/com/android/dialer/oem/AndroidManifest.xml
index a781521..94cd4fc 100644
--- a/java/com/android/dialer/oem/AndroidManifest.xml
+++ b/java/com/android/dialer/oem/AndroidManifest.xml
@@ -19,5 +19,5 @@
   <uses-permission android:name="com.cequint.ecid.CALLER_ID_LOOKUP"/>
 
   <!-- This is used by MotorolaInCallUiNotifier to send broadcasts. -->
-  <uses-permission android:name="com.motorola.incallui.action.INCOMING_CALL_VISIBILITY_CHANGED"/>
-</manifest>
\ No newline at end of file
+  <uses-permission android:name="com.motorola.incallui.permission.INCOMING_CALL_VISIBILITY_CHANGED"/>
+</manifest>
diff --git a/java/com/android/dialer/rootcomponentgenerator/processor/MetadataGeneratingStep.java b/java/com/android/dialer/rootcomponentgenerator/processor/MetadataGeneratingStep.java
index 70ad1b2..b7d31c0 100644
--- a/java/com/android/dialer/rootcomponentgenerator/processor/MetadataGeneratingStep.java
+++ b/java/com/android/dialer/rootcomponentgenerator/processor/MetadataGeneratingStep.java
@@ -18,7 +18,6 @@
 
 import static javax.tools.Diagnostic.Kind.ERROR;
 
-import com.android.dialer.rootcomponentgenerator.annotation.DialerComponent;
 import com.android.dialer.rootcomponentgenerator.annotation.InstallIn;
 import com.android.dialer.rootcomponentgenerator.annotation.RootComponentGeneratorMetadata;
 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
@@ -26,6 +25,7 @@
 import com.google.common.collect.SetMultimap;
 import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.TypeSpec;
+import dagger.Subcomponent;
 import java.lang.annotation.Annotation;
 import java.util.Collections;
 import java.util.Set;
@@ -33,7 +33,7 @@
 import javax.lang.model.element.Element;
 
 /**
- * Genereates metadata for every type annotated by {@link InstallIn} and {@link DialerComponent}.
+ * Genereates metadata for every type annotated by {@link InstallIn} and {@link Subcomponent}.
  *
  * <p>The metadata has the information where the annotated types are and it is used by annotation
  * processor when the processor tries to generate root component.
@@ -48,15 +48,15 @@
 
   @Override
   public Set<? extends Class<? extends Annotation>> annotations() {
-    return ImmutableSet.of(DialerComponent.class, InstallIn.class);
+    return ImmutableSet.of(Subcomponent.class, InstallIn.class);
   }
 
   @Override
   public Set<? extends Element> process(
       SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
 
-    for (Element element : elementsByAnnotation.get(DialerComponent.class)) {
-      generateMetadataFor(DialerComponent.class, element);
+    for (Element element : elementsByAnnotation.get(Subcomponent.class)) {
+      generateMetadataFor(Subcomponent.class, element);
     }
     for (Element element : elementsByAnnotation.get(InstallIn.class)) {
       if (element.getAnnotation(InstallIn.class).variants().length == 0) {
diff --git a/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentGeneratingStep.java b/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentGeneratingStep.java
index 86a0308..9b97ada 100644
--- a/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentGeneratingStep.java
+++ b/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentGeneratingStep.java
@@ -20,7 +20,6 @@
 import static com.google.auto.common.MoreElements.getAnnotationMirror;
 import static com.google.auto.common.MoreElements.isAnnotationPresent;
 
-import com.android.dialer.rootcomponentgenerator.annotation.DialerComponent;
 import com.android.dialer.rootcomponentgenerator.annotation.DialerRootComponent;
 import com.android.dialer.rootcomponentgenerator.annotation.DialerVariant;
 import com.android.dialer.rootcomponentgenerator.annotation.InstallIn;
@@ -36,6 +35,7 @@
 import com.squareup.javapoet.ClassName;
 import com.squareup.javapoet.TypeSpec;
 import dagger.Component;
+import dagger.Subcomponent;
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -90,11 +90,7 @@
             .addAnnotation(Singleton.class);
     for (TypeElement componentWithSuperInterface : componentList) {
       rootComponentClassBuilder.addSuperinterface(
-          ClassName.get(componentWithSuperInterface)
-              .peerClass(
-                  RootComponentUtils.GENERATED_COMPONENT_PREFIX
-                      + componentWithSuperInterface.getSimpleName())
-              .nestedClass("HasComponent"));
+          ClassName.get(componentWithSuperInterface).nestedClass("HasComponent"));
     }
     AnnotationSpec.Builder componentAnnotation = AnnotationSpec.builder(Component.class);
     for (TypeElement annotatedElement : componentModuleMap.get(dialerVariant)) {
@@ -108,7 +104,7 @@
 
   private List<TypeElement> generateComponentList() {
     List<TypeElement> list = new ArrayList<>();
-    extractInfoFromMetadata(DialerComponent.class, list::add);
+    extractInfoFromMetadata(Subcomponent.class, list::add);
     return list;
   }
 
diff --git a/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentProcessor.java b/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentProcessor.java
index 5e083d2..56caa9e 100644
--- a/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentProcessor.java
+++ b/java/com/android/dialer/rootcomponentgenerator/processor/RootComponentProcessor.java
@@ -29,7 +29,6 @@
   @Override
   protected Iterable<? extends ProcessingStep> initSteps() {
     return ImmutableList.of(
-        new ComponentGeneratingStep(processingEnv),
         new MetadataGeneratingStep(processingEnv),
         new RootComponentGeneratingStep(processingEnv));
   }
diff --git a/java/com/android/dialer/telecom/TelecomUtil.java b/java/com/android/dialer/telecom/TelecomUtil.java
index f05ec20..2608cb2 100644
--- a/java/com/android/dialer/telecom/TelecomUtil.java
+++ b/java/com/android/dialer/telecom/TelecomUtil.java
@@ -26,7 +26,6 @@
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.provider.CallLog.Calls;
-import android.provider.Settings;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresPermission;
@@ -300,11 +299,6 @@
     return instance.isDefaultDialer(context);
   }
 
-  public static boolean isRttEnabled(Context context) {
-    return Settings.System.getInt(context.getContentResolver(), Settings.System.RTT_CALLING_MODE, 0)
-        != 0;
-  }
-
   /** @return the other SIM based PhoneAccountHandle that is not {@code currentAccount} */
   @Nullable
   @RequiresPermission(permission.READ_PHONE_STATE)
diff --git a/java/com/android/incallui/CallButtonPresenter.java b/java/com/android/incallui/CallButtonPresenter.java
index 38c8da8..8334603 100644
--- a/java/com/android/incallui/CallButtonPresenter.java
+++ b/java/com/android/incallui/CallButtonPresenter.java
@@ -479,7 +479,7 @@
             // Most devices cannot make calls on 2 SIMs at the same time.
             && InCallPresenter.getInstance().getCallList().getAllCalls().size() == 1;
 
-    boolean showUpgradeToRtt = TelecomUtil.isRttEnabled(context) && call.canUpgradeToRttCall();
+    boolean showUpgradeToRtt = call.canUpgradeToRttCall();
 
     inCallButtonUi.showButton(InCallButtonIds.BUTTON_AUDIO, true);
     inCallButtonUi.showButton(InCallButtonIds.BUTTON_SWAP, showSwap);
diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java
index 1ba3683..65ef323 100644
--- a/java/com/android/incallui/InCallActivity.java
+++ b/java/com/android/incallui/InCallActivity.java
@@ -35,6 +35,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
+import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
@@ -1221,6 +1222,12 @@
     fragment.show(getSupportFragmentManager(), Tags.INTERNATIONAL_CALL_ON_WIFI);
   }
 
+  public void showDialogForRttRequest(DialerCall call, int rttRequestId) {
+    LogUtil.enterBlock("InCallActivity.showDialogForRttRequest");
+    DialogFragment fragment = RttRequestDialogFragment.newInstance(call.getId(), rttRequestId);
+    fragment.show(getSupportFragmentManager(), Tags.RTT_REQUEST_DIALOG);
+  }
+
   @Override
   public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
     super.onMultiWindowModeChanged(isInMultiWindowMode);
@@ -1763,6 +1770,7 @@
     static final String RTT_CALL_SCREEN = "tag_rtt_call_screen";
     static final String POST_CHAR_DIALOG_FRAGMENT = "tag_post_char_dialog_fragment";
     static final String SPEAK_EASY_SCREEN = "tag_speak_easy_screen";
+    static final String RTT_REQUEST_DIALOG = "tag_rtt_request_dialog";
   }
 
   private static final class ConfigNames {
diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java
index e11b376..6300dac 100644
--- a/java/com/android/incallui/InCallPresenter.java
+++ b/java/com/android/incallui/InCallPresenter.java
@@ -894,6 +894,13 @@
   }
 
   @Override
+  public void onUpgradeToRtt(DialerCall call, int rttRequestId) {
+    if (inCallActivity != null) {
+      inCallActivity.showDialogForRttRequest(call, rttRequestId);
+    }
+  }
+
+  @Override
   public void onSessionModificationStateChange(DialerCall call) {
     int newState = call.getVideoTech().getSessionModificationState();
     LogUtil.i("InCallPresenter.onSessionModificationStateChange", "state: %d", newState);
diff --git a/java/com/android/incallui/RttRequestDialogFragment.java b/java/com/android/incallui/RttRequestDialogFragment.java
new file mode 100644
index 0000000..fa9b0e5
--- /dev/null
+++ b/java/com/android/incallui/RttRequestDialogFragment.java
@@ -0,0 +1,149 @@
+/*
+ * 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.incallui;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.telephony.PhoneNumberUtils;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+import com.android.contacts.common.util.ContactDisplayUtils;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.incallui.ContactInfoCache.ContactCacheEntry;
+import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import java.lang.ref.WeakReference;
+
+/** Dialog that shown to user when receiving RTT request mid call. */
+public class RttRequestDialogFragment extends DialogFragment {
+
+  /**
+   * Returns a new instance of {@link RttRequestDialogFragment} with the given callback.
+   *
+   * <p>Prefer this method over the default constructor.
+   */
+  public static RttRequestDialogFragment newInstance(String callId, int rttRequestId) {
+    RttRequestDialogFragment fragment = new RttRequestDialogFragment();
+    Bundle args = new Bundle();
+    args.putString(ARG_CALL_ID, Assert.isNotNull(callId));
+    args.putInt(ARG_RTT_REQUEST_ID, rttRequestId);
+    fragment.setArguments(args);
+    return fragment;
+  }
+
+  /** Key in the arguments bundle for call id. */
+  private static final String ARG_CALL_ID = "call_id";
+
+  private static final String ARG_RTT_REQUEST_ID = "rtt_request_id";
+
+  private TextView detailsTextView;
+
+  @NonNull
+  @Override
+  public Dialog onCreateDialog(Bundle bundle) {
+    super.onCreateDialog(bundle);
+    LogUtil.enterBlock("RttRequestDialogFragment.onCreateDialog");
+
+    View dialogView = View.inflate(getActivity(), R.layout.frag_rtt_request_dialog, null);
+    detailsTextView = dialogView.findViewById(R.id.details);
+
+    ContactInfoCache cache = ContactInfoCache.getInstance(getContext());
+    DialerCall dialerCall =
+        CallList.getInstance().getCallById(getArguments().getString(ARG_CALL_ID));
+    cache.findInfo(dialerCall, false, new ContactLookupCallback(this));
+
+    dialogView
+        .findViewById(R.id.rtt_button_decline_request)
+        .setOnClickListener(v -> onNegativeButtonClick());
+    dialogView
+        .findViewById(R.id.rtt_button_accept_request)
+        .setOnClickListener(v -> onPositiveButtonClick());
+
+    AlertDialog alertDialog =
+        new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme)
+            .setCancelable(false)
+            .setView(dialogView)
+            .setTitle(R.string.rtt_request_dialog_title)
+            .create();
+
+    alertDialog.setCanceledOnTouchOutside(false);
+    return alertDialog;
+  }
+
+  private void onPositiveButtonClick() {
+    LogUtil.enterBlock("RttRequestDialogFragment.onPositiveButtonClick");
+
+    DialerCall call = CallList.getInstance().getCallById(getArguments().getString(ARG_CALL_ID));
+    call.respondToRttRequest(true, getArguments().getInt(ARG_RTT_REQUEST_ID));
+    dismiss();
+  }
+
+  private void onNegativeButtonClick() {
+    LogUtil.enterBlock("RttRequestDialogFragment.onNegativeButtonClick");
+
+    DialerCall call = CallList.getInstance().getCallById(getArguments().getString(ARG_CALL_ID));
+    call.respondToRttRequest(false, getArguments().getInt(ARG_RTT_REQUEST_ID));
+    dismiss();
+  }
+
+  private void setNameOrNumber(CharSequence nameOrNumber) {
+    detailsTextView.setText(getString(R.string.rtt_request_dialog_details, nameOrNumber));
+  }
+
+  private static class ContactLookupCallback implements ContactInfoCacheCallback {
+    private final WeakReference<RttRequestDialogFragment> rttRequestDialogFragmentWeakReference;
+
+    private ContactLookupCallback(RttRequestDialogFragment rttRequestDialogFragment) {
+      rttRequestDialogFragmentWeakReference = new WeakReference<>(rttRequestDialogFragment);
+    }
+
+    @Override
+    public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
+      RttRequestDialogFragment fragment = rttRequestDialogFragmentWeakReference.get();
+      if (fragment != null) {
+        fragment.setNameOrNumber(getNameOrNumber(entry, fragment.getContext()));
+      }
+    }
+
+    private CharSequence getNameOrNumber(ContactCacheEntry entry, Context context) {
+      String preferredName =
+          ContactDisplayUtils.getPreferredDisplayName(
+              entry.namePrimary,
+              entry.nameAlternative,
+              ContactsPreferencesFactory.newContactsPreferences(context));
+      if (TextUtils.isEmpty(preferredName)) {
+        return TextUtils.isEmpty(entry.number)
+            ? null
+            : PhoneNumberUtils.createTtsSpannable(
+                BidiFormatter.getInstance().unicodeWrap(entry.number, TextDirectionHeuristics.LTR));
+      }
+      return preferredName;
+    }
+
+    @Override
+    public void onImageLoadComplete(String callId, ContactCacheEntry entry) {}
+  }
+}
diff --git a/java/com/android/incallui/call/CallList.java b/java/com/android/incallui/call/CallList.java
index 01f3b9d..eccdcee 100644
--- a/java/com/android/incallui/call/CallList.java
+++ b/java/com/android/incallui/call/CallList.java
@@ -784,6 +784,12 @@
      */
     void onUpgradeToVideo(DialerCall call);
 
+    /**
+     * Called when a new RTT call request comes in This is the only method that gets called for RTT
+     * requests.
+     */
+    default void onUpgradeToRtt(DialerCall call, int rttRequestId) {}
+
     /** Called when the session modification state of a call changes. */
     void onSessionModificationStateChange(DialerCall call);
 
@@ -855,6 +861,13 @@
     public void onDialerCallLastForwardedNumberChange() {}
 
     @Override
+    public void onDialerCallUpgradeToRtt(int rttRequestId) {
+      for (Listener listener : listeners) {
+        listener.onUpgradeToRtt(call, rttRequestId);
+      }
+    }
+
+    @Override
     public void onDialerCallUpgradeToVideo() {
       for (Listener listener : listeners) {
         listener.onUpgradeToVideo(call);
diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java
index e08c926..35f9481 100644
--- a/java/com/android/incallui/call/DialerCall.java
+++ b/java/com/android/incallui/call/DialerCall.java
@@ -282,6 +282,9 @@
         @Override
         public void onRttRequest(Call call, int id) {
           LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id);
+          for (DialerCallListener listener : listeners) {
+            listener.onDialerCallUpgradeToRtt(id);
+          }
         }
 
         @Override
@@ -1033,6 +1036,11 @@
     getTelecomCall().sendRttRequest();
   }
 
+  @TargetApi(28)
+  public void respondToRttRequest(boolean accept, int rttRequestId) {
+    getTelecomCall().respondToRttRequest(rttRequestId, accept);
+  }
+
   public boolean hasReceivedVideoUpgradeRequest() {
     return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
   }
diff --git a/java/com/android/incallui/call/DialerCallListener.java b/java/com/android/incallui/call/DialerCallListener.java
index 5d24a4d..37c30d3 100644
--- a/java/com/android/incallui/call/DialerCallListener.java
+++ b/java/com/android/incallui/call/DialerCallListener.java
@@ -29,6 +29,8 @@
 
   void onDialerCallUpgradeToVideo();
 
+  default void onDialerCallUpgradeToRtt(int rttRequestId) {}
+
   void onDialerCallSessionModificationStateChange();
 
   void onWiFiToLteHandover();
diff --git a/java/com/android/incallui/res/layout/frag_rtt_request_dialog.xml b/java/com/android/incallui/res/layout/frag_rtt_request_dialog.xml
new file mode 100644
index 0000000..ab743eb
--- /dev/null
+++ b/java/com/android/incallui/res/layout/frag_rtt_request_dialog.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+  <TextView
+      android:id="@+id/details"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:paddingTop="20dp"
+      android:paddingBottom="12dp"
+      android:paddingStart="24dp"
+      android:paddingEnd="24dp"
+      android:textColor="#DE000000"
+      android:textSize="16sp"/>
+
+  <TextView
+      android:id="@+id/more_information"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:paddingBottom="16dp"
+      android:paddingStart="24dp"
+      android:paddingEnd="24dp"
+      android:text="@string/rtt_request_dialog_more_information"
+      android:textColor="#8A000000"
+      android:textSize="14sp"/>
+
+  <LinearLayout
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:padding="8dp"
+      android:orientation="horizontal">
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"/>
+    <Button
+        android:id="@+id/rtt_button_decline_request"
+        style="@style/Widget.AppCompat.Button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:backgroundTint="@android:color/white"
+        android:fontFamily="sans-serif-medium"
+        android:stateListAnimator="@null"
+        android:text="@string/rtt_button_decline_request"
+        android:textColor="#757575"/>
+    <Button
+        android:id="@+id/rtt_button_accept_request"
+        style="@style/Widget.AppCompat.Button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:backgroundTint="@color/dialer_theme_color"
+        android:fontFamily="sans-serif-medium"
+        android:stateListAnimator="@null"
+        android:text="@string/rtt_button_accept_request"
+        android:textColor="@android:color/white"/>
+  </LinearLayout>
+
+</LinearLayout>
+
diff --git a/java/com/android/incallui/res/values/strings.xml b/java/com/android/incallui/res/values/strings.xml
index 15cd2e0..c7e5677 100644
--- a/java/com/android/incallui/res/values/strings.xml
+++ b/java/com/android/incallui/res/values/strings.xml
@@ -202,4 +202,19 @@
   <!-- Text for bubble return-to-call button -->
   <string name="bubble_return_to_call">Back to call</string>
 
+  <!-- Title for RTT request dialog. [CHAR LIMIT=60] -->
+  <string name="rtt_request_dialog_title">Join RTT call?</string>
+
+  <!-- Details for RTT request dialog. [CHAR LIMIT=NONE] -->
+  <string name="rtt_request_dialog_details"><xliff:g id="caller">%1$s</xliff:g> wants to use messaging within your voice call.</string>
+
+  <!-- More information for RTT request dialog. [CHAR LIMIT=NONE] -->
+  <string name="rtt_request_dialog_more_information">RTT assists callers who are deaf, hard of hearing, have a speech disability, or need more than voice alone.</string>
+
+  <!-- Text for button to decline RTT request. [CHAR LIMIT=20] -->
+  <string name="rtt_button_decline_request">No thanks</string>
+
+  <!-- Text for button to accept RTT request. [CHAR LIMIT=20] -->
+  <string name="rtt_button_accept_request">Join RTT</string>
+
 </resources>
diff --git a/java/com/android/incallui/rtt/impl/res/values/strings.xml b/java/com/android/incallui/rtt/impl/res/values/strings.xml
index b0ac205..1d09f54 100644
--- a/java/com/android/incallui/rtt/impl/res/values/strings.xml
+++ b/java/com/android/incallui/rtt/impl/res/values/strings.xml
@@ -27,7 +27,7 @@
   <!-- Text for back button. [CHAR LIMIT=20] -->
   <string name="rtt_button_back">Back</string>
 
-  <!-- Text for status banner. [CHAT LIMIT=100] -->
+  <!-- Text for status banner. [CHAR LIMIT=100] -->
   <string name="rtt_status_banner_text">Waiting for <xliff:g id="name">%s</xliff:g> to join RTT call&#8230;</string>
 
 </resources>
\ No newline at end of file