Merge changes I706802c0,I59f9b334,Ica9301f4,I78e0e0fc,I1d574287, ...

* changes:
  Show Cequint info in the new call log.
  Implement CequintPhoneLookup
  Fix impression logging for spam blocking promo dialog shown in after call notification.
  Build out the bottom sheet for suggested contacts in Speed Dial.
  Fix bug that title of RTT call is not centered.
  Add advisory text to RTT call about transcript storage.
diff --git a/assets/quantum/res/drawable/quantum_ic_question_answer_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_question_answer_vd_theme_24.xml
new file mode 100644
index 0000000..f607d53
--- /dev/null
+++ b/assets/quantum/res/drawable/quantum_ic_question_answer_vd_theme_24.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M21,6h-2v9L6,15v2c0,0.55 0.45,1 1,1h11l4,4L22,7c0,-0.55 -0.45,-1 -1,-1zM17,12L17,3c0,-0.55 -0.45,-1 -1,-1L3,2c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/>
+</vector>
diff --git a/java/com/android/dialer/app/calllog/BlockReportSpamListener.java b/java/com/android/dialer/app/calllog/BlockReportSpamListener.java
index 5517918..987615f 100644
--- a/java/com/android/dialer/app/calllog/BlockReportSpamListener.java
+++ b/java/com/android/dialer/app/calllog/BlockReportSpamListener.java
@@ -206,11 +206,28 @@
   }
 
   private void showSpamBlockingPromoDialog() {
-    if (spamBlockingPromoHelper.shouldShowSpamBlockingPromo()) {
-      spamBlockingPromoHelper.showSpamBlockingPromoDialog(
-          fragmentManager,
-          success -> spamBlockingPromoHelper.showModifySettingOnCompleteSnackbar(rootView, success),
-          null /* onDissmissListener */);
+    if (!spamBlockingPromoHelper.shouldShowSpamBlockingPromo()) {
+      return;
     }
+
+    Logger.get(context).logImpression(DialerImpression.Type.SPAM_BLOCKING_CALL_LOG_PROMO_SHOWN);
+    spamBlockingPromoHelper.showSpamBlockingPromoDialog(
+        fragmentManager,
+        () -> {
+          Logger.get(context)
+              .logImpression(DialerImpression.Type.SPAM_BLOCKING_ENABLED_THROUGH_CALL_LOG_PROMO);
+          spamSettings.modifySpamBlockingSetting(
+              true,
+              success -> {
+                if (!success) {
+                  Logger.get(context)
+                      .logImpression(
+                          DialerImpression.Type
+                              .SPAM_BLOCKING_MODIFY_FAILURE_THROUGH_CALL_LOG_PROMO);
+                }
+                spamBlockingPromoHelper.showModifySettingOnCompleteSnackbar(rootView, success);
+              });
+        },
+        null /* onDismissListener */);
   }
 }
diff --git a/java/com/android/dialer/app/calllog/CallLogFragment.java b/java/com/android/dialer/app/calllog/CallLogFragment.java
index 4e968f0..1e55c63 100644
--- a/java/com/android/dialer/app/calllog/CallLogFragment.java
+++ b/java/com/android/dialer/app/calllog/CallLogFragment.java
@@ -449,7 +449,7 @@
     super.onStart();
     CequintCallerIdManager cequintCallerIdManager = null;
     if (CequintCallerIdManager.isCequintCallerIdEnabled(getContext())) {
-      cequintCallerIdManager = CequintCallerIdManager.createInstanceForCallLog();
+      cequintCallerIdManager = new CequintCallerIdManager();
     }
     contactInfoCache.setCequintCallerIdManager(cequintCallerIdManager);
   }
diff --git a/java/com/android/dialer/callintent/CallIntentBuilder.java b/java/com/android/dialer/callintent/CallIntentBuilder.java
index 0f9f890..92efd39 100644
--- a/java/com/android/dialer/callintent/CallIntentBuilder.java
+++ b/java/com/android/dialer/callintent/CallIntentBuilder.java
@@ -152,6 +152,7 @@
     return isVideoCall;
   }
 
