Support extracting info from a Call in PhoneLookup

Bug: 70988915
Test: CompositePhoneLookupTest, PhoneLookupTest
PiperOrigin-RevId: 193592973
Change-Id: I27b6a63049117ce6d31e50aea9c56c14f01d0e1d
diff --git a/java/com/android/dialer/metrics/Metrics.java b/java/com/android/dialer/metrics/Metrics.java
index 5572658..191b84d 100644
--- a/java/com/android/dialer/metrics/Metrics.java
+++ b/java/com/android/dialer/metrics/Metrics.java
@@ -66,7 +66,8 @@
   String GET_MOST_RECENT_INFO_TEMPLATE = "%s.GetMostRecentInfo";
   String ON_SUCCESSFUL_FILL_TEMPLATE = "%s.OnSuccessfulFill";
   String ON_SUCCESSFUL_BULK_UPDATE_TEMPLATE = "%s.OnSuccessfulBulkUpdate";
-  String LOOKUP_TEMPLATE = "%s.Lookup";
+  String LOOKUP_FOR_CALL_TEMPLATE = "%s.LookupForCall";
+  String LOOKUP_FOR_NUMBER_TEMPLATE = "%s.LookupForNumber";
 
   /** Start a timer. */
   void startTimer(String timerEventName);
diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java
index d11f023..0b9cbf6 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookup.java
@@ -16,11 +16,20 @@
 
 package com.android.dialer.phonelookup;
 
+import android.content.Context;
 import android.support.annotation.MainThread;
+import android.telecom.Call;
 import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.location.GeoUtil;
+import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
+import com.android.dialer.telecom.TelecomCallUtil;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
 
 /**
  * Provides operations related to retrieving information about phone numbers.
@@ -32,11 +41,44 @@
 public interface PhoneLookup<T> {
 
   /**
+   * Returns a future containing a new info for the number associated with the provided call.
+   *
+   * <p>The returned message should contain populated data for the sub-message corresponding to this
+   * {@link PhoneLookup}. For example, the CP2 implementation returns a {@link
+   * PhoneLookupInfo.Cp2Info} sub-message.
+   *
+   * <p>The default implementation is for all {@link PhoneLookup} implementations that don't need
+   * info in the given call, i.e., it simply extracts the phone number from the call and delegates
+   * to {@link #lookup(DialerPhoneNumber)}.
+   *
+   * <p>However, for {@link PhoneLookup} implementations that need info in the call (such as one for
+   * CNAP), they should override this method.
+   */
+  default ListenableFuture<T> lookup(Context appContext, Call call) {
+    ListeningExecutorService backgroundExecutor =
+        DialerExecutorComponent.get(appContext).backgroundExecutor();
+
+    ListenableFuture<DialerPhoneNumber> numberFuture =
+        backgroundExecutor.submit(
+            () -> {
+              DialerPhoneNumberUtil dialerPhoneNumberUtil = new DialerPhoneNumberUtil();
+              return dialerPhoneNumberUtil.parse(
+                  TelecomCallUtil.getNumber(call), GeoUtil.getCurrentCountryIso(appContext));
+            });
+
+    return Futures.transformAsync(numberFuture, this::lookup, MoreExecutors.directExecutor());
+  }
+
+  /**
    * Returns a future containing a new info for the provided number.
    *
    * <p>The returned message should contain populated data for the sub-message corresponding to this
    * {@link PhoneLookup}. For example, the CP2 implementation returns a {@link
    * PhoneLookupInfo.Cp2Info} sub-message.
+   *
+   * <p>If the lookup can't be done without info in a {@link Call} (e.g., CNAP), this method is
+   * expected to return existing info saved during the most recent lookup for a call to/from the
+   * provided number ({@link #lookup(Context, Call)}).
    */
   ListenableFuture<T> lookup(DialerPhoneNumber dialerPhoneNumber);
 
diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
index 7cc7a6d..1ac13df 100644
--- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
@@ -16,13 +16,16 @@
 
 package com.android.dialer.phonelookup.composite;
 
+import android.content.Context;
 import android.support.annotation.MainThread;
 import android.support.annotation.VisibleForTesting;
