From 8730ff591ed078b3e81d9dd6a2b36cc620fccb1a Mon Sep 17 00:00:00 2001 From: Santos Cordon Date: Fri, 6 Feb 2015 04:34:32 -0800 Subject: Add cache for isVoicemail check. bindView() gets called many times at dialer startup and layout updates. Besides being costly, it invokes cross-process TelecomManager methods. This change addresses the cross-process issues by adding a short-lived cache to isVoicemail() method invocations. Change-Id: Ib69c0eb3969a1b7d77c9fd1a2aa6e578a31fb5e9 --- src/com/android/dialer/calllog/CallLogAdapter.java | 2 + .../dialer/calllog/PhoneNumberUtilsWrapper.java | 51 +++++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) (limited to 'src/com/android/dialer') diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java index bb20a1306..225d34948 100644 --- a/src/com/android/dialer/calllog/CallLogAdapter.java +++ b/src/com/android/dialer/calllog/CallLogAdapter.java @@ -633,6 +633,8 @@ public class CallLogAdapter extends GroupingListAdapter /** * Binds the views in the entry to the data in the call log. + * TODO: This gets called 20-30 times when Dialer starts up for a single call log entry and + * should not. It invokes cross-process methods and the repeat execution can get costly. * * @param callLogItemView the view corresponding to this entry * @param c the cursor pointing to the entry in the call log diff --git a/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java b/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java index 11f4a67f6..6fa81435f 100644 --- a/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java +++ b/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java @@ -20,13 +20,15 @@ import android.content.Context; import android.provider.CallLog; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; -import android.telephony.PhoneNumberUtils; import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; import com.android.contacts.common.util.PhoneNumberHelper; - import com.google.common.collect.Sets; +import java.util.HashMap; +import java.util.Map; import java.util.Set; /** @@ -34,8 +36,20 @@ import java.util.Set; */ public class PhoneNumberUtilsWrapper { private static final Set LEGACY_UNKNOWN_NUMBERS = Sets.newHashSet("-1", "-2", "-3"); + private static final long MAX_VOICEMAIL_CACHE_AGE_IN_MS = 60 * 1000; // 60 seconds private final Context mContext; + // Keeps a cache of recently-made voicemail queries. The entire point of this cache is to + // reduce the number of cross-process requests to TelecomManager. + // Maps from a phone-account/number pair to a boolean because multiple numbers could return true + // for the voicemail number if those numbers are not pre-normalized. + // + // TODO: Dialer should be fixed so as not to check isVoicemail() so often but at the time of + // this writing, that was a much larger undertaking than creating this cache. + private final Map, Boolean> mVoicemailQueryCache = + new HashMap<>(); + private long mVoicemailCacheTimestamp = 0; + public PhoneNumberUtilsWrapper(Context context) { mContext = context; } @@ -50,11 +64,34 @@ public class PhoneNumberUtilsWrapper { * Returns true if the given number is the number of the configured voicemail. To be able to * mock-out this, it is not a static method. */ - public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, - CharSequence number) { - final TelecomManager telecomManager = - (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); - return number!= null && telecomManager.isVoiceMailNumber(accountHandle, number.toString()); + public boolean isVoicemailNumber(PhoneAccountHandle accountHandle, CharSequence number) { + if (TextUtils.isEmpty(number)) { + return false; + } + + long currentTime = System.currentTimeMillis(); + // check the age of the voicemail cache first. + if (currentTime - mVoicemailCacheTimestamp > MAX_VOICEMAIL_CACHE_AGE_IN_MS) { + mVoicemailQueryCache.clear(); + + // We set the timestamp of the voicemail cache to the point where the cache is recreated + // instead of when an item is added. + // 1) This is easier to write + // 2) Ensures that the oldest entry is never older than MAX_VOICEMAIL_CACHE_AGE + mVoicemailCacheTimestamp = currentTime; + } + + Pair key = new Pair<>(accountHandle, number); + if (mVoicemailQueryCache.containsKey(key)) { + return mVoicemailQueryCache.get(key); + } else { + final TelecomManager telecomManager = + (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); + Boolean isVoicemail = + telecomManager.isVoiceMailNumber(accountHandle, number.toString()); + mVoicemailQueryCache.put(key, isVoicemail); + return isVoicemail; + } } /** -- cgit v1.2.3