From ac069a79d2f0701d1ce0371a11e3d2ed63afc4de Mon Sep 17 00:00:00 2001 From: linyuh Date: Thu, 19 Apr 2018 16:23:10 -0700 Subject: Support extracting info from a Call in PhoneLookup Bug: 70988915 Test: CompositePhoneLookupTest, PhoneLookupTest PiperOrigin-RevId: 193592973 Change-Id: I27b6a63049117ce6d31e50aea9c56c14f01d0e1d --- .../android/dialer/phonelookup/PhoneLookup.java | 42 +++++++++++++ .../composite/CompositePhoneLookup.java | 70 +++++++++++++++++----- 2 files changed, 96 insertions(+), 16 deletions(-) (limited to 'java/com/android/dialer/phonelookup') diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java index d11f023af..0b9cbf6eb 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. @@ -31,12 +40,45 @@ import com.google.common.util.concurrent.ListenableFuture; */ public interface PhoneLookup { + /** + * Returns a future containing a new info for the number associated with the provided call. + * + *

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. + * + *

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)}. + * + *

However, for {@link PhoneLookup} implementations that need info in the call (such as one for + * CNAP), they should override this method. + */ + default ListenableFuture lookup(Context appContext, Call call) { + ListeningExecutorService backgroundExecutor = + DialerExecutorComponent.get(appContext).backgroundExecutor(); + + ListenableFuture 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. * *

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. + * + *

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 lookup(DialerPhoneNumber dialerPhoneNumber); diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java index 7cc7a6d7f..1ac13df33 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 @@ import javax.inject.Inject; */ public final class CompositePhoneLookup { + private final Context appContext; private final ImmutableList phoneLookups; private final FutureTimer futureTimer; private final CallLogState callLogState; @@ -59,10 +63,12 @@ public final class CompositePhoneLookup { @VisibleForTesting @Inject public CompositePhoneLookup( + @ApplicationContext Context appContext, ImmutableList phoneLookups, FutureTimer futureTimer, CallLogState callLogState, @LightweightExecutor ListeningExecutorService lightweightExecutorService) { + this.appContext = appContext; this.phoneLookups = phoneLookups; this.futureTimer = futureTimer; this.callLogState = callLogState; @@ -70,12 +76,37 @@ public final class CompositePhoneLookup { } /** - * 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. + * + *

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 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> 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 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. * *

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 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 @@ public final class CompositePhoneLookup { 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 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 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 combineSubMessageFutures( + List> 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. -- cgit v1.2.3