summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/providers/contacts/CallLogBackupAgent.java215
-rw-r--r--src/com/android/providers/contacts/DbModifierWithNotification.java5
2 files changed, 220 insertions, 0 deletions
diff --git a/src/com/android/providers/contacts/CallLogBackupAgent.java b/src/com/android/providers/contacts/CallLogBackupAgent.java
new file mode 100644
index 0000000..1371635
--- /dev/null
+++ b/src/com/android/providers/contacts/CallLogBackupAgent.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2015 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.providers.contacts;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.os.ParcelFileDescriptor;
+import android.provider.CallLog;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Call log backup agent.
+ */
+public class CallLogBackupAgent extends BackupAgent {
+
+ private static class CallLogBackupState {
+ int version;
+ SortedSet<Integer> callIds;
+ }
+
+ private static class Call {
+ int id;
+
+ @Override
+ public String toString() {
+ return "[" + id + "]";
+ }
+ }
+
+ private static final String TAG = "CallLogBackupAgent";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /** Current version of CallLogBackup. Used to track the backup format. */
+ private static final int VERSION = 1;
+ /** Version indicating that there exists no previous backup entry. */
+ private static final int VERSION_NO_PREVIOUS_STATE = 0;
+
+ private static final String[] CALL_LOG_PROJECTION = new String[] {
+ CallLog.Calls._ID,
+ CallLog.Calls.DATE,
+ CallLog.Calls.DURATION,
+ CallLog.Calls.NUMBER,
+ CallLog.Calls.TYPE,
+ CallLog.Calls.COUNTRY_ISO,
+ CallLog.Calls.GEOCODED_LOCATION,
+ CallLog.Calls.NUMBER_PRESENTATION,
+ CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME,
+ CallLog.Calls.PHONE_ACCOUNT_ID,
+ CallLog.Calls.PHONE_ACCOUNT_ADDRESS
+ };
+
+ /** ${inheritDoc} */
+ @Override
+ public void onBackup(ParcelFileDescriptor oldStateDescriptor, BackupDataOutput data,
+ ParcelFileDescriptor newStateDescriptor) throws IOException {
+
+ // Get the list of the previous calls IDs which were backed up.
+ CallLogBackupState state = readState(oldStateDescriptor);
+ SortedSet<Integer> callsToRemove = new TreeSet<>(state.callIds);
+
+ // Get all the existing call log entries.
+ Cursor cursor = getAllCallLogEntries();
+ if (DEBUG) {
+ Log.d(TAG, "Starting debug - state: " + state + ", cursor: " + cursor);
+ }
+ if (cursor == null) {
+ return;
+ }
+
+ try {
+ // Loop through all the call log entries to identify:
+ // (1) new calls
+ // (2) calls which have been deleted.
+ while (cursor.moveToNext()) {
+ Call call = readCallFromCursor(cursor);
+
+ if (!state.callIds.contains(call.id)) {
+
+ if (DEBUG) {
+ Log.d(TAG, "Adding call to backup: " + call);
+ }
+
+ // This call new (not in our list from the last backup), lets back it up.
+ addCallToBackup(data, call);
+ state.callIds.add(call.id);
+ } else {
+ // This call still exists in the current call log so delete it from the
+ // "callsToRemove" set since we want to keep it.
+ callsToRemove.remove(call.id);
+ }
+ }
+
+ // Remove calls which no longer exist in the set.
+ for (Integer i : callsToRemove) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing call from backup: " + i);
+ }
+
+ removeCallFromBackup(data, i);
+ state.callIds.remove(i);
+ }
+
+ // Rewrite the backup state.
+ writeState(newStateDescriptor, state);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /** ${inheritDoc} */
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ if (DEBUG) {
+ Log.d(TAG, "Performing Restore");
+ }
+ }
+
+ private Cursor getAllCallLogEntries() {
+ // We use the API here instead of querying ContactsDatabaseHelper directly because
+ // CallLogProvider has special locks in place for sychronizing when to read. Using the APIs
+ // gives us that for free.
+ ContentResolver resolver = getContentResolver();
+ return resolver.query(CallLog.Calls.CONTENT_URI, CALL_LOG_PROJECTION, null, null, null);
+ }
+
+ private CallLogBackupState readState(ParcelFileDescriptor oldState) throws IOException {
+ DataInputStream dataInput = new DataInputStream(
+ new FileInputStream(oldState.getFileDescriptor()));
+ CallLogBackupState state = new CallLogBackupState();
+ state.callIds = new TreeSet<>();
+
+ try {
+ // Read the version.
+ state.version = dataInput.readInt();
+
+ if (state.version >= 1) {
+ // Read the size.
+ int size = dataInput.readInt();
+
+ // Read all of the call IDs.
+ for (int i = 0; i < size; i++) {
+ state.callIds.add(dataInput.readInt());
+ }
+ }
+ } catch (EOFException e) {
+ state.version = VERSION_NO_PREVIOUS_STATE;
+ } finally {
+ dataInput.close();
+ }
+
+ return state;
+ }
+
+ private void writeState(ParcelFileDescriptor descriptor, CallLogBackupState state)
+ throws IOException {
+ DataOutputStream dataOutput = new DataOutputStream(new BufferedOutputStream(
+ new FileOutputStream(descriptor.getFileDescriptor())));
+
+ // Write version first of all
+ dataOutput.writeInt(VERSION);
+
+ // [Version 1]
+ // size + callIds
+ dataOutput.writeInt(state.callIds.size());
+ for (Integer i : state.callIds) {
+ dataOutput.writeInt(i);
+ }
+
+ // Done!
+ dataOutput.close();
+ }
+
+ private Call readCallFromCursor(Cursor cursor) {
+ Call call = new Call();
+ call.id = cursor.getInt(cursor.getColumnIndex(CallLog.Calls._ID));
+ // TODO: Rest of call data.
+ return call;
+ }
+
+ private void addCallToBackup(BackupDataOutput output, Call call) {
+ // TODO: Write the code
+ }
+
+ private void removeCallFromBackup(BackupDataOutput output, int callId) {
+ // TODO: Write the code
+ }
+}
diff --git a/src/com/android/providers/contacts/DbModifierWithNotification.java b/src/com/android/providers/contacts/DbModifierWithNotification.java
index 683a2bf..5ce41c4 100644
--- a/src/com/android/providers/contacts/DbModifierWithNotification.java
+++ b/src/com/android/providers/contacts/DbModifierWithNotification.java
@@ -20,6 +20,7 @@ package com.android.providers.contacts;
import static android.Manifest.permission.ADD_VOICEMAIL;
import static android.Manifest.permission.READ_VOICEMAIL;
+import android.app.backup.BackupManager;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -73,6 +74,8 @@ public class DbModifierWithNotification implements DatabaseModifier {
private final boolean mIsCallsTable;
private final VoicemailPermissions mVoicemailPermissions;
+ private BackupManager mBackupManager;
+
public DbModifierWithNotification(String tableName, SQLiteDatabase db, Context context) {
this(tableName, db, null, context);
}
@@ -88,6 +91,7 @@ public class DbModifierWithNotification implements DatabaseModifier {
mDb = db;
mInsertHelper = insertHelper;
mContext = context;
+ mBackupManager = new BackupManager(context);
mBaseUri = mTableName.equals(Tables.VOICEMAIL_STATUS) ?
Status.CONTENT_URI : Voicemails.CONTENT_URI;
mIsCallsTable = mTableName.equals(Tables.CALLS);
@@ -124,6 +128,7 @@ public class DbModifierWithNotification implements DatabaseModifier {
private void notifyCallLogChange() {
mContext.getContentResolver().notifyChange(Calls.CONTENT_URI, null, false);
+ mBackupManager.dataChanged();
}
private void notifyVoicemailChangeOnInsert(Uri notificationUri, Set<String> packagesModified) {