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.datasources.systemcalllog;
18 
19 import android.content.Context;
20 import android.database.ContentObserver;
21 import android.database.sqlite.SQLiteDatabase;
22 import android.net.Uri;
23 import android.os.Handler;
24 import android.provider.CallLog;
25 import android.support.annotation.MainThread;
26 import android.support.annotation.WorkerThread;
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.ThreadUtil;
32 import com.android.dialer.util.PermissionsUtil;
33 import javax.inject.Inject;
34 
35 /**
36  * Responsible for defining the rows in the annotated call log and maintaining the columns in it
37  * which are derived from the system call log.
38  */
39 public class SystemCallLogDataSource implements CallLogDataSource {
40 
41   @Inject
SystemCallLogDataSource()42   public SystemCallLogDataSource() {}
43 
44   @MainThread
45   @Override
registerContentObservers( Context appContext, ContentObserverCallbacks contentObserverCallbacks)46   public void registerContentObservers(
47       Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
48     Assert.isMainThread();
49 
50     if (!PermissionsUtil.hasCallLogReadPermissions(appContext)) {
51       LogUtil.i("SystemCallLogDataSource.registerContentObservers", "no call log permissions");
52       return;
53     }
54 
55     appContext
56         .getContentResolver()
57         .registerContentObserver(
58             CallLog.Calls.CONTENT_URI,
59             true,
60             new CallLogObserver(
61                 ThreadUtil.getUiThreadHandler(), appContext, contentObserverCallbacks));
62   }
63 
64   @WorkerThread
65   @Override
isDirty(Context appContext)66   public boolean isDirty(Context appContext) {
67     Assert.isWorkerThread();
68 
69     /*
70      * The system call log has a last updated timestamp, but deletes are physical (the "deleted"
71      * column is unused). This means that we can't detect deletes without scanning the entire table,
72      * which would be too slow. So, we just rely on content observers to trigger rebuilds when any
73      * change is made to the system call log.
74      */
75     return false;
76   }
77 
78   @WorkerThread
79   @Override
fill( Context appContext, SQLiteDatabase readableDatabase, long lastRebuildTimeMillis, CallLogMutations mutations)80   public void fill(
81       Context appContext,
82       SQLiteDatabase readableDatabase,
83       long lastRebuildTimeMillis,
84       CallLogMutations mutations) {
85     Assert.isWorkerThread();
86 
87     // This data source should always run first so the mutations should always be empty.
88     Assert.checkState(mutations.isEmpty());
89 
90     // TODO: Implementation.
91   }
92 
93   private static class CallLogObserver extends ContentObserver {
94     private final Context appContext;
95     private final ContentObserverCallbacks contentObserverCallbacks;
96 
CallLogObserver( Handler handler, Context appContext, ContentObserverCallbacks contentObserverCallbacks)97     CallLogObserver(
98         Handler handler, Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
99       super(handler);
100       this.appContext = appContext;
101       this.contentObserverCallbacks = contentObserverCallbacks;
102     }
103 
104     @MainThread
105     @Override
onChange(boolean selfChange, Uri uri)106     public void onChange(boolean selfChange, Uri uri) {
107       Assert.isMainThread();
108       LogUtil.enterBlock("SystemCallLogDataSource.CallLogObserver.onChange");
109       super.onChange(selfChange, uri);
110 
111       /*
112        * The system call log has a last updated timestamp, but deletes are physical (the "deleted"
113        * column is unused). This means that we can't detect deletes without scanning the entire
114        * table, which would be too slow. So, we just rely on content observers to trigger rebuilds
115        * when any change is made to the system call log.
116        */
117       contentObserverCallbacks.markDirtyAndNotify(appContext);
118     }
119   }
120 }
121