From fa3cd3bfb14f4b8ac65c0ee6757bc78b40a73573 Mon Sep 17 00:00:00 2001 From: linyuh Date: Thu, 26 Apr 2018 20:21:01 -0700 Subject: Implement CequintPhoneLookup Bug: 70989584 Test: CequintPhoneLookupTest PiperOrigin-RevId: 194493484 Change-Id: I59f9b334f0a218e7ba1c34948db4ddbf11490df3 --- .../dialer/app/calllog/CallLogFragment.java | 2 +- .../android/dialer/oem/CequintCallerIdManager.java | 113 +++++++++++++-------- .../phonelookup/cequint/CequintPhoneLookup.java | 90 ++++++++++++++-- .../dialer/phonenumbercache/ContactInfoHelper.java | 2 +- java/com/android/incallui/ContactInfoCache.java | 2 +- 5 files changed, 154 insertions(+), 55 deletions(-) diff --git a/java/com/android/dialer/app/calllog/CallLogFragment.java b/java/com/android/dialer/app/calllog/CallLogFragment.java index 4e968f095..1e55c6358 100644 --- a/java/com/android/dialer/app/calllog/CallLogFragment.java +++ b/java/com/android/dialer/app/calllog/CallLogFragment.java @@ -449,7 +449,7 @@ public class CallLogFragment extends Fragment super.onStart(); CequintCallerIdManager cequintCallerIdManager = null; if (CequintCallerIdManager.isCequintCallerIdEnabled(getContext())) { - cequintCallerIdManager = CequintCallerIdManager.createInstanceForCallLog(); + cequintCallerIdManager = new CequintCallerIdManager(); } contactInfoCache.setCequintCallerIdManager(cequintCallerIdManager); } diff --git a/java/com/android/dialer/oem/CequintCallerIdManager.java b/java/com/android/dialer/oem/CequintCallerIdManager.java index 55cafc15e..53f5352b2 100644 --- a/java/com/android/dialer/oem/CequintCallerIdManager.java +++ b/java/com/android/dialer/oem/CequintCallerIdManager.java @@ -24,6 +24,7 @@ import android.os.Build.VERSION_CODES; import android.support.annotation.AnyThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; @@ -45,7 +46,8 @@ import java.util.concurrent.ConcurrentHashMap; @TargetApi(VERSION_CODES.N) public class CequintCallerIdManager { - private static final String CONFIG_CALLER_ID_ENABLED = "config_caller_id_enabled"; + @VisibleForTesting + public static final String CONFIG_CALLER_ID_ENABLED = "config_caller_id_enabled"; private static final int CALLER_ID_LOOKUP_USER_PROVIDED_CID = 0x0001; private static final int CALLER_ID_LOOKUP_SYSTEM_PROVIDED_CID = 0x0002; @@ -53,36 +55,43 @@ public class CequintCallerIdManager { private static final String[] EMPTY_PROJECTION = new String[] {}; - // Column names in Cequint provider. - private static final String CITY_NAME = "cid_pCityName"; - private static final String STATE_NAME = "cid_pStateName"; - private static final String STATE_ABBR = "cid_pStateAbbr"; - private static final String COUNTRY_NAME = "cid_pCountryName"; - private static final String COMPANY = "cid_pCompany"; - private static final String NAME = "cid_pName"; - private static final String FIRST_NAME = "cid_pFirstName"; - private static final String LAST_NAME = "cid_pLastName"; - private static final String IMAGE = "cid_pLogo"; - private static final String DISPLAY_NAME = "cid_pDisplayName"; + /** Column names in Cequint content provider. */ + @VisibleForTesting + public static final class CequintColumnNames { + public static final String CITY_NAME = "cid_pCityName"; + public static final String STATE_NAME = "cid_pStateName"; + public static final String STATE_ABBR = "cid_pStateAbbr"; + public static final String COUNTRY_NAME = "cid_pCountryName"; + public static final String COMPANY = "cid_pCompany"; + public static final String NAME = "cid_pName"; + public static final String FIRST_NAME = "cid_pFirstName"; + public static final String LAST_NAME = "cid_pLastName"; + public static final String PHOTO_URI = "cid_pLogo"; + public static final String DISPLAY_NAME = "cid_pDisplayName"; + } private static boolean hasAlreadyCheckedCequintCallerIdPackage; private static String cequintProviderAuthority; - // TODO(wangqi): Revisit it and maybe remove it if it's not necessary. - private final ConcurrentHashMap callLogCache; + // TODO(a bug): Revisit it and maybe remove it if it's not necessary. + private final ConcurrentHashMap callLogCache = + new ConcurrentHashMap<>(); /** Cequint caller ID contact information. */ @AutoValue public abstract static class CequintCallerIdContact { + @Nullable public abstract String name(); /** * Description of the geolocation (e.g., "Mountain View, CA"), which is for display purpose * only. */ + @Nullable public abstract String geolocation(); + @Nullable public abstract String photoUri(); static Builder builder() { @@ -91,11 +100,11 @@ public class CequintCallerIdManager { @AutoValue.Builder abstract static class Builder { - abstract Builder setName(String name); + abstract Builder setName(@Nullable String name); - abstract Builder setGeolocation(String geolocation); + abstract Builder setGeolocation(@Nullable String geolocation); - abstract Builder setPhotoUri(String photoUri); + abstract Builder setPhotoUri(@Nullable String photoUri); abstract CequintCallerIdContact build(); } @@ -125,17 +134,14 @@ public class CequintCallerIdManager { return cequintProviderAuthority != null; } - public static CequintCallerIdManager createInstanceForCallLog() { - return new CequintCallerIdManager(); - } - + /** Returns a {@link CequintCallerIdContact} for a call. */ @WorkerThread @Nullable - public static CequintCallerIdContact getCequintCallerIdContactForInCall( + public static CequintCallerIdContact getCequintCallerIdContactForCall( Context context, String number, String cnapName, boolean isIncoming) { Assert.isWorkerThread(); LogUtil.d( - "CequintCallerIdManager.getCequintCallerIdContactForInCall", + "CequintCallerIdManager.getCequintCallerIdContactForCall", "number: %s, cnapName: %s, isIncoming: %b", LogUtil.sanitizePhoneNumber(number), LogUtil.sanitizePii(cnapName), @@ -151,29 +157,51 @@ public class CequintCallerIdManager { return lookup(context, getIncallLookupUri(), number, flags); } + /** + * Returns a cached {@link CequintCallerIdContact} associated with the provided number. If no + * contact can be found in the cache, look up the number using the Cequint content provider. + * + * @deprecated This method is for the old call log only. New code should use {@link + * #getCequintCallerIdContactForNumber(Context, String)}. + */ + @Deprecated @WorkerThread @Nullable - public CequintCallerIdContact getCequintCallerIdContact(Context context, String number) { + public CequintCallerIdContact getCachedCequintCallerIdContact(Context context, String number) { Assert.isWorkerThread(); LogUtil.d( - "CequintCallerIdManager.getCequintCallerIdContact", + "CequintCallerIdManager.getCachedCequintCallerIdContact", "number: %s", LogUtil.sanitizePhoneNumber(number)); if (callLogCache.containsKey(number)) { return callLogCache.get(number); } CequintCallerIdContact cequintCallerIdContact = - lookup( - context, - getLookupUri(), - PhoneNumberUtils.stripSeparators(number), - new String[] {"system"}); + getCequintCallerIdContactForNumber(context, number); if (cequintCallerIdContact != null) { callLogCache.put(number, cequintCallerIdContact); } return cequintCallerIdContact; } + /** + * Returns a {@link CequintCallerIdContact} associated with the provided number by looking it up + * using the Cequint content provider. + */ + @WorkerThread + @Nullable + public static CequintCallerIdContact getCequintCallerIdContactForNumber( + Context context, String number) { + Assert.isWorkerThread(); + LogUtil.d( + "CequintCallerIdManager.getCequintCallerIdContactForNumber", + "number: %s", + LogUtil.sanitizePhoneNumber(number)); + + return lookup( + context, getLookupUri(), PhoneNumberUtils.stripSeparators(number), new String[] {"system"}); + } + @WorkerThread @Nullable private static CequintCallerIdContact lookup( @@ -185,16 +213,17 @@ public class CequintCallerIdManager { try (Cursor cursor = context.getContentResolver().query(uri, EMPTY_PROJECTION, number, flags, null)) { if (cursor != null && cursor.moveToFirst()) { - String city = getString(cursor, cursor.getColumnIndex(CITY_NAME)); - String state = getString(cursor, cursor.getColumnIndex(STATE_NAME)); - String stateAbbr = getString(cursor, cursor.getColumnIndex(STATE_ABBR)); - String country = getString(cursor, cursor.getColumnIndex(COUNTRY_NAME)); - String company = getString(cursor, cursor.getColumnIndex(COMPANY)); - String name = getString(cursor, cursor.getColumnIndex(NAME)); - String firstName = getString(cursor, cursor.getColumnIndex(FIRST_NAME)); - String lastName = getString(cursor, cursor.getColumnIndex(LAST_NAME)); - String photoUri = getString(cursor, cursor.getColumnIndex(IMAGE)); - String displayName = getString(cursor, cursor.getColumnIndex(DISPLAY_NAME)); + String city = getString(cursor, cursor.getColumnIndex(CequintColumnNames.CITY_NAME)); + String state = getString(cursor, cursor.getColumnIndex(CequintColumnNames.STATE_NAME)); + String stateAbbr = getString(cursor, cursor.getColumnIndex(CequintColumnNames.STATE_ABBR)); + String country = getString(cursor, cursor.getColumnIndex(CequintColumnNames.COUNTRY_NAME)); + String company = getString(cursor, cursor.getColumnIndex(CequintColumnNames.COMPANY)); + String name = getString(cursor, cursor.getColumnIndex(CequintColumnNames.NAME)); + String firstName = getString(cursor, cursor.getColumnIndex(CequintColumnNames.FIRST_NAME)); + String lastName = getString(cursor, cursor.getColumnIndex(CequintColumnNames.LAST_NAME)); + String photoUri = getString(cursor, cursor.getColumnIndex(CequintColumnNames.PHOTO_URI)); + String displayName = + getString(cursor, cursor.getColumnIndex(CequintColumnNames.DISPLAY_NAME)); String contactName = TextUtils.isEmpty(displayName) @@ -293,8 +322,4 @@ public class CequintCallerIdManager { private static Uri getIncallLookupUri() { return Uri.parse("content://" + cequintProviderAuthority + "/incalllookup"); } - - private CequintCallerIdManager() { - callLogCache = new ConcurrentHashMap<>(); - } } diff --git a/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java b/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java index ce2cd18ad..36d0be40f 100644 --- a/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java +++ b/java/com/android/dialer/phonelookup/cequint/CequintPhoneLookup.java @@ -18,33 +18,85 @@ package com.android.dialer.phonelookup.cequint; import android.content.Context; import android.telecom.Call; +import android.text.TextUtils; import com.android.dialer.DialerPhoneNumber; +import com.android.dialer.common.Assert; +import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; +import com.android.dialer.common.concurrent.Annotations.LightweightExecutor; +import com.android.dialer.inject.ApplicationContext; +import com.android.dialer.location.GeoUtil; +import com.android.dialer.oem.CequintCallerIdManager; +import com.android.dialer.oem.CequintCallerIdManager.CequintCallerIdContact; import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; import com.android.dialer.phonelookup.PhoneLookupInfo.CequintInfo; +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 javax.inject.Inject; /** PhoneLookup implementation for Cequint. */ public class CequintPhoneLookup implements PhoneLookup { + private final Context appContext; + private final ListeningExecutorService backgroundExecutorService; + private final ListeningExecutorService lightweightExecutorService; + @Inject - CequintPhoneLookup() {} + CequintPhoneLookup( + @ApplicationContext Context appContext, + @BackgroundExecutor ListeningExecutorService backgroundExecutorService, + @LightweightExecutor ListeningExecutorService lightweightExecutorService) { + this.appContext = appContext; + this.backgroundExecutorService = backgroundExecutorService; + this.lightweightExecutorService = lightweightExecutorService; + } @Override public ListenableFuture lookup(Context appContext, Call call) { - // TODO(a bug): Override the default implementation in the PhoneLookup interface - // as a Cequint lookup requires info in the provided call. - return Futures.immediateFuture(CequintInfo.getDefaultInstance()); + if (!CequintCallerIdManager.isCequintCallerIdEnabled(appContext)) { + return Futures.immediateFuture(CequintInfo.getDefaultInstance()); + } + + ListenableFuture dialerPhoneNumberFuture = + backgroundExecutorService.submit( + () -> { + DialerPhoneNumberUtil dialerPhoneNumberUtil = new DialerPhoneNumberUtil(); + return dialerPhoneNumberUtil.parse( + TelecomCallUtil.getNumber(call), GeoUtil.getCurrentCountryIso(appContext)); + }); + String callerDisplayName = call.getDetails().getCallerDisplayName(); + boolean isIncomingCall = (call.getState() == Call.STATE_RINGING); + + return Futures.transformAsync( + dialerPhoneNumberFuture, + dialerPhoneNumber -> + backgroundExecutorService.submit( + () -> + buildCequintInfo( + CequintCallerIdManager.getCequintCallerIdContactForCall( + appContext, + Assert.isNotNull(dialerPhoneNumber).getNormalizedNumber(), + callerDisplayName, + isIncomingCall))), + lightweightExecutorService); } @Override public ListenableFuture lookup(DialerPhoneNumber dialerPhoneNumber) { - // TODO(a bug): Implement this method. - return Futures.immediateFuture(CequintInfo.getDefaultInstance()); + if (!CequintCallerIdManager.isCequintCallerIdEnabled(appContext)) { + return Futures.immediateFuture(CequintInfo.getDefaultInstance()); + } + + return backgroundExecutorService.submit( + () -> + buildCequintInfo( + CequintCallerIdManager.getCequintCallerIdContactForNumber( + appContext, dialerPhoneNumber.getNormalizedNumber()))); } @Override @@ -75,16 +127,38 @@ public class CequintPhoneLookup implements PhoneLookup { @Override public void registerContentObservers() { - // No content observers for Cequint info. + // No need to register a content observer as the Cequint content provider doesn't support batch + // queries. } @Override public void unregisterContentObservers() { - // No content observers for Cequint info. + // Nothing to be done as no content observer is registered. } @Override public ListenableFuture clearData() { return Futures.immediateFuture(null); } + + /** + * Builds a {@link CequintInfo} proto based on the given {@link CequintCallerIdContact} returned + * by {@link CequintCallerIdManager}. + */ + private static CequintInfo buildCequintInfo(CequintCallerIdContact cequintCallerIdContact) { + CequintInfo.Builder cequintInfoBuilder = CequintInfo.newBuilder(); + + // Every field in CequintCallerIdContact can be null. + if (!TextUtils.isEmpty(cequintCallerIdContact.name())) { + cequintInfoBuilder.setName(cequintCallerIdContact.name()); + } + if (!TextUtils.isEmpty(cequintCallerIdContact.geolocation())) { + cequintInfoBuilder.setGeolocation(cequintCallerIdContact.geolocation()); + } + if (!TextUtils.isEmpty(cequintCallerIdContact.photoUri())) { + cequintInfoBuilder.setPhotoUri(cequintCallerIdContact.photoUri()); + } + + return cequintInfoBuilder.build(); + } } diff --git a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java index 4302436a7..6179b5dcf 100644 --- a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java +++ b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java @@ -621,7 +621,7 @@ public class ContactInfoHelper { return; } CequintCallerIdContact cequintCallerIdContact = - cequintCallerIdManager.getCequintCallerIdContact(context, number); + cequintCallerIdManager.getCachedCequintCallerIdContact(context, number); if (cequintCallerIdContact == null) { return; } diff --git a/java/com/android/incallui/ContactInfoCache.java b/java/com/android/incallui/ContactInfoCache.java index eefd4833c..81c7b724d 100644 --- a/java/com/android/incallui/ContactInfoCache.java +++ b/java/com/android/incallui/ContactInfoCache.java @@ -514,7 +514,7 @@ public class ContactInfoCache implements OnImageLoadCompleteListener { return; } CequintCallerIdContact cequintCallerIdContact = - CequintCallerIdManager.getCequintCallerIdContactForInCall( + CequintCallerIdManager.getCequintCallerIdContactForCall( context, callerInfo.phoneNumber, cnapName, isIncoming); if (cequintCallerIdContact == null) { -- cgit v1.2.3