Added number presentation to AnnotatedCallLog.

Updated the new call log UI to properly show text based on the presentation.

Bug: 70989592
Test: unit
PiperOrigin-RevId: 183414195
Change-Id: I2123f37cd3c733060125b6e894c1a80be4193ad6
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 e301c9f..ed09eea 100644
--- a/java/com/android/dialer/calllog/database/Coalescer.java
+++ b/java/com/android/dialer/calllog/database/Coalescer.java
@@ -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 93c35c5..24410ee 100644
--- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -196,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)
@@ -239,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,
@@ -251,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",
@@ -273,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);
@@ -295,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);
@@ -333,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);
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 beb2cf0..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.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,14 +85,11 @@
     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 =
         TelecomUtil.composePhoneAccountHandle(
             row.phoneAccountComponentName(), row.phoneAccountId());
@@ -90,14 +99,14 @@
       // 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));
     }
   }
 
@@ -112,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 05af8bf..227b15e 100644
--- a/java/com/android/dialer/calllogutils/CallLogIntents.java
+++ b/java/com/android/dialer/calllogutils/CallLogIntents.java
@@ -19,10 +19,10 @@
 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;
 
@@ -39,18 +39,16 @@
    */
   @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(
                 TelecomUtil.composePhoneAccountHandle(
                     row.phoneAccountComponentName(), row.phoneAccountId()))
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/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/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);
       }