Added is_valid and post_dial_portion fields to DialerPhoneNumber.

These are frequently used attributes of numbers that we can compute once at parse-time.

Also did some general cleanup of DialerPhoneNumberUtil:

-Removed unused Future version of parse()
-Remove formatToValidE164 now that the new fields are available
-Inlined normalizeNumber()

Bug: 72563861
Test: existing
PiperOrigin-RevId: 183720128
Change-Id: I702dc265360e590439c5352c493ae8a858f36812
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 565a2a3..8fa6b67 100644
--- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -42,14 +42,12 @@
 import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator;
 import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract;
 import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
-import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import com.google.protobuf.InvalidProtocolBufferException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -192,11 +190,8 @@
           populateInserts(originalPhoneLookupHistoryDataByAnnotatedCallLogId.build(), mutations);
 
           // Compute and save the PhoneLookupHistory rows which can be deleted in onSuccessfulFill.
-          DialerPhoneNumberUtil dialerPhoneNumberUtil =
-              new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
           phoneLookupHistoryRowsToDelete.addAll(
-              computePhoneLookupHistoryRowsToDelete(
-                  annotatedCallLogIdsByNumber, mutations, dialerPhoneNumberUtil));
+              computePhoneLookupHistoryRowsToDelete(annotatedCallLogIdsByNumber, mutations));
 
           // Now compute the rows to update.
           ImmutableMap.Builder<Long, PhoneLookupInfo> rowsToUpdate = ImmutableMap.builder();
@@ -209,7 +204,8 @@
               }
               // Also save the updated information so that it can be written to PhoneLookupHistory
               // in onSuccessfulFill.
-              String normalizedNumber = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
+              // Note: This loses country info when number is not valid.
+              String normalizedNumber = dialerPhoneNumber.getNormalizedNumber();
               phoneLookupHistoryRowsToUpdate.put(normalizedNumber, upToDateInfo);
             }
           }
