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 42 public SystemCallLogDataSource() {} 43 44 @MainThread 45 @Override 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 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 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 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 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