Merge "Add glide annotation processor"
diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java
index 963967f..9e1d4a7 100644
--- a/java/com/android/dialer/app/calllog/CallLogAdapter.java
+++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java
@@ -66,7 +66,6 @@
 import com.android.dialer.calldetails.CallDetailsEntries.CallDetailsEntry;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction;
-import com.android.dialer.calllogutils.PhoneAccountUtils;
 import com.android.dialer.calllogutils.PhoneCallDetails;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
@@ -91,6 +90,7 @@
 import com.android.dialer.phonenumbercache.ContactInfoHelper;
 import com.android.dialer.phonenumberutil.PhoneNumberHelper;
 import com.android.dialer.spam.SpamComponent;
+import com.android.dialer.telecom.TelecomUtil;
 import com.android.dialer.util.PermissionsUtil;
 import java.util.ArrayList;
 import java.util.Map;
@@ -1020,7 +1020,7 @@
     }
 
     final PhoneAccountHandle accountHandle =
-        PhoneAccountUtils.getAccount(details.accountComponentName, details.accountId);
+        TelecomUtil.composePhoneAccountHandle(details.accountComponentName, details.accountId);
 
     final boolean isVoicemailNumber = callLogCache.isVoicemailNumber(accountHandle, details.number);
 
diff --git a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java
index a0bbfa0..0ddfb9f 100644
--- a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java
+++ b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java
@@ -31,11 +31,11 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import com.android.dialer.app.R;
-import com.android.dialer.calllogutils.PhoneAccountUtils;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.notification.DialerNotificationManager;
 import com.android.dialer.notification.NotificationChannelManager;
+import com.android.dialer.telecom.TelecomUtil;
 
 /** Shows a notification in the status bar for legacy vociemail. */
 @TargetApi(VERSION_CODES.O)