@@ -417,10 +413,9 @@
   /** Returned map must have same keys as {@code uniqueDialerPhoneNumbers} */
   private ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> queryPhoneLookupHistoryForNumbers(
       Context appContext, Set<DialerPhoneNumber> uniqueDialerPhoneNumbers) {
-    DialerPhoneNumberUtil dialerPhoneNumberUtil =
-        new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
+    // Note: This loses country info when number is not valid.
     Map<DialerPhoneNumber, String> dialerPhoneNumberToNormalizedNumbers =
-        Maps.asMap(uniqueDialerPhoneNumbers, dialerPhoneNumberUtil::normalizeNumber);
+        Maps.asMap(uniqueDialerPhoneNumbers, DialerPhoneNumber::getNormalizedNumber);
 
     // Convert values to a set to remove any duplicates that are the result of two
     // DialerPhoneNumbers mapping to the same normalized number.
@@ -543,9 +538,7 @@
   }
 
   private Set<String> computePhoneLookupHistoryRowsToDelete(
-      Map<DialerPhoneNumber, Set<Long>> annotatedCallLogIdsByNumber,
-      CallLogMutations mutations,
-      DialerPhoneNumberUtil dialerPhoneNumberUtil) {
+      Map<DialerPhoneNumber, Set<Long>> annotatedCallLogIdsByNumber, CallLogMutations mutations) {
     if (mutations.getDeletes().isEmpty()) {
       return ImmutableSet.of();
     }
@@ -555,7 +548,8 @@
     for (Entry<DialerPhoneNumber, Set<Long>> entry : annotatedCallLogIdsByNumber.entrySet()) {
       DialerPhoneNumber dialerPhoneNumber = entry.getKey();
       Set<Long> idsForDialerPhoneNumber = entry.getValue();
-      String normalizedNumber = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
+      // Note: This loses country info when number is not valid.
+      String normalizedNumber = dialerPhoneNumber.getNormalizedNumber();
       Set<Long> idsForNormalizedNumber = idsByNormalizedNumber.get(normalizedNumber);
       if (idsForNormalizedNumber == null) {
         idsForNormalizedNumber = new ArraySet<>();
diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
index f15c313..421c35f 100644
--- a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
+++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
@@ -36,13 +36,11 @@
 import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator;
 import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract;
 import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
-import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -151,9 +149,6 @@
     ListenableFuture<Integer> applyBatchFuture =
         backgroundExecutor.submit(
             () -> {
-              DialerPhoneNumberUtil dialerPhoneNumberUtil =
-                  new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
-
               ArrayList<ContentProviderOperation> operations = new ArrayList<>();
               long currentTimestamp = System.currentTimeMillis();
               for (Entry<DialerPhoneNumber, PhoneLookupInfo> entry : currentBatch.entrySet()) {
@@ -162,7 +157,8 @@
 
                 // Note: Multiple DialerPhoneNumbers can map to the same normalized number but we
                 // just write them all and the value for the last one will arbitrarily win.
-                String normalizedNumber = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
+                // Note: This loses country info when number is not valid.
+                String normalizedNumber = dialerPhoneNumber.getNormalizedNumber();
 
                 ContentValues contentValues = new ContentValues();
                 contentValues.put(
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
index 127569b..95b14a4 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
@@ -39,18 +39,16 @@
 import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info;
 import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo;
 import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
-import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
 import com.android.dialer.phonenumberproto.PartitionedNumbers;
 import com.android.dialer.storage.Unencrypted;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import com.google.protobuf.InvalidProtocolBufferException;
 import java.util.ArrayList;
 import java.util.List;
@@ -96,21 +94,32 @@
   }
 
   private Cp2Info lookupInternal(DialerPhoneNumber dialerPhoneNumber) {
-    DialerPhoneNumberUtil dialerPhoneNumberUtil =
-        new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
-    String number = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
+    String number = dialerPhoneNumber.getNormalizedNumber();
     if (TextUtils.isEmpty(number)) {
       return Cp2Info.getDefaultInstance();
     }
-    Optional<String> validE164 = dialerPhoneNumberUtil.formatToValidE164(dialerPhoneNumber);
+
     Set<Cp2ContactInfo> cp2ContactInfos = new ArraySet<>();
-    // Note: It would make sense to use PHONE_LOOKUP for E164 numbers as well, but we use PHONE to
-    // ensure consistency when the batch methods are used to update data.
-    try (Cursor cursor =
-        validE164.isPresent()
-            ? queryPhoneTableBasedOnE164(
-                Cp2Projections.getProjectionForPhoneTable(), ImmutableSet.of(validE164.get()))
-            : queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), number)) {
+
+    // Even though this is only a single number, use PartitionedNumbers to mimic the logic used
+    // during getMostRecentInfo.
+    PartitionedNumbers partitionedNumbers =
+        new PartitionedNumbers(ImmutableSet.of(dialerPhoneNumber));
+
+    Cursor cursor = null;
+    try {
+      // Note: It would make sense to use PHONE_LOOKUP for valid numbers as well, but we use PHONE
+      // to ensure consistency when the batch methods are used to update data.
+      if (!partitionedNumbers.validE164Numbers().isEmpty()) {
+        cursor =
+            queryPhoneTableBasedOnE164(
+                Cp2Projections.getProjectionForPhoneTable(), partitionedNumbers.validE164Numbers());
+      } else {
+        cursor =
+            queryPhoneLookup(
+                Cp2Projections.getProjectionForPhoneLookupTable(),
+                Iterables.getOnlyElement(partitionedNumbers.invalidNumbers()));
+      }
       if (cursor == null) {
         LogUtil.w("Cp2LocalPhoneLookup.lookupInternal", "null cursor");
         return Cp2Info.getDefaultInstance();
@@ -118,6 +127,10 @@
       while (cursor.moveToNext()) {
         cp2ContactInfos.add(Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor));
       }
+    } finally {
+      if (cursor != null) {
+        cursor.close();
+      }
     }
     return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
   }
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
index 9596f15..cc4fbf1 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
@@ -32,14 +32,12 @@
 import com.android.dialer.phonelookup.PhoneLookup;
 import com.android.dialer.phonelookup.PhoneLookupInfo;
 import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info;
-import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
 import com.android.dialer.phonenumberutil.PhoneNumberHelper;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import java.util.ArrayList;
 import java.util.List;
 import javax.inject.Inject;
@@ -114,9 +112,8 @@
       return Futures.immediateFuture(Cp2Info.getDefaultInstance());
     }
 
-    DialerPhoneNumberUtil dialerPhoneNumberUtil =
-        new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
-    String number = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
+    // Note: This loses country info when number is not valid.
+    String number = dialerPhoneNumber.getNormalizedNumber();
 
     List<ListenableFuture<Cp2Info>> cp2InfoFutures = new ArrayList<>();
     for (long remoteDirectoryId : remoteDirectoryIds) {
diff --git a/java/com/android/dialer/phonenumberproto/DialerPhoneNumberUtil.java b/java/com/android/dialer/phonenumberproto/DialerPhoneNumberUtil.java
index 319467c..a5b9520 100644
--- a/java/com/android/dialer/phonenumberproto/DialerPhoneNumberUtil.java
+++ b/java/com/android/dialer/phonenumberproto/DialerPhoneNumberUtil.java
@@ -16,7 +16,6 @@
 
 package com.android.dialer.phonenumberproto;
 
-import android.support.annotation.AnyThread;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.WorkerThread;
@@ -25,9 +24,6 @@
 import com.android.dialer.DialerPhoneNumber;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.i18n.phonenumbers.NumberParseException;
 import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import com.google.i18n.phonenumbers.PhoneNumberUtil.MatchType;
@@ -59,58 +55,30 @@
     Assert.isWorkerThread();
 
     DialerPhoneNumber.Builder dialerPhoneNumber = DialerPhoneNumber.newBuilder();
-    // Numbers can be null or empty for incoming "unknown" calls.
-    if (numberToParse != null) {
-      dialerPhoneNumber.setNormalizedNumber(normalizeNumber(numberToParse, defaultRegion));
-    }
+
     if (defaultRegion != null) {
       dialerPhoneNumber.setCountryIso(defaultRegion);
     }
-    return dialerPhoneNumber.build();
-  }
 
-  /**
-   * Parses the provided raw phone number into a Future result of {@link DialerPhoneNumber}.
-   *
-   * <p>Work is run on the provided {@link ListeningExecutorService}.
-   *
-   * @see PhoneNumberUtil#parse(CharSequence, String)
-   */
-  @AnyThread
-  public ListenableFuture<DialerPhoneNumber> parse(
-      @Nullable String numberToParse,
-      @Nullable String defaultRegion,
-      @NonNull ListeningExecutorService service) {
-    return service.submit(() -> parse(numberToParse, defaultRegion));
-  }
-
-  /**
-   * Formats the provided number to E164 format or return a normalized version of the raw number if
-   * the number is not valid according to {@link PhoneNumberUtil#isValidNumber(PhoneNumber)}.
-   *
-   * @see #formatToValidE164(DialerPhoneNumber)
-   * @see PhoneNumberUtils#normalizeNumber(String)
-   */
-  public String normalizeNumber(DialerPhoneNumber number) {
-    // TODO(zachh): Inline this method.
-    // TODO(zachh): This loses country info when number is not valid.
-    return number.getNormalizedNumber();
-  }
-
-  @WorkerThread
-  private String normalizeNumber(@NonNull String rawNumber, @Nullable String defaultRegion) {
-    Assert.isWorkerThread();
+    // Numbers can be null or empty for incoming "unknown" calls.
+    if (numberToParse == null) {
+      return dialerPhoneNumber.build();
+    }
 
     // If the number is a service number, just store the raw number and don't bother trying to parse
     // it. PhoneNumberUtil#parse ignores these characters which can lead to confusing behavior, such
     // as the numbers "#123" and "123" being considered the same. The "#" can appear in the middle
     // of a service number and the "*" can appear at the beginning (see a bug).
-    if (isServiceNumber(rawNumber)) {
-      return rawNumber;
+    if (isServiceNumber(numberToParse)) {
+      return dialerPhoneNumber.setNormalizedNumber(numberToParse).build();
     }
 
-    String postDialPortion = PhoneNumberUtils.extractPostDialPortion(rawNumber);
-    String networkPortion = PhoneNumberUtils.extractNetworkPortion(rawNumber);
+    String postDialPortion = PhoneNumberUtils.extractPostDialPortion(numberToParse);
+    if (!postDialPortion.isEmpty()) {
+      dialerPhoneNumber.setPostDialPortion(postDialPortion);
+    }
+
+    String networkPortion = PhoneNumberUtils.extractNetworkPortion(numberToParse);
 
     try {
       PhoneNumber phoneNumber = phoneNumberUtil.parse(networkPortion, defaultRegion);
@@ -118,18 +86,18 @@
         String validNumber = phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164);
         if (TextUtils.isEmpty(validNumber)) {
           throw new IllegalStateException(
-              "e164 number should not be empty: " + LogUtil.sanitizePii(rawNumber));
+              "e164 number should not be empty: " + LogUtil.sanitizePii(numberToParse));
         }
         // The E164 representation doesn't contain post-dial digits, but we need to preserve them.
-        if (postDialPortion != null) {
+        if (!postDialPortion.isEmpty()) {
           validNumber += postDialPortion;
         }
-        return validNumber;
+        return dialerPhoneNumber.setNormalizedNumber(validNumber).setIsValid(true).build();
       }
     } catch (NumberParseException e) {
       // fall through
     }
-    return networkPortion + postDialPortion;
+    return dialerPhoneNumber.setNormalizedNumber(networkPortion + postDialPortion).build();
   }
 
   /**
@@ -197,39 +165,7 @@
     return (matchType == MatchType.SHORT_NSN_MATCH
             || matchType == MatchType.NSN_MATCH
             || matchType == MatchType.EXACT_MATCH)
-        && samePostDialPortion(firstNumberIn, secondNumberIn);
-  }
-
-  private static boolean samePostDialPortion(DialerPhoneNumber number1, DialerPhoneNumber number2) {
-    return PhoneNumberUtils.extractPostDialPortion(number1.getNormalizedNumber())
-        .equals(PhoneNumberUtils.extractPostDialPortion(number2.getNormalizedNumber()));
-  }
-
-  /**
-   * If the provided number is "valid" (see {@link PhoneNumberUtil#isValidNumber(PhoneNumber)}),
-   * formats it to E.164. Otherwise, returns {@link Optional#absent()}.
-   *
-   * <p>This method is analogous to {@link PhoneNumberUtils#formatNumberToE164(String, String)} (but
-   * works with an already parsed {@link DialerPhoneNumber} object).
-   *
-   * @see PhoneNumberUtil#isValidNumber(PhoneNumber)
-   * @see PhoneNumberUtil#format(PhoneNumber, PhoneNumberFormat)
-   * @see PhoneNumberUtils#formatNumberToE164(String, String)
-   */
-  @WorkerThread
-  public Optional<String> formatToValidE164(DialerPhoneNumber number) {
-    // TODO(zachh): We could do something like store a "valid" bit in DialerPhoneNumber?
-    Assert.isWorkerThread();
-    PhoneNumber phoneNumber;
-    try {
-      phoneNumber = phoneNumberUtil.parse(number.getNormalizedNumber(), number.getCountryIso());
-    } catch (NumberParseException e) {
-      return Optional.absent();
-    }
-    if (phoneNumberUtil.isValidNumber(phoneNumber)) {
-      return Optional.fromNullable(phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164));
-    }
-    return Optional.absent();
+        && firstNumberIn.getPostDialPortion().equals(secondNumberIn.getPostDialPortion());
   }
 
   private boolean isServiceNumber(@NonNull String rawNumber) {
diff --git a/java/com/android/dialer/phonenumberproto/PartitionedNumbers.java b/java/com/android/dialer/phonenumberproto/PartitionedNumbers.java
index dbf9936..2c19c12 100644
--- a/java/com/android/dialer/phonenumberproto/PartitionedNumbers.java
+++ b/java/com/android/dialer/phonenumberproto/PartitionedNumbers.java
@@ -20,19 +20,19 @@
 import android.support.annotation.WorkerThread;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.util.ArraySet;
-import android.telephony.PhoneNumberUtils;
 import com.android.dialer.DialerPhoneNumber;
 import com.android.dialer.common.Assert;
-import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import java.util.Map;
 import java.util.Set;
 
 /**
  * Divides a set of {@link DialerPhoneNumber DialerPhoneNumbers} according to those that are valid
  * according to libphonenumber, and those that are not.
+ *
+ * <p>Numbers with post-dial portions are always considered invalid as most systems store E164
+ * numbers which do not support post-dial portions.
  */
 public final class PartitionedNumbers {
   private final ImmutableMap<String, ImmutableSet<DialerPhoneNumber>>
@@ -40,29 +40,27 @@
   private final ImmutableMap<String, ImmutableSet<DialerPhoneNumber>>
       invalidNumbersToDialerPhoneNumbers;
 
+  /**
+   * Divides a set of {@link DialerPhoneNumber DialerPhoneNumbers} according to those that are valid
+   * according to libphonenumber, and those that are not.
+   *
+   * <p>Numbers with post-dial portions are always considered invalid as most systems store E164
+   * numbers which do not support post-dial portions.
+   */
   @WorkerThread
   public PartitionedNumbers(@NonNull ImmutableSet<DialerPhoneNumber> dialerPhoneNumbers) {
     Assert.isWorkerThread();
-    DialerPhoneNumberUtil dialerPhoneNumberUtil =
-        new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
     Map<String, Set<DialerPhoneNumber>> e164MapBuilder = new ArrayMap<>();
     Map<String, Set<DialerPhoneNumber>> invalidMapBuilder = new ArrayMap<>();
 
     for (DialerPhoneNumber dialerPhoneNumber : dialerPhoneNumbers) {
-      Optional<String> optValidE164 = dialerPhoneNumberUtil.formatToValidE164(dialerPhoneNumber);
       /*
        * Numbers with post-dial digits are considered valid and can be converted to E164, but their
-       * post dial digits are lost in the process. Similarly, if a contact's number has a post-dial
-       * digits, the normalized version of it stored in the contacts database does not include the
-       * post dial digits.
-       *
-       * A number with post-dial digits should not match a contact whose number does not have
-       * post-dial digits, which means that we cannot normalize such numbers for use in bulk lookup.
-       * Treat them as invalid which will cause them to be processed individually using
-       * ContactsContract.PHONE_LOOKUP.
+       * post dial digits are lost in the process. For example, the normalized version of a number
+       * with a post-dial portion in the contacts database is stored without the post-dial portion.
        */
-      if (optValidE164.isPresent() && !hasPostDialDigits(dialerPhoneNumber)) {
-        String validE164 = optValidE164.get();
+      if (dialerPhoneNumber.getIsValid() && dialerPhoneNumber.getPostDialPortion().isEmpty()) {
+        String validE164 = dialerPhoneNumber.getNormalizedNumber();
         Set<DialerPhoneNumber> currentNumbers = e164MapBuilder.get(validE164);
         if (currentNumbers == null) {
           currentNumbers = new ArraySet<>();
@@ -84,11 +82,6 @@
     invalidNumbersToDialerPhoneNumbers = makeImmutable(invalidMapBuilder);
   }
 
-  private boolean hasPostDialDigits(DialerPhoneNumber dialerPhoneNumber) {
-    return !PhoneNumberUtils.extractPostDialPortion(dialerPhoneNumber.getNormalizedNumber())
-        .isEmpty();
-  }
-
   /** Returns the set of invalid numbers from the original DialerPhoneNumbers */
   @NonNull
   public ImmutableSet<String> invalidNumbers() {
diff --git a/java/com/android/dialer/phonenumberproto/dialer_phone_number.proto b/java/com/android/dialer/phonenumberproto/dialer_phone_number.proto
index 941de04..ad8a8f9 100644
--- a/java/com/android/dialer/phonenumberproto/dialer_phone_number.proto
+++ b/java/com/android/dialer/phonenumberproto/dialer_phone_number.proto
@@ -40,6 +40,11 @@
   //  number is a 7-digit US number (missing an area code) like "456-7890" which
   //  would be stored as "4567890".
   //
+  //  Note: Using this field without country_iso effectively loses country info
+  //  when the number is not valid and no country prefix was prepended. This may
+  //  cause numbers like {"456-7890", "US"} to be treated equivalently to
+  //  {"456-7890", "DE"}, when they are not in fact equivalent.
+  //
   //  See DialerPhoneNumberUtil#parse.
   optional string normalized_number = 1;
 
@@ -47,4 +52,15 @@
   // CallLog.Calls#COUNTRY: "The ISO 3166-1 two letters country code of the
   // country where the user received or made the call."
   optional string country_iso = 2;
+
+  // True if the number is valid according to libphonenumber.
+  optional bool is_valid = 3;
+
+  // The post dial portion of the number as described by
+  // PhoneNumberUtils#extractPostDialPortion. Note that this is also part of
+  // normalized_number, but this information is duplicated here for convenience.
+  //
+  // This includes pause and wait characters, but strips other characters, so
+  // for example would be ",123;456" given the raw input of "456-7890,123; 456".
+  optional string post_dial_portion = 4;
 }