+import android.telecom.Call;
 import com.android.dialer.DialerPhoneNumber;
 import com.android.dialer.calllog.CallLogState;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
 import com.android.dialer.common.concurrent.DialerFutures;
+import com.android.dialer.inject.ApplicationContext;
 import com.android.dialer.metrics.FutureTimer;
 import com.android.dialer.metrics.FutureTimer.LogCatMode;
 import com.android.dialer.metrics.Metrics;
@@ -51,6 +54,7 @@
  */
 public final class CompositePhoneLookup {
 
+  private final Context appContext;
   private final ImmutableList<PhoneLookup> phoneLookups;
   private final FutureTimer futureTimer;
   private final CallLogState callLogState;
@@ -59,10 +63,12 @@
   @VisibleForTesting
   @Inject
   public CompositePhoneLookup(
+      @ApplicationContext Context appContext,
       ImmutableList<PhoneLookup> phoneLookups,
       FutureTimer futureTimer,
       CallLogState callLogState,
       @LightweightExecutor ListeningExecutorService lightweightExecutorService) {
+    this.appContext = appContext;
     this.phoneLookups = phoneLookups;
     this.futureTimer = futureTimer;
     this.callLogState = callLogState;
@@ -70,12 +76,37 @@
   }
 
   /**
-   * Delegates to a set of dependent lookups to build a complete {@link PhoneLookupInfo}.
+   * Delegates to a set of dependent lookups to build a complete {@link PhoneLookupInfo} for the
+   * number associated with the provided call.
    *
    * <p>Note: If any of the dependent lookups fails, the returned future will also fail. If any of
    * the dependent lookups does not complete, the returned future will also not complete.
    */
-  @SuppressWarnings({"unchecked", "rawtype"})
+  public ListenableFuture<PhoneLookupInfo> lookup(Call call) {
+    // TODO(zachh): Add short-circuiting logic so that this call is not blocked on low-priority
+    // lookups finishing when a higher-priority one has already finished.
+    List<ListenableFuture<?>> futures = new ArrayList<>();
+    for (PhoneLookup<?> phoneLookup : phoneLookups) {
+      ListenableFuture<?> lookupFuture = phoneLookup.lookup(appContext, call);
+      String eventName =
+          String.format(Metrics.LOOKUP_FOR_CALL_TEMPLATE, phoneLookup.getClass().getSimpleName());
+      futureTimer.applyTiming(lookupFuture, eventName);
+      futures.add(lookupFuture);
+    }
+    ListenableFuture<PhoneLookupInfo> combinedFuture = combineSubMessageFutures(futures);
+    String eventName =
+        String.format(Metrics.LOOKUP_FOR_CALL_TEMPLATE, CompositePhoneLookup.class.getSimpleName());
+    futureTimer.applyTiming(combinedFuture, eventName);
+    return combinedFuture;
+  }
+
+  /**
+   * Delegates to a set of dependent lookups to build a complete {@link PhoneLookupInfo} for the
+   * provided number.
+   *
+   * <p>Note: If any of the dependent lookups fails, the returned future will also fail. If any of
+   * the dependent lookups does not complete, the returned future will also not complete.
+   */
   public ListenableFuture<PhoneLookupInfo> lookup(DialerPhoneNumber dialerPhoneNumber) {
     // TODO(zachh): Add short-circuiting logic so that this call is not blocked on low-priority
     // lookups finishing when a higher-priority one has already finished.
@@ -83,28 +114,35 @@
     for (PhoneLookup<?> phoneLookup : phoneLookups) {
       ListenableFuture<?> lookupFuture = phoneLookup.lookup(dialerPhoneNumber);
       String eventName =
-          String.format(Metrics.LOOKUP_TEMPLATE, phoneLookup.getClass().getSimpleName());
+          String.format(Metrics.LOOKUP_FOR_NUMBER_TEMPLATE, phoneLookup.getClass().getSimpleName());
       futureTimer.applyTiming(lookupFuture, eventName);
       futures.add(lookupFuture);
     }
-    ListenableFuture<PhoneLookupInfo> combinedFuture =
-        Futures.transform(
-            Futures.allAsList(futures),
-            infos -> {
-              Builder mergedInfo = PhoneLookupInfo.newBuilder();
-              for (int i = 0; i < infos.size(); i++) {
-                PhoneLookup phoneLookup = phoneLookups.get(i);
-                phoneLookup.setSubMessage(mergedInfo, infos.get(i));
-              }
-              return mergedInfo.build();
-            },
-            lightweightExecutorService);
+    ListenableFuture<PhoneLookupInfo> combinedFuture = combineSubMessageFutures(futures);
     String eventName =
-        String.format(Metrics.LOOKUP_TEMPLATE, CompositePhoneLookup.class.getSimpleName());
+        String.format(
+            Metrics.LOOKUP_FOR_NUMBER_TEMPLATE, CompositePhoneLookup.class.getSimpleName());
     futureTimer.applyTiming(combinedFuture, eventName);
     return combinedFuture;
   }
 
+  /** Combines a list of sub-message futures into a future for {@link PhoneLookupInfo}. */
+  @SuppressWarnings({"unchecked", "rawtype"})
+  private ListenableFuture<PhoneLookupInfo> combineSubMessageFutures(
+      List<ListenableFuture<?>> subMessageFutures) {
+    return Futures.transform(
+        Futures.allAsList(subMessageFutures),
+        subMessages -> {
+          Builder mergedInfo = PhoneLookupInfo.newBuilder();
+          for (int i = 0; i < subMessages.size(); i++) {
+            PhoneLookup phoneLookup = phoneLookups.get(i);
+            phoneLookup.setSubMessage(mergedInfo, subMessages.get(i));
+          }
+          return mergedInfo.build();
+        },
+        lightweightExecutorService);
+  }
+
   /**
    * Delegates to sub-lookups' {@link PhoneLookup#isDirty(ImmutableSet)} completing when the first
    * sub-lookup which returns true completes.
diff --git a/java/com/android/incallui/PhoneLookupHistoryRecorder.java b/java/com/android/incallui/PhoneLookupHistoryRecorder.java
index 4f41370..4c5cf8a 100644
--- a/java/com/android/incallui/PhoneLookupHistoryRecorder.java
+++ b/java/com/android/incallui/PhoneLookupHistoryRecorder.java
@@ -19,23 +19,18 @@
 import android.content.Context;
 import android.support.annotation.Nullable;
 import android.telecom.Call;
-import com.android.dialer.DialerPhoneNumber;
 import com.android.dialer.calllog.config.CallLogConfigComponent;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.location.GeoUtil;
 import com.android.dialer.phonelookup.PhoneLookupComponent;
 import com.android.dialer.phonelookup.PhoneLookupInfo;
 import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
-import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
 import com.android.dialer.telecom.TelecomCallUtil;
 import com.google.common.base.Optional;
 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.common.util.concurrent.MoreExecutors;
 
 /**
  * Fetches the current {@link PhoneLookupInfo} for the provided call and writes it to the
@@ -52,25 +47,8 @@
       return;
     }
 
-    ListeningExecutorService backgroundExecutor =
-        DialerExecutorComponent.get(appContext).backgroundExecutor();
-
-    ListenableFuture<DialerPhoneNumber> numberFuture =
-        backgroundExecutor.submit(
-            () -> {
-              DialerPhoneNumberUtil dialerPhoneNumberUtil = new DialerPhoneNumberUtil();
-              return dialerPhoneNumberUtil.parse(
-                  TelecomCallUtil.getNumber(call), GeoUtil.getCurrentCountryIso(appContext));
-            });
-
     ListenableFuture<PhoneLookupInfo> infoFuture =
-        Futures.transformAsync(
-            numberFuture,
-            dialerPhoneNumber ->
-                PhoneLookupComponent.get(appContext)
-                    .compositePhoneLookup()
-                    .lookup(dialerPhoneNumber),
-            MoreExecutors.directExecutor());
+        PhoneLookupComponent.get(appContext).compositePhoneLookup().lookup(call);
 
     Futures.addCallback(
         infoFuture,
@@ -103,6 +81,6 @@
                 "PhoneLookupHistoryRecorder.onFailure", "could not write PhoneLookupHistory", t);
           }
         },
-        backgroundExecutor);
+        DialerExecutorComponent.get(appContext).backgroundExecutor());
   }
 }