summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortwyen <twyen@google.com>2018-01-05 11:52:45 -0800
committerEric Erfanian <erfanian@google.com>2018-01-05 11:57:00 -0800
commitfb112d870c3a564d2dcb0e72dcdcabb6e0375520 (patch)
treedbda20e83cb3458fefec613b56d4b9d0a4814e66
parent417be6a9e3482472cce238e0a51b6367b86aba1f (diff)
Implement dialer blocked number phone lookup
This CL implements looking up the dialer internal database for blocked numbers when the system database is not available yet. Data is only invalidated when dialer is alive since that is the only time blocked numbers can be set and removed. Bug: 70989538,70989547 Test: DialerBlockedNumberPhoneLookupTest PiperOrigin-RevId: 180956355 Change-Id: Ie7acf091bf58a074d0a1ee39613fad035d2e6e60
-rw-r--r--AndroidManifest.xml4
-rw-r--r--java/com/android/dialer/binary/google/AndroidManifest.xml4
-rw-r--r--java/com/android/dialer/calllog/CallLogFramework.java4
-rw-r--r--java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java40
-rw-r--r--java/com/android/dialer/common/database/Selection.java28
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookup.java14
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookupModule.java8
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java210
-rw-r--r--java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java13
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java64
-rw-r--r--java/com/android/dialer/phonelookup/phone_lookup_info.proto22
-rw-r--r--java/com/android/dialer/phonelookup/selector/AndroidManifest.xml18
-rw-r--r--java/com/android/dialer/phonelookup/selector/PhoneLookupSelector.java (renamed from java/com/android/dialer/phonelookup/PhoneLookupSelector.java)38
-rw-r--r--java/com/android/dialer/phonelookup/selector/res/values/strings.xml22
-rw-r--r--java/com/android/dialer/phonenumberproto/PartitionedNumbers.java114
-rw-r--r--java/com/android/dialer/telecom/TelecomCallUtil.java23
-rw-r--r--packages.mk1
17 files changed, 532 insertions, 95 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b459a44c0..d19d0c9ab 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
coreApp="true"
package="com.android.dialer"
- android:versionCode="190000"
- android:versionName="14.0">
+ android:versionCode="220000"
+ android:versionName="17.0">
<uses-sdk
android:minSdkVersion="23"
diff --git a/java/com/android/dialer/binary/google/AndroidManifest.xml b/java/com/android/dialer/binary/google/AndroidManifest.xml
index 86f6bcb81..0ebc006d6 100644
--- a/java/com/android/dialer/binary/google/AndroidManifest.xml
+++ b/java/com/android/dialer/binary/google/AndroidManifest.xml
@@ -16,8 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
coreApp="true"
package="com.google.android.google_stub_dialer"
- android:versionCode="190000"
- android:versionName="14.0">
+ android:versionCode="220000"
+ android:versionName="17.0">
<uses-sdk
android:minSdkVersion="23"
diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java
index e4bb4c89a..c9d5f0907 100644
--- a/java/com/android/dialer/calllog/CallLogFramework.java
+++ b/java/com/android/dialer/calllog/CallLogFramework.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import com.android.dialer.buildtype.BuildType;
import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.DataSources;
@@ -38,7 +39,8 @@ import javax.inject.Singleton;
@Singleton
public final class CallLogFramework implements CallLogDataSource.ContentObserverCallbacks {
- static final String PREF_FORCE_REBUILD = "callLogFrameworkForceRebuild";
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public static final String PREF_FORCE_REBUILD = "callLogFrameworkForceRebuild";
private final DataSources dataSources;
private final SharedPreferences sharedPreferences;
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 214862793..6ec11ad13 100644
--- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -33,14 +33,15 @@ import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.Ann
import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.CallLogMutations;
import com.android.dialer.calllog.datasources.util.RowCombiner;
+import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
import com.android.dialer.phonelookup.PhoneLookup;
import com.android.dialer.phonelookup.PhoneLookupInfo;
-import com.android.dialer.phonelookup.PhoneLookupSelector;
import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract;
import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
+import com.android.dialer.phonelookup.selector.PhoneLookupSelector;
import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -63,9 +64,11 @@ import javax.inject.Inject;
* Responsible for maintaining the columns in the annotated call log which are derived from phone
* numbers.
*/
-public final class PhoneLookupDataSource implements CallLogDataSource {
+public final class PhoneLookupDataSource
+ implements CallLogDataSource, PhoneLookup.ContentObserverCallbacks {
private final PhoneLookup<PhoneLookupInfo> phoneLookup;
+ private final PhoneLookupSelector phoneLookupSelector;
private final ListeningExecutorService backgroundExecutorService;
private final ListeningExecutorService lightweightExecutorService;
@@ -86,12 +89,16 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
*/
private final Set<String> phoneLookupHistoryRowsToDelete = new ArraySet<>();
+ private CallLogDataSource.ContentObserverCallbacks dataSourceContentObserverCallbacks;
+
@Inject
PhoneLookupDataSource(
PhoneLookup<PhoneLookupInfo> phoneLookup,
+ PhoneLookupSelector phoneLookupSelector,
@BackgroundExecutor ListeningExecutorService backgroundExecutorService,
@LightweightExecutor ListeningExecutorService lightweightExecutorService) {
this.phoneLookup = phoneLookup;
+ this.phoneLookupSelector = phoneLookupSelector;
this.backgroundExecutorService = backgroundExecutorService;
this.lightweightExecutorService = lightweightExecutorService;
}
@@ -275,8 +282,16 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
@MainThread
@Override
public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
- // No content observers required for this data source.
+ Context appContext, CallLogDataSource.ContentObserverCallbacks contentObserverCallbacks) {
+ dataSourceContentObserverCallbacks = contentObserverCallbacks;
+ phoneLookup.registerContentObservers(appContext, this);
+ }
+
+ @MainThread
+ @Override
+ public void markDirtyAndNotify(Context appContext) {
+ Assert.isMainThread();
+ dataSourceContentObserverCallbacks.markDirtyAndNotify(appContext);
}
private static ImmutableSet<DialerPhoneNumber>
@@ -455,7 +470,7 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
}));
}
- private static void populateInserts(
+ private void populateInserts(
ImmutableMap<Long, PhoneLookupInfo> existingInfo, CallLogMutations mutations) {
for (Entry<Long, ContentValues> entry : mutations.getInserts().entrySet()) {
long id = entry.getKey();
@@ -468,7 +483,7 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
}
}
- private static void updateMutations(
+ private void updateMutations(
ImmutableMap<Long, PhoneLookupInfo> updatesToApply, CallLogMutations mutations) {
for (Entry<Long, PhoneLookupInfo> entry : updatesToApply.entrySet()) {
long id = entry.getKey();
@@ -554,17 +569,16 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
return normalizedNumbersToDelete;
}
- private static void updateContentValues(
- ContentValues contentValues, PhoneLookupInfo phoneLookupInfo) {
- contentValues.put(AnnotatedCallLog.NAME, PhoneLookupSelector.selectName(phoneLookupInfo));
+ private void updateContentValues(ContentValues contentValues, PhoneLookupInfo phoneLookupInfo) {
+ contentValues.put(AnnotatedCallLog.NAME, phoneLookupSelector.selectName(phoneLookupInfo));
contentValues.put(
- AnnotatedCallLog.PHOTO_URI, PhoneLookupSelector.selectPhotoUri(phoneLookupInfo));
+ AnnotatedCallLog.PHOTO_URI, phoneLookupSelector.selectPhotoUri(phoneLookupInfo));
contentValues.put(
- AnnotatedCallLog.PHOTO_ID, PhoneLookupSelector.selectPhotoId(phoneLookupInfo));
+ AnnotatedCallLog.PHOTO_ID, phoneLookupSelector.selectPhotoId(phoneLookupInfo));
contentValues.put(
- AnnotatedCallLog.LOOKUP_URI, PhoneLookupSelector.selectLookupUri(phoneLookupInfo));
+ AnnotatedCallLog.LOOKUP_URI, phoneLookupSelector.selectLookupUri(phoneLookupInfo));
contentValues.put(
- AnnotatedCallLog.NUMBER_TYPE_LABEL, PhoneLookupSelector.selectNumberLabel(phoneLookupInfo));
+ AnnotatedCallLog.NUMBER_TYPE_LABEL, phoneLookupSelector.selectNumberLabel(phoneLookupInfo));
contentValues.put(
AnnotatedCallLog.CAN_REPORT_AS_INVALID_NUMBER,
PhoneLookupSelector.canReportAsInvalidNumber(phoneLookupInfo));
diff --git a/java/com/android/dialer/common/database/Selection.java b/java/com/android/dialer/common/database/Selection.java
index b61472d2f..e449fd9f6 100644
--- a/java/com/android/dialer/common/database/Selection.java
+++ b/java/com/android/dialer/common/database/Selection.java
@@ -18,8 +18,11 @@ package com.android.dialer.common.database;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.text.TextUtils;
import com.android.dialer.common.Assert;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -106,7 +109,14 @@ public final class Selection {
* enclosed in a parenthesis.
*/
@NonNull
+ @SuppressWarnings("rawtypes")
public static Selection fromString(@Nullable String selection, @Nullable String... args) {
+ return new Builder(selection, args == null ? Collections.emptyList() : Arrays.asList(args))
+ .build();
+ }
+
+ @NonNull
+ public static Selection fromString(@Nullable String selection, Collection<String> args) {
return new Builder(selection, args).build();
}
@@ -149,6 +159,16 @@ public final class Selection {
public Selection is(@NonNull String condition) {
return fromString(column + " " + Assert.isNotNull(condition));
}
+
+ public Selection in(String... values) {
+ return in(values == null ? Collections.emptyList() : Arrays.asList(values));
+ }
+
+ public Selection in(Collection<String> values) {
+ return fromString(
+ column + " IN (" + TextUtils.join(",", Collections.nCopies(values.size(), "?")) + ")",
+ values);
+ }
}
/** Builder for {@link Selection} */
@@ -159,14 +179,14 @@ public final class Selection {
private Builder() {}
- private Builder(@Nullable String selection, @Nullable String... args) {
+ private Builder(@Nullable String selection, Collection<String> args) {
if (selection == null) {
return;
}
checkArgsCount(selection, args);
this.selection.append(parenthesized(selection));
if (args != null) {
- Collections.addAll(selectionArgs, args);
+ selectionArgs.addAll(args);
}
}
@@ -213,14 +233,14 @@ public final class Selection {
return this;
}
- private static void checkArgsCount(@NonNull String selection, @Nullable String... args) {
+ private static void checkArgsCount(@NonNull String selection, Collection<String> args) {
int argsInSelection = 0;
for (int i = 0; i < selection.length(); i++) {
if (selection.charAt(i) == '?') {
argsInSelection++;
}
}
- Assert.checkArgument(argsInSelection == (args == null ? 0 : args.length));
+ Assert.checkArgument(argsInSelection == (args == null ? 0 : args.size()));
}
}
diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java
index 859085e7b..118ae603e 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookup.java
@@ -16,6 +16,8 @@
package com.android.dialer.phonelookup;
+import android.content.Context;
+import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.telecom.Call;
import com.android.dialer.DialerPhoneNumber;
@@ -82,4 +84,16 @@ public interface PhoneLookup<T> {
* be efficiently implemented.
*/
ListenableFuture<Void> onSuccessfulBulkUpdate();
+
+ @MainThread
+ void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks);
+
+ /**
+ * Methods which may optionally be called as a result of a phone lookup's content observer firing.
+ */
+ interface ContentObserverCallbacks {
+ @MainThread
+ void markDirtyAndNotify(Context appContext);
+ }
}
diff --git a/java/com/android/dialer/phonelookup/PhoneLookupModule.java b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
index 8a590052d..b4f37872e 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookupModule.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
@@ -16,6 +16,7 @@
package com.android.dialer.phonelookup;
+import com.android.dialer.phonelookup.blockednumber.DialerBlockedNumberPhoneLookup;
import com.android.dialer.phonelookup.composite.CompositePhoneLookup;
import com.android.dialer.phonelookup.cp2.Cp2PhoneLookup;
import com.google.common.collect.ImmutableList;
@@ -27,8 +28,11 @@ import dagger.Provides;
public abstract class PhoneLookupModule {
@Provides
- static ImmutableList<PhoneLookup> providePhoneLookupList(Cp2PhoneLookup cp2PhoneLookup) {
- return ImmutableList.of(cp2PhoneLookup);
+ @SuppressWarnings({"unchecked", "rawtype"})
+ static ImmutableList<PhoneLookup> providePhoneLookupList(
+ Cp2PhoneLookup cp2PhoneLookup,
+ DialerBlockedNumberPhoneLookup dialerBlockedNumberPhoneLookup) {
+ return ImmutableList.of(cp2PhoneLookup, dialerBlockedNumberPhoneLookup);
}
@Provides
diff --git a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
new file mode 100644
index 000000000..54df3995c
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.phonelookup.blockednumber;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.telecom.Call;
+import android.util.ArraySet;
+import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.common.database.Selection;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
+import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.phonelookup.PhoneLookup;
+import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.BlockedState;
+import com.android.dialer.phonelookup.PhoneLookupInfo.DialerBlockedNumberInfo;
+import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
+import com.android.dialer.phonenumberproto.PartitionedNumbers;
+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 com.google.i18n.phonenumbers.PhoneNumberUtil;
+import java.util.Set;
+import javax.inject.Inject;
+
+/**
+ * Lookup blocked numbers in the dialer internal database. This is used when the system database is
+ * not yet available.
+ */
+public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerBlockedNumberInfo> {
+
+ private final Context appContext;
+ private final ListeningExecutorService executorService;
+
+ @Inject
+ DialerBlockedNumberPhoneLookup(
+ @ApplicationContext Context appContext,
+ @BackgroundExecutor ListeningExecutorService executorService) {
+ this.appContext = appContext;
+ this.executorService = executorService;
+ }
+
+ @Override
+ public ListenableFuture<DialerBlockedNumberInfo> lookup(@NonNull Call call) {
+ return executorService.submit(
+ () -> {
+ DialerPhoneNumberUtil dialerPhoneNumberUtil =
+ new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
+
+ DialerPhoneNumber number =
+ dialerPhoneNumberUtil.parse(
+ TelecomCallUtil.getNumber(call),
+ TelecomCallUtil.getCountryCode(appContext, call).orNull());
+ return queryNumbers(ImmutableSet.of(number)).get(number);
+ });
+ }
+
+ @Override
+ public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
+ // Dirty state is recorded with PhoneLookupDataSource.markDirtyAndNotify(), which will force
+ // rebuild with the CallLogFramework
+ return Futures.immediateFuture(false);
+ }
+
+ @Override
+ public ListenableFuture<ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo>>
+ getMostRecentInfo(ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo> existingInfoMap) {
+ LogUtil.enterBlock("DialerBlockedNumberPhoneLookup.getMostRecentPhoneLookupInfo");
+ return executorService.submit(() -> queryNumbers(existingInfoMap.keySet()));
+ }
+
+ @Override
+ public void setSubMessage(
+ PhoneLookupInfo.Builder phoneLookupInfo, DialerBlockedNumberInfo subMessage) {
+ phoneLookupInfo.setDialerBlockedNumberInfo(subMessage);
+ }
+
+ @Override
+ public DialerBlockedNumberInfo getSubMessage(PhoneLookupInfo phoneLookupInfo) {
+ return phoneLookupInfo.getDialerBlockedNumberInfo();
+ }
+
+ @Override
+ public ListenableFuture<Void> onSuccessfulBulkUpdate() {
+ return Futures.immediateFuture(null);
+ }
+
+ @WorkerThread
+ private ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo> queryNumbers(
+ ImmutableSet<DialerPhoneNumber> numbers) {
+ Assert.isWorkerThread();
+ PartitionedNumbers partitionedNumbers = new PartitionedNumbers(numbers);
+
+ Set<DialerPhoneNumber> blockedNumbers = new ArraySet<>();
+
+ Selection normalizedSelection =
+ Selection.column(FilteredNumberColumns.NORMALIZED_NUMBER)
+ .in(partitionedNumbers.validE164Numbers());
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(
+ FilteredNumber.CONTENT_URI,
+ new String[] {FilteredNumberColumns.NORMALIZED_NUMBER, FilteredNumberColumns.TYPE},
+ normalizedSelection.getSelection(),
+ normalizedSelection.getSelectionArgs(),
+ null)) {
+ while (cursor != null && cursor.moveToNext()) {
+ if (cursor.getInt(1) == FilteredNumberTypes.BLOCKED_NUMBER) {
+ blockedNumbers.addAll(partitionedNumbers.dialerPhoneNumbersForE164(cursor.getString(0)));
+ }
+ }
+ }
+
+ Selection rawSelection =
+ Selection.column(FilteredNumberColumns.NUMBER)
+ .in(
+ partitionedNumbers
+ .unformattableNumbers()
+ .toArray(new String[partitionedNumbers.unformattableNumbers().size()]));
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(
+ FilteredNumber.CONTENT_URI,
+ new String[] {FilteredNumberColumns.NUMBER, FilteredNumberColumns.TYPE},
+ rawSelection.getSelection(),
+ rawSelection.getSelectionArgs(),
+ null)) {
+ while (cursor != null && cursor.moveToNext()) {
+ if (cursor.getInt(1) == FilteredNumberTypes.BLOCKED_NUMBER) {
+ blockedNumbers.addAll(
+ partitionedNumbers.dialerPhoneNumbersForUnformattable(cursor.getString(0)));
+ }
+ }
+ }
+
+ ImmutableMap.Builder<DialerPhoneNumber, DialerBlockedNumberInfo> result =
+ ImmutableMap.builder();
+
+ for (DialerPhoneNumber number : numbers) {
+ result.put(
+ number,
+ DialerBlockedNumberInfo.newBuilder()
+ .setBlockedState(
+ blockedNumbers.contains(number) ? BlockedState.BLOCKED : BlockedState.NOT_BLOCKED)
+ .build());
+ }
+
+ return result.build();
+ }
+
+ @Override
+ public void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ appContext
+ .getContentResolver()
+ .registerContentObserver(
+ FilteredNumber.CONTENT_URI,
+ true, // FilteredNumberProvider notifies on the item
+ new FilteredNumberObserver(appContext, contentObserverCallbacks));
+ }
+
+ private static class FilteredNumberObserver extends ContentObserver {
+ private final Context appContext;
+ private final ContentObserverCallbacks contentObserverCallbacks;
+
+ FilteredNumberObserver(Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ super(null);
+ this.appContext = appContext;
+ this.contentObserverCallbacks = contentObserverCallbacks;
+ }
+
+ @MainThread
+ @Override
+ @SuppressWarnings("FutureReturnValueIgnored") // never throws.
+ public void onChange(boolean selfChange, Uri uri) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("DialerBlockedNumberPhoneLookup.FilteredNumberObserver.onChange");
+ contentObserverCallbacks.markDirtyAndNotify(appContext);
+ }
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
index da4378bb7..34f35311f 100644
--- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
@@ -16,6 +16,8 @@
package com.android.dialer.phonelookup.composite;
+import android.content.Context;
+import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.telecom.Call;
import com.android.dialer.DialerPhoneNumber;
@@ -60,7 +62,7 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
* <p>Note: If any of the dependent lookups fails, the returned future will also fail. If any of
* the dependent lookups does not complete, the returned future will also not complete.
*/
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "rawtype"})
@Override
public ListenableFuture<PhoneLookupInfo> lookup(@NonNull Call call) {
// TODO(zachh): Add short-circuiting logic so that this call is not blocked on low-priority
@@ -164,4 +166,13 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
return Futures.transform(
Futures.allAsList(futures), unused -> null, lightweightExecutorService);
}
+
+ @Override
+ @MainThread
+ public void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ for (PhoneLookup phoneLookup : phoneLookups) {
+ phoneLookup.registerContentObservers(appContext, contentObserverCallbacks);
+ }
+ }
}
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java
index 60c934ace..307e0a434 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java
@@ -37,7 +37,7 @@ 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.phonenumberproto.PartitionedNumbers;
import com.android.dialer.storage.Unencrypted;
import com.android.dialer.telecom.TelecomCallUtil;
import com.google.common.base.Optional;
@@ -45,7 +45,6 @@ import com.google.common.collect.ImmutableMap;
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;
@@ -392,6 +391,12 @@ public final class Cp2PhoneLookup implements PhoneLookup<Cp2Info> {
});
}
+ @Override
+ public void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ // Do nothing since CP2 changes are too noisy.
+ }
+
/**
* 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
@@ -475,7 +480,8 @@ public final class Cp2PhoneLookup implements PhoneLookup<Cp2Info> {
// Divide the numbers into those we can format to E164 and those we can't. Then run separate
// queries against the contacts table using the NORMALIZED_NUMBER and NUMBER columns.
// TODO(zachh): These queries are inefficient without a lastModified column to filter on.
- PartitionedNumbers partitionedNumbers = new PartitionedNumbers(updatedNumbers);
+ PartitionedNumbers partitionedNumbers =
+ new PartitionedNumbers(ImmutableSet.copyOf(updatedNumbers));
if (!partitionedNumbers.validE164Numbers().isEmpty()) {
try (Cursor cursor =
queryPhoneTableBasedOnE164(CP2_INFO_PROJECTION, partitionedNumbers.validE164Numbers())) {
@@ -701,56 +707,4 @@ public final class Cp2PhoneLookup implements PhoneLookup<Cp2Info> {
}
return where.toString();
}
-
- /**
- * Divides a set of {@link DialerPhoneNumber DialerPhoneNumbers} by those that can be formatted to
- * E164 and those that cannot.
- */
- private static class PartitionedNumbers {
- private Map<String, Set<DialerPhoneNumber>> e164NumbersToDialerPhoneNumbers = new ArrayMap<>();
- private Map<String, Set<DialerPhoneNumber>> unformattableNumbersToDialerPhoneNumbers =
- new ArrayMap<>();
-
- PartitionedNumbers(Set<DialerPhoneNumber> dialerPhoneNumbers) {
- DialerPhoneNumberUtil dialerPhoneNumberUtil =
- new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
- for (DialerPhoneNumber dialerPhoneNumber : dialerPhoneNumbers) {
- Optional<String> e164 = dialerPhoneNumberUtil.formatToE164(dialerPhoneNumber);
- if (e164.isPresent()) {
- String validE164 = e164.get();
- Set<DialerPhoneNumber> currentNumbers = e164NumbersToDialerPhoneNumbers.get(validE164);
- if (currentNumbers == null) {
- currentNumbers = new ArraySet<>();
- e164NumbersToDialerPhoneNumbers.put(validE164, currentNumbers);
- }
- currentNumbers.add(dialerPhoneNumber);
- } else {
- String unformattableNumber = dialerPhoneNumber.getRawInput().getNumber();
- Set<DialerPhoneNumber> currentNumbers =
- unformattableNumbersToDialerPhoneNumbers.get(unformattableNumber);
- if (currentNumbers == null) {
- currentNumbers = new ArraySet<>();
- unformattableNumbersToDialerPhoneNumbers.put(unformattableNumber, currentNumbers);
- }
- currentNumbers.add(dialerPhoneNumber);
- }
- }
- }
-
- Set<String> unformattableNumbers() {
- return unformattableNumbersToDialerPhoneNumbers.keySet();
- }
-
- Set<String> validE164Numbers() {
- return e164NumbersToDialerPhoneNumbers.keySet();
- }
-
- Set<DialerPhoneNumber> dialerPhoneNumbersForE164(String e164) {
- return e164NumbersToDialerPhoneNumbers.get(e164);
- }
-
- Set<DialerPhoneNumber> dialerPhoneNumbersForUnformattable(String unformattableNumber) {
- return unformattableNumbersToDialerPhoneNumbers.get(unformattableNumber);
- }
- }
}
diff --git a/java/com/android/dialer/phonelookup/phone_lookup_info.proto b/java/com/android/dialer/phonelookup/phone_lookup_info.proto
index c42faf49d..75423b9ee 100644
--- a/java/com/android/dialer/phonelookup/phone_lookup_info.proto
+++ b/java/com/android/dialer/phonelookup/phone_lookup_info.proto
@@ -83,4 +83,26 @@ message PhoneLookupInfo {
optional InfoType info_type = 6;
}
optional PeopleApiInfo people_api_info = 3;
+
+ // Whether a number is blocked or not. Used by both the system blacklist and
+ // dialer fallback
+ enum BlockedState {
+ UNKNOWN = 0;
+ BLOCKED = 1;
+ NOT_BLOCKED = 2;
+ }
+
+ // Message for the android system BlockedNumber lookup. Available starting in
+ // N.
+ message SystemBlockedNumberInfo {
+ optional BlockedState blocked_state = 1;
+ }
+ optional SystemBlockedNumberInfo system_blocked_number_info = 4;
+
+ // Message for the dialer fallback for blocked number. Used in M or when the
+ // migration to the system has not been completed.
+ message DialerBlockedNumberInfo {
+ optional BlockedState blocked_state = 1;
+ }
+ optional DialerBlockedNumberInfo dialer_blocked_number_info = 5;
} \ No newline at end of file
diff --git a/java/com/android/dialer/phonelookup/selector/AndroidManifest.xml b/java/com/android/dialer/phonelookup/selector/AndroidManifest.xml
new file mode 100644
index 000000000..5d836c791
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/selector/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<manifest
+ package="com.android.dialer.phonelookup.selector">
+</manifest>
diff --git a/java/com/android/dialer/phonelookup/PhoneLookupSelector.java b/java/com/android/dialer/phonelookup/selector/PhoneLookupSelector.java
index c933af728..a960d4e96 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookupSelector.java
+++ b/java/com/android/dialer/phonelookup/selector/PhoneLookupSelector.java
@@ -13,13 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package com.android.dialer.phonelookup;
+package com.android.dialer.phonelookup.selector;
+import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.phonelookup.PhoneLookup;
+import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.BlockedState;
import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo;
import com.android.dialer.phonelookup.PhoneLookupInfo.PeopleApiInfo;
import com.android.dialer.phonelookup.PhoneLookupInfo.PeopleApiInfo.InfoType;
+import javax.inject.Inject;
/**
* Prioritizes information from a {@link PhoneLookupInfo}.
@@ -35,12 +42,20 @@ import com.android.dialer.phonelookup.PhoneLookupInfo.PeopleApiInfo.InfoType;
*/
public final class PhoneLookupSelector {
+ private final Context appContext;
+
+ @Inject
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public PhoneLookupSelector(@ApplicationContext Context appContext) {
+ this.appContext = appContext;
+ }
+
/**
* Select the name associated with this number. Examples of this are a local contact's name or a
* business name received from caller ID.
*/
@NonNull
- public static String selectName(PhoneLookupInfo phoneLookupInfo) {
+ public String selectName(PhoneLookupInfo phoneLookupInfo) {
Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
if (firstLocalContact != null) {
String name = firstLocalContact.getName();
@@ -56,7 +71,7 @@ public final class PhoneLookupSelector {
/** Select the photo URI associated with this number. */
@NonNull
- public static String selectPhotoUri(PhoneLookupInfo phoneLookupInfo) {
+ public String selectPhotoUri(PhoneLookupInfo phoneLookupInfo) {
Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
if (firstLocalContact != null) {
String photoUri = firstLocalContact.getPhotoUri();
@@ -68,7 +83,7 @@ public final class PhoneLookupSelector {
}
/** Select the photo ID associated with this number, or 0 if there is none. */
- public static long selectPhotoId(PhoneLookupInfo phoneLookupInfo) {
+ public long selectPhotoId(PhoneLookupInfo phoneLookupInfo) {
Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
if (firstLocalContact != null) {
long photoId = firstLocalContact.getPhotoId();
@@ -81,7 +96,7 @@ public final class PhoneLookupSelector {
/** Select the lookup URI associated with this number. */
@NonNull
- public static String selectLookupUri(PhoneLookupInfo phoneLookupInfo) {
+ public String selectLookupUri(PhoneLookupInfo phoneLookupInfo) {
Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
if (firstLocalContact != null) {
String lookupUri = firstLocalContact.getLookupUri();
@@ -97,7 +112,11 @@ public final class PhoneLookupSelector {
* set by the user.
*/
@NonNull
- public static String selectNumberLabel(PhoneLookupInfo phoneLookupInfo) {
+ public String selectNumberLabel(PhoneLookupInfo phoneLookupInfo) {
+ if (isBlocked(phoneLookupInfo)) {
+ return appContext.getString(R.string.blocked_number_new_call_log_label);
+ }
+
Cp2ContactInfo firstLocalContact = firstLocalContact(phoneLookupInfo);
if (firstLocalContact != null) {
String label = firstLocalContact.getLabel();
@@ -135,10 +154,15 @@ public final class PhoneLookupSelector {
* show a synthesized photo containing photos of both "Mom" and "Dad").
*/
@Nullable
- private static Cp2ContactInfo firstLocalContact(PhoneLookupInfo phoneLookupInfo) {
+ private Cp2ContactInfo firstLocalContact(PhoneLookupInfo phoneLookupInfo) {
if (phoneLookupInfo.getCp2Info().getCp2ContactInfoCount() > 0) {
return phoneLookupInfo.getCp2Info().getCp2ContactInfo(0);
}
return null;
}
+
+ private static boolean isBlocked(PhoneLookupInfo info) {
+ return info.hasDialerBlockedNumberInfo()
+ && info.getDialerBlockedNumberInfo().getBlockedState().equals(BlockedState.BLOCKED);
+ }
}
diff --git a/java/com/android/dialer/phonelookup/selector/res/values/strings.xml b/java/com/android/dialer/phonelookup/selector/res/values/strings.xml
new file mode 100644
index 000000000..2080b3975
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/selector/res/values/strings.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Label under the name of a blocked number in the call log. [CHAR LIMIT=15] -->
+ <string name="blocked_number_new_call_log_label">Blocked</string>
+
+</resources>
diff --git a/java/com/android/dialer/phonenumberproto/PartitionedNumbers.java b/java/com/android/dialer/phonenumberproto/PartitionedNumbers.java
new file mode 100644
index 000000000..372f21ee8
--- /dev/null
+++ b/java/com/android/dialer/phonenumberproto/PartitionedNumbers.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.phonenumberproto;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.support.v4.util.ArrayMap;
+import android.support.v4.util.ArraySet;
+import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.common.Assert;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Divides a set of {@link DialerPhoneNumber DialerPhoneNumbers} by those that can be formatted to
+ * E164 and those that cannot.
+ */
+public final class PartitionedNumbers {
+ private final ImmutableMap<String, ImmutableSet<DialerPhoneNumber>>
+ e164NumbersToDialerPhoneNumbers;
+ private final ImmutableMap<String, ImmutableSet<DialerPhoneNumber>>
+ unformattableNumbersToDialerPhoneNumbers;
+
+ @WorkerThread
+ public PartitionedNumbers(@NonNull ImmutableSet<DialerPhoneNumber> dialerPhoneNumbers) {
+ Assert.isWorkerThread();
+ DialerPhoneNumberUtil dialerPhoneNumberUtil =
+ new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
+ Map<String, Set<DialerPhoneNumber>> e164MapBuilder = new ArrayMap<>();
+ Map<String, Set<DialerPhoneNumber>> unformattableMapBuilder = new ArrayMap<>();
+
+ for (DialerPhoneNumber dialerPhoneNumber : dialerPhoneNumbers) {
+ Optional<String> e164 = dialerPhoneNumberUtil.formatToE164(dialerPhoneNumber);
+ if (e164.isPresent()) {
+ String validE164 = e164.get();
+ Set<DialerPhoneNumber> currentNumbers = e164MapBuilder.get(validE164);
+ if (currentNumbers == null) {
+ currentNumbers = new ArraySet<>();
+ e164MapBuilder.put(validE164, currentNumbers);
+ }
+ currentNumbers.add(dialerPhoneNumber);
+ } else {
+ String unformattableNumber = dialerPhoneNumber.getRawInput().getNumber();
+ Set<DialerPhoneNumber> currentNumbers = unformattableMapBuilder.get(unformattableNumber);
+ if (currentNumbers == null) {
+ currentNumbers = new ArraySet<>();
+ unformattableMapBuilder.put(unformattableNumber, currentNumbers);
+ }
+ currentNumbers.add(dialerPhoneNumber);
+ }
+ }
+
+ e164NumbersToDialerPhoneNumbers = makeImmutable(e164MapBuilder);
+ unformattableNumbersToDialerPhoneNumbers = makeImmutable(unformattableMapBuilder);
+ }
+
+ /** Returns the set of formatted number from the original DialerPhoneNumbers */
+ @NonNull
+ public ImmutableSet<String> unformattableNumbers() {
+ return unformattableNumbersToDialerPhoneNumbers.keySet();
+ }
+
+ /** Returns the set of raw number that is unformattable from the original DialerPhoneNumbers */
+ @NonNull
+ public ImmutableSet<String> validE164Numbers() {
+ return e164NumbersToDialerPhoneNumbers.keySet();
+ }
+
+ /**
+ * Returns the corresponding set of original DialerPhoneNumber that maps to the e.164 number, or
+ * an empty set if the number is not found.
+ */
+ @NonNull
+ public ImmutableSet<DialerPhoneNumber> dialerPhoneNumbersForE164(String e164) {
+ return Assert.isNotNull(e164NumbersToDialerPhoneNumbers.get(e164));
+ }
+
+ /**
+ * Returns the corresponding set of original DialerPhoneNumber that maps to the unformattable
+ * number returned by {@link #unformattableNumbers()}, or an empty set if the number is not found.
+ */
+ @NonNull
+ public ImmutableSet<DialerPhoneNumber> dialerPhoneNumbersForUnformattable(
+ String unformattableNumber) {
+ return Assert.isNotNull(unformattableNumbersToDialerPhoneNumbers.get(unformattableNumber));
+ }
+
+ private static <K, V> ImmutableMap<K, ImmutableSet<V>> makeImmutable(
+ Map<K, Set<V>> mutableMapOfSet) {
+ ImmutableMap.Builder<K, ImmutableSet<V>> mapBuilder = ImmutableMap.builder();
+ for (Map.Entry<K, Set<V>> entry : mutableMapOfSet.entrySet()) {
+ mapBuilder.put(entry.getKey(), ImmutableSet.copyOf(entry.getValue()));
+ }
+ return mapBuilder.build();
+ }
+}
diff --git a/java/com/android/dialer/telecom/TelecomCallUtil.java b/java/com/android/dialer/telecom/TelecomCallUtil.java
index b877a7392..7d71b4b90 100644
--- a/java/com/android/dialer/telecom/TelecomCallUtil.java
+++ b/java/com/android/dialer/telecom/TelecomCallUtil.java
@@ -103,20 +103,27 @@ public class TelecomCallUtil {
@WorkerThread
public static Optional<String> getE164Number(Context appContext, Call call) {
Assert.isWorkerThread();
- PhoneAccountHandle phoneAccountHandle = call.getDetails().getAccountHandle();
- Optional<SubscriptionInfo> subscriptionInfo =
- TelecomUtil.getSubscriptionInfo(appContext, phoneAccountHandle);
String rawNumber = getNumber(call);
if (TextUtils.isEmpty(rawNumber)) {
return Optional.absent();
}
- String countryCode =
- subscriptionInfo.isPresent() ? subscriptionInfo.get().getCountryIso() : null;
- if (countryCode == null) {
+ Optional<String> countryCode = getCountryCode(appContext, call);
+ if (!countryCode.isPresent()) {
LogUtil.w("TelecomCallUtil.getE164Number", "couldn't find a country code for call");
return Optional.absent();
}
- return Optional.fromNullable(
- PhoneNumberUtils.formatNumberToE164(rawNumber, countryCode.toUpperCase(Locale.US)));
+ return Optional.fromNullable(PhoneNumberUtils.formatNumberToE164(rawNumber, countryCode.get()));
+ }
+
+ @WorkerThread
+ public static Optional<String> getCountryCode(Context appContext, Call call) {
+ Assert.isWorkerThread();
+ PhoneAccountHandle phoneAccountHandle = call.getDetails().getAccountHandle();
+ Optional<SubscriptionInfo> subscriptionInfo =
+ TelecomUtil.getSubscriptionInfo(appContext, phoneAccountHandle);
+ if (subscriptionInfo.isPresent() && subscriptionInfo.get().getCountryIso() != null) {
+ return Optional.of(subscriptionInfo.get().getCountryIso().toUpperCase(Locale.US));
+ }
+ return Optional.absent();
}
}
diff --git a/packages.mk b/packages.mk
index a1cf7fc93..03268fd57 100644
--- a/packages.mk
+++ b/packages.mk
@@ -39,6 +39,7 @@ LOCAL_AAPT_FLAGS := \
com.android.dialer.notification \
com.android.dialer.oem \
com.android.dialer.phonelookup.database \
+ com.android.dialer.phonelookup.selector \
com.android.dialer.phonenumberutil \
com.android.dialer.postcall \
com.android.dialer.precall.impl \