From 250bbb0e04747f162beb7490ce3f7ef05a0ce841 Mon Sep 17 00:00:00 2001 From: Nancy Chen Date: Wed, 11 Nov 2015 18:19:35 -0800 Subject: Fix NPE with contact cache, and guard against race condition. Because the image loading and contact interactions information are loaded asynchronously at the same time, one of them may be loaded before the other. To ensure that the main thread is aware when whether one or both of them have completed, keep a flag in ContactCacheInfo. This will prevent the callbacks from being cleared before all callbacks are completed. Also guard against NPEs by making sure the cache entry is added to the map of callIds to cacheEntries and check for null before proceeding in the callback for contact interactions. Bug: 25618632 Change-Id: I7662242e040948a1e445f66c362f2eaea4cf03af --- .../src/com/android/incallui/ContactInfoCache.java | 46 +++++++++++++++++----- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/InCallUI/src/com/android/incallui/ContactInfoCache.java b/InCallUI/src/com/android/incallui/ContactInfoCache.java index c5176b1a1..6bbe52f46 100644 --- a/InCallUI/src/com/android/incallui/ContactInfoCache.java +++ b/InCallUI/src/com/android/incallui/ContactInfoCache.java @@ -241,6 +241,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete Log.d(TAG, "Contact lookup. Local contact found, starting image load"); // Load the image with a callback to update the image state. // When the load is finished, onImageLoadComplete() will be called. + cacheEntry.isLoadingPhoto = true; ContactsAsyncHelper.startObtainPhotoAsync(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE, mContext, cacheEntry.displayPhotoUri, ContactInfoCache.this, callId); } else { @@ -302,19 +303,21 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete entry.photo = mContext.getResources().getDrawable(R.drawable.img_business); } - boolean hasContactInteractions = false; + mInfoMap.put(mCallId, entry); + sendInfoNotifications(mCallId, entry); + if (mContactUtils != null) { // This method will callback "onContactInteractionsFound". - hasContactInteractions = mContactUtils.retrieveContactInteractionsFromLookupKey( - info.getLookupKey(), this); + entry.isLoadingContactInteractions = + mContactUtils.retrieveContactInteractionsFromLookupKey( + info.getLookupKey(), this); } - // Add the contact info to the cache. - mInfoMap.put(mCallId, entry); - sendInfoNotifications(mCallId, entry); + entry.isLoadingPhoto = info.getImageUrl() != null; - // If there is no image then we should not expect another callback. - if (info.getImageUrl() == null && !hasContactInteractions) { + // If there is no image or contact interactions then we should not expect another + // callback. + if (!entry.isLoadingPhoto && !entry.isLoadingContactInteractions) { // We're done, so clear callbacks clearCallbacks(mCallId); } @@ -329,10 +332,23 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete public void onContactInteractionsFound(Address address, List> openingHours) { final ContactCacheEntry entry = mInfoMap.get(mCallId); + if (entry == null) { + Log.e(this, "Contact context received for empty search entry."); + clearCallbacks(mCallId); + return; + } + + entry.isLoadingContactInteractions = false; + + Log.v(ContactInfoCache.this, "Setting contact interactions for entry: ", entry); + entry.locationAddress = address; entry.openingHours = openingHours; sendContactInteractionsNotifications(mCallId, entry); - clearCallbacks(mCallId); + + if (!entry.isLoadingPhoto) { + clearCallbacks(mCallId); + } } } @@ -354,6 +370,9 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete clearCallbacks(callId); return; } + + entry.isLoadingPhoto = false; + Log.d(this, "setting photo for entry: ", entry); // Conference call icons are being handled in CallCardPresenter. @@ -369,7 +388,10 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete } sendImageNotifications(callId, entry); - clearCallbacks(callId); + + if (!entry.isLoadingContactInteractions) { + clearCallbacks(callId); + } } /** @@ -617,6 +639,10 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete public String label; public Drawable photo; public boolean isSipCall; + // Note in cache entry whether this is a pending async loading action to know whether to + // wait for its callback or not. + public boolean isLoadingPhoto; + public boolean isLoadingContactInteractions; /** This will be used for the "view" notification. */ public Uri contactUri; /** Either a display photo or a thumbnail URI. */ -- cgit v1.2.3