+  /** Default false. Should only be set to true if the number has a lookup URI. */
   public CallIntentBuilder setAllowAssistedDial(boolean allowAssistedDial) {
     this.allowAssistedDial = allowAssistedDial;
     return this;
diff --git a/java/com/android/dialer/calllog/database/contract/number_attributes.proto b/java/com/android/dialer/calllog/database/contract/number_attributes.proto
index f99693d..f42974d 100644
--- a/java/com/android/dialer/calllog/database/contract/number_attributes.proto
+++ b/java/com/android/dialer/calllog/database/contract/number_attributes.proto
@@ -24,7 +24,7 @@
 import "java/com/android/dialer/logging/contact_source.proto";
 
 // Information related to the phone number of the call.
-// Next ID: 13
+// Next ID: 14
 message NumberAttributes {
   // The name (which may be a person's name or business name, but not a number)
   // formatted exactly as it should appear to the user. If the user's locale or
@@ -70,4 +70,8 @@
 
   // Whether the number can be reached via a carrier video call.
   optional bool can_support_carrier_video_call = 12;
+
+  // Description of the number's geolocation (e.g., "Mountain View, CA").
+  // This string is for display purpose only.
+  optional string geolocation = 13;
 }
\ No newline at end of file
diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java
index e346de0..a1a2a3b 100644
--- a/java/com/android/dialer/calllogutils/CallLogEntryText.java
+++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java
@@ -214,7 +214,12 @@
     // (1) there is no number type label, and
     // (2) the number is not spam.
     if (TextUtils.isEmpty(numberTypeLabel) && !row.getNumberAttributes().getIsSpam()) {
-      String location = row.getGeocodedLocation();
+      // If number attributes contain a location (obtained from a PhoneLookup), use it instead
+      // of the one from the annotated call log.
+      String location =
+          !TextUtils.isEmpty(row.getNumberAttributes().getGeolocation())
+              ? row.getNumberAttributes().getGeolocation()
+              : row.getGeocodedLocation();
       if (!TextUtils.isEmpty(location)) {
         if (secondaryText.length() > 0) {
           secondaryText.append(", ");
diff --git a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java
index f4fab84..9f07fda 100644
--- a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java
+++ b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java
@@ -56,6 +56,7 @@
         .setCanReportAsInvalidNumber(phoneLookupInfoConsolidator.canReportAsInvalidNumber())
         .setIsCp2InfoIncomplete(phoneLookupInfoConsolidator.isDefaultCp2InfoIncomplete())
         .setContactSource(phoneLookupInfoConsolidator.getContactSource())
-        .setCanSupportCarrierVideoCall(phoneLookupInfoConsolidator.canSupportCarrierVideoCall());
+        .setCanSupportCarrierVideoCall(phoneLookupInfoConsolidator.canSupportCarrierVideoCall())
+        .setGeolocation(phoneLookupInfoConsolidator.getGeolocation());
   }
 }
diff --git a/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java b/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java
index 79205a7..28663c1 100644
--- a/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java
+++ b/java/com/android/dialer/historyitemactions/HistoryItemActionBottomSheet.java
@@ -17,8 +17,10 @@
 package com.android.dialer.historyitemactions;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.os.Bundle;
 import android.support.design.widget.BottomSheetDialog;
+import android.support.v4.content.ContextCompat;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -109,6 +111,12 @@
     ((TextView) moduleView.findViewById(R.id.module_text)).setText(module.getStringId());
     ((ImageView) moduleView.findViewById(R.id.module_image))
         .setImageResource(module.getDrawableId());
+    if (module.tintDrawable()) {
+      ((ImageView) moduleView.findViewById(R.id.module_image))
+          .setImageTintList(
+              ColorStateList.valueOf(
+                  ContextCompat.getColor(getContext(), R.color.secondary_text_color)));
+    }
     moduleView.setOnClickListener(this);
     moduleView.setTag(module);
     return moduleView;
diff --git a/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java b/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java
index d64cbca..e948924 100644
--- a/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java
+++ b/java/com/android/dialer/historyitemactions/HistoryItemActionModule.java
@@ -32,6 +32,11 @@
   @DrawableRes
   int getDrawableId();
 
+  /** Returns true if tint can be applied to the drawable. */
+  default boolean tintDrawable() {
+    return true;
+  }
+
   /** @return true if the bottom sheet should close, false otherwise */
   boolean onClick();
 }
diff --git a/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml b/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml
index 9aee679..0630519 100644
--- a/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml
+++ b/java/com/android/dialer/historyitemactions/res/layout/module_layout.xml
@@ -28,7 +28,6 @@
       android:layout_height="@dimen/contact_actions_image_size"
       android:layout_marginStart="@dimen/contact_actions_image_margin"
       android:layout_marginEnd="@dimen/contact_actions_image_margin"
-      android:tint="@color/dialer_secondary_text_color"
       android:scaleType="center"/>
 
   <TextView
diff --git a/java/com/android/dialer/oem/CequintCallerIdManager.java b/java/com/android/dialer/oem/CequintCallerIdManager.java
index 55cafc1..53f5352 100644
--- a/java/com/android/dialer/oem/CequintCallerIdManager.java
+++ b/java/com/android/dialer/oem/CequintCallerIdManager.java
@@ -24,6 +24,7 @@
 import android.support.annotation.AnyThread;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
@@ -45,7 +46,8 @@
 @TargetApi(VERSION_CODES.N)
 public class CequintCallerIdManager {
 
-  private static final String CONFIG_CALLER_ID_ENABLED = "config_caller_id_enabled";
+  @VisibleForTesting
+  public static final String CONFIG_CALLER_ID_ENABLED = "config_caller_id_enabled";
 
   private static final int CALLER_ID_LOOKUP_USER_PROVIDED_CID = 0x0001;
   private static final int CALLER_ID_LOOKUP_SYSTEM_PROVIDED_CID = 0x0002;
@@ -53,36 +55,43 @@
 
   private static final String[] EMPTY_PROJECTION = new String[] {};
 
-  // Column names in Cequint provider.
-  private static final String CITY_NAME = "cid_pCityName";
-  private static final String STATE_NAME = "cid_pStateName";
-  private static final String STATE_ABBR = "cid_pStateAbbr";
-  private static final String COUNTRY_NAME = "cid_pCountryName";
-  private static final String COMPANY = "cid_pCompany";
-  private static final String NAME = "cid_pName";
-  private static final String FIRST_NAME = "cid_pFirstName";
-  private static final String LAST_NAME = "cid_pLastName";
-  private static final String IMAGE = "cid_pLogo";
-  private static final String DISPLAY_NAME = "cid_pDisplayName";
+  /** Column names in Cequint content provider. */
+  @VisibleForTesting
+  public static final class CequintColumnNames {
+    public static final String CITY_NAME = "cid_pCityName";
+    public static final String STATE_NAME = "cid_pStateName";
+    public static final String STATE_ABBR = "cid_pStateAbbr";
+    public static final String COUNTRY_NAME = "cid_pCountryName";
+    public static final String COMPANY = "cid_pCompany";
+    public static final String NAME = "cid_pName";
+    public static final String FIRST_NAME = "cid_pFirstName";
+    public static final String LAST_NAME = "cid_pLastName";
+    public static final String PHOTO_URI = "cid_pLogo";
+    public static final String DISPLAY_NAME = "cid_pDisplayName";
+  }
 
   private static boolean hasAlreadyCheckedCequintCallerIdPackage;
   private static String cequintProviderAuthority;
 
-  // TODO(wangqi): Revisit it and maybe remove it if it's not necessary.
-  private final ConcurrentHashMap<String, CequintCallerIdContact> callLogCache;
+  // TODO(a bug): Revisit it and maybe remove it if it's not necessary.
+  private final ConcurrentHashMap<String, CequintCallerIdContact> callLogCache =
+      new ConcurrentHashMap<>();
 
   /** Cequint caller ID contact information. */
   @AutoValue
   public abstract static class CequintCallerIdContact {
 
+    @Nullable
     public abstract String name();
 
     /**
      * Description of the geolocation (e.g., "Mountain View, CA"), which is for display purpose
      * only.
      */
+    @Nullable
     public abstract String geolocation();
 
+    @Nullable
     public abstract String photoUri();
 
     static Builder builder() {
@@ -91,11 +100,11 @@
 
     @AutoValue.Builder
     abstract static class Builder {
-      abstract Builder setName(String name);
+      abstract Builder setName(@Nullable String name);
 
-      abstract Builder setGeolocation(String geolocation);
+      abstract Builder setGeolocation(@Nullable String geolocation);
 
-      abstract Builder setPhotoUri(String photoUri);
+      abstract Builder setPhotoUri(@Nullable String photoUri);
 
       abstract CequintCallerIdContact build();
     }
@@ -125,17 +134,14 @@
     return cequintProviderAuthority != null;
   }
 
-  public static CequintCallerIdManager createInstanceForCallLog() {
-    return new CequintCallerIdManager();
-  }
-
+  /** Returns a {@link CequintCallerIdContact} for a call. */
   @WorkerThread
   @Nullable
-  public static CequintCallerIdContact getCequintCallerIdContactForInCall(
+  public static CequintCallerIdContact getCequintCallerIdContactForCall(
       Context context, String number, String cnapName, boolean isIncoming) {
     Assert.isWorkerThread();
     LogUtil.d(
-        "CequintCallerIdManager.getCequintCallerIdContactForInCall",
+        "CequintCallerIdManager.getCequintCallerIdContactForCall",
         "number: %s, cnapName: %s, isIncoming: %b",
         LogUtil.sanitizePhoneNumber(number),
         LogUtil.sanitizePii(cnapName),
@@ -151,29 +157,51 @@
     return lookup(context, getIncallLookupUri(), number, flags);
   }
 
+  /**
+   * Returns a cached {@link CequintCallerIdContact} associated with the provided number. If no
+   * contact can be found in the cache, look up the number using the Cequint content provider.
+   *
+   * @deprecated This method is for the old call log only. New code should use {@link
+   *     #getCequintCallerIdContactForNumber(Context, String)}.
+   */
+  @Deprecated
   @WorkerThread
   @Nullable
-  public CequintCallerIdContact getCequintCallerIdContact(Context context, String number) {
+  public CequintCallerIdContact getCachedCequintCallerIdContact(Context context, String number) {
     Assert.isWorkerThread();
     LogUtil.d(
-        "CequintCallerIdManager.getCequintCallerIdContact",
+        "CequintCallerIdManager.getCachedCequintCallerIdContact",
         "number: %s",
         LogUtil.sanitizePhoneNumber(number));
     if (callLogCache.containsKey(number)) {
       return callLogCache.get(number);
     }
     CequintCallerIdContact cequintCallerIdContact =
-        lookup(
-            context,
-            getLookupUri(),
-            PhoneNumberUtils.stripSeparators(number),
-            new String[] {"system"});
+        getCequintCallerIdContactForNumber(context, number);
     if (cequintCallerIdContact != null) {
       callLogCache.put(number, cequintCallerIdContact);
     }
     return cequintCallerIdContact;
   }
 
+  /**
+   * Returns a {@link CequintCallerIdContact} associated with the provided number by looking it up
+   * using the Cequint content provider.
+   */
+  @WorkerThread
+  @Nullable
+  public static CequintCallerIdContact getCequintCallerIdContactForNumber(
+      Context context, String number) {
+    Assert.isWorkerThread();
+    LogUtil.d(
+        "CequintCallerIdManager.getCequintCallerIdContactForNumber",
+        "number: %s",
+        LogUtil.sanitizePhoneNumber(number));
+
+    return lookup(
+        context, getLookupUri(), PhoneNumberUtils.stripSeparators(number), new String[] {"system"});
+  }
+
   @WorkerThread
   @Nullable
   private static CequintCallerIdContact lookup(
@@ -185,16 +213,17 @@
     try (Cursor cursor =
         context.getContentResolver().query(uri, EMPTY_PROJECTION, number, flags, null)) {
       if (cursor != null && cursor.moveToFirst()) {
-        String city = getString(cursor, cursor.getColumnIndex(CITY_NAME));
-        String state = getString(cursor, cursor.getColumnIndex(STATE_NAME));
-        String stateAbbr = getString(cursor, cursor.getColumnIndex(STATE_ABBR));
-        String country = getString(cursor, cursor.getColumnIndex(COUNTRY_NAME));
-        String company = getString(cursor, cursor.getColumnIndex(COMPANY));
-        String name = getString(cursor, cursor.getColumnIndex(NAME));
-        String firstName = getString(cursor, cursor.getColumnIndex(FIRST_NAME));
-        String lastName = getString(cursor, cursor.getColumnIndex(LAST_NAME));
-        String photoUri = getString(cursor, cursor.getColumnIndex(IMAGE));
-        String displayName = getString(cursor, cursor.getColumnIndex(DISPLAY_NAME));
+        String city = getString(cursor, cursor.getColumnIndex(CequintColumnNames.CITY_NAME));
+        String state = getString(cursor, cursor.getColumnIndex(CequintColumnNames.STATE_NAME));
+        String stateAbbr = getString(cursor, cursor.getColumnIndex(CequintColumnNames.STATE_ABBR));
+        String country = getString(cursor, cursor.getColumnIndex(CequintColumnNames.COUNTRY_NAME));
+        String company = getString(cursor, cursor.getColumnIndex(CequintColumnNames.COMPANY));
+        String name = getString(cursor, cursor.getColumnIndex(CequintColumnNames.NAME));
+        String firstName = getString(cursor, cursor.getColumnIndex(CequintColumnNames.FIRST_NAME));
+        String lastName = getString(cursor, cursor.getColumnIndex(CequintColumnNames.LAST_NAME));
+        String photoUri = getString(cursor, cursor.getColumnIndex(CequintColumnNames.PHOTO_URI));
+        String displayName =
+            getString(cursor, cursor.getColumnIndex(CequintColumnNames.DISPLAY_NAME));
 
         String contactName =
             TextUtils.isEmpty(displayName)
@@ -293,8 +322,4 @@
   private static Uri getIncallLookupUri() {
     return Uri.parse("content://" + cequintProviderAuthority + "/incalllookup");
   }
-
-  private CequintCallerIdManager() {
-    callLogCache = new ConcurrentHashMap<>();
-  }
 }
diff --git a/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java b/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java
index ce2cd18..36d0be4 100644
--- a/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java
@@ -18,33 +18,85 @@
 
 import android.content.Context;
 import android.telecom.Call;
+import android.text.TextUtils;
 import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.location.GeoUtil;
+import com.android.dialer.oem.CequintCallerIdManager;
+import com.android.dialer.oem.CequintCallerIdManager.CequintCallerIdContact;
 import com.android.dialer.phonelookup.PhoneLookup;
 import com.android.dialer.phonelookup.PhoneLookupInfo;
 import com.android.dialer.phonelookup.PhoneLookupInfo.CequintInfo;
+import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
+import com.android.dialer.telecom.TelecomCallUtil;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 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;
 
 /** PhoneLookup implementation for Cequint. */
 public class CequintPhoneLookup implements PhoneLookup<CequintInfo> {
 
+  private final Context appContext;
+  private final ListeningExecutorService backgroundExecutorService;
+  private final ListeningExecutorService lightweightExecutorService;
+
   @Inject
-  CequintPhoneLookup() {}
+  CequintPhoneLookup(
+      @ApplicationContext Context appContext,
+      @BackgroundExecutor ListeningExecutorService backgroundExecutorService,
+      @LightweightExecutor ListeningExecutorService lightweightExecutorService) {
+    this.appContext = appContext;
+    this.backgroundExecutorService = backgroundExecutorService;
+    this.lightweightExecutorService = lightweightExecutorService;
+  }
 
   @Override
   public ListenableFuture<CequintInfo> lookup(Context appContext, Call call) {
-    // TODO(a bug): Override the default implementation in the PhoneLookup interface
-    // as a Cequint lookup requires info in the provided call.
-    return Futures.immediateFuture(CequintInfo.getDefaultInstance());
+    if (!CequintCallerIdManager.isCequintCallerIdEnabled(appContext)) {
+      return Futures.immediateFuture(CequintInfo.getDefaultInstance());
+    }
+
+    ListenableFuture<DialerPhoneNumber> dialerPhoneNumberFuture =
+        backgroundExecutorService.submit(
+            () -> {
+              DialerPhoneNumberUtil dialerPhoneNumberUtil = new DialerPhoneNumberUtil();
+              return dialerPhoneNumberUtil.parse(
+                  TelecomCallUtil.getNumber(call), GeoUtil.getCurrentCountryIso(appContext));
+            });
+    String callerDisplayName = call.getDetails().getCallerDisplayName();
+    boolean isIncomingCall = (call.getState() == Call.STATE_RINGING);
+
+    return Futures.transformAsync(
+        dialerPhoneNumberFuture,
+        dialerPhoneNumber ->
+            backgroundExecutorService.submit(
+                () ->
+                    buildCequintInfo(
+                        CequintCallerIdManager.getCequintCallerIdContactForCall(
+                            appContext,
+                            Assert.isNotNull(dialerPhoneNumber).getNormalizedNumber(),
+                            callerDisplayName,
+                            isIncomingCall))),
+        lightweightExecutorService);
   }
 
   @Override
   public ListenableFuture<CequintInfo> lookup(DialerPhoneNumber dialerPhoneNumber) {
-    // TODO(a bug): Implement this method.
-    return Futures.immediateFuture(CequintInfo.getDefaultInstance());
+    if (!CequintCallerIdManager.isCequintCallerIdEnabled(appContext)) {
+      return Futures.immediateFuture(CequintInfo.getDefaultInstance());
+    }
+
+    return backgroundExecutorService.submit(
+        () ->
+            buildCequintInfo(
+                CequintCallerIdManager.getCequintCallerIdContactForNumber(
+                    appContext, dialerPhoneNumber.getNormalizedNumber())));
   }
 
   @Override
@@ -75,16 +127,38 @@
 
   @Override
   public void registerContentObservers() {
-    // No content observers for Cequint info.
+    // No need to register a content observer as the Cequint content provider doesn't support batch
+    // queries.
   }
 
   @Override
   public void unregisterContentObservers() {
-    // No content observers for Cequint info.
+    // Nothing to be done as no content observer is registered.
   }
 
   @Override
   public ListenableFuture<Void> clearData() {
     return Futures.immediateFuture(null);
   }
+
+  /**
+   * Builds a {@link CequintInfo} proto based on the given {@link CequintCallerIdContact} returned
+   * by {@link CequintCallerIdManager}.
+   */
+  private static CequintInfo buildCequintInfo(CequintCallerIdContact cequintCallerIdContact) {
+    CequintInfo.Builder cequintInfoBuilder = CequintInfo.newBuilder();
+
+    // Every field in CequintCallerIdContact can be null.
+    if (!TextUtils.isEmpty(cequintCallerIdContact.name())) {
+      cequintInfoBuilder.setName(cequintCallerIdContact.name());
+    }
+    if (!TextUtils.isEmpty(cequintCallerIdContact.geolocation())) {
+      cequintInfoBuilder.setGeolocation(cequintCallerIdContact.geolocation());
+    }
+    if (!TextUtils.isEmpty(cequintCallerIdContact.photoUri())) {
+      cequintInfoBuilder.setPhotoUri(cequintCallerIdContact.photoUri());
+    }
+
+    return cequintInfoBuilder.build();
+  }
 }
diff --git a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
index 07aea74..23ecc83 100644
--- a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
+++ b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
@@ -46,6 +46,7 @@
     NameSource.CP2_DEFAULT_DIRECTORY,
     NameSource.CP2_EXTENDED_DIRECTORY,
     NameSource.PEOPLE_API,
+    NameSource.CEQUINT,
     NameSource.CNAP
   })
   @interface NameSource {
@@ -53,7 +54,8 @@
     int CP2_DEFAULT_DIRECTORY = 1;
     int CP2_EXTENDED_DIRECTORY = 2;
     int PEOPLE_API = 3;
-    int CNAP = 4;
+    int CEQUINT = 4;
+    int CNAP = 5;
   }
 
   /**
@@ -78,6 +80,7 @@
           NameSource.CP2_DEFAULT_DIRECTORY,
           NameSource.CP2_EXTENDED_DIRECTORY,
           NameSource.PEOPLE_API,
+          NameSource.CEQUINT,
           NameSource.CNAP);
 
   private final @NameSource int nameSource;
@@ -106,6 +109,8 @@
         return ContactSource.Type.SOURCE_TYPE_EXTENDED;
       case NameSource.PEOPLE_API:
         return getRefinedPeopleApiSource();
+      case NameSource.CEQUINT:
+        return ContactSource.Type.SOURCE_TYPE_CEQUINT_CALLER_ID;
       case NameSource.CNAP:
         return ContactSource.Type.SOURCE_TYPE_CNAP;
       case NameSource.NONE:
@@ -146,6 +151,8 @@
         return Assert.isNotNull(firstExtendedCp2Contact).getName();
       case NameSource.PEOPLE_API:
         return phoneLookupInfo.getPeopleApiInfo().getDisplayName();
+      case NameSource.CEQUINT:
+        return phoneLookupInfo.getCequintInfo().getName();
       case NameSource.CNAP:
         return phoneLookupInfo.getCnapInfo().getName();
       case NameSource.NONE:
@@ -170,6 +177,7 @@
       case NameSource.CP2_EXTENDED_DIRECTORY:
         return Assert.isNotNull(firstExtendedCp2Contact).getPhotoThumbnailUri();
       case NameSource.PEOPLE_API:
+      case NameSource.CEQUINT:
       case NameSource.CNAP:
       case NameSource.NONE:
         return "";
@@ -192,6 +200,8 @@
         return Assert.isNotNull(firstDefaultCp2Contact).getPhotoUri();
       case NameSource.CP2_EXTENDED_DIRECTORY:
         return Assert.isNotNull(firstExtendedCp2Contact).getPhotoUri();
+      case NameSource.CEQUINT:
+        return phoneLookupInfo.getCequintInfo().getPhotoUri();
       case NameSource.PEOPLE_API:
       case NameSource.CNAP:
       case NameSource.NONE:
@@ -213,6 +223,7 @@
       case NameSource.CP2_EXTENDED_DIRECTORY:
         return Math.max(Assert.isNotNull(firstExtendedCp2Contact).getPhotoId(), 0);
       case NameSource.PEOPLE_API:
+      case NameSource.CEQUINT:
       case NameSource.CNAP:
       case NameSource.NONE:
         return 0;
@@ -235,6 +246,7 @@
         return Assert.isNotNull(firstExtendedCp2Contact).getLookupUri();
       case NameSource.PEOPLE_API:
         return Assert.isNotNull(phoneLookupInfo.getPeopleApiInfo().getLookupUri());
+      case NameSource.CEQUINT:
       case NameSource.CNAP:
       case NameSource.NONE:
         return "";
@@ -259,6 +271,30 @@
       case NameSource.CP2_EXTENDED_DIRECTORY:
         return Assert.isNotNull(firstExtendedCp2Contact).getLabel();
       case NameSource.PEOPLE_API:
+      case NameSource.CEQUINT:
+      case NameSource.CNAP:
+      case NameSource.NONE:
+        return "";
+      default:
+        throw Assert.createUnsupportedOperationFailException(
+            String.format("Unsupported name source: %s", nameSource));
+    }
+  }
+
+  /**
+   * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method
+   * returns the number's geolocation (which is for display purpose only).
+   *
+   * <p>If no geolocation can be obtained from the {@link PhoneLookupInfo}, an empty string will be
+   * returned.
+   */
+  public String getGeolocation() {
+    switch (nameSource) {
+      case NameSource.CEQUINT:
+        return phoneLookupInfo.getCequintInfo().getGeolocation();
+      case NameSource.CP2_DEFAULT_DIRECTORY:
+      case NameSource.CP2_EXTENDED_DIRECTORY:
+      case NameSource.PEOPLE_API:
       case NameSource.CNAP:
       case NameSource.NONE:
         return "";
@@ -320,6 +356,7 @@
     switch (nameSource) {
       case NameSource.CP2_DEFAULT_DIRECTORY:
       case NameSource.CP2_EXTENDED_DIRECTORY:
+      case NameSource.CEQUINT:
       case NameSource.CNAP:
       case NameSource.NONE:
         return false;
@@ -343,6 +380,7 @@
         return Assert.isNotNull(firstDefaultCp2Contact).getCanSupportCarrierVideoCall();
       case NameSource.CP2_EXTENDED_DIRECTORY:
       case NameSource.PEOPLE_API:
+      case NameSource.CEQUINT:
       case NameSource.CNAP:
       case NameSource.NONE:
         return false;
@@ -396,6 +434,11 @@
             return NameSource.PEOPLE_API;
           }
           break;
+        case NameSource.CEQUINT:
+          if (!phoneLookupInfo.getCequintInfo().getName().isEmpty()) {
+            return NameSource.CEQUINT;
+          }
+          break;
         case NameSource.CNAP:
           if (!phoneLookupInfo.getCnapInfo().getName().isEmpty()) {
             return NameSource.CNAP;
diff --git a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java
index 4302436..6179b5d 100644
--- a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java
+++ b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java
@@ -621,7 +621,7 @@
       return;
     }
     CequintCallerIdContact cequintCallerIdContact =
-        cequintCallerIdManager.getCequintCallerIdContact(context, number);
+        cequintCallerIdManager.getCachedCequintCallerIdContact(context, number);
     if (cequintCallerIdContact == null) {
       return;
     }
diff --git a/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java b/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java
index 891ac44..6a8cde8 100644
--- a/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java
+++ b/java/com/android/dialer/spam/promo/SpamBlockingPromoHelper.java
@@ -34,14 +34,14 @@
 import com.android.dialer.notification.DialerNotificationManager;
 import com.android.dialer.notification.NotificationChannelId;
 import com.android.dialer.spam.SpamSettings;
-import com.android.dialer.spam.SpamSettings.ModifySettingListener;
+import com.android.dialer.spam.promo.SpamBlockingPromoDialogFragment.OnEnableListener;
 
 /** Helper class for showing spam blocking on-boarding promotions. */
 public class SpamBlockingPromoHelper {
 
   static final String SPAM_BLOCKING_PROMO_PERIOD_MILLIS = "spam_blocking_promo_period_millis";
   static final String SPAM_BLOCKING_PROMO_LAST_SHOW_MILLIS = "spam_blocking_promo_last_show_millis";
-  static final String ENABLE_SPAM_BLOCKING_PROMO = "enable_spam_blocking_promo";
+  public static final String ENABLE_SPAM_BLOCKING_PROMO = "enable_spam_blocking_promo";
 
   private final Context context;
   private final SpamSettings spamSettings;
@@ -81,33 +81,15 @@
    * Shows a spam blocking promo dialog.
    *
    * @param fragmentManager the fragment manager to show the dialog.
-   * @param modifySettingListener the listener called after spam blocking setting is modified.
+   * @param onEnableListener the listener called when enable button is clicked.
    * @param onDismissListener the listener called when the dialog is dismissed.
    */
   public void showSpamBlockingPromoDialog(
       FragmentManager fragmentManager,
-      ModifySettingListener modifySettingListener,
+      OnEnableListener onEnableListener,
       OnDismissListener onDismissListener) {
     updateLastShowSpamTimestamp();
-    Logger.get(context).logImpression(DialerImpression.Type.SPAM_BLOCKING_CALL_LOG_PROMO_SHOWN);
-    SpamBlockingPromoDialogFragment.newInstance(
-            () -> {
-              Logger.get(context)
-                  .logImpression(
-                      DialerImpression.Type.SPAM_BLOCKING_ENABLED_THROUGH_CALL_LOG_PROMO);
-              spamSettings.modifySpamBlockingSetting(
-                  true,
-                  success -> {
-                    if (!success) {
-                      Logger.get(context)
-                          .logImpression(
-                              DialerImpression.Type
-                                  .SPAM_BLOCKING_MODIFY_FAILURE_THROUGH_CALL_LOG_PROMO);
-                    }
-                    modifySettingListener.onComplete(success);
-                  });
-            },
-            onDismissListener)
+    SpamBlockingPromoDialogFragment.newInstance(onEnableListener, onDismissListener)
         .show(fragmentManager, SpamBlockingPromoDialogFragment.SPAM_BLOCKING_PROMO_DIALOG_TAG);
   }
 
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index b76db1c..018f978 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -16,6 +16,7 @@
 
 package com.android.dialer.speeddial;
 
+import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
@@ -41,6 +42,12 @@
 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;
+import com.android.dialer.historyitemactions.HistoryItemBottomSheetHeaderInfo;
+import com.android.dialer.historyitemactions.IntentModule;
+import com.android.dialer.historyitemactions.SharedModules;
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
 import com.android.dialer.precall.PreCall;
@@ -54,8 +61,11 @@
 import com.android.dialer.speeddial.loader.SpeedDialUiItem;
 import com.android.dialer.speeddial.loader.UiItemLoaderComponent;
 import com.android.dialer.util.IntentUtil;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Futures;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Fragment for displaying:
@@ -301,8 +311,61 @@
   private final class SpeedDialSuggestedListener implements SuggestedContactsListener {
 
     @Override
-    public void onOverFlowMenuClicked(SpeedDialUiItem speedDialUiItem) {
-      // TODO(calderwoodra) show overflow menu for suggested contacts
+    public void onOverFlowMenuClicked(
+        SpeedDialUiItem speedDialUiItem, HistoryItemBottomSheetHeaderInfo headerInfo) {
+      List<HistoryItemActionModule> modules = new ArrayList<>();
+      Channel defaultChannel = speedDialUiItem.defaultChannel();
+
+      // Add voice call module
+      Channel voiceChannel = speedDialUiItem.getDeterministicVoiceChannel();
+      if (voiceChannel != null) {
+        modules.add(
+            IntentModule.newCallModule(
+                getContext(),
+                new CallIntentBuilder(voiceChannel.number(), CallInitiationType.Type.SPEED_DIAL)
+                    .setAllowAssistedDial(true)));
+      } else {
+        modules.add(new DisambigDialogModule(speedDialUiItem, /* isVideo = */ false));
+      }
+
+      // Add video if we can determine the correct channel
+      Channel videoChannel = speedDialUiItem.getDeterministicVideoChannel();
+      if (videoChannel != null) {
+        modules.add(
+            IntentModule.newCallModule(
+                getContext(),
+                new CallIntentBuilder(videoChannel.number(), CallInitiationType.Type.SPEED_DIAL)
+                    .setIsVideoCall(true)
+                    .setAllowAssistedDial(true)));
+      } else if (speedDialUiItem.hasVideoChannels()) {
+        modules.add(new DisambigDialogModule(speedDialUiItem, /* isVideo = */ true));
+      }
+
+      // Add sms module
+      Optional<HistoryItemActionModule> smsModule =
+          SharedModules.createModuleForSendingTextMessage(
+              getContext(), defaultChannel.number(), false);
+      if (smsModule.isPresent()) {
+        modules.add(smsModule.get());
+      }
+
+      modules.add(new DividerModule());
+
+      // TODO(calderwoodra): add to favorites module
+      // TODO(calderwoodra): remove from strequent module
+
+      // Contact info module
+      modules.add(
+          new ContactInfoModule(
+              getContext(),
+              new Intent(
+                  Intent.ACTION_VIEW,
+                  Uri.withAppendedPath(
+                      Contacts.CONTENT_URI, String.valueOf(speedDialUiItem.contactId()))),
+              R.string.contact_menu_contact_info,
+              R.drawable.context_menu_contact_icon));
+
+      HistoryItemActionBottomSheet.show(getContext(), headerInfo, modules);
     }
 
     @Override
@@ -321,6 +384,53 @@
           new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL)
               .setIsVideoCall(channel.isVideoTechnology()));
     }
+
+    private final class ContactInfoModule extends IntentModule {
+
+      public ContactInfoModule(Context context, Intent intent, int text, int image) {
+        super(context, intent, text, image);
+      }
+
+      @Override
+      public boolean tintDrawable() {
+        return false;
+      }
+    }
+
+    private final class DisambigDialogModule implements HistoryItemActionModule {
+
+      private final SpeedDialUiItem speedDialUiItem;
+      private final boolean isVideo;
+
+      DisambigDialogModule(SpeedDialUiItem speedDialUiItem, boolean isVideo) {
+        this.speedDialUiItem = speedDialUiItem;
+        this.isVideo = isVideo;
+      }
+
+      @Override
+      public int getStringId() {
+        if (isVideo) {
+          return R.string.contact_menu_video_call;
+        } else {
+          return R.string.contact_menu_voice_call;
+        }
+      }
+
+      @Override
+      public int getDrawableId() {
+        if (isVideo) {
+          return R.drawable.quantum_ic_videocam_vd_theme_24;
+        } else {
+          return R.drawable.quantum_ic_phone_vd_theme_24;
+        }
+      }
+
+      @Override
+      public boolean onClick() {
+        DisambigDialog.show(speedDialUiItem, getChildFragmentManager());
+        return true;
+      }
+    }
   }
 
   private static final class SpeedDialContextMenuItemListener implements ContextMenuItemListener {
diff --git a/java/com/android/dialer/speeddial/SuggestionViewHolder.java b/java/com/android/dialer/speeddial/SuggestionViewHolder.java
index 546ffbd..578e0b3 100644
--- a/java/com/android/dialer/speeddial/SuggestionViewHolder.java
+++ b/java/com/android/dialer/speeddial/SuggestionViewHolder.java
@@ -17,27 +17,25 @@
 package com.android.dialer.speeddial;
 
 import android.content.Context;
-import android.provider.ContactsContract.Contacts;
 import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.widget.QuickContactBadge;
 import android.widget.TextView;
 import com.android.dialer.common.Assert;
-import com.android.dialer.glidephotomanager.GlidePhotoManagerComponent;
-import com.android.dialer.glidephotomanager.PhotoInfo;
+import com.android.dialer.historyitemactions.HistoryItemBottomSheetHeaderInfo;
 import com.android.dialer.location.GeoUtil;
 import com.android.dialer.phonenumberutil.PhoneNumberHelper;
 import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
 import com.android.dialer.speeddial.loader.SpeedDialUiItem;
+import com.android.dialer.widget.ContactPhotoView;
 
 /** ViewHolder for displaying suggested contacts in {@link SpeedDialFragment}. */
 public class SuggestionViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
 
   private final SuggestedContactsListener listener;
 
-  private final QuickContactBadge photoView;
+  private final ContactPhotoView photoView;
   private final TextView nameOrNumberView;
   private final TextView numberView;
 
@@ -71,34 +69,31 @@
     nameOrNumberView.setText(speedDialUiItem.name());
     numberView.setText(secondaryInfo);
 
-    GlidePhotoManagerComponent.get(context)
-        .glidePhotoManager()
-        .loadQuickContactBadge(
-            photoView,
-            PhotoInfo.newBuilder()
-                .setPhotoId(speedDialUiItem.photoId())
-                .setPhotoUri(speedDialUiItem.photoUri())
-                .setName(speedDialUiItem.name())
-                .setIsVideo(speedDialUiItem.defaultChannel().isVideoTechnology())
-                .setLookupUri(
-                    Contacts.getLookupUri(speedDialUiItem.contactId(), speedDialUiItem.lookupKey())
-                        .toString())
-                .build());
+    photoView.setPhoto(speedDialUiItem.getPhotoInfo());
   }
 
   @Override
   public void onClick(View v) {
     if (v.getId() == R.id.overflow) {
-      listener.onOverFlowMenuClicked(speedDialUiItem);
+      listener.onOverFlowMenuClicked(speedDialUiItem, getHeaderInfo());
     } else {
       listener.onRowClicked(speedDialUiItem.defaultChannel());
     }
   }
 
+  private HistoryItemBottomSheetHeaderInfo getHeaderInfo() {
+    return HistoryItemBottomSheetHeaderInfo.newBuilder()
+        .setPhotoInfo(speedDialUiItem.getPhotoInfo())
+        .setPrimaryText(nameOrNumberView.getText().toString())
+        .setSecondaryText(numberView.getText().toString())
+        .build();
+  }
+
   /** Listener/Callback for {@link SuggestionViewHolder} parents. */
   public interface SuggestedContactsListener {
 
-    void onOverFlowMenuClicked(SpeedDialUiItem speedDialUiItem);
+    void onOverFlowMenuClicked(
+        SpeedDialUiItem speedDialUiItem, HistoryItemBottomSheetHeaderInfo headerInfo);
 
     /** Called when a suggested contact is clicked. */
     void onRowClicked(Channel channel);
diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
index a2bdfb8..c5a3ea3 100644
--- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
+++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java
@@ -18,10 +18,12 @@
 
 import android.database.Cursor;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import com.android.dialer.common.Assert;
+import com.android.dialer.glidephotomanager.PhotoInfo;
 import com.android.dialer.speeddial.database.SpeedDialEntry;
 import com.android.dialer.speeddial.database.SpeedDialEntry.Channel;
 import com.google.auto.value.AutoValue;
@@ -139,6 +141,16 @@
     return builder.build();
   }
 
+  public PhotoInfo getPhotoInfo() {
+    return PhotoInfo.newBuilder()
+        .setPhotoId(photoId())
+        .setPhotoUri(photoUri())
+        .setName(name())
+        .setIsVideo(defaultChannel() != null && defaultChannel().isVideoTechnology())
+        .setLookupUri(Contacts.getLookupUri(contactId(), lookupKey()).toString())
+        .build();
+  }
+
   public SpeedDialEntry buildSpeedDialEntry() {
     return SpeedDialEntry.builder()
         .setId(speedDialEntryId())
diff --git a/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml b/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml
index ff95b59..8686060 100644
--- a/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml
+++ b/java/com/android/dialer/speeddial/res/layout/suggestion_row_layout.xml
@@ -21,7 +21,7 @@
     android:minHeight="72dp"
     android:background="?android:attr/selectableItemBackground">
 
-  <QuickContactBadge
+  <com.android.dialer.widget.ContactPhotoView
       android:id="@+id/avatar"
       android:layout_width="48dp"
       android:layout_height="48dp"
@@ -59,5 +59,6 @@
       android:scaleType="center"
       android:tint="@color/secondary_text_color"
       android:src="@drawable/quantum_ic_more_vert_white_24"
-      android:background="?android:selectableItemBackgroundBorderless"/>
+      android:background="?android:selectableItemBackgroundBorderless"
+      android:contentDescription="@string/content_description_overflow"/>
 </RelativeLayout>
\ No newline at end of file
diff --git a/java/com/android/incallui/ContactInfoCache.java b/java/com/android/incallui/ContactInfoCache.java
index eefd483..81c7b72 100644
--- a/java/com/android/incallui/ContactInfoCache.java
+++ b/java/com/android/incallui/ContactInfoCache.java
@@ -514,7 +514,7 @@
       return;
     }
     CequintCallerIdContact cequintCallerIdContact =
-        CequintCallerIdManager.getCequintCallerIdContactForInCall(
+        CequintCallerIdManager.getCequintCallerIdContactForCall(
             context, callerInfo.phoneNumber, cnapName, isIncoming);
 
     if (cequintCallerIdContact == null) {
diff --git a/java/com/android/incallui/rtt/impl/AdvisoryViewHolder.java b/java/com/android/incallui/rtt/impl/AdvisoryViewHolder.java
new file mode 100644
index 0000000..8f081be
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/AdvisoryViewHolder.java
@@ -0,0 +1,29 @@
+/*
+ * 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.rtt.impl;
+
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.view.View;
+
+/** ViewHolder class for RTT advisory text. */
+public class AdvisoryViewHolder extends ViewHolder {
+
+  public AdvisoryViewHolder(@NonNull View itemView) {
+    super(itemView);
+  }
+}
diff --git a/java/com/android/incallui/rtt/impl/RttChatAdapter.java b/java/com/android/incallui/rtt/impl/RttChatAdapter.java
index 6922663..f1cde75 100644
--- a/java/com/android/incallui/rtt/impl/RttChatAdapter.java
+++ b/java/com/android/incallui/rtt/impl/RttChatAdapter.java
@@ -18,8 +18,10 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -27,11 +29,29 @@
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.rtt.RttTranscript;
 import com.android.dialer.rtt.RttTranscriptMessage;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
 /** Adapter class for holding RTT chat data. */
-public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolder> {
+public class RttChatAdapter extends RecyclerView.Adapter<ViewHolder> {
+
+  /** IntDef for the different types of rows that can be shown in the call log. */
+  @Retention(RetentionPolicy.SOURCE)
+  @IntDef({
+    RowType.ADVISORY,
+    RowType.MESSAGE,
+  })
+  @interface RowType {
+    /** The transcript advisory message. */
+    int ADVISORY = 1;
+
+    /** RTT chat message. */
+    int MESSAGE = 2;
+  }
+
+  private static final int POSITION_ADVISORY = 0;
 
   private Drawable avatarDrawable;
 
@@ -45,6 +65,7 @@
   private List<RttChatMessage> rttMessages = new ArrayList<>();
   private int lastIndexOfLocalMessage = -1;
   private final MessageListener messageListener;
+  private boolean shouldShowAdvisory;
 
   RttChatAdapter(Context context, MessageListener listener) {
     this.context = context;
@@ -52,29 +73,54 @@
   }
 
   @Override
-  public RttChatMessageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+  public ViewHolder onCreateViewHolder(ViewGroup parent, @RowType int viewType) {
     LayoutInflater layoutInflater = LayoutInflater.from(context);
-    View view = layoutInflater.inflate(R.layout.rtt_chat_list_item, parent, false);
-    return new RttChatMessageViewHolder(view);
+    switch (viewType) {
+      case RowType.ADVISORY:
+        View view = layoutInflater.inflate(R.layout.rtt_transcript_advisory, parent, false);
+        return new AdvisoryViewHolder(view);
+      case RowType.MESSAGE:
+        view = layoutInflater.inflate(R.layout.rtt_chat_list_item, parent, false);
+        return new RttChatMessageViewHolder(view);
+      default:
+        throw new RuntimeException("Unknown row type.");
+    }
   }
 
   @Override
   public int getItemViewType(int position) {
-    return super.getItemViewType(position);
+    if (shouldShowAdvisory && position == POSITION_ADVISORY) {
+      return RowType.ADVISORY;
+    } else {
+      return RowType.MESSAGE;
+    }
   }
 
   @Override
-  public void onBindViewHolder(RttChatMessageViewHolder rttChatMessageViewHolder, int i) {
-    boolean isSameGroup = false;
-    if (i > 0) {
-      isSameGroup = rttMessages.get(i).isRemote == rttMessages.get(i - 1).isRemote;
+  public void onBindViewHolder(ViewHolder viewHolder, int itemPosition) {
+    switch (getItemViewType(itemPosition)) {
+      case RowType.ADVISORY:
+        return;
+      case RowType.MESSAGE:
+        RttChatMessageViewHolder rttChatMessageViewHolder = (RttChatMessageViewHolder) viewHolder;
+        int messagePosition = toMessagePosition(itemPosition);
+        boolean isSameGroup = false;
+        if (messagePosition > 0) {
+          isSameGroup =
+              rttMessages.get(messagePosition).isRemote
+                  == rttMessages.get(messagePosition - 1).isRemote;
+        }
+        rttChatMessageViewHolder.setMessage(
+            rttMessages.get(messagePosition), isSameGroup, avatarDrawable);
+        return;
+      default:
+        throw new RuntimeException("Unknown row type.");
     }
-    rttChatMessageViewHolder.setMessage(rttMessages.get(i), isSameGroup, avatarDrawable);
   }
 
   @Override
   public int getItemCount() {
-    return rttMessages.size();
+    return shouldShowAdvisory ? rttMessages.size() + 1 : rttMessages.size();
   }
 
   private void updateCurrentLocalMessage(String newMessage) {
@@ -96,11 +142,31 @@
         notifyItemRemoved(lastIndexOfLocalMessage);
         lastIndexOfLocalMessage = -1;
       } else {
-        notifyItemChanged(lastIndexOfLocalMessage);
+        notifyItemChanged(toItemPosition(lastIndexOfLocalMessage));
       }
     }
   }
 
+  private int toMessagePosition(int itemPosition) {
+    if (shouldShowAdvisory) {
+      return itemPosition - 1;
+    } else {
+      return itemPosition;
+    }
+  }
+
+  // Converts position in message list into item position in adapter.
+  private int toItemPosition(int messagePosition) {
+    if (messagePosition < 0) {
+      return messagePosition;
+    }
+    if (shouldShowAdvisory) {
+      return messagePosition + 1;
+    } else {
+      return messagePosition;
+    }
+  }
+
   private void updateCurrentRemoteMessage(String newMessage) {
     RttChatMessage.updateRemoteRttChatMessage(rttMessages, newMessage);
     lastIndexOfLocalMessage = RttChatMessage.getLastIndexLocalMessage(rttMessages);
@@ -110,14 +176,14 @@
   void addLocalMessage(String message) {
     updateCurrentLocalMessage(message);
     if (messageListener != null) {
-      messageListener.onUpdateLocalMessage(lastIndexOfLocalMessage);
+      messageListener.onUpdateLocalMessage(toItemPosition(lastIndexOfLocalMessage));
     }
   }
 
   void submitLocalMessage() {
     LogUtil.enterBlock("RttChatAdapater.submitLocalMessage");
     rttMessages.get(lastIndexOfLocalMessage).finish();
-    notifyItemChanged(lastIndexOfLocalMessage);
+    notifyItemChanged(toItemPosition(lastIndexOfLocalMessage));
     lastIndexOfLocalMessage = -1;
   }
 
@@ -139,10 +205,21 @@
     }
     updateCurrentRemoteMessage(message);
     if (messageListener != null) {
-      messageListener.onUpdateRemoteMessage(RttChatMessage.getLastIndexRemoteMessage(rttMessages));
+      messageListener.onUpdateRemoteMessage(
+          toItemPosition(RttChatMessage.getLastIndexRemoteMessage(rttMessages)));
     }
   }
 
+  void hideAdvisory() {
+    shouldShowAdvisory = false;
+    notifyItemRemoved(POSITION_ADVISORY);
+  }
+
+  void showAdvisory() {
+    shouldShowAdvisory = true;
+    notifyItemInserted(POSITION_ADVISORY);
+  }
+
   /**
    * Retrieve last local message and update the index. This is used when deleting to previous
    * message bubble.
diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java
index 47036cd..13e013f 100644
--- a/java/com/android/incallui/rtt/impl/RttChatFragment.java
+++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java
@@ -467,6 +467,7 @@
       if (editText.requestFocus()) {
         UiUtil.openKeyboardFrom(getContext(), editText);
       }
+      adapter.showAdvisory();
     }
     if (primaryCallState.state() == State.DIALING) {
       showWaitingForJoinBanner();
diff --git a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml
index f995185..cff2b3f 100644
--- a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml
+++ b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml
@@ -23,7 +23,7 @@
       android:id="@+id/rtt_recycler_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
-      android:paddingTop="70dp"
+      android:paddingTop="56dp"
       android:paddingBottom="70dp"
       android:clipToPadding="false"/>
 
diff --git a/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml
index 4b03ad8..8a5bba2 100644
--- a/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml
+++ b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml
@@ -19,30 +19,30 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical">
-  <RelativeLayout
+  <LinearLayout
       android:layout_width="match_parent"
-      android:layout_height="?android:attr/actionBarSize"
+      android:layout_height="@dimen/rtt_banner_height"
       android:background="#F305228F"
-      android:elevation="3dp">
+      android:elevation="3dp"
+      android:orientation="horizontal">
     <ImageButton
         android:id="@+id/rtt_end_call_button"
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:layout_marginStart="16dp"
-        android:layout_alignParentStart="true"
-        android:layout_centerVertical="true"
+        android:layout_width="@dimen/rtt_banner_height"
+        android:layout_height="@dimen/rtt_banner_height"
+        android:layout_marginStart="4dp"
+        android:layout_marginEnd="4dp"
+        android:padding="@dimen/rtt_banner_button_padding"
         android:background="@android:color/transparent"
         android:contentDescription="@string/incall_content_description_end_call"
         android:scaleType="fitXY"
         android:src="@drawable/quantum_ic_call_end_vd_theme_24"
         android:tint="#FFDF0000"/>
     <LinearLayout
-        android:layout_width="260dp"
+        android:layout_width="0dp"
         android:layout_height="match_parent"
+        android:layout_weight="1"
         android:layout_marginTop="8dp"
         android:layout_marginBottom="8dp"
-        android:layout_marginStart="32dp"
-        android:layout_toEndOf="@id/rtt_end_call_button"
         android:orientation="vertical">
       <TextView
           android:id="@+id/rtt_name_or_number"
@@ -67,18 +67,17 @@
     </LinearLayout>
     <ImageButton
         android:id="@+id/rtt_overflow_button"
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:layout_marginEnd="12dp"
-        android:layout_alignParentEnd="true"
-        android:layout_centerVertical="true"
+        android:layout_width="@dimen/rtt_banner_height"
+        android:layout_height="@dimen/rtt_banner_height"
+        android:layout_marginStart="8dp"
+        android:padding="@dimen/rtt_banner_button_padding"
         android:background="@android:color/transparent"
         android:contentDescription="@string/content_description_overflow"
         android:scaleType="fitXY"
         android:src="@drawable/quantum_ic_more_vert_vd_theme_24"
         android:tint="#FFFFFF"/>
 
-  </RelativeLayout>
+  </LinearLayout>
   <FrameLayout
       android:id="@id/rtt_on_hold_banner"
       android:layout_width="match_parent"
diff --git a/java/com/android/incallui/rtt/impl/res/layout/rtt_transcript_advisory.xml b/java/com/android/incallui/rtt/impl/res/layout/rtt_transcript_advisory.xml
new file mode 100644
index 0000000..a2cf3e7
--- /dev/null
+++ b/java/com/android/incallui/rtt/impl/res/layout/rtt_transcript_advisory.xml
@@ -0,0 +1,41 @@
+<?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="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="24dp"
+    android:paddingBottom="16dp"
+    android:orientation="vertical">
+  <ImageView
+      android:layout_width="16dp"
+      android:layout_height="16dp"
+      android:layout_gravity="center_horizontal"
+      android:src="@drawable/quantum_ic_question_answer_vd_theme_24"
+      android:tint="#DEFFFFFF"
+      android:tintMode="src_in"/>
+  <TextView
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:paddingTop="4dp"
+      android:paddingBottom="9dp"
+      android:paddingStart="64dp"
+      android:paddingEnd="64dp"
+      android:gravity="center_horizontal"
+      android:text="@string/rtt_transcript_advisory"
+      android:textColor="#FFFFFF"
+      android:textSize="12sp"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/java/com/android/incallui/rtt/impl/res/values/dimens.xml b/java/com/android/incallui/rtt/impl/res/values/dimens.xml
index c3d28da..cab9da0 100644
--- a/java/com/android/incallui/rtt/impl/res/values/dimens.xml
+++ b/java/com/android/incallui/rtt/impl/res/values/dimens.xml
@@ -20,4 +20,7 @@
   <dimen name="rtt_overflow_menu_width">180dp</dimen>
   <dimen name="rtt_overflow_menu_elevation">8dp</dimen>
   <dimen name="rtt_avatar_size">40dp</dimen>
+  <dimen name="rtt_banner_height">56dp</dimen>
+  <!-- This is used to control image size inside the button. -->
+  <dimen name="rtt_banner_button_padding">12dp</dimen>
 </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 1d09f54..462eea5 100644
--- a/java/com/android/incallui/rtt/impl/res/values/strings.xml
+++ b/java/com/android/incallui/rtt/impl/res/values/strings.xml
@@ -30,4 +30,7 @@
   <!-- 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>
 
+  <!-- Text for RTT transcript advisory. [CHAR LIMIT=NONE] -->
+  <string name="rtt_transcript_advisory">The other party can see you typing. Transcripts stored on your device in the call history.</string>
+
 </resources>
\ No newline at end of file
diff --git a/java/com/android/incallui/spam/SpamNotificationActivity.java b/java/com/android/incallui/spam/SpamNotificationActivity.java
index c04a071..e10dea3 100644
--- a/java/com/android/incallui/spam/SpamNotificationActivity.java
+++ b/java/com/android/incallui/spam/SpamNotificationActivity.java
@@ -43,6 +43,7 @@
 import com.android.dialer.notification.DialerNotificationManager;
 import com.android.dialer.phonenumberutil.PhoneNumberHelper;
 import com.android.dialer.spam.SpamComponent;
+import com.android.dialer.spam.SpamSettings;
 import com.android.dialer.spam.promo.SpamBlockingPromoHelper;
 import com.android.incallui.call.DialerCall;
 
@@ -86,6 +87,8 @@
         }
       };
   private FilteredNumberAsyncQueryHandler filteredNumberAsyncQueryHandler;
+  private SpamSettings spamSettings;
+  private SpamBlockingPromoHelper spamBlockingPromoHelper;
 
   /**
    * Creates an intent to start this activity.
@@ -160,6 +163,8 @@
     super.onCreate(savedInstanceState);
     setFinishOnTouchOutside(true);
     filteredNumberAsyncQueryHandler = new FilteredNumberAsyncQueryHandler(this);
+    spamSettings = SpamComponent.get(this).spamSettings();
+    spamBlockingPromoHelper = new SpamBlockingPromoHelper(getApplicationContext(), spamSettings);
     cancelNotification();
   }
 
@@ -417,7 +422,7 @@
                   dismiss();
                   spamNotificationActivity.maybeShowBlockReportSpamDialog(
                       number, contactLookupResultType);
-                  spamNotificationActivity.showSpamBlockingPromoDialog();
+                  spamNotificationActivity.maybeShowSpamBlockingPromoAndFinish();
                 }
               })
           .setNegativeButton(
@@ -515,23 +520,43 @@
                   dismiss();
                   spamNotificationActivity.maybeShowBlockReportSpamDialog(
                       number, contactLookupResultType);
+                  spamNotificationActivity.maybeShowSpamBlockingPromoAndFinish();
                 }
               })
           .create();
     }
   }
 
-  private void showSpamBlockingPromoDialog() {
-    SpamBlockingPromoHelper spamBlockingPromoHelper =
-        new SpamBlockingPromoHelper(
-            getApplicationContext(), SpamComponent.get(this).spamSettings());
+  private void maybeShowSpamBlockingPromoAndFinish() {
     if (!spamBlockingPromoHelper.shouldShowSpamBlockingPromo()) {
       finish();
-    } else {
-      spamBlockingPromoHelper.showSpamBlockingPromoDialog(
-          getFragmentManager(),
-          success -> spamBlockingPromoHelper.showModifySettingOnCompleteToast(success),
-          dialog -> finish());
+      return;
     }
+    Logger.get(this)
+        .logImpression(DialerImpression.Type.SPAM_BLOCKING_AFTER_CALL_NOTIFICATION_PROMO_SHOWN);
+    showSpamBlockingPromoDialog();
+  }
+
+  private void showSpamBlockingPromoDialog() {
+    spamBlockingPromoHelper.showSpamBlockingPromoDialog(
+        getFragmentManager(),
+        () -> {
+          Logger.get(this)
+              .logImpression(
+                  DialerImpression.Type
+                      .SPAM_BLOCKING_ENABLED_THROUGH_AFTER_CALL_NOTIFICATION_PROMO);
+          spamSettings.modifySpamBlockingSetting(
+              true,
+              success -> {
+                if (!success) {
+                  Logger.get(this)
+                      .logImpression(
+                          DialerImpression.Type
+                              .SPAM_BLOCKING_MODIFY_FAILURE_THROUGH_AFTER_CALL_NOTIFICATION_PROMO);
+                }
+                spamBlockingPromoHelper.showModifySettingOnCompleteToast(success);
+              });
+        },
+        dialog -> finish());
   }
 }