@@ -137,7 +137,7 @@
   @NonNull
   private static String getNotificationText(
       @NonNull Context context, PhoneAccountHandle handle, String voicemailNumber) {
-    if (PhoneAccountUtils.getSubscriptionPhoneAccounts(context).size() > 1) {
+    if (TelecomUtil.getCallCapablePhoneAccounts(context).size() > 1) {
       TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
       PhoneAccount phoneAccount = telecomManager.getPhoneAccount(handle);
       return phoneAccount.getShortDescription().toString();
diff --git a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
index eed77eb..fea3e91 100644
--- a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
+++ b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
@@ -41,6 +41,7 @@
           + (AnnotatedCallLog.TIMESTAMP + " integer, ")
           + (AnnotatedCallLog.NUMBER + " blob, ")
           + (AnnotatedCallLog.FORMATTED_NUMBER + " text, ")
+          + (AnnotatedCallLog.NUMBER_PRESENTATION + " integer, ")
           + (AnnotatedCallLog.DURATION + " integer, ")
           + (AnnotatedCallLog.DATA_USAGE + " integer, ")
           + (AnnotatedCallLog.IS_READ + " integer, ")
@@ -54,7 +55,7 @@
           + (AnnotatedCallLog.TRANSCRIPTION + " integer, ")
           + (AnnotatedCallLog.VOICEMAIL_URI + " text, ")
           + (AnnotatedCallLog.CALL_TYPE + " integer not null, ")
-          + (AnnotatedCallLog.NUMBER_ATTRIBUTES + " blob ")
+          + (AnnotatedCallLog.NUMBER_ATTRIBUTES + " blob")
           + ");";
 
   /**
diff --git a/java/com/android/dialer/calllog/database/Coalescer.java b/java/com/android/dialer/calllog/database/Coalescer.java
index b10dea2..ed09eea 100644
--- a/java/com/android/dialer/calllog/database/Coalescer.java
+++ b/java/com/android/dialer/calllog/database/Coalescer.java
@@ -27,10 +27,10 @@
 import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.CoalescedAnnotatedCallLog;
 import com.android.dialer.calllog.datasources.CallLogDataSource;
 import com.android.dialer.calllog.datasources.DataSources;
-import com.android.dialer.calllogutils.PhoneAccountUtils;
 import com.android.dialer.common.Assert;
 import com.android.dialer.compat.telephony.TelephonyManagerCompat;
 import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
+import com.android.dialer.telecom.TelecomUtil;
 import com.google.common.base.Preconditions;
 import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import com.google.protobuf.InvalidProtocolBufferException;
@@ -144,11 +144,11 @@
       DialerPhoneNumberUtil dialerPhoneNumberUtil, ContentValues row1, ContentValues row2) {
     // Don't combine rows which don't use the same phone account.
     PhoneAccountHandle phoneAccount1 =
-        PhoneAccountUtils.getAccount(
+        TelecomUtil.composePhoneAccountHandle(
             row1.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME),
             row1.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_ID));
     PhoneAccountHandle phoneAccount2 =
-        PhoneAccountUtils.getAccount(
+        TelecomUtil.composePhoneAccountHandle(
             row2.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME),
             row2.getAsString(AnnotatedCallLog.PHONE_ACCOUNT_ID));
 
@@ -156,6 +156,11 @@
       return false;
     }
 
+    if (!row1.getAsInteger(AnnotatedCallLog.NUMBER_PRESENTATION)
+        .equals(row2.getAsInteger(AnnotatedCallLog.NUMBER_PRESENTATION))) {
+      return false;
+    }
+
     if (!meetsAssistedDialingCriteria(row1, row2)) {
       return false;
     }
diff --git a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
index 96a6409..4fee4e5 100644
--- a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
+++ b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
@@ -59,6 +59,13 @@
     String FORMATTED_NUMBER = "formatted_number";
 
     /**
+     * See {@link android.provider.CallLog.Calls#NUMBER_PRESENTATION}.
+     *
+     * <p>Type: INTEGER (int)
+     */
+    String NUMBER_PRESENTATION = "presentation";
+
+    /**
      * See {@link android.provider.CallLog.Calls#IS_READ}.
      *
      * <p>TYPE: INTEGER (boolean)
@@ -136,6 +143,7 @@
           TIMESTAMP,
           NUMBER,
           FORMATTED_NUMBER,
+          NUMBER_PRESENTATION,
           IS_READ,
           NEW,
           GEOCODED_LOCATION,
@@ -145,7 +153,7 @@
           PHONE_ACCOUNT_COLOR,
           FEATURES,
           NUMBER_ATTRIBUTES,
-          CALL_TYPE,
+          CALL_TYPE
         };
   }
 
diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
index 14cde46..24410ee 100644
--- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -51,6 +51,7 @@
 import com.android.dialer.common.concurrent.ThreadUtil;
 import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
 import com.android.dialer.storage.StorageComponent;
+import com.android.dialer.telecom.TelecomUtil;
 import com.android.dialer.theme.R;
 import com.android.dialer.util.PermissionsUtil;
 import com.google.common.collect.Iterables;
@@ -195,6 +196,7 @@
         // recent one.
         .useMostRecentBlob(AnnotatedCallLog.NUMBER)
         .useMostRecentString(AnnotatedCallLog.FORMATTED_NUMBER)
+        .useSingleValueInt(AnnotatedCallLog.NUMBER_PRESENTATION)
         .useMostRecentString(AnnotatedCallLog.GEOCODED_LOCATION)
         .useSingleValueString(AnnotatedCallLog.PHONE_ACCOUNT_COMPONENT_NAME)
         .useSingleValueString(AnnotatedCallLog.PHONE_ACCOUNT_ID)
@@ -238,6 +240,7 @@
                   Calls.DATE,
                   Calls.LAST_MODIFIED, // TODO(a bug): Not available in M
                   Calls.NUMBER,
+                  Calls.NUMBER_PRESENTATION,
                   Calls.TYPE,
                   Calls.COUNTRY_ISO,
                   Calls.DURATION,
@@ -250,7 +253,7 @@
                   Calls.PHONE_ACCOUNT_COMPONENT_NAME,
                   Calls.PHONE_ACCOUNT_ID,
                   Calls.FEATURES,
-                  Calls.POST_DIAL_DIGITS, // TODO(a bug): Not available in M
+                  Calls.POST_DIAL_DIGITS // TODO(a bug): Not available in M
                 },
                 // TODO(a bug): LAST_MODIFIED not available on M
                 Calls.LAST_MODIFIED + " > ? AND " + Voicemails.DELETED + " = 0",
@@ -272,6 +275,7 @@
         int dateColumn = cursor.getColumnIndexOrThrow(Calls.DATE);
         int lastModifiedColumn = cursor.getColumnIndexOrThrow(Calls.LAST_MODIFIED);
         int numberColumn = cursor.getColumnIndexOrThrow(Calls.NUMBER);
+        int presentationColumn = cursor.getColumnIndexOrThrow(Calls.NUMBER_PRESENTATION);
         int typeColumn = cursor.getColumnIndexOrThrow(Calls.TYPE);
         int countryIsoColumn = cursor.getColumnIndexOrThrow(Calls.COUNTRY_ISO);
         int durationsColumn = cursor.getColumnIndexOrThrow(Calls.DURATION);
@@ -294,11 +298,18 @@
           long id = cursor.getLong(idColumn);
           long date = cursor.getLong(dateColumn);
           String numberAsStr = cursor.getString(numberColumn);
-          long type;
+          int type;
           if (cursor.isNull(typeColumn) || (type = cursor.getInt(typeColumn)) == 0) {
             // CallLog.Calls#TYPE lists the allowed values, which are non-null and non-zero.
             throw new IllegalStateException("call type is missing");
           }
+          int presentation;
+          if (cursor.isNull(presentationColumn)
+              || (presentation = cursor.getInt(presentationColumn)) == 0) {
+            // CallLog.Calls#NUMBER_PRESENTATION lists the allowed values, which are non-null and
+            // non-zero.
+            throw new IllegalStateException("presentation is missing");
+          }
           String countryIso = cursor.getString(countryIsoColumn);
           int duration = cursor.getInt(durationsColumn);
           int dataUsage = cursor.getInt(dataUsageColumn);
@@ -332,6 +343,7 @@
             contentValues.put(
                 AnnotatedCallLog.NUMBER, DialerPhoneNumber.getDefaultInstance().toByteArray());
           }
+          contentValues.put(AnnotatedCallLog.NUMBER_PRESENTATION, presentation);
           contentValues.put(AnnotatedCallLog.CALL_TYPE, type);
           contentValues.put(AnnotatedCallLog.IS_READ, isRead);
           contentValues.put(AnnotatedCallLog.NEW, isNew);
@@ -363,7 +375,7 @@
       String phoneAccountComponentName,
       String phoneAccountId) {
     PhoneAccountHandle phoneAccountHandle =
-        PhoneAccountUtils.getAccount(phoneAccountComponentName, phoneAccountId);
+        TelecomUtil.composePhoneAccountHandle(phoneAccountComponentName, phoneAccountId);
     if (phoneAccountHandle == null) {
       return;
     }
diff --git a/java/com/android/dialer/calllog/datasources/util/RowCombiner.java b/java/com/android/dialer/calllog/datasources/util/RowCombiner.java
index 6e33db5..2bb65cc 100644
--- a/java/com/android/dialer/calllog/datasources/util/RowCombiner.java
+++ b/java/com/android/dialer/calllog/datasources/util/RowCombiner.java
@@ -80,6 +80,18 @@
     return this;
   }
 
+  /** Asserts that all column values for the given column name are the same, and uses it. */
+  public RowCombiner useSingleValueInt(String columnName) {
+    Iterator<ContentValues> iterator = individualRowsSortedByTimestampDesc.iterator();
+    Integer singleValue = iterator.next().getAsInteger(columnName);
+    while (iterator.hasNext()) {
+      Integer current = iterator.next().getAsInteger(columnName);
+      Assert.checkState(Objects.equals(singleValue, current), "Values different for " + columnName);
+    }
+    combinedRow.put(columnName, singleValue);
+    return this;
+  }
+
   /** Performs a bitwise OR on the specified column and yields the result. */
   public RowCombiner bitwiseOr(String columnName) {
     int combinedValue = 0;
diff --git a/java/com/android/dialer/calllog/model/CoalescedRow.java b/java/com/android/dialer/calllog/model/CoalescedRow.java
index 312c29c..2b6db97 100644
--- a/java/com/android/dialer/calllog/model/CoalescedRow.java
+++ b/java/com/android/dialer/calllog/model/CoalescedRow.java
@@ -32,6 +32,7 @@
         .setId(0)
         .setTimestamp(0)
         .setNumber(DialerPhoneNumber.getDefaultInstance())
+        .setNumberPresentation(0)
         .setIsRead(false)
         .setIsNew(false)
         .setPhoneAccountColor(0)
@@ -52,6 +53,8 @@
   @Nullable
   public abstract String formattedNumber();
 
+  public abstract int numberPresentation();
+
   public abstract boolean isRead();
 
   public abstract boolean isNew();
@@ -91,6 +94,8 @@
 
     public abstract Builder setFormattedNumber(@Nullable String formattedNumber);
 
+    public abstract Builder setNumberPresentation(int presentation);
+
     public abstract Builder setIsRead(boolean isRead);
 
     public abstract Builder setIsNew(boolean isNew);
diff --git a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
index d72544b..0b1c6c9 100644
--- a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
+++ b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
@@ -34,17 +34,18 @@
   private static final int TIMESTAMP = 1;
   private static final int NUMBER = 2;
   private static final int FORMATTED_NUMBER = 3;
-  private static final int IS_READ = 4;
-  private static final int NEW = 5;
-  private static final int GEOCODED_LOCATION = 6;
-  private static final int PHONE_ACCOUNT_COMPONENT_NAME = 7;
-  private static final int PHONE_ACCOUNT_ID = 8;
-  private static final int PHONE_ACCOUNT_LABEL = 9;
-  private static final int PHONE_ACCOUNT_COLOR = 10;
-  private static final int FEATURES = 11;
-  private static final int NUMBER_ATTRIBUTES = 12;
-  private static final int CALL_TYPE = 13;
-  private static final int COALESCED_IDS = 14;
+  private static final int NUMBER_PRESENTATION = 4;
+  private static final int IS_READ = 5;
+  private static final int NEW = 6;
+  private static final int GEOCODED_LOCATION = 7;
+  private static final int PHONE_ACCOUNT_COMPONENT_NAME = 8;
+  private static final int PHONE_ACCOUNT_ID = 9;
+  private static final int PHONE_ACCOUNT_LABEL = 10;
+  private static final int PHONE_ACCOUNT_COLOR = 11;
+  private static final int FEATURES = 12;
+  private static final int NUMBER_ATTRIBUTES = 13;
+  private static final int CALL_TYPE = 14;
+  private static final int COALESCED_IDS = 15;
 
   CoalescedAnnotatedCallLogCursorLoader(Context context) {
     // CoalescedAnnotatedCallLog requires that PROJECTION be ALL_COLUMNS and the following params be
@@ -86,6 +87,7 @@
         .setTimestamp(cursor.getLong(TIMESTAMP))
         .setNumber(number)
         .setFormattedNumber(cursor.getString(FORMATTED_NUMBER))
+        .setNumberPresentation(cursor.getInt(NUMBER_PRESENTATION))
         .setIsRead(cursor.getInt(IS_READ) == 1)
         .setIsNew(cursor.getInt(NEW) == 1)
         .setGeocodedLocation(cursor.getString(GEOCODED_LOCATION))
diff --git a/java/com/android/dialer/calllog/ui/menu/Modules.java b/java/com/android/dialer/calllog/ui/menu/Modules.java
index 67e5168..96e5951 100644
--- a/java/com/android/dialer/calllog/ui/menu/Modules.java
+++ b/java/com/android/dialer/calllog/ui/menu/Modules.java
@@ -24,12 +24,15 @@
 import com.android.dialer.callintent.CallInitiationType;
 import com.android.dialer.calllog.model.CoalescedRow;
 import com.android.dialer.calllogutils.CallLogContactTypes;
-import com.android.dialer.calllogutils.PhoneAccountUtils;
+import com.android.dialer.calllogutils.PhoneNumberDisplayUtil;
 import com.android.dialer.contactactions.ContactActionModule;
 import com.android.dialer.contactactions.DividerModule;
 import com.android.dialer.contactactions.IntentModule;
 import com.android.dialer.contactactions.SharedModules;
 import com.android.dialer.dialercontact.DialerContact;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.android.dialer.telecom.TelecomUtil;
+import com.google.common.base.Optional;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -43,16 +46,23 @@
     // Conditionally add each module, which are items in the bottom sheet's menu.
     List<ContactActionModule> modules = new ArrayList<>();
 
-    maybeAddModuleForVideoOrAudioCall(context, modules, row);
-    SharedModules.maybeAddModuleForAddingToContacts(
-        context,
-        modules,
-        row.number(),
-        row.numberAttributes().getName(),
-        row.numberAttributes().getLookupUri());
+    // TODO(zach): Don't use raw input.
+    String normalizedNumber = row.number().getRawInput().getNumber();
+    boolean canPlaceCalls =
+        PhoneNumberHelper.canPlaceCallsTo(normalizedNumber, row.numberPresentation());
 
-    String originalNumber = row.number().getRawInput().getNumber();
-    SharedModules.maybeAddModuleForSendingTextMessage(context, modules, originalNumber);
+    if (canPlaceCalls) {
+      addModuleForVideoOrAudioCall(context, modules, row, normalizedNumber);
+
+      SharedModules.maybeAddModuleForAddingToContacts(
+          context,
+          modules,
+          row.number(),
+          row.numberAttributes().getName(),
+          row.numberAttributes().getLookupUri());
+
+      SharedModules.maybeAddModuleForSendingTextMessage(context, modules, normalizedNumber);
+    }
 
     if (!modules.isEmpty()) {
       modules.add(new DividerModule());
@@ -62,7 +72,9 @@
     // TODO(zachh): Module for blocking/unblocking spam.
     // TODO(zachh): Module for CallComposer.
 
-    SharedModules.maybeAddModuleForCopyingNumber(context, modules, originalNumber);
+    if (canPlaceCalls) {
+      SharedModules.maybeAddModuleForCopyingNumber(context, modules, normalizedNumber);
+    }
 
     // TODO(zachh): Revisit if DialerContact is the best thing to pass to CallDetails; could
     // it use a ContactPrimaryActionInfo instead?
@@ -73,30 +85,28 @@
     return modules;
   }
 
-  private static void maybeAddModuleForVideoOrAudioCall(
-      Context context, List<ContactActionModule> modules, CoalescedRow row) {
-    String originalNumber = row.number().getRawInput().getNumber();
-    if (TextUtils.isEmpty(originalNumber)) {
-      // Skip adding the menu item if the phone number is unknown.
-      return;
-    }
-
+  private static void addModuleForVideoOrAudioCall(
+      Context context,
+      List<ContactActionModule> modules,
+      CoalescedRow row,
+      String normalizedNumber) {
     PhoneAccountHandle phoneAccountHandle =
-        PhoneAccountUtils.getAccount(row.phoneAccountComponentName(), row.phoneAccountId());
+        TelecomUtil.composePhoneAccountHandle(
+            row.phoneAccountComponentName(), row.phoneAccountId());
 
     if ((row.features() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
       // Add an audio call item for video calls. Clicking the top entry on the bottom sheet will
       // trigger a video call.
       modules.add(
           IntentModule.newCallModule(
-              context, originalNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
+              context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
     } else {
       // Add a video call item for audio calls. Click the top entry on the bottom sheet will
       // trigger an audio call.
       // TODO(zachh): Only show video option if video capabilities present?
       modules.add(
           IntentModule.newVideoCallModule(
-              context, originalNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
+              context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
     }
   }
 
@@ -111,20 +121,28 @@
             CallDetailsActivity.newInstance(
                 context,
                 row.coalescedIds(),
-                createDialerContactFromRow(row),
+                createDialerContactFromRow(context, row),
                 canReportAsInvalidNumber,
                 canSupportAssistedDialing),
             R.string.call_details_menu_label,
             R.drawable.quantum_ic_info_outline_vd_theme_24));
   }
 
-  private static DialerContact createDialerContactFromRow(CoalescedRow row) {
-    // TODO(zachh): Do something with parsed values to make more dialable?
-    String originalNumber = row.number().getRawInput().getNumber();
+  private static DialerContact createDialerContactFromRow(Context context, CoalescedRow row) {
+    Optional<String> presentationName =
+        PhoneNumberDisplayUtil.getNameForPresentation(context, row.numberPresentation());
+    if (presentationName.isPresent()) {
+      return DialerContact.newBuilder()
+          .setNameOrNumber(presentationName.get())
+          .setContactType(CallLogContactTypes.getContactType(row))
+          .build();
+    }
 
+    // TODO(zachh): Don't use raw input.
+    String normalizedNumber = row.number().getRawInput().getNumber();
     DialerContact.Builder dialerContactBuilder =
         DialerContact.newBuilder()
-            .setNumber(originalNumber)
+            .setNumber(normalizedNumber)
             .setContactType(CallLogContactTypes.getContactType(row))
             .setPhotoId(row.numberAttributes().getPhotoId());
 
diff --git a/java/com/android/dialer/calllogutils/CallLogContactTypes.java b/java/com/android/dialer/calllogutils/CallLogContactTypes.java
index 01ae653..5865156 100644
--- a/java/com/android/dialer/calllogutils/CallLogContactTypes.java
+++ b/java/com/android/dialer/calllogutils/CallLogContactTypes.java
@@ -29,7 +29,7 @@
     boolean isVoicemail = false;
     boolean isSpam = false;
     boolean isBusiness = false;
-    int numberPresentation = 0;
+    int numberPresentation = row.numberPresentation();
     boolean isConference = false;
 
     return LetterTileDrawable.getContactTypeFromPrimitives(
diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java
index aa45a69..49f5e42 100644
--- a/java/com/android/dialer/calllogutils/CallLogEntryText.java
+++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java
@@ -21,6 +21,7 @@
 import android.text.TextUtils;
 import com.android.dialer.calllog.model.CoalescedRow;
 import com.android.dialer.time.Clock;
+import com.google.common.base.Optional;
 import com.google.common.collect.Collections2;
 import java.util.ArrayList;
 import java.util.List;
@@ -40,16 +41,25 @@
    * following the primary text.)
    */
   public static CharSequence buildPrimaryText(Context context, CoalescedRow row) {
-    StringBuilder primaryText = new StringBuilder();
-    if (!TextUtils.isEmpty(row.numberAttributes().getName())) {
-      primaryText.append(row.numberAttributes().getName());
-    } else if (!TextUtils.isEmpty(row.formattedNumber())) {
-      primaryText.append(row.formattedNumber());
-    } else {
-      // TODO(zachh): Handle CallLog.Calls.PRESENTATION_*, including Verizon restricted numbers.
-      primaryText.append(context.getText(R.string.new_call_log_unknown));
+    // Always prefer the presentation name, like "Restricted".
+    Optional<String> presentationName =
+        PhoneNumberDisplayUtil.getNameForPresentation(context, row.numberPresentation());
+    if (presentationName.isPresent()) {
+      return presentationName.get();
     }
-    return primaryText.toString();
+
+    // Otherwise prefer the name.
+    if (!TextUtils.isEmpty(row.numberAttributes().getName())) {
+      return row.numberAttributes().getName();
+    }
+
+    // Otherwise prefer the formatted number.
+    if (!TextUtils.isEmpty(row.formattedNumber())) {
+      return row.formattedNumber();
+    }
+
+    // If there's no formatted number, just return "Unknown".
+    return context.getText(R.string.new_call_log_unknown);
   }
 
   /**
@@ -112,6 +122,14 @@
 
     components.add(getNumberTypeLabel(context, row));
 
+    // If there's a presentation name, we showed it in the primary text and shouldn't show any name
+    // or number here.
+    Optional<String> presentationName =
+        PhoneNumberDisplayUtil.getNameForPresentation(context, row.numberPresentation());
+    if (presentationName.isPresent()) {
+      return joinSecondaryTextComponents(components);
+    }
+
     if (TextUtils.isEmpty(row.numberAttributes().getName())) {
       // If the name is empty the number is shown as the primary text and there's nothing to add.
       return joinSecondaryTextComponents(components);
diff --git a/java/com/android/dialer/calllogutils/CallLogIntents.java b/java/com/android/dialer/calllogutils/CallLogIntents.java
index b06fe6e..227b15e 100644
--- a/java/com/android/dialer/calllogutils/CallLogIntents.java
+++ b/java/com/android/dialer/calllogutils/CallLogIntents.java
@@ -19,11 +19,12 @@
 import android.content.Intent;
 import android.provider.CallLog.Calls;
 import android.support.annotation.Nullable;
-import android.text.TextUtils;
 import com.android.dialer.callintent.CallInitiationType;
 import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.calllog.model.CoalescedRow;
+import com.android.dialer.phonenumberutil.PhoneNumberHelper;
 import com.android.dialer.precall.PreCall;
+import com.android.dialer.telecom.TelecomUtil;
 
 /** Provides intents related to call log entries. */
 public final class CallLogIntents {
@@ -38,20 +39,19 @@
    */
   @Nullable
   public static Intent getCallBackIntent(Context context, CoalescedRow row) {
-    // TODO(zachh): Do something with parsed values to make more dialable?
-    String originalNumber = row.number().getRawInput().getNumber();
-    // TODO(zachh): Make this more sophisticated, e.g. return null for non-dialable numbers?
-    if (TextUtils.isEmpty(originalNumber)) {
+    // TODO(zachh): Don't use raw input.
+    String normalizedNumber = row.number().getRawInput().getNumber();
+    if (!PhoneNumberHelper.canPlaceCallsTo(normalizedNumber, row.numberPresentation())) {
       return null;
     }
 
     // TODO(zachh): More granular logging?
-    // TODO(zachh): Support assisted dialing.
     return PreCall.getIntent(
         context,
-        new CallIntentBuilder(originalNumber, CallInitiationType.Type.CALL_LOG)
+        new CallIntentBuilder(normalizedNumber, CallInitiationType.Type.CALL_LOG)
             .setPhoneAccountHandle(
-                PhoneAccountUtils.getAccount(row.phoneAccountComponentName(), row.phoneAccountId()))
+                TelecomUtil.composePhoneAccountHandle(
+                    row.phoneAccountComponentName(), row.phoneAccountId()))
             .setIsVideoCall((row.features() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO));
   }
 }
diff --git a/java/com/android/dialer/calllogutils/PhoneAccountUtils.java b/java/com/android/dialer/calllogutils/PhoneAccountUtils.java
index 153f291..2ee50a1 100644
--- a/java/com/android/dialer/calllogutils/PhoneAccountUtils.java
+++ b/java/com/android/dialer/calllogutils/PhoneAccountUtils.java
@@ -16,47 +16,15 @@
 
 package com.android.dialer.calllogutils;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.support.annotation.Nullable;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
 import com.android.dialer.telecom.TelecomUtil;
-import java.util.ArrayList;
-import java.util.List;
 
 /** Methods to help extract {@code PhoneAccount} information from database and Telecomm sources. */
 public class PhoneAccountUtils {
 
-  /** Return a list of phone accounts that are subscription/SIM accounts. */
-  public static List<PhoneAccountHandle> getSubscriptionPhoneAccounts(Context context) {
-    List<PhoneAccountHandle> subscriptionAccountHandles = new ArrayList<>();
-    final List<PhoneAccountHandle> accountHandles =
-        TelecomUtil.getCallCapablePhoneAccounts(context);
-    for (PhoneAccountHandle accountHandle : accountHandles) {
-      PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
-      if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
-        subscriptionAccountHandles.add(accountHandle);
-      }
-    }
-    return subscriptionAccountHandles;
-  }
-
-  /** Compose PhoneAccount object from component name and account id. */
-  @Nullable
-  public static PhoneAccountHandle getAccount(
-      @Nullable String componentString, @Nullable String accountId) {
-    if (TextUtils.isEmpty(componentString) || TextUtils.isEmpty(accountId)) {
-      return null;
-    }
-    final ComponentName componentName = ComponentName.unflattenFromString(componentString);
-    if (componentName == null) {
-      return null;
-    }
-    return new PhoneAccountHandle(componentName, accountId);
-  }
-
   /** Extract account label from PhoneAccount object. */
   @Nullable
   public static String getAccountLabel(
diff --git a/java/com/android/dialer/calllogutils/PhoneNumberDisplayUtil.java b/java/com/android/dialer/calllogutils/PhoneNumberDisplayUtil.java
index 9bebfac..f0f6963 100644
--- a/java/com/android/dialer/calllogutils/PhoneNumberDisplayUtil.java
+++ b/java/com/android/dialer/calllogutils/PhoneNumberDisplayUtil.java
@@ -23,6 +23,7 @@
 import android.text.TextUtils;
 import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
 import com.android.dialer.phonenumberutil.PhoneNumberHelper;
+import com.google.common.base.Optional;
 
 /** Helper for formatting and managing the display of phone numbers. */
 public class PhoneNumberDisplayUtil {
@@ -30,14 +31,9 @@
   /** Returns the string to display for the given phone number if there is no matching contact. */
   public static CharSequence getDisplayName(
       Context context, CharSequence number, int presentation, boolean isVoicemail) {
-    if (presentation == Calls.PRESENTATION_UNKNOWN) {
-      return context.getResources().getString(R.string.unknown);
-    }
-    if (presentation == Calls.PRESENTATION_RESTRICTED) {
-      return PhoneNumberHelper.getDisplayNameForRestrictedNumber(context);
-    }
-    if (presentation == Calls.PRESENTATION_PAYPHONE) {
-      return context.getResources().getString(R.string.payphone);
+    Optional<String> presentationString = getNameForPresentation(context, presentation);
+    if (presentationString.isPresent()) {
+      return presentationString.get();
     }
     if (isVoicemail) {
       return context.getResources().getString(R.string.voicemail_string);
@@ -48,13 +44,27 @@
     return "";
   }
 
+  /** Returns the string associated with the given presentation. */
+  public static Optional<String> getNameForPresentation(Context appContext, int presentation) {
+    if (presentation == Calls.PRESENTATION_UNKNOWN) {
+      return Optional.of(appContext.getResources().getString(R.string.unknown));
+    }
+    if (presentation == Calls.PRESENTATION_RESTRICTED) {
+      return Optional.of(PhoneNumberHelper.getDisplayNameForRestrictedNumber(appContext));
+    }
+    if (presentation == Calls.PRESENTATION_PAYPHONE) {
+      return Optional.of(appContext.getResources().getString(R.string.payphone));
+    }
+    return Optional.absent();
+  }
+
   /**
    * Returns the string to display for the given phone number.
    *
    * @param number the number to display
    * @param formattedNumber the formatted number if available, may be null
    */
-  public static CharSequence getDisplayNumber(
+  static CharSequence getDisplayNumber(
       Context context,
       CharSequence number,
       int presentation,
diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java
index 9cee7bc..6b8401e 100644
--- a/java/com/android/dialer/dialpadview/DialpadFragment.java
+++ b/java/com/android/dialer/dialpadview/DialpadFragment.java
@@ -77,7 +77,6 @@
 import com.android.dialer.animation.AnimUtils;
 import com.android.dialer.callintent.CallInitiationType;
 import com.android.dialer.callintent.CallIntentBuilder;
-import com.android.dialer.calllogutils.PhoneAccountUtils;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.FragmentUtils;
 import com.android.dialer.common.LogUtil;
@@ -967,7 +966,7 @@
         removePreviousDigitIfPossible('1');
 
         List<PhoneAccountHandle> subscriptionAccountHandles =
-            PhoneAccountUtils.getSubscriptionPhoneAccounts(getActivity());
+            TelecomUtil.getSubscriptionPhoneAccounts(getActivity());
         boolean hasUserSelectedDefault =
             subscriptionAccountHandles.contains(
                 TelecomUtil.getDefaultOutgoingPhoneAccount(
diff --git a/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java b/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java
index 7689255..2e4caa9 100644
--- a/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java
+++ b/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java
@@ -44,7 +44,6 @@
 import com.android.contacts.common.util.ContactDisplayUtils;
 import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
 import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
-import com.android.dialer.calllogutils.PhoneAccountUtils;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.compat.telephony.TelephonyManagerCompat;
@@ -215,7 +214,7 @@
         sc.progressDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
 
         List<PhoneAccountHandle> subscriptionAccountHandles =
-            PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
+            TelecomUtil.getSubscriptionPhoneAccounts(context);
         Context applicationContext = context.getApplicationContext();
         boolean hasUserSelectedDefault =
             subscriptionAccountHandles.contains(
@@ -272,7 +271,7 @@
   static boolean handlePinEntry(final Context context, final String input) {
     if ((input.startsWith("**04") || input.startsWith("**05")) && input.endsWith("#")) {
       List<PhoneAccountHandle> subscriptionAccountHandles =
-          PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
+          TelecomUtil.getSubscriptionPhoneAccounts(context);
       boolean hasUserSelectedDefault =
           subscriptionAccountHandles.contains(
               TelecomUtil.getDefaultOutgoingPhoneAccount(context, PhoneAccount.SCHEME_TEL));
diff --git a/java/com/android/dialer/phonenumberproto/DialerPhoneNumberUtil.java b/java/com/android/dialer/phonenumberproto/DialerPhoneNumberUtil.java
index 4e7d300..841524b 100644
--- a/java/com/android/dialer/phonenumberproto/DialerPhoneNumberUtil.java
+++ b/java/com/android/dialer/phonenumberproto/DialerPhoneNumberUtil.java
@@ -181,6 +181,7 @@
   @WorkerThread
   public String normalizeNumber(DialerPhoneNumber number) {
     Assert.isWorkerThread();
+    // TODO(zachh): This loses country info when number is not valid.
     return formatToValidE164(number)
         .or(PhoneNumberUtils.normalizeNumber(number.getRawInput().getNumber()));
   }
diff --git a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java
index 12e1469..b58739d 100644
--- a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java
+++ b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java
@@ -341,7 +341,7 @@
    * PRESENTATION_RESTRICTED. For Verizon we want this to be displayed as "Restricted". For all
    * other carriers we want this to be be displayed as "Private number".
    */
-  public static CharSequence getDisplayNameForRestrictedNumber(Context context) {
+  public static String getDisplayNameForRestrictedNumber(Context context) {
     if (isVerizon(context)) {
       return context.getString(R.string.private_num_verizon);
     } else {
diff --git a/java/com/android/dialer/precall/impl/AssistedDialAction.java b/java/com/android/dialer/precall/impl/AssistedDialAction.java
index 8d2b176..15a889e 100644
--- a/java/com/android/dialer/precall/impl/AssistedDialAction.java
+++ b/java/com/android/dialer/precall/impl/AssistedDialAction.java
@@ -79,6 +79,7 @@
           .putBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS, assistedDialingExtras);
       builder.setUri(
           CallUtil.getCallUri(Assert.isNotNull(transformedNumber.get().transformedNumber())));
+      LogUtil.i("AssistedDialAction.runWithoutUi", "assisted dialing was used.");
     }
   }
 
diff --git a/java/com/android/dialer/precall/impl/CallingAccountSelector.java b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
index 9397d18..d5ee0f2 100644
--- a/java/com/android/dialer/precall/impl/CallingAccountSelector.java
+++ b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
@@ -56,6 +56,7 @@
 import com.android.dialer.precall.PreCallAction;
 import com.android.dialer.precall.PreCallCoordinator;
 import com.android.dialer.precall.PreCallCoordinator.PendingAction;
+import com.android.dialer.preferredsim.PreferredAccountUtil;
 import com.android.dialer.preferredsim.PreferredSimFallbackContract;
 import com.android.dialer.preferredsim.PreferredSimFallbackContract.PreferredSim;
 import com.android.dialer.preferredsim.suggestion.SimSuggestionComponent;
@@ -483,7 +484,8 @@
       if (number != null) {
         DialerExecutorComponent.get(coordinator.getActivity())
             .dialerExecutorFactory()
-            .createNonUiTaskBuilder(new UserSelectionReporter(selectedAccountHandle, number))
+            .createNonUiTaskBuilder(
+                new UserSelectionReporter(selectedAccountHandle, number, setDefault))
             .build()
             .executeParallel(coordinator.getActivity());
       }
@@ -505,11 +507,13 @@
 
     private final String number;
     private final PhoneAccountHandle phoneAccountHandle;
+    private final boolean remember;
 
     public UserSelectionReporter(
-        @NonNull PhoneAccountHandle phoneAccountHandle, @Nullable String number) {
+        @NonNull PhoneAccountHandle phoneAccountHandle, @Nullable String number, boolean remember) {
       this.phoneAccountHandle = Assert.isNotNull(phoneAccountHandle);
       this.number = Assert.isNotNull(number);
+      this.remember = remember;
     }
 
     @Nullable
@@ -517,7 +521,7 @@
     public Void doInBackground(@NonNull Context context) throws Throwable {
       SimSuggestionComponent.get(context)
           .getSuggestionProvider()
-          .reportUserSelection(context, number, phoneAccountHandle);
+          .reportUserSelection(context, number, phoneAccountHandle, remember);
       return null;
     }
   }
diff --git a/java/com/android/dialer/precall/impl/PreferredAccountUtil.java b/java/com/android/dialer/preferredsim/PreferredAccountUtil.java
similarity index 97%
rename from java/com/android/dialer/precall/impl/PreferredAccountUtil.java
rename to java/com/android/dialer/preferredsim/PreferredAccountUtil.java
index 6505888..1cfdbb1 100644
--- a/java/com/android/dialer/precall/impl/PreferredAccountUtil.java
+++ b/java/com/android/dialer/preferredsim/PreferredAccountUtil.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.dialer.precall.impl;
+package com.android.dialer.preferredsim;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -64,7 +64,7 @@
     return Optional.absent();
   }
 
-  private static boolean isPhoneAccountValid(
+  public static boolean isPhoneAccountValid(
       Context context, PhoneAccountHandle phoneAccountHandle) {
     if (VERSION.SDK_INT >= VERSION_CODES.O) {
       return context
diff --git a/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java b/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java
index c1114b3..bb50889 100644
--- a/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java
+++ b/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java
@@ -62,8 +62,10 @@
   void reportUserSelection(
       @NonNull Context context,
       @NonNull String number,
-      @NonNull PhoneAccountHandle phoneAccountHandle);
+      @NonNull PhoneAccountHandle phoneAccountHandle,
+      boolean rememberSelection);
 
   @WorkerThread
-  void reportIncorrectSuggestion(@NonNull Context context, @NonNull String number);
+  void reportIncorrectSuggestion(
+      @NonNull Context context, @NonNull String number, @NonNull PhoneAccountHandle newAccount);
 }
diff --git a/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java b/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java
index 6fb73ac..bd54ddb 100644
--- a/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java
+++ b/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java
@@ -40,8 +40,10 @@
   public void reportUserSelection(
       @NonNull Context context,
       @NonNull String number,
-      @NonNull PhoneAccountHandle phoneAccountHandle) {}
+      @NonNull PhoneAccountHandle phoneAccountHandle,
+      boolean rememberSelection) {}
 
   @Override
-  public void reportIncorrectSuggestion(@NonNull Context context, @NonNull String number) {}
+  public void reportIncorrectSuggestion(
+      @NonNull Context context, @NonNull String number, PhoneAccountHandle newAccount) {}
 }
diff --git a/java/com/android/dialer/simulator/Simulator.java b/java/com/android/dialer/simulator/Simulator.java
index 3931ae4..3c2526b 100644
--- a/java/com/android/dialer/simulator/Simulator.java
+++ b/java/com/android/dialer/simulator/Simulator.java
@@ -63,11 +63,24 @@
   @StringDef({
     IS_VOLTE,
     PRESENTATION_CHOICE,
+    IS_ENRICHED_CALL,
   })
   @interface BundleKey {}
 
   public final String IS_VOLTE = "ISVOLTE";
   public final String PRESENTATION_CHOICE = "PRESENTATIONCHOICE";
+  public final String IS_ENRICHED_CALL = "ISENRICHEDCALL";
+
+  /** Phone numbers for outgoing and incoming enriched call scenario. */
+  public static final String ENRICHED_CALL_OUTGOING_NUMBER = "+55-31-2128-6800";
+
+  public static final String ENRICHED_CALL_INCOMING_NUMBER = "+44 (0) 20 7031 3000";
+
+  boolean isSimulatorMode();
+
+  void enableSimulatorMode();
+
+  void disableSimulatorMode();
 
   /** Information about a connection event. */
   public static class Event {
diff --git a/java/com/android/dialer/simulator/SimulatorEnrichedCall.java b/java/com/android/dialer/simulator/SimulatorEnrichedCall.java
index f6c8a6c..ae9447b 100644
--- a/java/com/android/dialer/simulator/SimulatorEnrichedCall.java
+++ b/java/com/android/dialer/simulator/SimulatorEnrichedCall.java
@@ -16,13 +16,14 @@
 
 package com.android.dialer.simulator;
 
-import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
+import com.google.common.util.concurrent.ListenableFuture;
 
 /** Setup enriched calling environment for {@link Simulator}. */
-public interface SimulatorEnrichedCall extends StateChangedListener {
+public interface SimulatorEnrichedCall extends EnrichedCallManager.StateChangedListener {
   /** Setup a session for an incoming enriched call. */
-  long setupIncomingEnrichedCall(String number);
+  ListenableFuture<Void> setupIncomingEnrichedCall(String number);
 
   /** Setup a session for outgoing enriched call. */
-  long setupOutgoingEnrichedCall(String number);
+  ListenableFuture<Void> setupOutgoingEnrichedCall(String number);
 }
diff --git a/java/com/android/dialer/simulator/impl/SimulatorImpl.java b/java/com/android/dialer/simulator/impl/SimulatorImpl.java
index be86763..24f3410 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorImpl.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorImpl.java
@@ -25,7 +25,9 @@
 
 /** The entry point for the simulator feature. */
 final class SimulatorImpl implements Simulator {
-  
+
+  private boolean simulatorMode = false;
+
   @Inject
   public SimulatorImpl() {}
 
@@ -38,4 +40,19 @@
   public ActionProvider getActionProvider(AppCompatActivity activity) {
     return SimulatorMainMenu.getActionProvider(activity);
   }
+
+  @Override
+  public boolean isSimulatorMode() {
+    return simulatorMode;
+  }
+
+  @Override
+  public void enableSimulatorMode() {
+    simulatorMode = true;
+  }
+
+  @Override
+  public void disableSimulatorMode() {
+    simulatorMode = false;
+  }
 }
diff --git a/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java b/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java
index 174aab5..69da2f4 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java
@@ -32,6 +32,7 @@
 import com.android.dialer.enrichedcall.simulator.EnrichedCallSimulatorActivity;
 import com.android.dialer.persistentlog.PersistentLogger;
 import com.android.dialer.preferredsim.PreferredSimFallbackContract;
+import com.android.dialer.simulator.SimulatorComponent;
 import com.android.incallui.rtt.impl.RttChatActivity;
 import com.android.incallui.speakeasy.SpeakEasy;
 import com.android.incallui.speakeasy.SpeakEasyActivity;
@@ -65,7 +66,19 @@
             "Enriched call simulator",
             () ->
                 activity.startActivity(
-                    EnrichedCallSimulatorActivity.newIntent(activity.getApplicationContext())));
+                    EnrichedCallSimulatorActivity.newIntent(activity.getApplicationContext())))
+        .addItem(
+            "Enable simulator mode",
+            () ->
+                SimulatorComponent.get(activity.getApplicationContext())
+                    .getSimulator()
+                    .enableSimulatorMode())
+        .addItem(
+            "Disable simulator mode",
+            () ->
+                SimulatorComponent.get(activity.getApplicationContext())
+                    .getSimulator()
+                    .disableSimulatorMode());
     SpeakEasy speakEasy = SpeakEasyComponent.get(activity.getApplicationContext()).speakEasy();
     if (speakEasy.isEnabled()) {
       simulatorSubMenu.addItem(
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
index ff00dd8..67a2db8 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
@@ -26,15 +26,21 @@
 import android.view.ActionProvider;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
 import com.android.dialer.common.concurrent.ThreadUtil;
+import com.android.dialer.enrichedcall.EnrichedCallComponent;
+import com.android.dialer.enrichedcall.EnrichedCallManager;
 import com.android.dialer.simulator.Simulator;
 import com.android.dialer.simulator.Simulator.Event;
+import com.android.dialer.simulator.SimulatorComponent;
+import com.android.dialer.simulator.SimulatorEnrichedCall;
 
 /** Entry point in the simulator to create voice calls. */
 final class SimulatorVoiceCall
     implements SimulatorConnectionService.Listener, SimulatorConnection.Listener {
   @NonNull private final Context context;
   @Nullable private String connectionTag;
+  private final SimulatorEnrichedCall simulatorEnrichedCall;
 
   static ActionProvider getActionProvider(@NonNull AppCompatActivity activity) {
     return new SimulatorSubMenu(activity.getApplicationContext())
@@ -55,6 +61,12 @@
                 new SimulatorVoiceCall(activity.getApplicationContext())
                     .addNewOutgoingCall(activity))
         .addItem(
+            "Incoming enriched call",
+            () -> new SimulatorVoiceCall(activity.getApplicationContext()).incomingEnrichedCall())
+        .addItem(
+            "Outgoing enriched call",
+            () -> new SimulatorVoiceCall(activity.getApplicationContext()).outgoingEnrichedCall())
+        .addItem(
             "Spam incoming call",
             () -> new SimulatorVoiceCall(activity.getApplicationContext()).addSpamIncomingCall())
         .addItem(
@@ -77,23 +89,47 @@
 
   private SimulatorVoiceCall(@NonNull Context context) {
     this.context = Assert.isNotNull(context);
+    simulatorEnrichedCall = SimulatorComponent.get(context).getSimulatorEnrichedCall();
     SimulatorConnectionService.addListener(this);
     SimulatorConnectionService.addListener(
         new SimulatorConferenceCreator(context, Simulator.CONFERENCE_TYPE_GSM));
   }
 
+  private void incomingEnrichedCall() {
+    simulatorEnrichedCall
+        .setupIncomingEnrichedCall(Simulator.ENRICHED_CALL_INCOMING_NUMBER)
+        .addListener(
+            () -> {
+              Bundle extras = new Bundle();
+              extras.putBoolean(Simulator.IS_ENRICHED_CALL, true);
+              connectionTag =
+                  SimulatorSimCallManager.addNewIncomingCall(
+                      context, Simulator.ENRICHED_CALL_INCOMING_NUMBER, false, extras);
+            },
+            DialerExecutorComponent.get(context).uiExecutor());
+  }
+
+  private void outgoingEnrichedCall() {
+    getEnrichedCallManager().registerStateChangedListener(simulatorEnrichedCall);
+    simulatorEnrichedCall
+        .setupOutgoingEnrichedCall(Simulator.ENRICHED_CALL_OUTGOING_NUMBER)
+        .addListener(
+            () -> {
+              Bundle extras = new Bundle();
+              extras.putBoolean(Simulator.IS_ENRICHED_CALL, true);
+              connectionTag =
+                  SimulatorSimCallManager.addNewOutgoingCall(
+                      context, Simulator.ENRICHED_CALL_OUTGOING_NUMBER, false, extras);
+            },
+            DialerExecutorComponent.get(context).uiExecutor());
+  }
+
   private void addNewIncomingCall() {
     String callerId = "+44 (0) 20 7031 3000" /* Google London office */;
     connectionTag =
         SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */);
   }
 
-  private void addNewOutgoingCall() {
-    String callerId = "+55-31-2128-6800"; // Brazil office.
-    connectionTag =
-        SimulatorSimCallManager.addNewOutgoingCall(context, callerId, false /* isVideo */);
-  }
-
   private void addNewIncomingCall(AppCompatActivity activity) {
     SimulatorDialogFragment.newInstance(
             (callerId, callerIdPresentation) -> {
@@ -106,6 +142,12 @@
         .show(activity.getSupportFragmentManager(), "SimulatorDialog");
   }
 
+  private void addNewOutgoingCall() {
+    String callerId = "+55-31-2128-6800"; // Brazil office.
+    connectionTag =
+        SimulatorSimCallManager.addNewOutgoingCall(context, callerId, false /* isVideo */);
+  }
+
   private void addNewOutgoingCall(AppCompatActivity activity) {
     SimulatorDialogFragment.newInstance(
             (callerId, callerIdPresentation) -> {
@@ -184,6 +226,9 @@
         break;
       case Event.DISCONNECT:
         connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
+        if (connection.getExtras().getBoolean(Simulator.IS_ENRICHED_CALL)) {
+          getEnrichedCallManager().unregisterStateChangedListener(simulatorEnrichedCall);
+        }
         break;
       case Event.SESSION_MODIFY_REQUEST:
         ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000);
@@ -194,7 +239,8 @@
     }
   }
 
-  private interface DialogCallback {
-    void callback(String callerId, int callerIdPresentation);
+  @NonNull
+  private EnrichedCallManager getEnrichedCallManager() {
+    return EnrichedCallComponent.get(context).getEnrichedCallManager();
   }
 }
diff --git a/java/com/android/dialer/simulator/stub/SimulatorEnrichedCallStub.java b/java/com/android/dialer/simulator/stub/SimulatorEnrichedCallStub.java
index 056722f..5b07271 100644
--- a/java/com/android/dialer/simulator/stub/SimulatorEnrichedCallStub.java
+++ b/java/com/android/dialer/simulator/stub/SimulatorEnrichedCallStub.java
@@ -17,6 +17,8 @@
 package com.android.dialer.simulator.stub;
 
 import com.android.dialer.simulator.SimulatorEnrichedCall;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 import javax.inject.Inject;
 
 /** Stub implementation of {@link SimulatorEnrichedCall}. */
@@ -26,13 +28,13 @@
   public SimulatorEnrichedCallStub() {}
 
   @Override
-  public long setupIncomingEnrichedCall(String number) {
-    return -1;
+  public ListenableFuture<Void> setupIncomingEnrichedCall(String number) {
+    return Futures.immediateFuture(null);
   }
 
   @Override
-  public long setupOutgoingEnrichedCall(String number) {
-    return -1;
+  public ListenableFuture<Void> setupOutgoingEnrichedCall(String number) {
+    return Futures.immediateFuture(null);
   }
 
   @Override
diff --git a/java/com/android/dialer/telecom/TelecomUtil.java b/java/com/android/dialer/telecom/TelecomUtil.java
index f79ca86..56349b6 100644
--- a/java/com/android/dialer/telecom/TelecomUtil.java
+++ b/java/com/android/dialer/telecom/TelecomUtil.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.Manifest.permission;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -49,6 +50,7 @@
  * perform the required check and return the fallback default if the permission is missing,
  * otherwise return the value from TelecomManager.
  */
+@SuppressWarnings("MissingPermission")
 public abstract class TelecomUtil {
 
   private static final String TAG = "TelecomUtil";
@@ -148,6 +150,34 @@
     return new ArrayList<>();
   }
 
+  /** Return a list of phone accounts that are subscription/SIM accounts. */
+  public static List<PhoneAccountHandle> getSubscriptionPhoneAccounts(Context context) {
+    List<PhoneAccountHandle> subscriptionAccountHandles = new ArrayList<>();
+    final List<PhoneAccountHandle> accountHandles =
+        TelecomUtil.getCallCapablePhoneAccounts(context);
+    for (PhoneAccountHandle accountHandle : accountHandles) {
+      PhoneAccount account = TelecomUtil.getPhoneAccount(context, accountHandle);
+      if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+        subscriptionAccountHandles.add(accountHandle);
+      }
+    }
+    return subscriptionAccountHandles;
+  }
+
+  /** Compose {@link PhoneAccountHandle} object from component name and account id. */
+  @Nullable
+  public static PhoneAccountHandle composePhoneAccountHandle(
+      @Nullable String componentString, @Nullable String accountId) {
+    if (TextUtils.isEmpty(componentString) || TextUtils.isEmpty(accountId)) {
+      return null;
+    }
+    final ComponentName componentName = ComponentName.unflattenFromString(componentString);
+    if (componentName == null) {
+      return null;
+    }
+    return new PhoneAccountHandle(componentName, accountId);
+  }
+
   /**
    * @return the {@link SubscriptionInfo} of the SIM if {@code phoneAccountHandle} corresponds to a
    *     valid SIM. Absent otherwise.
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
index 5519aa4..318f797 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
@@ -25,6 +25,7 @@
 import android.net.Uri;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -43,9 +44,15 @@
 import com.android.dialer.common.concurrent.ThreadUtil;
 import com.android.dialer.time.Clock;
 import com.android.dialer.voicemail.listui.NewVoicemailViewHolder.NewVoicemailViewHolderListener;
+import com.android.dialer.voicemail.listui.error.VoicemailErrorMessage;
+import com.android.dialer.voicemail.listui.error.VoicemailErrorMessageCreator;
+import com.android.dialer.voicemail.listui.error.VoicemailStatus;
 import com.android.dialer.voicemail.model.VoicemailEntry;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
 
@@ -66,6 +73,7 @@
   }
 
   private Cursor cursor;
+  private Cursor voicemailStatusCursor;
   private final Clock clock;
 
   /** {@link Integer#MAX_VALUE} when the "Today" header should not be displayed. */
@@ -81,6 +89,8 @@
   /** A valid id for {@link VoicemailEntry} is greater than 0 */
   private int currentlyExpandedViewHolderId = -1;
 
+  private VoicemailErrorMessage voicemailErrorMessage;
+
   /**
    * It takes time to delete voicemails from the server, so we "remove" them and remember the
    * positions we removed until a new cursor is ready.
@@ -248,34 +258,14 @@
     if (viewHolder instanceof NewVoicemailHeaderViewHolder) {
       LogUtil.i(
           "NewVoicemailAdapter.onBindViewHolder", "view holder at pos:%d is a header", position);
-      NewVoicemailHeaderViewHolder headerViewHolder = (NewVoicemailHeaderViewHolder) viewHolder;
-      @RowType int viewType = getItemViewType(position);
-      if (position == todayHeaderPosition) {
-        headerViewHolder.setHeader(R.string.new_voicemail_header_today);
-      } else if (position == yesterdayHeaderPosition) {
-        headerViewHolder.setHeader(R.string.new_voicemail_header_yesterday);
-      } else if (position == olderHeaderPosition) {
-        headerViewHolder.setHeader(R.string.new_voicemail_header_older);
-      } else {
-        throw Assert.createIllegalStateFailException(
-            "Unexpected view type " + viewType + " at position: " + position);
-      }
+      onBindHeaderViewHolder(viewHolder, position);
       return;
     }
 
     if (viewHolder instanceof NewVoicemailAlertViewHolder) {
       LogUtil.i(
           "NewVoicemailAdapter.onBindViewHolder", "view holder at pos:%d is a alert", position);
-      NewVoicemailAlertViewHolder alertViewHolder = (NewVoicemailAlertViewHolder) viewHolder;
-      @RowType int viewType = getItemViewType(position);
-      Assert.checkArgument(position == 0);
-      if (position == voicemailAlertPosition) {
-        // TODO(a bug): Update this with the alert messages
-        alertViewHolder.setHeader("Temporary placeholder, update this with the alert messages");
-      } else {
-        throw Assert.createIllegalStateFailException(
-            "Unexpected view type " + viewType + " at position: " + position);
-      }
+      onBindAlertViewHolder(viewHolder, position);
       return;
     }
 
@@ -285,26 +275,13 @@
         position);
 
     NewVoicemailViewHolder newVoicemailViewHolder = (NewVoicemailViewHolder) viewHolder;
-
-    int previousHeaders = 0;
-    if (voicemailAlertPosition != Integer.MAX_VALUE && position > voicemailAlertPosition) {
-      previousHeaders++;
-    }
-    if (todayHeaderPosition != Integer.MAX_VALUE && position > todayHeaderPosition) {
-      previousHeaders++;
-    }
-    if (yesterdayHeaderPosition != Integer.MAX_VALUE && position > yesterdayHeaderPosition) {
-      previousHeaders++;
-    }
-    if (olderHeaderPosition != Integer.MAX_VALUE && position > olderHeaderPosition) {
-      previousHeaders++;
-    }
+    int nonVoicemailEntryHeaders = getHeaderCountAtPosition(position);
 
     LogUtil.i(
         "NewVoicemailAdapter.onBindViewHolder",
-        "view holder at pos:%d, prevHeaderCount:%d",
+        "view holder at pos:%d, nonVoicemailEntryHeaders:%d",
         position,
-        previousHeaders);
+        nonVoicemailEntryHeaders);
 
     // Remove if the viewholder is being recycled.
     if (newVoicemailViewHolderArrayMap.containsKey(newVoicemailViewHolder.getViewHolderId())) {
@@ -322,7 +299,7 @@
     }
 
     newVoicemailViewHolder.reset();
-    cursor.moveToPosition(position - previousHeaders);
+    cursor.moveToPosition(position - nonVoicemailEntryHeaders);
     newVoicemailViewHolder.bindViewHolderValuesFromAdapter(
         cursor, fragmentManager, mediaPlayer, position, currentlyExpandedViewHolderId);
 
@@ -378,6 +355,72 @@
     printArrayMap();
   }
 
+  private int getHeaderCountAtPosition(int position) {
+    int previousHeaders = 0;
+    if (voicemailAlertPosition != Integer.MAX_VALUE && position > voicemailAlertPosition) {
+      previousHeaders++;
+    }
+    if (todayHeaderPosition != Integer.MAX_VALUE && position > todayHeaderPosition) {
+      previousHeaders++;
+    }
+    if (yesterdayHeaderPosition != Integer.MAX_VALUE && position > yesterdayHeaderPosition) {
+      previousHeaders++;
+    }
+    if (olderHeaderPosition != Integer.MAX_VALUE && position > olderHeaderPosition) {
+      previousHeaders++;
+    }
+    return previousHeaders;
+  }
+
+  private void onBindAlertViewHolder(ViewHolder viewHolder, int position) {
+    LogUtil.i(
+        "NewVoicemailAdapter.onBindAlertViewHolder",
+        "pos:%d, voicemailAlertPosition:%d",
+        position,
+        voicemailAlertPosition);
+
+    NewVoicemailAlertViewHolder alertViewHolder = (NewVoicemailAlertViewHolder) viewHolder;
+    @RowType int viewType = getItemViewType(position);
+
+    Assert.checkArgument(position == 0, "position is not 0");
+    Assert.checkArgument(
+        position == voicemailAlertPosition,
+        String.format(
+            Locale.US,
+            "position:%d and voicemailAlertPosition:%d are different",
+            position,
+            voicemailAlertPosition));
+    Assert.checkArgument(viewType == RowType.VOICEMAIL_ALERT, "Invalid row type: " + viewType);
+    Assert.checkArgument(
+        voicemailErrorMessage.getActions().size() <= 2,
+        "Too many actions: " + voicemailErrorMessage.getActions().size());
+
+    alertViewHolder.setTitle(voicemailErrorMessage.getTitle());
+    alertViewHolder.setDescription(voicemailErrorMessage.getDescription());
+
+    if (!voicemailErrorMessage.getActions().isEmpty()) {
+      alertViewHolder.setPrimaryButton(voicemailErrorMessage.getActions().get(0));
+    }
+    if (voicemailErrorMessage.getActions().size() > 1) {
+      alertViewHolder.setSecondaryButton(voicemailErrorMessage.getActions().get(1));
+    }
+  }
+
+  private void onBindHeaderViewHolder(ViewHolder viewHolder, int position) {
+    NewVoicemailHeaderViewHolder headerViewHolder = (NewVoicemailHeaderViewHolder) viewHolder;
+    @RowType int viewType = getItemViewType(position);
+    if (position == todayHeaderPosition) {
+      headerViewHolder.setHeader(R.string.new_voicemail_header_today);
+    } else if (position == yesterdayHeaderPosition) {
+      headerViewHolder.setHeader(R.string.new_voicemail_header_yesterday);
+    } else if (position == olderHeaderPosition) {
+      headerViewHolder.setHeader(R.string.new_voicemail_header_older);
+    } else {
+      throw Assert.createIllegalStateFailException(
+          "Unexpected view type " + viewType + " at position: " + position);
+    }
+  }
+
   private void printArrayMap() {
     LogUtil.i(
         "NewVoicemailAdapter.printArrayMap",
@@ -958,4 +1001,47 @@
       LogUtil.i("NewVoicemailAdapter.checkAndPlayVoicemail", "not playing downloaded voicemail");
     }
   }
+
+  @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+  public void setVoicemailStatusCursor(Cursor voicemailStatusCursor) {
+    this.voicemailStatusCursor = voicemailStatusCursor;
+  }
+
+  // TODO(uabdullah): Handle ToS properly
+  public void updateAlert(Context context) {
+    if (voicemailStatusCursor == null) {
+      LogUtil.i("NewVoicemailAdapter.updateAlert", "status cursor was null");
+      return;
+    }
+
+    LogUtil.i(
+        "NewVoicemailAdapter.updateAlert",
+        "status cursor size was " + voicemailStatusCursor.getCount());
+
+    List<VoicemailStatus> statuses = new ArrayList<>();
+
+    while (voicemailStatusCursor.moveToNext()) {
+      VoicemailStatus status = new VoicemailStatus(context, voicemailStatusCursor);
+      if (status.isActive()) {
+        statuses.add(status);
+        // TODO(uabdullah): addServiceStateListener
+      }
+    }
+
+    voicemailErrorMessage = null;
+    VoicemailErrorMessageCreator messageCreator = new VoicemailErrorMessageCreator();
+
+    for (VoicemailStatus status : statuses) {
+      voicemailErrorMessage = messageCreator.create(context, status, null);
+      if (voicemailErrorMessage != null) {
+        break;
+      }
+    }
+
+    if (voicemailErrorMessage != null) {
+      voicemailAlertPosition = 0;
+      updateHeaderPositions();
+      notifyItemChanged(0);
+    }
+  }
 }
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAlertViewHolder.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAlertViewHolder.java
index ec603b5..ac989a8 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailAlertViewHolder.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAlertViewHolder.java
@@ -18,19 +18,43 @@
 
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.View;
+import android.widget.Button;
 import android.widget.TextView;
+import com.android.dialer.voicemail.listui.error.VoicemailErrorMessage.Action;
 
 /** ViewHolder for {@link NewVoicemailAdapter} to display voicemail error states. */
 final class NewVoicemailAlertViewHolder extends ViewHolder {
 
-  private final TextView errorTextView;
+  private final TextView voicemailErrorTitleTextView;
+  private final TextView voicemailErrorDetailsTextView;
+  private final Button primaryButton;
+  private final Button secondaryButton;
 
   NewVoicemailAlertViewHolder(View view) {
     super(view);
-    errorTextView = view.findViewById(R.id.new_voicemail_alert_text);
+    voicemailErrorTitleTextView = view.findViewById(R.id.voicemail_alert_header);
+    voicemailErrorDetailsTextView = view.findViewById(R.id.voicemail_alert_details);
+    primaryButton = view.findViewById(R.id.voicemail_alert_primary_button);
+    secondaryButton = view.findViewById(R.id.voicemail_alert_primary_button);
   }
 
-  void setHeader(String error) {
-    errorTextView.setText(error);
+  void setTitle(CharSequence error) {
+    voicemailErrorTitleTextView.setText(error);
+  }
+
+  void setDescription(CharSequence error) {
+    voicemailErrorDetailsTextView.setText(error);
+  }
+
+  void setPrimaryButton(Action action) {
+    primaryButton.setVisibility(View.VISIBLE);
+    primaryButton.setText(action.getText());
+    primaryButton.setOnClickListener(action.getListener());
+  }
+
+  void setSecondaryButton(Action action) {
+    secondaryButton.setVisibility(View.VISIBLE);
+    secondaryButton.setText(action.getText());
+    secondaryButton.setOnClickListener(action.getListener());
   }
 }
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
index b4be424..0d91f01 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
@@ -28,11 +28,15 @@
 import android.view.View;
 import android.view.ViewGroup;
 import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.CallLogQueryHandler;
+import com.android.dialer.database.CallLogQueryHandler.Listener;
 
+// TODO(uabdullah): Register content observer for VoicemailContract.Status.CONTENT_URI in onStart
 /** Fragment for Dialer Voicemail Tab. */
 public final class NewVoicemailFragment extends Fragment implements LoaderCallbacks<Cursor> {
 
   private RecyclerView recyclerView;
+  private CallLogQueryHandler callLogQueryHandler;
 
   @Nullable
   @Override
@@ -72,6 +76,31 @@
       ((NewVoicemailAdapter) recyclerView.getAdapter()).updateCursor(data);
       ((NewVoicemailAdapter) recyclerView.getAdapter()).checkAndPlayVoicemail();
     }
+    callLogQueryHandler =
+        new CallLogQueryHandler(
+            getContext(), getContext().getContentResolver(), new NewVoicemailFragmentListener());
+    callLogQueryHandler.fetchVoicemailStatus();
+  }
+
+  private final class NewVoicemailFragmentListener implements Listener {
+
+    @Override
+    public void onVoicemailStatusFetched(Cursor statusCursor) {
+      LogUtil.enterBlock("NewVoicemailFragmentListener.onVoicemailStatusFetched");
+      ((NewVoicemailAdapter) recyclerView.getAdapter()).setVoicemailStatusCursor(statusCursor);
+      ((NewVoicemailAdapter) recyclerView.getAdapter()).updateAlert(getContext());
+    }
+
+    @Override
+    public void onVoicemailUnreadCountFetched(Cursor cursor) {}
+
+    @Override
+    public void onMissedCallsUnreadCountFetched(Cursor cursor) {}
+
+    @Override
+    public boolean onCallsFetched(Cursor combinedCursor) {
+      return false;
+    }
   }
 
   @Override
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayer.java b/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayer.java
index 48062a8..519a0df 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayer.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayer.java
@@ -26,6 +26,7 @@
 import android.support.annotation.Nullable;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
+import com.android.dialer.strictmode.StrictModeUtils;
 import java.io.IOException;
 
 /** A wrapper around {@link MediaPlayer} */
@@ -45,6 +46,7 @@
     mediaPlayer = Assert.isNotNull(player);
   }
 
+  // TODO(uabdullah): Consider removing the StrictModeUtils.bypass (a bug)
   public void prepareMediaPlayerAndPlayVoicemailWhenReady(Context context, Uri uri)
       throws IOException {
     Assert.checkArgument(uri != null, "Media player cannot play a null uri");
@@ -57,9 +59,23 @@
       voicemailUriLastPreparedOrPreparingToPlay = uri;
       verifyListenersNotNull();
       LogUtil.i("NewVoicemailMediaPlayer", "setData source");
-      mediaPlayer.setDataSource(context, uri);
+      StrictModeUtils.bypass(
+          () -> {
+            try {
+              mediaPlayer.setDataSource(context, uri);
+            } catch (IOException e) {
+              LogUtil.i(
+                  "NewVoicemailMediaPlayer",
+                  "threw an Exception when setting datasource "
+                      + e
+                      + " for uri: "
+                      + uri
+                      + "for context : "
+                      + context);
+            }
+          });
       LogUtil.i("NewVoicemailMediaPlayer", "prepare async");
-      mediaPlayer.prepareAsync();
+      StrictModeUtils.bypass(() -> mediaPlayer.prepareAsync());
     } catch (IllegalStateException e) {
       LogUtil.i(
           "NewVoicemailMediaPlayer", "caught an IllegalStateException state exception : \n" + e);
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java b/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java
index 66a6fea..3becd27 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java
@@ -17,16 +17,17 @@
 package com.android.dialer.voicemail.listui;
 
 import android.app.FragmentManager;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
+import android.media.AudioManager;
 import android.net.Uri;
 import android.provider.VoicemailContract;
 import android.provider.VoicemailContract.Voicemails;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.util.Pair;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -35,12 +36,15 @@
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
+import com.android.dialer.callintent.CallInitiationType.Type;
+import com.android.dialer.callintent.CallIntentBuilder;
 import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener;
 import com.android.dialer.common.concurrent.DialerExecutor.Worker;
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.precall.PreCall;
 import com.android.dialer.voicemail.listui.NewVoicemailViewHolder.NewVoicemailViewHolderListener;
 import com.android.dialer.voicemail.model.VoicemailEntry;
 import java.util.Locale;
@@ -60,6 +64,7 @@
   private TextView totalDurationView;
   private TextView voicemailLoadingStatusView;
   private Uri voicemailUri;
+  private String numberVoicemailFrom;
   private FragmentManager fragmentManager;
   private NewVoicemailViewHolder newVoicemailViewHolder;
   private NewVoicemailMediaPlayer mediaPlayer;
@@ -103,9 +108,12 @@
   }
 
   public void reset() {
-    LogUtil.i("NewVoicemailMediaPlayer.reset", "the uri for this is " + voicemailUri);
+    LogUtil.i(
+        "NewVoicemailMediaPlayer.reset",
+        "the uri for this is " + voicemailUri + " and number is " + numberVoicemailFrom);
     voicemailUri = null;
     voicemailLoadingStatusView.setVisibility(GONE);
+    numberVoicemailFrom = null;
   }
 
   /**
@@ -134,6 +142,7 @@
 
     Assert.isNotNull(voicemailEntryFromAdapter);
     Uri uri = Uri.parse(voicemailEntryFromAdapter.voicemailUri());
+    numberVoicemailFrom = voicemailEntryFromAdapter.number().getRawInput().getNumber();
     Assert.isNotNull(viewHolder);
     Assert.isNotNull(uri);
     Assert.isNotNull(listener);
@@ -452,31 +461,40 @@
               "NewVoicemailMediaPlayer.speakerButtonListener",
               "speaker request for voicemailUri: %s",
               voicemailUri.toString());
+          AudioManager audioManager =
+              (AudioManager) getContext().getSystemService(AudioManager.class);
+          audioManager.setMode(AudioManager.STREAM_MUSIC);
+          if (audioManager.isSpeakerphoneOn()) {
+            LogUtil.i(
+                "NewVoicemailMediaPlayer.phoneButtonListener", "speaker was on, turning it off");
+            audioManager.setSpeakerphoneOn(false);
+          } else {
+            LogUtil.i(
+                "NewVoicemailMediaPlayer.phoneButtonListener", "speaker was off, turning it on");
+            audioManager.setSpeakerphoneOn(true);
+          }
+          // TODO(uabdullah): Handle colors of speaker icon when speaker is on and off.
         }
       };
 
+  // TODO(uabdullah): Add phone account handle (a bug)
+  // TODO(uabdullah): If the call cannot be made then the phone icon should be greyed out
+  // (a bug)
   private final View.OnClickListener phoneButtonListener =
       new View.OnClickListener() {
         @Override
         public void onClick(View view) {
           LogUtil.i(
               "NewVoicemailMediaPlayer.phoneButtonListener",
-              "speaker request for voicemailUri: %s",
-              voicemailUri.toString());
-          ContentValues contentValues = new ContentValues();
-          contentValues.put("has_content", 0);
-          // TODO(uabdullah): It only sets the has_content to 0, to allow the annotated call log to
-          // change, and refresh the fragment. This is used to demo and test the downloading of
-          // voicemails from the server. This will be removed once we implement this listener.
-          try {
-            getContext().getContentResolver().update(voicemailUri, contentValues, "type = 4", null);
-          } catch (Exception e) {
-            LogUtil.i(
-                "NewVoicemailMediaPlayer.deleteButtonListener",
-                "update has content of voicemailUri %s caused an error: %s",
-                voicemailUri.toString(),
-                e.toString());
-          }
+              "phone request for voicemailUri: %s with number:%s",
+              voicemailUri.toString(),
+              numberVoicemailFrom);
+
+          Assert.checkArgument(
+              !TextUtils.isEmpty(numberVoicemailFrom),
+              "number cannot be empty:" + numberVoicemailFrom);
+          PreCall.start(
+              getContext(), new CallIntentBuilder(numberVoicemailFrom, Type.VOICEMAIL_LOG));
         }
       };
 
diff --git a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_alert.xml b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_alert.xml
index e8dcd02..28d6391 100644
--- a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_alert.xml
+++ b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_alert.xml
@@ -14,17 +14,96 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<RelativeLayout
+
+<!-- TODO(uabdullah): Use a relative layout instead of nested linear layouts.-->
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="48dp"
+    android:id="@+id/voicemail_alert_content"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-  <!-- TODO(uabdullah): Confirm with UX on mocks -->
-  <TextView
-      android:id="@+id/new_voicemail_alert_text"
-      style="@style/SecondaryText"
-      android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+  <LinearLayout
+      android:layout_width="match_parent"
       android:layout_height="wrap_content"
-      android:layout_marginStart="@dimen/voicemail_header_margin_start"
-      android:layout_centerVertical="true"/>
-</RelativeLayout>
+      android:paddingTop="@dimen/alert_main_padding"
+      android:paddingBottom="@dimen/alert_main_padding"
+      android:paddingStart="@dimen/alert_main_padding"
+      android:paddingEnd="@dimen/alert_main_padding"
+      android:gravity="top"
+      android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/voicemail_alert_icon"
+        android:layout_width="@dimen/voicemail_promo_card_icon_size"
+        android:layout_height="@dimen/voicemail_promo_card_icon_size"
+        android:layout_gravity="top"
+        android:src="@drawable/ic_voicemail_error_24px"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/voicemail_promo_card_main_padding"
+        android:gravity="center_vertical"
+        android:orientation="vertical">
+
+      <TextView
+          android:id="@+id/voicemail_alert_header"
+          android:textStyle="bold"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginBottom="@dimen/voicemail_promo_card_title_padding"
+          android:layout_gravity="center_vertical"
+          android:singleLine="false"
+          android:text="Voicemail Alert"
+          android:textSize="@dimen/voicemail_promo_card_title_text_size"/>
+
+      <TextView
+          android:id="@+id/voicemail_alert_details"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:lineSpacingExtra="@dimen/voicemail_promo_card_line_spacing"
+          android:singleLine="false"
+          android:text="This is a voicemail alert message."
+          android:textSize="@dimen/voicemail_promo_card_message_size"/>
+    </LinearLayout>
+  </LinearLayout>
+
+  <LinearLayout
+      android:id="@+id/voicemail_alert_button"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_gravity="end"
+      android:paddingTop="10dp"
+      android:paddingBottom="10dp"
+      android:paddingStart="16dp"
+      android:paddingEnd="16dp"
+      android:gravity="end"
+      android:minHeight="56dp"
+      android:orientation="horizontal">
+    <Button
+        android:id="@+id/voicemail_alert_secondary_button"
+        style="@style/TosButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/dialer_theme_color"/>
+
+    <Button
+        android:id="@+id/voicemail_alert_primary_button"
+        style="@style/TosButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/dialer_theme_color"/>
+  </LinearLayout>
+
+  <LinearLayout
+      android:layout_width="0dip"
+      android:layout_height="match_parent"
+      android:layout_weight="1"
+      android:layout_gravity="center"
+      android:divider="?android:dividerHorizontal"
+      android:gravity="center"
+      android:orientation="vertical"
+      android:showDividers="middle">
+  </LinearLayout>
+</LinearLayout>
diff --git a/java/com/android/dialer/voicemail/listui/res/values/dimens.xml b/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
index 59da7f2..960d327 100644
--- a/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
+++ b/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
@@ -41,4 +41,13 @@
 
   <dimen name="voicemail_tos_image_size">280dp</dimen>
 
+  <!-- Dimensions for voicemail alert -->
+  <dimen name="alert_main_padding">24dp</dimen>
+  <dimen name="voicemail_promo_card_icon_size">24dp</dimen>
+  <dimen name="voicemail_promo_card_main_padding">24dp</dimen>
+  <dimen name="voicemail_promo_card_title_padding">12dp</dimen>
+  <dimen name="voicemail_promo_card_title_text_size">16sp</dimen>
+  <dimen name="voicemail_promo_card_line_spacing">4dp</dimen>
+  <dimen name="voicemail_promo_card_message_size">14sp</dimen>
+
 </resources>
diff --git a/java/com/android/incallui/ContactInfoCache.java b/java/com/android/incallui/ContactInfoCache.java
index fbee743..fc41df4 100644
--- a/java/com/android/incallui/ContactInfoCache.java
+++ b/java/com/android/incallui/ContactInfoCache.java
@@ -291,7 +291,7 @@
       return name;
     } else {
       if (presentation == TelecomManager.PRESENTATION_RESTRICTED) {
-        name = PhoneNumberHelper.getDisplayNameForRestrictedNumber(context).toString();
+        name = PhoneNumberHelper.getDisplayNameForRestrictedNumber(context);
       } else if (presentation == TelecomManager.PRESENTATION_PAYPHONE) {
         name = context.getString(R.string.payphone);
       }
diff --git a/java/com/android/incallui/multisim/SwapSimWorker.java b/java/com/android/incallui/multisim/SwapSimWorker.java
index 28cf293..1014d82 100644
--- a/java/com/android/incallui/multisim/SwapSimWorker.java
+++ b/java/com/android/incallui/multisim/SwapSimWorker.java
@@ -102,7 +102,7 @@
     try {
       SimSuggestionComponent.get(context)
           .getSuggestionProvider()
-          .reportIncorrectSuggestion(context, number);
+          .reportIncorrectSuggestion(context, number, otherAccount);
 
       if (!PermissionsUtil.hasPhonePermissions(context)) {
         LogUtil.e("SwapSimWorker.doInBackground", "missing phone permission");
diff --git a/java/com/android/voicemail/impl/sync/VvmNetworkRequestCallback.java b/java/com/android/voicemail/impl/sync/VvmNetworkRequestCallback.java
index 60ba166..c0ab678 100644
--- a/java/com/android/voicemail/impl/sync/VvmNetworkRequestCallback.java
+++ b/java/com/android/voicemail/impl/sync/VvmNetworkRequestCallback.java
@@ -56,6 +56,7 @@
   private final VoicemailStatus.Editor status;
   private boolean requestSent = false;
   private boolean resultReceived = false;
+  private boolean released = false;
 
   public VvmNetworkRequestCallback(
       Context context, PhoneAccountHandle phoneAccount, VoicemailStatus.Editor status) {
@@ -112,7 +113,7 @@
   @Override
   @CallSuper
   public void onLost(Network network) {
-    VvmLog.d(TAG, "onLost");
+    VvmLog.i(TAG, "onLost");
     resultReceived = true;
     onFailed(NETWORK_REQUEST_FAILED_LOST);
   }
@@ -126,7 +127,7 @@
 
   @CallSuper
   public void onUnavailable() {
-    // TODO(twyen): a bug this is hidden, do we really need this?
+    VvmLog.i(TAG, "onUnavailable");
     resultReceived = true;
     onFailed(NETWORK_REQUEST_FAILED_TIMEOUT);
   }
@@ -156,8 +157,13 @@
   }
 
   public void releaseNetwork() {
-    VvmLog.d(TAG, "releaseNetwork");
-    getConnectivityManager().unregisterNetworkCallback(this);
+    VvmLog.i(TAG, "releaseNetwork");
+    if (!released) {
+      getConnectivityManager().unregisterNetworkCallback(this);
+      released = true;
+    } else {
+      VvmLog.w(TAG, "already released");
+    }
   }
 
   public ConnectivityManager getConnectivityManager() {
@@ -170,7 +176,7 @@
 
   @CallSuper
   public void onFailed(String reason) {
-    VvmLog.d(TAG, "onFailed: " + reason);
+    VvmLog.i(TAG, "onFailed: " + reason);
     if (carrierConfigHelper.isCellularDataRequired()) {
       carrierConfigHelper.handleEvent(status, OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED);
     } else {