use of com.android.dialer.phonenumberproto.PartitionedNumbers in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method lookupInternal.
private Cp2Info lookupInternal(DialerPhoneNumber dialerPhoneNumber) {
String number = dialerPhoneNumber.getNormalizedNumber();
if (TextUtils.isEmpty(number)) {
return Cp2Info.getDefaultInstance();
}
Set<Cp2ContactInfo> cp2ContactInfos = new ArraySet<>();
// Even though this is only a single number, use PartitionedNumbers to mimic the logic used
// during getMostRecentInfo.
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(ImmutableSet.of(dialerPhoneNumber));
Cursor cursor = null;
try {
// to ensure consistency when the batch methods are used to update data.
if (!partitionedNumbers.validE164Numbers().isEmpty()) {
cursor = queryPhoneTableBasedOnE164(Cp2Projections.getProjectionForPhoneTable(), partitionedNumbers.validE164Numbers());
} else {
cursor = queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), Iterables.getOnlyElement(partitionedNumbers.invalidNumbers()));
}
if (cursor == null) {
LogUtil.w("Cp2DefaultDirectoryPhoneLookup.lookupInternal", "null cursor");
return Cp2Info.getDefaultInstance();
}
while (cursor.moveToNext()) {
cp2ContactInfos.add(Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor, Directory.DEFAULT));
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
}
use of com.android.dialer.phonenumberproto.PartitionedNumbers in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method buildMapForUpdatedOrAddedContacts.
/**
* 1. get all contact ids. if the id is unset, add the number to the list of contacts to look up.
* 2. reduce our list of contact ids to those that were updated after lastModified. 3. Now we have
* the smallest set of dialer phone numbers to query cp2 against. 4. build and return the map of
* dialerphonenumbers to their new Cp2ContactInfo
*
* @return Map of {@link DialerPhoneNumber} to {@link Cp2Info} with updated {@link
* Cp2ContactInfo}.
*/
private ListenableFuture<Map<DialerPhoneNumber, Set<Cp2ContactInfo>>> buildMapForUpdatedOrAddedContacts(Map<DialerPhoneNumber, Cp2Info> existingInfoMap, long lastModified, Set<DialerPhoneNumber> deletedPhoneNumbers) {
// Start by building a set of DialerPhoneNumbers that we want to update.
ListenableFuture<Set<DialerPhoneNumber>> updatedNumbersFuture = findNumbersToUpdate(existingInfoMap, lastModified, deletedPhoneNumbers);
return Futures.transformAsync(updatedNumbersFuture, updatedNumbers -> {
if (updatedNumbers.isEmpty()) {
return Futures.immediateFuture(new ArrayMap<>());
}
// Divide the numbers into those that are valid and those that are not. Issue a single
// batch query for the valid numbers against the PHONE table, and in parallel issue
// individual queries against PHONE_LOOKUP for each invalid number.
// TODO(zachh): These queries are inefficient without a lastModified column to filter on.
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(ImmutableSet.copyOf(updatedNumbers));
ListenableFuture<Map<String, Set<Cp2ContactInfo>>> validNumbersFuture = batchQueryForValidNumbers(partitionedNumbers.validE164Numbers());
List<ListenableFuture<Set<Cp2ContactInfo>>> invalidNumbersFuturesList = new ArrayList<>();
for (String invalidNumber : partitionedNumbers.invalidNumbers()) {
invalidNumbersFuturesList.add(individualQueryForInvalidNumber(invalidNumber));
}
ListenableFuture<List<Set<Cp2ContactInfo>>> invalidNumbersFuture = Futures.allAsList(invalidNumbersFuturesList);
Callable<Map<DialerPhoneNumber, Set<Cp2ContactInfo>>> computeMap = () -> {
// These get() calls are safe because we are using whenAllSucceed below.
Map<String, Set<Cp2ContactInfo>> validNumbersResult = validNumbersFuture.get();
List<Set<Cp2ContactInfo>> invalidNumbersResult = invalidNumbersFuture.get();
Map<DialerPhoneNumber, Set<Cp2ContactInfo>> map = new ArrayMap<>();
// First update the map with the valid number results.
for (Entry<String, Set<Cp2ContactInfo>> entry : validNumbersResult.entrySet()) {
String validNumber = entry.getKey();
Set<Cp2ContactInfo> cp2ContactInfos = entry.getValue();
Set<DialerPhoneNumber> dialerPhoneNumbers = partitionedNumbers.dialerPhoneNumbersForValidE164(validNumber);
addInfo(map, dialerPhoneNumbers, cp2ContactInfos);
// 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);
}
// Next update the map with the invalid results.
int i = 0;
for (String invalidNumber : partitionedNumbers.invalidNumbers()) {
Set<Cp2ContactInfo> cp2Infos = invalidNumbersResult.get(i++);
Set<DialerPhoneNumber> dialerPhoneNumbers = partitionedNumbers.dialerPhoneNumbersForInvalid(invalidNumber);
addInfo(map, dialerPhoneNumbers, cp2Infos);
// 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);
}
// information for them.
for (DialerPhoneNumber dialerPhoneNumber : updatedNumbers) {
map.put(dialerPhoneNumber, ImmutableSet.of());
}
LogUtil.v("Cp2DefaultDirectoryPhoneLookup.buildMapForUpdatedOrAddedContacts", "found %d numbers that may need updating", updatedNumbers.size());
return map;
};
return Futures.whenAllSucceed(validNumbersFuture, invalidNumbersFuture).call(computeMap, lightweightExecutorService);
}, lightweightExecutorService);
}
use of com.android.dialer.phonenumberproto.PartitionedNumbers in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method isDirty.
@Override
public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
if (!PermissionsUtil.hasContactsReadPermissions(appContext)) {
LogUtil.w("Cp2DefaultDirectoryPhoneLookup.isDirty", "missing permissions");
Predicate<PhoneLookupInfo> phoneLookupInfoIsDirtyFn = phoneLookupInfo -> !phoneLookupInfo.getDefaultCp2Info().equals(Cp2Info.getDefaultInstance());
return missingPermissionsOperations.isDirtyForMissingPermissions(phoneNumbers, phoneLookupInfoIsDirtyFn);
}
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(phoneNumbers);
if (partitionedNumbers.invalidNumbers().size() > getMaxSupportedInvalidNumbers()) {
// If there are N invalid numbers, we can't determine determine dirtiness without running N
// queries; since running this many queries is not feasible for the (lightweight) isDirty
// check, simply return true. The expectation is that this should rarely be the case as the
// vast majority of numbers in call logs should be valid.
LogUtil.v("Cp2DefaultDirectoryPhoneLookup.isDirty", "returning true because too many invalid numbers (%d)", partitionedNumbers.invalidNumbers().size());
return Futures.immediateFuture(true);
}
ListenableFuture<Long> lastModifiedFuture = backgroundExecutorService.submit(() -> sharedPreferences.getLong(PREF_LAST_TIMESTAMP_PROCESSED, 0L));
return Futures.transformAsync(lastModifiedFuture, lastModified -> {
// We are always going to need to do this check and it is pretty cheap so do it first.
ListenableFuture<Boolean> anyContactsDeletedFuture = anyContactsDeletedSince(lastModified);
return Futures.transformAsync(anyContactsDeletedFuture, anyContactsDeleted -> {
if (anyContactsDeleted) {
LogUtil.v("Cp2DefaultDirectoryPhoneLookup.isDirty", "returning true because contacts deleted");
return Futures.immediateFuture(true);
}
// Hopefully the most common case is there are no contacts updated; we can detect
// this cheaply.
ListenableFuture<Boolean> noContactsModifiedSinceFuture = noContactsModifiedSince(lastModified);
return Futures.transformAsync(noContactsModifiedSinceFuture, noContactsModifiedSince -> {
if (noContactsModifiedSince) {
LogUtil.v("Cp2DefaultDirectoryPhoneLookup.isDirty", "returning false because no contacts modified since last run");
return Futures.immediateFuture(false);
}
// This method is more expensive but is probably the most likely scenario; we
// are looking for changes to contacts which have been called.
ListenableFuture<Set<Long>> contactIdsFuture = queryPhoneTableForContactIds(phoneNumbers);
ListenableFuture<Boolean> contactsUpdatedFuture = Futures.transformAsync(contactIdsFuture, contactIds -> contactsUpdated(contactIds, lastModified), MoreExecutors.directExecutor());
return Futures.transformAsync(contactsUpdatedFuture, contactsUpdated -> {
if (contactsUpdated) {
LogUtil.v("Cp2DefaultDirectoryPhoneLookup.isDirty", "returning true because a previously called contact was updated");
return Futures.immediateFuture(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.
ListenableFuture<Set<Long>> phoneLookupContactIdsFuture = queryPhoneLookupHistoryForContactIds();
return Futures.transformAsync(phoneLookupContactIdsFuture, phoneLookupContactIds -> contactsUpdated(phoneLookupContactIds, lastModified), MoreExecutors.directExecutor());
}, MoreExecutors.directExecutor());
}, MoreExecutors.directExecutor());
}, MoreExecutors.directExecutor());
}, MoreExecutors.directExecutor());
}
use of com.android.dialer.phonenumberproto.PartitionedNumbers in project android_packages_apps_Dialer by LineageOS.
the class SystemBlockedNumberPhoneLookup method queryNumbers.
@WorkerThread
private ImmutableMap<DialerPhoneNumber, SystemBlockedNumberInfo> queryNumbers(ImmutableSet<DialerPhoneNumber> numbers) {
Assert.isWorkerThread();
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(numbers);
Set<DialerPhoneNumber> blockedNumbers = new ArraySet<>();
Selection normalizedSelection = Selection.column(BlockedNumbers.COLUMN_E164_NUMBER).in(partitionedNumbers.validE164Numbers());
try (Cursor cursor = appContext.getContentResolver().query(BlockedNumbers.CONTENT_URI, new String[] { BlockedNumbers.COLUMN_E164_NUMBER }, normalizedSelection.getSelection(), normalizedSelection.getSelectionArgs(), null)) {
while (cursor != null && cursor.moveToNext()) {
blockedNumbers.addAll(partitionedNumbers.dialerPhoneNumbersForValidE164(cursor.getString(0)));
}
}
Selection rawSelection = Selection.column(BlockedNumbers.COLUMN_ORIGINAL_NUMBER).in(partitionedNumbers.invalidNumbers());
try (Cursor cursor = appContext.getContentResolver().query(BlockedNumbers.CONTENT_URI, new String[] { BlockedNumbers.COLUMN_ORIGINAL_NUMBER }, rawSelection.getSelection(), rawSelection.getSelectionArgs(), null)) {
while (cursor != null && cursor.moveToNext()) {
blockedNumbers.addAll(partitionedNumbers.dialerPhoneNumbersForInvalid(cursor.getString(0)));
}
}
ImmutableMap.Builder<DialerPhoneNumber, SystemBlockedNumberInfo> result = ImmutableMap.builder();
for (DialerPhoneNumber number : numbers) {
result.put(number, SystemBlockedNumberInfo.newBuilder().setBlockedState(blockedNumbers.contains(number) ? BlockedState.BLOCKED : BlockedState.NOT_BLOCKED).build());
}
return result.build();
}
use of com.android.dialer.phonenumberproto.PartitionedNumbers in project android_packages_apps_Dialer by LineageOS.
the class Cp2DefaultDirectoryPhoneLookup method queryPhoneTableForContactIds.
/**
* Returns set of contact ids that correspond to {@code dialerPhoneNumbers} if the contact exists.
*/
private ListenableFuture<Set<Long>> queryPhoneTableForContactIds(ImmutableSet<DialerPhoneNumber> dialerPhoneNumbers) {
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(dialerPhoneNumbers);
List<ListenableFuture<Set<Long>>> queryFutures = new ArrayList<>();
// First use the valid E164 numbers to query the NORMALIZED_NUMBER column.
queryFutures.add(queryPhoneTableForContactIdsBasedOnE164(partitionedNumbers.validE164Numbers()));
// Then run a separate query for each invalid number. Separate queries are done to accomplish
// loose matching which couldn't be accomplished with a batch query.
Assert.checkState(partitionedNumbers.invalidNumbers().size() <= getMaxSupportedInvalidNumbers());
for (String invalidNumber : partitionedNumbers.invalidNumbers()) {
queryFutures.add(queryPhoneLookupTableForContactIdsBasedOnRawNumber(invalidNumber));
}
return Futures.transform(Futures.allAsList(queryFutures), listOfSets -> {
Set<Long> contactIds = new ArraySet<>();
for (Set<Long> ids : listOfSets) {
contactIds.addAll(ids);
}
return contactIds;
}, lightweightExecutorService);
}
Aggregations