Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3002ad66 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Track initial call log building metrics separately from incremental building metrics."

parents 7efc1f03 0d970153
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -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))) {
+67 −0
Original line number Diff line number Diff line
/*
 * 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));
  }
}
+51 −14
Original line number Diff line number Diff line
@@ -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;
  }
}
+13 −8
Original line number Diff line number Diff line
@@ -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>
+6 −5
Original line number Diff line number Diff line
@@ -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 -> {
Loading