From 6456d8302885e9c6f24d678fca798b730d101a17 Mon Sep 17 00:00:00 2001 From: zachh Date: Wed, 20 Dec 2017 13:35:19 -0800 Subject: Handle contacts which have been disassociated with a number in Cp2PhoneLookup. Bug: 34672501 Test: unit PiperOrigin-RevId: 179726904 Change-Id: I6a81ef28675af7f95139193b69f87decddc4c844 --- .../dialer/phonelookup/cp2/Cp2PhoneLookup.java | 103 +++++++++++++++++++-- 1 file changed, 97 insertions(+), 6 deletions(-) (limited to 'java/com/android/dialer/phonelookup') diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java index 4261b763a..a477e035c 100644 --- a/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java +++ b/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java @@ -36,6 +36,7 @@ import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info; import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo; +import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil; import com.android.dialer.storage.Unencrypted; import com.android.dialer.telecom.TelecomCallUtil; @@ -45,6 +46,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.protobuf.InvalidProtocolBufferException; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -131,9 +133,25 @@ public final class Cp2PhoneLookup implements PhoneLookup { private boolean isDirtyInternal(ImmutableSet phoneNumbers) { long lastModified = sharedPreferences.getLong(PREF_LAST_TIMESTAMP_PROCESSED, 0L); - // TODO(zachh): If a number got disassociated with a contact; the contactIds will be empty. - return contactsUpdated(queryPhoneTableForContactIds(phoneNumbers), lastModified) - || contactsDeleted(lastModified); + // We are always going to need to do this check and it is pretty cheap so do it first. + if (anyContactsDeletedSince(lastModified)) { + return true; + } + // Hopefully the most common case is there are no contacts updated; we can detect this cheaply. + if (noContactsModifiedSince(lastModified)) { + return false; + } + // This method is more expensive but is probably the most likely scenario; we are looking for + // changes to contacts which have been called. + if (contactsUpdated(queryPhoneTableForContactIds(phoneNumbers), lastModified)) { + return true; + } + // This is the most expensive method so do it last; the scenario is that a contact which has + // been called got disassociated with a number and we need to clear their information. + if (contactsUpdated(queryPhoneLookupHistoryForContactIds(), lastModified)) { + return true; + } + return false; } /** @@ -156,6 +174,46 @@ public final class Cp2PhoneLookup implements PhoneLookup { return contactIds; } + /** Gets all of the contact ids from PhoneLookupHistory. */ + private Set queryPhoneLookupHistoryForContactIds() { + Set contactIds = new ArraySet<>(); + try (Cursor cursor = + appContext + .getContentResolver() + .query( + PhoneLookupHistory.CONTENT_URI, + new String[] { + PhoneLookupHistory.PHONE_LOOKUP_INFO, + }, + null, + null, + null)) { + + if (cursor == null) { + LogUtil.w("Cp2PhoneLookup.queryPhoneLookupHistoryForContactIds", "null cursor"); + return contactIds; + } + + if (cursor.moveToFirst()) { + int phoneLookupInfoColumn = + cursor.getColumnIndexOrThrow(PhoneLookupHistory.PHONE_LOOKUP_INFO); + do { + PhoneLookupInfo phoneLookupInfo; + try { + phoneLookupInfo = PhoneLookupInfo.parseFrom(cursor.getBlob(phoneLookupInfoColumn)); + } catch (InvalidProtocolBufferException e) { + throw new IllegalStateException(e); + } + for (Cp2ContactInfo info : phoneLookupInfo.getCp2Info().getCp2ContactInfoList()) { + contactIds.add(info.getContactId()); + } + } while (cursor.moveToNext()); + } + } + + return contactIds; + } + private Set queryPhoneTableForContactIdsBasedOnE164(Set validE164Numbers) { Set contactIds = new ArraySet<>(); if (validE164Numbers.isEmpty()) { @@ -227,8 +285,26 @@ public final class Cp2PhoneLookup implements PhoneLookup { null); } + private boolean noContactsModifiedSince(long lastModified) { + try (Cursor cursor = + appContext + .getContentResolver() + .query( + Contacts.CONTENT_URI, + new String[] {Contacts._ID}, + Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?", + new String[] {Long.toString(lastModified)}, + Contacts._ID + " limit 1")) { + if (cursor == null) { + LogUtil.w("Cp2PhoneLookup.noContactsModifiedSince", "null cursor"); + return false; + } + return cursor.getCount() == 0; + } + } + /** Returns true if any contacts were deleted after {@code lastModified}. */ - private boolean contactsDeleted(long lastModified) { + private boolean anyContactsDeletedSince(long lastModified) { try (Cursor cursor = appContext .getContentResolver() @@ -237,9 +313,9 @@ public final class Cp2PhoneLookup implements PhoneLookup { new String[] {DeletedContacts.CONTACT_DELETED_TIMESTAMP}, DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?", new String[] {Long.toString(lastModified)}, - null)) { + DeletedContacts.CONTACT_DELETED_TIMESTAMP + " limit 1")) { if (cursor == null) { - LogUtil.w("Cp2PhoneLookup.contactsDeleted", "null cursor"); + LogUtil.w("Cp2PhoneLookup.anyContactsDeletedSince", "null cursor"); return false; } return cursor.getCount() > 0; @@ -413,6 +489,11 @@ public final class Cp2PhoneLookup implements PhoneLookup { partitionedNumbers.dialerPhoneNumbersForE164(e164Number); Cp2ContactInfo info = buildCp2ContactInfoFromPhoneCursor(appContext, cursor); addInfo(map, dialerPhoneNumbers, info); + + // We are going to remove the numbers that we've handled so that we later can detect + // numbers that weren't handled and therefore need to have their contact information + // removed. + updatedNumbers.removeAll(dialerPhoneNumbers); } } } @@ -430,10 +511,20 @@ public final class Cp2PhoneLookup implements PhoneLookup { partitionedNumbers.dialerPhoneNumbersForUnformattable(unformattableNumber); Cp2ContactInfo info = buildCp2ContactInfoFromPhoneCursor(appContext, cursor); addInfo(map, dialerPhoneNumbers, info); + + // We are going to remove the numbers that we've handled so that we later can detect + // numbers that weren't handled and therefore need to have their contact information + // removed. + updatedNumbers.removeAll(dialerPhoneNumbers); } } } } + // The leftovers in updatedNumbers that weren't removed are numbers that were previously + // associated with contacts, but are no longer. Remove the contact information for them. + for (DialerPhoneNumber dialerPhoneNumber : updatedNumbers) { + map.put(dialerPhoneNumber, ImmutableSet.of()); + } return map; } -- cgit v1.2.3