Home | History | Annotate | Download | only in calllog
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.dialer.calllog;
     18 
     19 import android.annotation.TargetApi;
     20 import android.content.Context;
     21 import android.content.SharedPreferences;
     22 import android.database.sqlite.SQLiteDatabase;
     23 import android.os.Build;
     24 import android.preference.PreferenceManager;
     25 import android.support.annotation.WorkerThread;
     26 import com.android.dialer.calllog.database.AnnotatedCallLog;
     27 import com.android.dialer.calllog.database.CallLogMutations;
     28 import com.android.dialer.calllog.datasources.CallLogDataSource;
     29 import com.android.dialer.common.Assert;
     30 import com.android.dialer.common.LogUtil;
     31 import com.android.dialer.common.concurrent.DialerExecutor.Worker;
     32 import javax.inject.Inject;
     33 
     34 /**
     35  * Worker which brings the annotated call log up to date, if necessary.
     36  *
     37  * <p>Accepts a boolean which indicates if the dirty check should be skipped, and returns true if
     38  * the annotated call log was updated.
     39  */
     40 public class RefreshAnnotatedCallLogWorker implements Worker<Boolean, Boolean> {
     41 
     42   private final Context appContext;
     43   private final DataSources dataSources;
     44 
     45   @Inject
     46   public RefreshAnnotatedCallLogWorker(Context appContext, DataSources dataSources) {
     47     this.appContext = appContext;
     48     this.dataSources = dataSources;
     49   }
     50 
     51   @Override
     52   public Boolean doInBackground(Boolean skipDirtyCheck) {
     53     LogUtil.enterBlock("RefreshAnnotatedCallLogWorker.doInBackgroundFallible");
     54 
     55     long startTime = System.currentTimeMillis();
     56     boolean annotatedCallLogUpdated = checkDirtyAndRebuildIfNecessary(appContext, skipDirtyCheck);
     57     LogUtil.i(
     58         "RefreshAnnotatedCallLogWorker.doInBackgroundFallible",
     59         "updated? %s, took %dms",
     60         annotatedCallLogUpdated,
     61         System.currentTimeMillis() - startTime);
     62     return annotatedCallLogUpdated;
     63   }
     64 
     65   @WorkerThread
     66   private boolean checkDirtyAndRebuildIfNecessary(Context appContext, boolean skipDirtyCheck) {
     67     Assert.isWorkerThread();
     68 
     69     long startTime = System.currentTimeMillis();
     70 
     71     SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
     72     long lastRebuildTimeMillis =
     73         sharedPreferences.getLong(CallLogFramework.PREF_LAST_REBUILD_TIMESTAMP_MILLIS, 0);
     74     if (lastRebuildTimeMillis == 0) {
     75       LogUtil.i(
     76           "RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
     77           "annotated call log has never been built, marking it dirty");
     78     }
     79     boolean forceRebuildPrefValue =
     80         sharedPreferences.getBoolean(CallLogFramework.PREF_FORCE_REBUILD, false);
     81     if (forceRebuildPrefValue) {
     82       LogUtil.i(
     83           "RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
     84           "call log has been marked dirty");
     85     }
     86 
     87     boolean isDirty =
     88         lastRebuildTimeMillis == 0
     89             || skipDirtyCheck
     90             || forceRebuildPrefValue
     91             || isDirty(appContext);
     92     LogUtil.i(
     93         "RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
     94         "isDirty took: %dms",
     95         System.currentTimeMillis() - startTime);
     96     if (isDirty) {
     97       startTime = System.currentTimeMillis();
     98       rebuild(appContext, lastRebuildTimeMillis);
     99       LogUtil.i(
    100           "RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
    101           "rebuild took: %dms",
    102           System.currentTimeMillis() - startTime);
    103       return true; // Annotated call log was updated.
    104     }
    105     return false; // Annotated call log was not updated.
    106   }
    107 
    108   @WorkerThread
    109   private boolean isDirty(Context appContext) {
    110     Assert.isWorkerThread();
    111 
    112     for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
    113       String dataSourceName = getName(dataSource);
    114       long startTime = System.currentTimeMillis();
    115       LogUtil.i("RefreshAnnotatedCallLogWorker.isDirty", "running isDirty for %s", dataSourceName);
    116       boolean isDirty = dataSource.isDirty(appContext);
    117       LogUtil.i(
    118           "RefreshAnnotatedCallLogWorker.isDirty",
    119           "%s.isDirty returned %b in %dms",
    120           dataSourceName,
    121           isDirty,
    122           System.currentTimeMillis() - startTime);
    123       if (isDirty) {
    124         return true;
    125       }
    126     }
    127     return false;
    128   }
    129 
    130   @TargetApi(Build.VERSION_CODES.M) // Uses try-with-resources
    131   @WorkerThread
    132   private void rebuild(Context appContext, long lastRebuildTimeMillis) {
    133     Assert.isWorkerThread();
    134 
    135     // TODO: Start a transaction?
    136     try (SQLiteDatabase database = AnnotatedCallLog.getWritableDatabase(appContext)) {
    137 
    138       CallLogMutations mutations = new CallLogMutations();
    139 
    140       // System call log data source must go first!
    141       CallLogDataSource systemCallLogDataSource = dataSources.getSystemCallLogDataSource();
    142       String dataSourceName = getName(systemCallLogDataSource);
    143       LogUtil.i("RefreshAnnotatedCallLogWorker.rebuild", "filling %s", dataSourceName);
    144       long startTime = System.currentTimeMillis();
    145       systemCallLogDataSource.fill(appContext, database, lastRebuildTimeMillis, mutations);
    146       LogUtil.i(
    147           "RefreshAnnotatedCallLogWorker.rebuild",
    148           "%s.fill took: %dms",
    149           dataSourceName,
    150           System.currentTimeMillis() - startTime);
    151 
    152       for (CallLogDataSource dataSource : dataSources.getDataSourcesExcludingSystemCallLog()) {
    153         dataSourceName = getName(dataSource);
    154         LogUtil.i("RefreshAnnotatedCallLogWorker.rebuild", "filling %s", dataSourceName);
    155         startTime = System.currentTimeMillis();
    156         dataSource.fill(appContext, database, lastRebuildTimeMillis, mutations);
    157         LogUtil.i(
    158             "CallLogFramework.rebuild",
    159             "%s.fill took: %dms",
    160             dataSourceName,
    161             System.currentTimeMillis() - startTime);
    162       }
    163       LogUtil.i("RefreshAnnotatedCallLogWorker.rebuild", "applying mutations to database");
    164       startTime = System.currentTimeMillis();
    165       mutations.applyToDatabase(database);
    166       LogUtil.i(
    167           "RefreshAnnotatedCallLogWorker.rebuild",
    168           "applyToDatabase took: %dms",
    169           System.currentTimeMillis() - startTime);
    170     }
    171 
    172     SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext);
    173     sharedPreferences
    174         .edit()
    175         .putBoolean(CallLogFramework.PREF_FORCE_REBUILD, false)
    176         .putLong(CallLogFramework.PREF_LAST_REBUILD_TIMESTAMP_MILLIS, System.currentTimeMillis())
    177         .commit();
    178   }
    179 
    180   private static String getName(CallLogDataSource dataSource) {
    181     return dataSource.getClass().getSimpleName();
    182   }
    183 }
    184