summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authorzachh <zachh@google.com>2018-02-23 18:24:16 -0800
committerCopybara-Service <copybara-piper@google.com>2018-02-23 18:25:57 -0800
commit0d9701531f3e1d68b13e460e8cf6197ad8f6b619 (patch)
treecbdc437062f5d60fd191d57f23dfe32995887d1b /java
parent1c1e4c7caa0edd1f8f9aa951c6468c1d653ee281 (diff)
Track initial call log building metrics separately from incremental building metrics.
This required creating "CallLogState" which is currently just a boolean value which can only be turned on once (when the annotated call log flow finishes for the first time). This CL also changes CompositePhoneLookup to no longer implement PhoneLookup. This was done to support a now reverted implementation of CallLogState but it's easier for me to keep the change and it shouldn't be harmful. Bug: 70989667 Test: unit PiperOrigin-RevId: 186852257 Change-Id: I3f342737aaf909f8230b8a69d9c21e6e5c19b84e
Diffstat (limited to 'java')
-rw-r--r--java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java9
-rw-r--r--java/com/android/dialer/calllog/CallLogState.java67
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java65
-rw-r--r--java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java21
-rw-r--r--java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java11
-rw-r--r--java/com/android/dialer/metrics/Metrics.java9
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookupComponent.java3
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookupModule.java7
-rw-r--r--java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java161
-rw-r--r--java/com/android/incallui/PhoneLookupHistoryRecorder.java4
10 files changed, 246 insertions, 111 deletions
diff --git a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java
index f8c6fcef1..e2e112cd0 100644
--- a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java
+++ b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java
@@ -18,6 +18,7 @@ package com.android.dialer.calllog;
import android.content.Context;
import android.content.SharedPreferences;
+import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
import com.android.dialer.configprovider.ConfigProviderBindings;
import com.android.dialer.inject.ApplicationContext;
@@ -39,7 +40,7 @@ public final class AnnotatedCallLogMigrator {
private final Context appContext;
private final SharedPreferences sharedPreferences;
private final RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
- private final ListeningExecutorService backgorundExecutor;
+ private final ListeningExecutorService backgroundExecutor;
@Inject
AnnotatedCallLogMigrator(
@@ -49,7 +50,7 @@ public final class AnnotatedCallLogMigrator {
RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker) {
this.appContext = appContext;
this.sharedPreferences = sharedPreferences;
- this.backgorundExecutor = backgroundExecutor;
+ this.backgroundExecutor = backgroundExecutor;
this.refreshAnnotatedCallLogWorker = refreshAnnotatedCallLogWorker;
}
@@ -58,13 +59,13 @@ public final class AnnotatedCallLogMigrator {
* the latency the first time call log is shown.
*/
public ListenableFuture<Void> migrate() {
-
return Futures.transformAsync(
shouldMigrate(),
(shouldMigrate) -> {
if (!shouldMigrate) {
return Futures.immediateFuture(null);
}
+ LogUtil.i("AnnotatedCallLogMigrator.migrate", "migrating annotated call log");
return Futures.transform(
refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck(),
(unused) -> {
@@ -77,7 +78,7 @@ public final class AnnotatedCallLogMigrator {
}
private ListenableFuture<Boolean> shouldMigrate() {
- return backgorundExecutor.submit(
+ return backgroundExecutor.submit(
() -> {
if (!(ConfigProviderBindings.get(appContext)
.getBoolean("is_nui_shortcut_enabled", false))) {
diff --git a/java/com/android/dialer/calllog/CallLogState.java b/java/com/android/dialer/calllog/CallLogState.java
new file mode 100644
index 000000000..bdb30a769
--- /dev/null
+++ b/java/com/android/dialer/calllog/CallLogState.java
@@ -0,0 +1,67 @@
+/*
+ * 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.calllog;
+
+import android.content.SharedPreferences;
+import android.support.annotation.AnyThread;
+import android.support.annotation.VisibleForTesting;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.storage.Unencrypted;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import javax.annotation.concurrent.ThreadSafe;
+import javax.inject.Inject;
+
+/** Provides information about the state of the annotated call log. */
+@ThreadSafe
+public final class CallLogState {
+
+ private static final String ANNOTATED_CALL_LOG_BUILT_PREF = "annotated_call_log_built";
+
+ private final SharedPreferences sharedPreferences;
+ private final ListeningExecutorService backgroundExecutor;
+
+ @VisibleForTesting
+ @Inject
+ public CallLogState(
+ @Unencrypted SharedPreferences sharedPreferences,
+ @BackgroundExecutor ListeningExecutorService backgroundExecutor) {
+ this.sharedPreferences = sharedPreferences;
+ this.backgroundExecutor = backgroundExecutor;
+ }
+
+ /**
+ * Mark the call log as having been built. This is written to disk the first time the annotated
+ * call log has been built and shouldn't ever be reset unless the user clears data.
+ */
+ @AnyThread
+ public void markBuilt() {
+ sharedPreferences.edit().putBoolean(ANNOTATED_CALL_LOG_BUILT_PREF, true).apply();
+ }
+
+ /**
+ * Returns true if the annotated call log has been built at least once.
+ *
+ * <p>It may not yet have been built if the user was just upgraded to the new call log, or they
+ * just cleared data.
+ */
+ @AnyThread
+ public ListenableFuture<Boolean> isBuilt() {
+ return backgroundExecutor.submit(
+ () -> sharedPreferences.getBoolean(ANNOTATED_CALL_LOG_BUILT_PREF, false));
+ }
+}
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
index 3765831ae..e05b77268 100644
--- a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
@@ -37,6 +37,7 @@ import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
@@ -51,6 +52,7 @@ public class RefreshAnnotatedCallLogWorker {
private final SharedPreferences sharedPreferences;
private final MutationApplier mutationApplier;
private final FutureTimer futureTimer;
+ private final CallLogState callLogState;
private final ListeningExecutorService backgroundExecutorService;
private final ListeningExecutorService lightweightExecutorService;
// Used to ensure that only one refresh flow runs at a time. (Note that
@@ -64,6 +66,7 @@ public class RefreshAnnotatedCallLogWorker {
@Unencrypted SharedPreferences sharedPreferences,
MutationApplier mutationApplier,
FutureTimer futureTimer,
+ CallLogState callLogState,
@BackgroundExecutor ListeningExecutorService backgroundExecutorService,
@LightweightExecutor ListeningExecutorService lightweightExecutorService) {
this.appContext = appContext;
@@ -71,17 +74,18 @@ public class RefreshAnnotatedCallLogWorker {
this.sharedPreferences = sharedPreferences;
this.mutationApplier = mutationApplier;
this.futureTimer = futureTimer;
+ this.callLogState = callLogState;
this.backgroundExecutorService = backgroundExecutorService;
this.lightweightExecutorService = lightweightExecutorService;
}
/** Checks if the annotated call log is dirty and refreshes it if necessary. */
- public ListenableFuture<Void> refreshWithDirtyCheck() {
+ ListenableFuture<Void> refreshWithDirtyCheck() {
return refresh(true);
}
/** Refreshes the annotated call log, bypassing dirty checks. */
- public ListenableFuture<Void> refreshWithoutDirtyCheck() {
+ ListenableFuture<Void> refreshWithoutDirtyCheck() {
return refresh(false);
}
@@ -131,7 +135,11 @@ public class RefreshAnnotatedCallLogWorker {
"RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
"isDirty: %b",
Preconditions.checkNotNull(isDirty));
- return isDirty ? rebuild() : Futures.immediateFuture(null);
+ if (isDirty) {
+ return Futures.transformAsync(
+ callLogState.isBuilt(), this::rebuild, MoreExecutors.directExecutor());
+ }
+ return Futures.immediateFuture(null);
},
lightweightExecutorService);
}
@@ -152,14 +160,13 @@ public class RefreshAnnotatedCallLogWorker {
return isDirtyFuture;
}
- private ListenableFuture<Void> rebuild() {
+ private ListenableFuture<Void> rebuild(boolean isBuilt) {
CallLogMutations mutations = new CallLogMutations();
// Start by filling the data sources--the system call log data source must go first!
CallLogDataSource systemCallLogDataSource = dataSources.getSystemCallLogDataSource();
ListenableFuture<Void> fillFuture = systemCallLogDataSource.fill(appContext, mutations);
- String systemEventName =
- String.format(Metrics.FILL_TEMPLATE, systemCallLogDataSource.getClass().getSimpleName());
+ String systemEventName = eventNameForFill(systemCallLogDataSource, isBuilt);
futureTimer.applyTiming(fillFuture, systemEventName);
// After the system call log data source is filled, call fill sequentially on each remaining
@@ -171,14 +178,14 @@ public class RefreshAnnotatedCallLogWorker {
fillFuture,
unused -> {
ListenableFuture<Void> dataSourceFuture = dataSource.fill(appContext, mutations);
- String eventName =
- String.format(Metrics.FILL_TEMPLATE, dataSource.getClass().getSimpleName());
+ String eventName = eventNameForFill(dataSource, isBuilt);
futureTimer.applyTiming(dataSourceFuture, eventName);
return dataSourceFuture;
},
lightweightExecutorService);
}
- futureTimer.applyTiming(fillFuture, Metrics.FILL_EVENT_NAME);
+
+ futureTimer.applyTiming(fillFuture, eventNameForOverallFill(isBuilt));
// After all data sources are filled, apply mutations (at this point "fillFuture" is the result
// of filling the last data source).
@@ -188,7 +195,7 @@ public class RefreshAnnotatedCallLogWorker {
unused -> {
ListenableFuture<Void> mutationApplierFuture =
mutationApplier.applyToDatabase(mutations, appContext);
- futureTimer.applyTiming(mutationApplierFuture, Metrics.APPLY_MUTATIONS_EVENT_NAME);
+ futureTimer.applyTiming(mutationApplierFuture, eventNameForApplyMutations(isBuilt));
return mutationApplierFuture;
},
lightweightExecutorService);
@@ -203,13 +210,11 @@ public class RefreshAnnotatedCallLogWorker {
dataSources.getDataSourcesIncludingSystemCallLog()) {
ListenableFuture<Void> dataSourceFuture = dataSource.onSuccessfulFill(appContext);
onSuccessfulFillFutures.add(dataSourceFuture);
- String eventName =
- String.format(
- Metrics.ON_SUCCESSFUL_FILL_TEMPLATE, dataSource.getClass().getSimpleName());
+ String eventName = eventNameForOnSuccessfulFill(dataSource, isBuilt);
futureTimer.applyTiming(dataSourceFuture, eventName);
}
ListenableFuture<List<Void>> allFutures = Futures.allAsList(onSuccessfulFillFutures);
- futureTimer.applyTiming(allFutures, Metrics.ON_SUCCESSFUL_FILL_EVENT_NAME);
+ futureTimer.applyTiming(allFutures, eventNameForOverallOnSuccessfulFill(isBuilt));
return allFutures;
},
lightweightExecutorService);
@@ -219,8 +224,40 @@ public class RefreshAnnotatedCallLogWorker {
onSuccessfulFillFuture,
unused -> {
sharedPreferences.edit().putBoolean(SharedPrefKeys.FORCE_REBUILD, false).apply();
+ callLogState.markBuilt();
return null;
},
backgroundExecutorService);
}
+
+ private static String eventNameForFill(CallLogDataSource dataSource, boolean isBuilt) {
+ return String.format(
+ !isBuilt ? Metrics.INITIAL_FILL_TEMPLATE : Metrics.FILL_TEMPLATE,
+ dataSource.getClass().getSimpleName());
+ }
+
+ private static String eventNameForOverallFill(boolean isBuilt) {
+ return !isBuilt ? Metrics.INITIAL_FILL_EVENT_NAME : Metrics.FILL_EVENT_NAME;
+ }
+
+ private static String eventNameForOnSuccessfulFill(
+ CallLogDataSource dataSource, boolean isBuilt) {
+ return String.format(
+ !isBuilt
+ ? Metrics.INITIAL_ON_SUCCESSFUL_FILL_TEMPLATE
+ : Metrics.ON_SUCCESSFUL_FILL_TEMPLATE,
+ dataSource.getClass().getSimpleName());
+ }
+
+ private static String eventNameForOverallOnSuccessfulFill(boolean isBuilt) {
+ return !isBuilt
+ ? Metrics.INITIAL_ON_SUCCESSFUL_FILL_EVENT_NAME
+ : Metrics.ON_SUCCESSFUL_FILL_EVENT_NAME;
+ }
+
+ private static String eventNameForApplyMutations(boolean isBuilt) {
+ return !isBuilt
+ ? Metrics.INITIAL_APPLY_MUTATIONS_EVENT_NAME
+ : Metrics.APPLY_MUTATIONS_EVENT_NAME;
+ }
}
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 40788f42a..ff8c931ad 100644
--- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -39,6 +39,7 @@ 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.composite.CompositePhoneLookup;
import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract;
import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
import com.google.common.collect.ImmutableMap;
@@ -63,7 +64,7 @@ import javax.inject.Inject;
*/
public final class PhoneLookupDataSource implements CallLogDataSource {
- private final PhoneLookup<PhoneLookupInfo> phoneLookup;
+ private final CompositePhoneLookup compositePhoneLookup;
private final ListeningExecutorService backgroundExecutorService;
private final ListeningExecutorService lightweightExecutorService;
@@ -86,10 +87,10 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
@Inject
PhoneLookupDataSource(
- PhoneLookup<PhoneLookupInfo> phoneLookup,
+ CompositePhoneLookup compositePhoneLookup,
@BackgroundExecutor ListeningExecutorService backgroundExecutorService,
@LightweightExecutor ListeningExecutorService lightweightExecutorService) {
- this.phoneLookup = phoneLookup;
+ this.compositePhoneLookup = compositePhoneLookup;
this.backgroundExecutorService = backgroundExecutorService;
this.lightweightExecutorService = lightweightExecutorService;
}
@@ -99,7 +100,8 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
ListenableFuture<ImmutableSet<DialerPhoneNumber>> phoneNumbers =
backgroundExecutorService.submit(
() -> queryDistinctDialerPhoneNumbersFromAnnotatedCallLog(appContext));
- return Futures.transformAsync(phoneNumbers, phoneLookup::isDirty, lightweightExecutorService);
+ return Futures.transformAsync(
+ phoneNumbers, compositePhoneLookup::isDirty, lightweightExecutorService);
}
/**
@@ -157,10 +159,13 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
queryPhoneLookupHistoryForNumbers(appContext, annotatedCallLogIdsByNumber.keySet()),
backgroundExecutorService);
- // Use the original info map to generate the updated info map by delegating to phoneLookup.
+ // Use the original info map to generate the updated info map by delegating to
+ // compositePhoneLookup.
ListenableFuture<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> updatedInfoMapFuture =
Futures.transformAsync(
- originalInfoMapFuture, phoneLookup::getMostRecentInfo, lightweightExecutorService);
+ originalInfoMapFuture,
+ compositePhoneLookup::getMostRecentInfo,
+ lightweightExecutorService);
// This is the computation that will use the result of all of the above.
Callable<ImmutableMap<Long, PhoneLookupInfo>> computeRowsToUpdate =
@@ -241,7 +246,7 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
// the AnnotatedCallLog and PhoneLookupHistory have been successfully updated.
return Futures.transformAsync(
writePhoneLookupHistory,
- unused -> phoneLookup.onSuccessfulBulkUpdate(),
+ unused -> compositePhoneLookup.onSuccessfulBulkUpdate(),
lightweightExecutorService);
}
@@ -286,7 +291,7 @@ public final class PhoneLookupDataSource implements CallLogDataSource {
@MainThread
@Override
public void registerContentObservers(Context appContext) {
- phoneLookup.registerContentObservers(appContext);
+ compositePhoneLookup.registerContentObservers(appContext);
}
private static ImmutableSet<DialerPhoneNumber>
diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
index 69c431953..22c3a3c11 100644
--- a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
+++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
@@ -31,8 +31,8 @@ import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
import com.android.dialer.common.concurrent.Annotations.Ui;
import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.inject.ApplicationContext;
-import com.android.dialer.phonelookup.PhoneLookup;
import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.android.dialer.phonelookup.composite.CompositePhoneLookup;
import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract;
import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
import com.google.common.collect.ImmutableMap;
@@ -68,7 +68,7 @@ public final class RealtimeRowProcessor {
@VisibleForTesting static final long BATCH_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3);
private final Context appContext;
- private final PhoneLookup<PhoneLookupInfo> phoneLookup;
+ private final CompositePhoneLookup compositePhoneLookup;
private final ListeningExecutorService uiExecutor;
private final ListeningExecutorService backgroundExecutor;
@@ -83,11 +83,11 @@ public final class RealtimeRowProcessor {
@ApplicationContext Context appContext,
@Ui ListeningExecutorService uiExecutor,
@BackgroundExecutor ListeningExecutorService backgroundExecutor,
- PhoneLookup<PhoneLookupInfo> phoneLookup) {
+ CompositePhoneLookup compositePhoneLookup) {
this.appContext = appContext;
this.uiExecutor = uiExecutor;
this.backgroundExecutor = backgroundExecutor;
- this.phoneLookup = phoneLookup;
+ this.compositePhoneLookup = compositePhoneLookup;
}
/**
@@ -106,7 +106,8 @@ public final class RealtimeRowProcessor {
return Futures.immediateFuture(applyPhoneLookupInfoToRow(cachedPhoneLookupInfo, row));
}
- ListenableFuture<PhoneLookupInfo> phoneLookupInfoFuture = phoneLookup.lookup(row.number());
+ ListenableFuture<PhoneLookupInfo> phoneLookupInfoFuture =
+ compositePhoneLookup.lookup(row.number());
return Futures.transform(
phoneLookupInfoFuture,
phoneLookupInfo -> {
diff --git a/java/com/android/dialer/metrics/Metrics.java b/java/com/android/dialer/metrics/Metrics.java
index 8c18ac942..383b3a3f3 100644
--- a/java/com/android/dialer/metrics/Metrics.java
+++ b/java/com/android/dialer/metrics/Metrics.java
@@ -34,12 +34,21 @@ public interface Metrics {
String NEW_CALL_LOG_JANK_EVENT_NAME = "NewCallLog.Jank";
// Events related to refreshing the annotated call log.
+ String INITIAL_FILL_EVENT_NAME = "RefreshAnnotatedCallLog.Initial.Fill";
+ String INITIAL_ON_SUCCESSFUL_FILL_EVENT_NAME = "RefreshAnnotatedCallLog.Initial.OnSuccessfulFill";
+ String INITIAL_APPLY_MUTATIONS_EVENT_NAME = "RefreshAnnotatedCallLog.Initial.ApplyMutations";
+
String IS_DIRTY_EVENT_NAME = "RefreshAnnotatedCallLog.IsDirty";
String FILL_EVENT_NAME = "RefreshAnnotatedCallLog.Fill";
String ON_SUCCESSFUL_FILL_EVENT_NAME = "RefreshAnnotatedCallLog.OnSuccessfulFill";
String APPLY_MUTATIONS_EVENT_NAME = "RefreshAnnotatedCallLog.ApplyMutations";
// These templates are prefixed with a CallLogDataSource or PhoneLookup simple class name.
+ String INITIAL_FILL_TEMPLATE = "%s.Initial.Fill";
+ String INITIAL_GET_MOST_RECENT_INFO_TEMPLATE = "%s.Initial.GetMostRecentInfo";
+ String INITIAL_ON_SUCCESSFUL_FILL_TEMPLATE = "%s.Initial.OnSuccessfulFill";
+ String INITIAL_ON_SUCCESSFUL_BULK_UPDATE_TEMPLATE = "%s.Initial.OnSuccessfulBulkUpdate";
+
String IS_DIRTY_TEMPLATE = "%s.IsDirty";
String FILL_TEMPLATE = "%s.Fill";
String GET_MOST_RECENT_INFO_TEMPLATE = "%s.GetMostRecentInfo";
diff --git a/java/com/android/dialer/phonelookup/PhoneLookupComponent.java b/java/com/android/dialer/phonelookup/PhoneLookupComponent.java
index f59886bcd..832587c81 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookupComponent.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookupComponent.java
@@ -17,13 +17,14 @@ package com.android.dialer.phonelookup;
import android.content.Context;
import com.android.dialer.inject.HasRootComponent;
+import com.android.dialer.phonelookup.composite.CompositePhoneLookup;
import dagger.Subcomponent;
/** Dagger component for the PhoneLookup package. */
@Subcomponent
public abstract class PhoneLookupComponent {
- public abstract PhoneLookup<PhoneLookupInfo> phoneLookup();
+ public abstract CompositePhoneLookup compositePhoneLookup();
public static PhoneLookupComponent get(Context context) {
return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
diff --git a/java/com/android/dialer/phonelookup/PhoneLookupModule.java b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
index d4cd60a04..3e21e7c77 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookupModule.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
@@ -18,7 +18,6 @@ package com.android.dialer.phonelookup;
import com.android.dialer.phonelookup.blockednumber.DialerBlockedNumberPhoneLookup;
import com.android.dialer.phonelookup.blockednumber.SystemBlockedNumberPhoneLookup;
-import com.android.dialer.phonelookup.composite.CompositePhoneLookup;
import com.android.dialer.phonelookup.cp2.Cp2LocalPhoneLookup;
import com.android.dialer.phonelookup.cp2.Cp2RemotePhoneLookup;
import com.android.dialer.phonelookup.spam.SpamPhoneLookup;
@@ -45,10 +44,4 @@ public abstract class PhoneLookupModule {
systemBlockedNumberPhoneLookup,
spamPhoneLookup);
}
-
- @Provides
- static PhoneLookup<PhoneLookupInfo> providePhoneLookup(
- CompositePhoneLookup compositePhoneLookup) {
- return compositePhoneLookup;
- }
}
diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
index 7be7732e3..0d84a2eda 100644
--- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
@@ -18,7 +18,9 @@ package com.android.dialer.phonelookup.composite;
import android.content.Context;
import android.support.annotation.MainThread;
+import android.support.annotation.VisibleForTesting;
import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.calllog.CallLogState;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
import com.android.dialer.common.concurrent.DialerFutures;
@@ -36,6 +38,7 @@ import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -44,20 +47,26 @@ import javax.inject.Inject;
/**
* {@link PhoneLookup} which delegates to a configured set of {@link PhoneLookup PhoneLookups},
* iterating, prioritizing, and coalescing data as necessary.
+ *
+ * <p>TODO(zachh): Consider renaming and moving this file since it does not implement PhoneLookup.
*/
-public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo> {
+public final class CompositePhoneLookup {
private final ImmutableList<PhoneLookup> phoneLookups;
private final FutureTimer futureTimer;
+ private final CallLogState callLogState;
private final ListeningExecutorService lightweightExecutorService;
+ @VisibleForTesting
@Inject
- CompositePhoneLookup(
+ public CompositePhoneLookup(
ImmutableList<PhoneLookup> phoneLookups,
FutureTimer futureTimer,
+ CallLogState callLogState,
@LightweightExecutor ListeningExecutorService lightweightExecutorService) {
this.phoneLookups = phoneLookups;
this.futureTimer = futureTimer;
+ this.callLogState = callLogState;
this.lightweightExecutorService = lightweightExecutorService;
}
@@ -68,7 +77,6 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
* the dependent lookups does not complete, the returned future will also not complete.
*/
@SuppressWarnings({"unchecked", "rawtype"})
- @Override
public ListenableFuture<PhoneLookupInfo> lookup(DialerPhoneNumber dialerPhoneNumber) {
// TODO(zachh): Add short-circuiting logic so that this call is not blocked on low-priority
// lookups finishing when a higher-priority one has already finished.
@@ -98,7 +106,10 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
return combinedFuture;
}
- @Override
+ /**
+ * Delegates to sub-lookups' {@link PhoneLookup#isDirty(ImmutableSet)} completing when the first
+ * sub-lookup which returns true completes.
+ */
public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
List<ListenableFuture<Boolean>> futures = new ArrayList<>();
for (PhoneLookup<?> phoneLookup : phoneLookups) {
@@ -125,46 +136,50 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
* the dependent lookups does not complete, the returned future will also not complete.
*/
@SuppressWarnings("unchecked")
- @Override
public ListenableFuture<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> getMostRecentInfo(
ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> existingInfoMap) {
- List<ListenableFuture<ImmutableMap<DialerPhoneNumber, ?>>> futures = new ArrayList<>();
- for (PhoneLookup phoneLookup : phoneLookups) {
- futures.add(buildSubmapAndGetMostRecentInfo(existingInfoMap, phoneLookup));
- }
- ListenableFuture<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> combinedFuture =
- Futures.transform(
- Futures.allAsList(futures),
- (allMaps) -> {
- ImmutableMap.Builder<DialerPhoneNumber, PhoneLookupInfo> combinedMap =
- ImmutableMap.builder();
- for (DialerPhoneNumber dialerPhoneNumber : existingInfoMap.keySet()) {
- PhoneLookupInfo.Builder combinedInfo = PhoneLookupInfo.newBuilder();
- for (int i = 0; i < allMaps.size(); i++) {
- ImmutableMap<DialerPhoneNumber, ?> map = allMaps.get(i);
- Object subInfo = map.get(dialerPhoneNumber);
- if (subInfo == null) {
- throw new IllegalStateException(
- "A sublookup didn't return an info for number: "
- + LogUtil.sanitizePhoneNumber(dialerPhoneNumber.getNormalizedNumber()));
- }
- phoneLookups.get(i).setSubMessage(combinedInfo, subInfo);
- }
- combinedMap.put(dialerPhoneNumber, combinedInfo.build());
- }
- return combinedMap.build();
- },
- lightweightExecutorService);
- String eventName =
- String.format(
- Metrics.GET_MOST_RECENT_INFO_TEMPLATE, CompositePhoneLookup.class.getSimpleName());
- futureTimer.applyTiming(combinedFuture, eventName);
- return combinedFuture;
+ return Futures.transformAsync(
+ callLogState.isBuilt(),
+ isBuilt -> {
+ List<ListenableFuture<ImmutableMap<DialerPhoneNumber, ?>>> futures = new ArrayList<>();
+ for (PhoneLookup phoneLookup : phoneLookups) {
+ futures.add(buildSubmapAndGetMostRecentInfo(existingInfoMap, phoneLookup, isBuilt));
+ }
+ ListenableFuture<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> combinedFuture =
+ Futures.transform(
+ Futures.allAsList(futures),
+ (allMaps) -> {
+ ImmutableMap.Builder<DialerPhoneNumber, PhoneLookupInfo> combinedMap =
+ ImmutableMap.builder();
+ for (DialerPhoneNumber dialerPhoneNumber : existingInfoMap.keySet()) {
+ PhoneLookupInfo.Builder combinedInfo = PhoneLookupInfo.newBuilder();
+ for (int i = 0; i < allMaps.size(); i++) {
+ ImmutableMap<DialerPhoneNumber, ?> map = allMaps.get(i);
+ Object subInfo = map.get(dialerPhoneNumber);
+ if (subInfo == null) {
+ throw new IllegalStateException(
+ "A sublookup didn't return an info for number: "
+ + LogUtil.sanitizePhoneNumber(
+ dialerPhoneNumber.getNormalizedNumber()));
+ }
+ phoneLookups.get(i).setSubMessage(combinedInfo, subInfo);
+ }
+ combinedMap.put(dialerPhoneNumber, combinedInfo.build());
+ }
+ return combinedMap.build();
+ },
+ lightweightExecutorService);
+ String eventName = getMostRecentInfoEventName(this, isBuilt);
+ futureTimer.applyTiming(combinedFuture, eventName);
+ return combinedFuture;
+ },
+ MoreExecutors.directExecutor());
}
private <T> ListenableFuture<ImmutableMap<DialerPhoneNumber, T>> buildSubmapAndGetMostRecentInfo(
ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> existingInfoMap,
- PhoneLookup<T> phoneLookup) {
+ PhoneLookup<T> phoneLookup,
+ boolean isBuilt) {
Map<DialerPhoneNumber, T> submap =
Maps.transformEntries(
existingInfoMap,
@@ -172,50 +187,54 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
phoneLookup.getSubMessage(existingInfoMap.get(dialerPhoneNumber)));
ListenableFuture<ImmutableMap<DialerPhoneNumber, T>> mostRecentInfoFuture =
phoneLookup.getMostRecentInfo(ImmutableMap.copyOf(submap));
- String eventName =
- String.format(
- Metrics.GET_MOST_RECENT_INFO_TEMPLATE, phoneLookup.getClass().getSimpleName());
+ String eventName = getMostRecentInfoEventName(phoneLookup, isBuilt);
futureTimer.applyTiming(mostRecentInfoFuture, eventName);
return mostRecentInfoFuture;
}
- @Override
- public void setSubMessage(PhoneLookupInfo.Builder destination, PhoneLookupInfo source) {
- throw new UnsupportedOperationException(
- "This method is only expected to be called by CompositePhoneLookup itself");
- }
-
- @Override
- public PhoneLookupInfo getSubMessage(PhoneLookupInfo phoneLookupInfo) {
- throw new UnsupportedOperationException(
- "This method is only expected to be called by CompositePhoneLookup itself");
- }
-
- @Override
+ /** Delegates to sub-lookups' {@link PhoneLookup#onSuccessfulBulkUpdate()}. */
public ListenableFuture<Void> onSuccessfulBulkUpdate() {
- List<ListenableFuture<Void>> futures = new ArrayList<>();
- for (PhoneLookup<?> phoneLookup : phoneLookups) {
- ListenableFuture<Void> phoneLookupFuture = phoneLookup.onSuccessfulBulkUpdate();
- futures.add(phoneLookupFuture);
- String eventName =
- String.format(
- Metrics.ON_SUCCESSFUL_BULK_UPDATE_TEMPLATE, phoneLookup.getClass().getSimpleName());
- futureTimer.applyTiming(phoneLookupFuture, eventName);
- }
- ListenableFuture<Void> combinedFuture =
- Futures.transform(Futures.allAsList(futures), unused -> null, lightweightExecutorService);
- String eventName =
- String.format(
- Metrics.ON_SUCCESSFUL_BULK_UPDATE_TEMPLATE, CompositePhoneLookup.class.getSimpleName());
- futureTimer.applyTiming(combinedFuture, eventName);
- return combinedFuture;
+ return Futures.transformAsync(
+ callLogState.isBuilt(),
+ isBuilt -> {
+ List<ListenableFuture<Void>> futures = new ArrayList<>();
+ for (PhoneLookup<?> phoneLookup : phoneLookups) {
+ ListenableFuture<Void> phoneLookupFuture = phoneLookup.onSuccessfulBulkUpdate();
+ futures.add(phoneLookupFuture);
+ String eventName = onSuccessfulBulkUpdatedEventName(phoneLookup, isBuilt);
+ futureTimer.applyTiming(phoneLookupFuture, eventName);
+ }
+ ListenableFuture<Void> combinedFuture =
+ Futures.transform(
+ Futures.allAsList(futures), unused -> null, lightweightExecutorService);
+ String eventName = onSuccessfulBulkUpdatedEventName(this, isBuilt);
+ futureTimer.applyTiming(combinedFuture, eventName);
+ return combinedFuture;
+ },
+ MoreExecutors.directExecutor());
}
- @Override
+ /** Delegates to sub-lookups' {@link PhoneLookup#registerContentObservers(Context)}. */
@MainThread
public void registerContentObservers(Context appContext) {
for (PhoneLookup phoneLookup : phoneLookups) {
phoneLookup.registerContentObservers(appContext);
}
}
+
+ private static String getMostRecentInfoEventName(Object classNameSource, boolean isBuilt) {
+ return String.format(
+ !isBuilt
+ ? Metrics.INITIAL_GET_MOST_RECENT_INFO_TEMPLATE
+ : Metrics.GET_MOST_RECENT_INFO_TEMPLATE,
+ classNameSource.getClass().getSimpleName());
+ }
+
+ private static String onSuccessfulBulkUpdatedEventName(Object classNameSource, boolean isBuilt) {
+ return String.format(
+ !isBuilt
+ ? Metrics.INITIAL_ON_SUCCESSFUL_BULK_UPDATE_TEMPLATE
+ : Metrics.ON_SUCCESSFUL_BULK_UPDATE_TEMPLATE,
+ classNameSource.getClass().getSimpleName());
+ }
}
diff --git a/java/com/android/incallui/PhoneLookupHistoryRecorder.java b/java/com/android/incallui/PhoneLookupHistoryRecorder.java
index 017e6f43c..802bf63cb 100644
--- a/java/com/android/incallui/PhoneLookupHistoryRecorder.java
+++ b/java/com/android/incallui/PhoneLookupHistoryRecorder.java
@@ -69,7 +69,9 @@ final class PhoneLookupHistoryRecorder {
Futures.transformAsync(
numberFuture,
dialerPhoneNumber ->
- PhoneLookupComponent.get(appContext).phoneLookup().lookup(dialerPhoneNumber),
+ PhoneLookupComponent.get(appContext)
+ .compositePhoneLookup()
+ .lookup(dialerPhoneNumber),
MoreExecutors.directExecutor());
Futures.addCallback(