diff options
Diffstat (limited to 'java/com/android/dialer/backup')
-rw-r--r-- | java/com/android/dialer/backup/AndroidManifest.xml | 27 | ||||
-rw-r--r-- | java/com/android/dialer/backup/DialerBackupAgent.java | 276 | ||||
-rw-r--r-- | java/com/android/dialer/backup/DialerBackupUtils.java | 320 | ||||
-rw-r--r-- | java/com/android/dialer/backup/proto/VoicemailInfo.java | 377 |
4 files changed, 1000 insertions, 0 deletions
diff --git a/java/com/android/dialer/backup/AndroidManifest.xml b/java/com/android/dialer/backup/AndroidManifest.xml new file mode 100644 index 000000000..cfdb3d93d --- /dev/null +++ b/java/com/android/dialer/backup/AndroidManifest.xml @@ -0,0 +1,27 @@ +<!-- + ~ Copyright (C) 2016 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 + --> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.dialer.backup"> + + <application + android:backupAgent="com.android.dialer.backup.DialerBackupAgent" + android:fullBackupOnly="true" + android:restoreAnyVersion="true" + android:name="com.android.dialer.app.DialerApplication" + /> + +</manifest>
\ No newline at end of file diff --git a/java/com/android/dialer/backup/DialerBackupAgent.java b/java/com/android/dialer/backup/DialerBackupAgent.java new file mode 100644 index 000000000..391a93f29 --- /dev/null +++ b/java/com/android/dialer/backup/DialerBackupAgent.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2016 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.backup; + +import android.annotation.TargetApi; +import android.app.backup.BackupAgent; +import android.app.backup.BackupDataInput; +import android.app.backup.BackupDataOutput; +import android.app.backup.FullBackupDataOutput; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build.VERSION_CODES; +import android.os.ParcelFileDescriptor; +import android.provider.CallLog; +import android.provider.CallLog.Calls; +import android.provider.VoicemailContract; +import android.provider.VoicemailContract.Voicemails; +import android.util.Pair; +import com.android.dialer.backup.nano.VoicemailInfo; +import com.android.dialer.common.Assert; +import com.android.dialer.common.ConfigProviderBindings; +import com.android.dialer.common.LogUtil; +import com.android.dialer.logging.Logger; +import com.android.dialer.logging.nano.DialerImpression; +import com.android.dialer.telecom.TelecomUtil; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Locale; + +/** + * The Dialer backup agent to backup voicemails, and files under files, shared prefs and databases + */ +public class DialerBackupAgent extends BackupAgent { + // File names suffix for backup/restore. + private static final String VOICEMAIL_BACKUP_FILE_SUFFIX = "_voicemail_backup.proto"; + // File name formats for backup. It looks like 000000_voicemail_backup.proto, 0000001... + private static final String VOICEMAIL_BACKUP_FILE_FORMAT = "%06d" + VOICEMAIL_BACKUP_FILE_SUFFIX; + // Order by Date entries from database. We start backup from the newest. + private static final String ORDER_BY_DATE = "date DESC"; + // Voicemail Uri Column + public static final String VOICEMAIL_URI = "voicemail_uri"; + // Voicemail packages to backup + public static final String VOICEMAIL_SOURCE_PACKAGE = "com.android.phone"; + + private long voicemailsBackedupSoFar = 0; + private long sizeOfVoicemailsBackedupSoFar = 0; + private boolean maxVoicemailBackupReached = false; + + /** + * onBackup is used for Key/Value backup. Since we are using Dolly/Android Auto backup, we do not + * need to implement this method and Dolly should not be calling this. Instead Dolly will be + * calling onFullBackup. + */ + @Override + public void onBackup( + ParcelFileDescriptor parcelFileDescriptor, + BackupDataOutput backupDataOutput, + ParcelFileDescriptor parcelFileDescriptor1) + throws IOException { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_BACKUP); + Assert.fail("Android Backup should not call DialerBackupAgent.onBackup"); + } + + /** + * onRestore is used for Key/Value restore. Since we are using Dolly/Android Auto backup/restore, + * we do not need to implement this method as Dolly should not be calling this method. Instead + * onFileRestore will be called by Dolly. + */ + @Override + public void onRestore( + BackupDataInput backupDataInput, int i, ParcelFileDescriptor parcelFileDescriptor) + throws IOException { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE); + Assert.fail("Android Backup should not call DialerBackupAgent.onRestore"); + } + + @TargetApi(VERSION_CODES.M) + @Override + public void onFullBackup(FullBackupDataOutput data) throws IOException { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_FULL_BACKUP); + LogUtil.i("DialerBackupAgent.onFullBackup", "performing dialer backup"); + boolean autoBackupEnabled = + ConfigProviderBindings.get(this).getBoolean("enable_autobackup", true); + boolean vmBackupEnabled = + ConfigProviderBindings.get(this).getBoolean("enable_vm_backup", false); + + if (autoBackupEnabled) { + if (!maxVoicemailBackupReached && vmBackupEnabled) { + voicemailsBackedupSoFar = 0; + sizeOfVoicemailsBackedupSoFar = 0; + + LogUtil.i("DialerBackupAgent.onFullBackup", "autoBackup is enabled"); + ContentResolver contentResolver = getContentResolver(); + int limit = 1000; + + Uri uri = + TelecomUtil.getCallLogUri(this) + .buildUpon() + .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit)) + .build(); + + LogUtil.i("DialerBackupAgent.onFullBackup", "backing up from: " + uri); + + try (Cursor cursor = + contentResolver.query( + uri, + null, + String.format( + "(%s = ? AND deleted = 0 AND %s = ?)", Calls.TYPE, Voicemails.SOURCE_PACKAGE), + new String[] { + Integer.toString(CallLog.Calls.VOICEMAIL_TYPE), VOICEMAIL_SOURCE_PACKAGE + }, + ORDER_BY_DATE, + null)) { + + if (cursor == null) { + LogUtil.i("DialerBackupAgent.onFullBackup", "cursor was null"); + return; + } + + LogUtil.i("DialerBackupAgent.onFullBackup", "cursor count: " + cursor.getCount()); + if (cursor.moveToFirst()) { + int fileNum = 0; + do { + backupRow( + data, cursor, String.format(Locale.US, VOICEMAIL_BACKUP_FILE_FORMAT, fileNum++)); + } while (cursor.moveToNext() && !maxVoicemailBackupReached); + } else { + LogUtil.i("DialerBackupAgent.onFullBackup", "cursor.moveToFirst failed"); + } + } + } + LogUtil.i( + "DialerBackupAgent.onFullBackup", + "vm files backed up: %d, vm size backed up:%d, " + + "max vm backup reached:%b, vm backup enabled:%b", + voicemailsBackedupSoFar, + sizeOfVoicemailsBackedupSoFar, + maxVoicemailBackupReached, + vmBackupEnabled); + super.onFullBackup(data); + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_FULL_BACKED_UP); + } else { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_BACKUP_DISABLED); + LogUtil.i("DialerBackupAgent.onFullBackup", "autoBackup is disabled"); + } + } + + private void backupRow(FullBackupDataOutput data, Cursor cursor, String fileName) + throws IOException { + + VoicemailInfo cursorRowInProto = + DialerBackupUtils.convertVoicemailCursorRowToProto(cursor, getContentResolver()); + + File file = new File(getFilesDir(), fileName); + DialerBackupUtils.writeProtoToFile(file, cursorRowInProto); + + if (sizeOfVoicemailsBackedupSoFar + file.length() + > DialerBackupUtils.maxVoicemailSizeToBackup) { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_MAX_VM_BACKUP_REACHED); + maxVoicemailBackupReached = true; + file.delete(); + return; + } + + backupFile(file, data); + } + + // TODO: Write to FullBackupDataOutput directly (b/33849960) + private void backupFile(File file, FullBackupDataOutput data) throws IOException { + try { + super.fullBackupFile(file, data); + sizeOfVoicemailsBackedupSoFar = sizeOfVoicemailsBackedupSoFar + file.length(); + voicemailsBackedupSoFar++; + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_VOICEMAIL_BACKED_UP); + LogUtil.i("DialerBackupAgent.backupFile", "file backed up:" + file.getAbsolutePath()); + } finally { + file.delete(); + } + } + + // Being tracked in b/33839952 + @Override + public void onQuotaExceeded(long backupDataBytes, long quotaBytes) { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_QUOTA_EXCEEDED); + LogUtil.i("DialerBackupAgent.onQuotaExceeded", "does nothing"); + } + + @TargetApi(VERSION_CODES.M) + @Override + public void onRestoreFile( + ParcelFileDescriptor data, long size, File destination, int type, long mode, long mtime) + throws IOException { + LogUtil.i("DialerBackupAgent.onRestoreFile", "size:" + size + " destination: " + destination); + + String fileName = destination.getName(); + LogUtil.i("DialerBackupAgent.onRestoreFile", "file name: " + fileName); + + if (ConfigProviderBindings.get(this).getBoolean("enable_autobackup", true)) { + if (fileName.endsWith(VOICEMAIL_BACKUP_FILE_SUFFIX) + && ConfigProviderBindings.get(this).getBoolean("enable_vm_restore", true)) { + if (DialerBackupUtils.canRestoreVoicemails(getContentResolver(), this)) { + try { + super.onRestoreFile(data, size, destination, type, mode, mtime); + restoreVoicemail(destination); + destination.delete(); + } catch (IOException e) { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_IO_EXCEPTION); + LogUtil.e( + "DialerBackupAgent.onRestoreFile", + "could not restore voicemail - IOException: ", + e); + } + } else { + LogUtil.i( + "DialerBackupAgent.onRestoreFile", "build does not support restoring voicemails"); + } + + } else { + super.onRestoreFile(data, size, destination, type, mode, mtime); + LogUtil.i("DialerBackupAgent.onRestoreFile", "restored: " + fileName); + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_RESTORED_FILE); + } + } else { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_DISABLED); + LogUtil.i("DialerBackupAgent.onRestoreFile", "autoBackup is disabled"); + } + } + + @Override + public void onRestoreFinished() { + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_FINISHED); + LogUtil.i("DialerBackupAgent.onRestoreFinished", "do nothing"); + } + + @TargetApi(VERSION_CODES.M) + private void restoreVoicemail(File file) throws IOException { + Pair<ContentValues, byte[]> pair = + DialerBackupUtils.convertVoicemailProtoFileToContentValueAndAudioBytes( + file, getApplicationContext()); + + if (pair == null) { + LogUtil.i("DialerBackupAgent.restoreVoicemail", "not restoring VM due to duplicate"); + Logger.get(this) + .logImpression(DialerImpression.Type.BACKUP_ON_RESTORE_VM_DUPLICATE_NOT_RESTORING); + return; + } + + // TODO: Uniquely identify backup agent as the creator of this voicemail b/34084298 + try (OutputStream restoreStream = + getContentResolver() + .openOutputStream( + getContentResolver() + .insert(VoicemailContract.Voicemails.CONTENT_URI, pair.first))) { + DialerBackupUtils.copyAudioBytesToContentUri(pair.second, restoreStream); + Logger.get(this).logImpression(DialerImpression.Type.BACKUP_RESTORED_VOICEMAIL); + } + } +} diff --git a/java/com/android/dialer/backup/DialerBackupUtils.java b/java/com/android/dialer/backup/DialerBackupUtils.java new file mode 100644 index 000000000..ff0cb4f7c --- /dev/null +++ b/java/com/android/dialer/backup/DialerBackupUtils.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2016 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.backup; + +import android.annotation.TargetApi; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build.VERSION_CODES; +import android.provider.VoicemailContract; +import android.provider.VoicemailContract.Voicemails; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Pair; +import com.android.dialer.backup.nano.VoicemailInfo; +import com.android.dialer.common.ConfigProviderBindings; +import com.android.dialer.common.LogUtil; +import com.google.common.io.ByteStreams; +import com.google.common.io.Files; +import com.google.protobuf.nano.MessageNano; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** Helper functions for DialerBackupAgent */ +public class DialerBackupUtils { + // Backup voicemails up to 20MB + static long maxVoicemailSizeToBackup = 20000000L; + static final String RESTORED_COLUMN = "restored"; + + private DialerBackupUtils() {} + + public static void copyAudioBytesToContentUri( + @NonNull byte[] audioBytesArray, @NonNull OutputStream restoreStream) throws IOException { + LogUtil.i("DialerBackupUtils.copyStream", "audioByteArray length: " + audioBytesArray.length); + + ByteArrayInputStream decodedStream = new ByteArrayInputStream(audioBytesArray); + LogUtil.i( + "DialerBackupUtils.copyStream", "decodedStream.available: " + decodedStream.available()); + + ByteStreams.copy(decodedStream, restoreStream); + } + + public static @Nullable byte[] audioStreamToByteArray(@NonNull InputStream stream) + throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + if (stream.available() > 0) { + ByteStreams.copy(stream, buffer); + } else { + LogUtil.i("DialerBackupUtils.audioStreamToByteArray", "no audio stream to backup"); + } + return buffer.toByteArray(); + } + + public static void writeProtoToFile(@NonNull File file, @NonNull VoicemailInfo voicemailInfo) + throws IOException { + LogUtil.i( + "DialerBackupUtils.writeProtoToFile", + "backup " + voicemailInfo + " to " + file.getAbsolutePath()); + + byte[] bytes = MessageNano.toByteArray(voicemailInfo); + Files.write(bytes, file); + } + + /** Only restore voicemails that have the restored column in calllog (NMR2+ builds) */ + @TargetApi(VERSION_CODES.M) + public static boolean canRestoreVoicemails(ContentResolver contentResolver, Context context) { + try (Cursor cursor = contentResolver.query(Voicemails.CONTENT_URI, null, null, null, null)) { + // Restored column only exists in NMR2 and above builds. + if (cursor.getColumnIndex(RESTORED_COLUMN) != -1) { + LogUtil.i("DialerBackupUtils.canRestoreVoicemails", "Build supports restore"); + return true; + } else { + LogUtil.i("DialerBackupUtils.canRestoreVoicemails", "Build does not support restore"); + return false; + } + } + } + + public static VoicemailInfo protoFileToVoicemailInfo(@NonNull File file) throws IOException { + byte[] byteArray = Files.toByteArray(file); + return VoicemailInfo.parseFrom(byteArray); + } + + @TargetApi(VERSION_CODES.M) + public static VoicemailInfo convertVoicemailCursorRowToProto( + @NonNull Cursor cursor, @NonNull ContentResolver contentResolver) throws IOException { + + VoicemailInfo voicemailInfo = new VoicemailInfo(); + + for (int i = 0; i < cursor.getColumnCount(); ++i) { + String name = cursor.getColumnName(i); + String value = cursor.getString(i); + + LogUtil.i( + "DialerBackupUtils.convertVoicemailCursorRowToProto", + "column index: %d, column name: %s, column value: %s", + i, + name, + value); + + switch (name) { + case Voicemails.DATE: + voicemailInfo.date = value; + break; + case Voicemails.DELETED: + voicemailInfo.deleted = value; + break; + case Voicemails.DIRTY: + voicemailInfo.dirty = value; + break; + case Voicemails.DIR_TYPE: + voicemailInfo.dirType = value; + break; + case Voicemails.DURATION: + voicemailInfo.duration = value; + break; + case Voicemails.HAS_CONTENT: + voicemailInfo.hasContent = value; + break; + case Voicemails.IS_READ: + voicemailInfo.isRead = value; + break; + case Voicemails.ITEM_TYPE: + voicemailInfo.itemType = value; + break; + case Voicemails.LAST_MODIFIED: + voicemailInfo.lastModified = value; + break; + case Voicemails.MIME_TYPE: + voicemailInfo.mimeType = value; + break; + case Voicemails.NUMBER: + voicemailInfo.number = value; + break; + case Voicemails.PHONE_ACCOUNT_COMPONENT_NAME: + voicemailInfo.phoneAccountComponentName = value; + break; + case Voicemails.PHONE_ACCOUNT_ID: + voicemailInfo.phoneAccountId = value; + break; + case Voicemails.SOURCE_DATA: + voicemailInfo.sourceData = value; + break; + case Voicemails.SOURCE_PACKAGE: + voicemailInfo.sourcePackage = value; + break; + case Voicemails.TRANSCRIPTION: + voicemailInfo.transcription = value; + break; + case DialerBackupAgent.VOICEMAIL_URI: + try (InputStream audioStream = contentResolver.openInputStream(Uri.parse(value))) { + voicemailInfo.encodedVoicemailKey = audioStreamToByteArray(audioStream); + } + break; + default: + LogUtil.i( + "DialerBackupUtils.convertVoicemailCursorRowToProto", + "Not backing up column: %s, with value: %s", + name, + value); + break; + } + } + return voicemailInfo; + } + + public static Pair<ContentValues, byte[]> convertVoicemailProtoFileToContentValueAndAudioBytes( + @NonNull File file, Context context) throws IOException { + + VoicemailInfo voicemailInfo = DialerBackupUtils.protoFileToVoicemailInfo(file); + LogUtil.i( + "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio", + "file name: " + + file.getName() + + " voicemailInfo size: " + + voicemailInfo.getSerializedSize()); + + if (isDuplicate(context, voicemailInfo)) { + LogUtil.i( + "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio", + "voicemail already exists"); + return null; + } else { + ContentValues contentValues = new ContentValues(); + + if (!voicemailInfo.date.isEmpty()) { + contentValues.put(Voicemails.DATE, voicemailInfo.date); + } + if (!voicemailInfo.deleted.isEmpty()) { + contentValues.put(Voicemails.DELETED, voicemailInfo.deleted); + } + if (!voicemailInfo.dirty.isEmpty()) { + contentValues.put(Voicemails.DIRTY, voicemailInfo.dirty); + } + if (!voicemailInfo.duration.isEmpty()) { + contentValues.put(Voicemails.DURATION, voicemailInfo.duration); + } + if (!voicemailInfo.isRead.isEmpty()) { + contentValues.put(Voicemails.IS_READ, voicemailInfo.isRead); + } + if (!voicemailInfo.lastModified.isEmpty()) { + contentValues.put(Voicemails.LAST_MODIFIED, voicemailInfo.lastModified); + } + if (!voicemailInfo.mimeType.isEmpty()) { + contentValues.put(Voicemails.MIME_TYPE, voicemailInfo.mimeType); + } + if (!voicemailInfo.number.isEmpty()) { + contentValues.put(Voicemails.NUMBER, voicemailInfo.number); + } + if (!voicemailInfo.phoneAccountComponentName.isEmpty()) { + contentValues.put( + Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, voicemailInfo.phoneAccountComponentName); + } + if (!voicemailInfo.phoneAccountId.isEmpty()) { + contentValues.put(Voicemails.PHONE_ACCOUNT_ID, voicemailInfo.phoneAccountId); + } + if (!voicemailInfo.sourceData.isEmpty()) { + contentValues.put(Voicemails.SOURCE_DATA, voicemailInfo.sourceData); + } + if (!voicemailInfo.sourcePackage.isEmpty()) { + contentValues.put(Voicemails.SOURCE_PACKAGE, voicemailInfo.sourcePackage); + } + if (!voicemailInfo.transcription.isEmpty()) { + contentValues.put(Voicemails.TRANSCRIPTION, voicemailInfo.transcription); + } + contentValues.put(VoicemailContract.Voicemails.HAS_CONTENT, 1); + contentValues.put(RESTORED_COLUMN, "1"); + contentValues.put(Voicemails.SOURCE_PACKAGE, getSourcePackage(context, voicemailInfo)); + + LogUtil.i( + "DialerBackupUtils.convertVoicemailProtoFileToContentValueAndEncodedAudio", + "cv: " + contentValues); + + return Pair.create(contentValues, voicemailInfo.encodedVoicemailKey); + } + } + + /** + * We should be using the system package name as the source package if there is no endless VM/VM + * archive present on the device. This is to separate pre-O (no endless VM) and O+ (endless VM) + * devices. This ensures that the source of truth for VMs is the VM server when endless VM is not + * enabled, and when endless VM/archived VMs is present, the source of truth for VMs is the device + * itself. + */ + private static String getSourcePackage(Context context, VoicemailInfo voicemailInfo) { + if (ConfigProviderBindings.get(context) + .getBoolean("voicemail_restore_force_system_source_package", false)) { + LogUtil.i("DialerBackupUtils.getSourcePackage", "forcing system source package"); + return "com.android.phone"; + } + if (ConfigProviderBindings.get(context) + .getBoolean("voicemail_restore_check_archive_for_source_package", true)) { + if ("1".equals(voicemailInfo.archived)) { + LogUtil.i( + "DialerBackupUtils.getSourcePackage", + "voicemail was archived, using app source package"); + // Using our app's source package will prevent the archived voicemail from being deleted by + // the system when it syncs with the voicemail server. In most cases the user will not see + // duplicate voicemails because this voicemail was archived and likely deleted from the + // voicemail server. + return context.getPackageName(); + } else { + // Use the system source package. This means that if the voicemail is not present on the + // voicemail server then the system will delete it when it syncs. + LogUtil.i( + "DialerBackupUtils.getSourcePackage", + "voicemail was not archived, using system source package"); + return "com.android.phone"; + } + } + // Use our app's source package. This means that if the system syncs voicemail from the server + // the user could potentially get duplicate voicemails. + LogUtil.i("DialerBackupUtils.getSourcePackage", "defaulting to using app source package"); + return context.getPackageName(); + } + + @TargetApi(VERSION_CODES.M) + private static boolean isDuplicate(Context context, VoicemailInfo voicemailInfo) { + // This checks for VM that might already exist, and doesn't restore them + try (Cursor cursor = + context + .getContentResolver() + .query( + VoicemailContract.Voicemails.CONTENT_URI, + null, + String.format( + "(%s = ? AND %s = ? AND %s = ?)", + Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION), + new String[] {voicemailInfo.number, voicemailInfo.date, voicemailInfo.duration}, + null, + null)) { + if (cursor.moveToFirst() + && ConfigProviderBindings.get(context) + .getBoolean("enable_vm_restore_no_duplicate", true)) { + return true; + } + } + return false; + } +} diff --git a/java/com/android/dialer/backup/proto/VoicemailInfo.java b/java/com/android/dialer/backup/proto/VoicemailInfo.java new file mode 100644 index 000000000..9ff8423f3 --- /dev/null +++ b/java/com/android/dialer/backup/proto/VoicemailInfo.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2017 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. + */ + +// Generated by the protocol buffer compiler. DO NOT EDIT! + +package com.android.dialer.backup.nano; + +@SuppressWarnings("hiding") +public final class VoicemailInfo extends + com.google.protobuf.nano.ExtendableMessageNano<VoicemailInfo> { + + private static volatile VoicemailInfo[] _emptyArray; + public static VoicemailInfo[] emptyArray() { + // Lazily initializes the empty array + if (_emptyArray == null) { + synchronized ( + com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) { + if (_emptyArray == null) { + _emptyArray = new VoicemailInfo[0]; + } + } + } + return _emptyArray; + } + + // optional string date = 1; + public java.lang.String date; + + // optional string deleted = 2; + public java.lang.String deleted; + + // optional string dirty = 3; + public java.lang.String dirty; + + // optional string dir_type = 4; + public java.lang.String dirType; + + // optional string duration = 5; + public java.lang.String duration; + + // optional string has_content = 6; + public java.lang.String hasContent; + + // optional string is_read = 7; + public java.lang.String isRead; + + // optional string item_type = 8; + public java.lang.String itemType; + + // optional string last_modified = 9; + public java.lang.String lastModified; + + // optional string mime_type = 10; + public java.lang.String mimeType; + + // optional string number = 11; + public java.lang.String number; + + // optional string phone_account_component_name = 12; + public java.lang.String phoneAccountComponentName; + + // optional string phone_account_id = 13; + public java.lang.String phoneAccountId; + + // optional string source_data = 14; + public java.lang.String sourceData; + + // optional string source_package = 15; + public java.lang.String sourcePackage; + + // optional string transcription = 16; + public java.lang.String transcription; + + // optional string voicemail_uri = 17; + public java.lang.String voicemailUri; + + // optional bytes encoded_voicemail_key = 18; + public byte[] encodedVoicemailKey; + + // optional string archived = 19; + public java.lang.String archived; + + // @@protoc_insertion_point(class_scope:com.android.dialer.backup.VoicemailInfo) + + public VoicemailInfo() { + clear(); + } + + public VoicemailInfo clear() { + date = ""; + deleted = ""; + dirty = ""; + dirType = ""; + duration = ""; + hasContent = ""; + isRead = ""; + itemType = ""; + lastModified = ""; + mimeType = ""; + number = ""; + phoneAccountComponentName = ""; + phoneAccountId = ""; + sourceData = ""; + sourcePackage = ""; + transcription = ""; + voicemailUri = ""; + encodedVoicemailKey = com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES; + archived = ""; + unknownFieldData = null; + cachedSize = -1; + return this; + } + + @Override + public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output) + throws java.io.IOException { + if (this.date != null && !this.date.equals("")) { + output.writeString(1, this.date); + } + if (this.deleted != null && !this.deleted.equals("")) { + output.writeString(2, this.deleted); + } + if (this.dirty != null && !this.dirty.equals("")) { + output.writeString(3, this.dirty); + } + if (this.dirType != null && !this.dirType.equals("")) { + output.writeString(4, this.dirType); + } + if (this.duration != null && !this.duration.equals("")) { + output.writeString(5, this.duration); + } + if (this.hasContent != null && !this.hasContent.equals("")) { + output.writeString(6, this.hasContent); + } + if (this.isRead != null && !this.isRead.equals("")) { + output.writeString(7, this.isRead); + } + if (this.itemType != null && !this.itemType.equals("")) { + output.writeString(8, this.itemType); + } + if (this.lastModified != null && !this.lastModified.equals("")) { + output.writeString(9, this.lastModified); + } + if (this.mimeType != null && !this.mimeType.equals("")) { + output.writeString(10, this.mimeType); + } + if (this.number != null && !this.number.equals("")) { + output.writeString(11, this.number); + } + if (this.phoneAccountComponentName != null && !this.phoneAccountComponentName.equals("")) { + output.writeString(12, this.phoneAccountComponentName); + } + if (this.phoneAccountId != null && !this.phoneAccountId.equals("")) { + output.writeString(13, this.phoneAccountId); + } + if (this.sourceData != null && !this.sourceData.equals("")) { + output.writeString(14, this.sourceData); + } + if (this.sourcePackage != null && !this.sourcePackage.equals("")) { + output.writeString(15, this.sourcePackage); + } + if (this.transcription != null && !this.transcription.equals("")) { + output.writeString(16, this.transcription); + } + if (this.voicemailUri != null && !this.voicemailUri.equals("")) { + output.writeString(17, this.voicemailUri); + } + if (!java.util.Arrays.equals(this.encodedVoicemailKey, com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES)) { + output.writeBytes(18, this.encodedVoicemailKey); + } + if (this.archived != null && !this.archived.equals("")) { + output.writeString(19, this.archived); + } + super.writeTo(output); + } + + @Override + protected int computeSerializedSize() { + int size = super.computeSerializedSize(); + if (this.date != null && !this.date.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(1, this.date); + } + if (this.deleted != null && !this.deleted.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(2, this.deleted); + } + if (this.dirty != null && !this.dirty.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(3, this.dirty); + } + if (this.dirType != null && !this.dirType.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(4, this.dirType); + } + if (this.duration != null && !this.duration.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(5, this.duration); + } + if (this.hasContent != null && !this.hasContent.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(6, this.hasContent); + } + if (this.isRead != null && !this.isRead.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(7, this.isRead); + } + if (this.itemType != null && !this.itemType.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(8, this.itemType); + } + if (this.lastModified != null && !this.lastModified.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(9, this.lastModified); + } + if (this.mimeType != null && !this.mimeType.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(10, this.mimeType); + } + if (this.number != null && !this.number.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(11, this.number); + } + if (this.phoneAccountComponentName != null && !this.phoneAccountComponentName.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(12, this.phoneAccountComponentName); + } + if (this.phoneAccountId != null && !this.phoneAccountId.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(13, this.phoneAccountId); + } + if (this.sourceData != null && !this.sourceData.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(14, this.sourceData); + } + if (this.sourcePackage != null && !this.sourcePackage.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(15, this.sourcePackage); + } + if (this.transcription != null && !this.transcription.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(16, this.transcription); + } + if (this.voicemailUri != null && !this.voicemailUri.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(17, this.voicemailUri); + } + if (!java.util.Arrays.equals(this.encodedVoicemailKey, com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES)) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeBytesSize(18, this.encodedVoicemailKey); + } + if (this.archived != null && !this.archived.equals("")) { + size += com.google.protobuf.nano.CodedOutputByteBufferNano + .computeStringSize(19, this.archived); + } + return size; + } + + @Override + public VoicemailInfo mergeFrom( + com.google.protobuf.nano.CodedInputByteBufferNano input) + throws java.io.IOException { + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + return this; + default: { + if (!super.storeUnknownField(input, tag)) { + return this; + } + break; + } + case 10: { + this.date = input.readString(); + break; + } + case 18: { + this.deleted = input.readString(); + break; + } + case 26: { + this.dirty = input.readString(); + break; + } + case 34: { + this.dirType = input.readString(); + break; + } + case 42: { + this.duration = input.readString(); + break; + } + case 50: { + this.hasContent = input.readString(); + break; + } + case 58: { + this.isRead = input.readString(); + break; + } + case 66: { + this.itemType = input.readString(); + break; + } + case 74: { + this.lastModified = input.readString(); + break; + } + case 82: { + this.mimeType = input.readString(); + break; + } + case 90: { + this.number = input.readString(); + break; + } + case 98: { + this.phoneAccountComponentName = input.readString(); + break; + } + case 106: { + this.phoneAccountId = input.readString(); + break; + } + case 114: { + this.sourceData = input.readString(); + break; + } + case 122: { + this.sourcePackage = input.readString(); + break; + } + case 130: { + this.transcription = input.readString(); + break; + } + case 138: { + this.voicemailUri = input.readString(); + break; + } + case 146: { + this.encodedVoicemailKey = input.readBytes(); + break; + } + case 154: { + this.archived = input.readString(); + break; + } + } + } + } + + public static VoicemailInfo parseFrom(byte[] data) + throws com.google.protobuf.nano.InvalidProtocolBufferNanoException { + return com.google.protobuf.nano.MessageNano.mergeFrom(new VoicemailInfo(), data); + } + + public static VoicemailInfo parseFrom( + com.google.protobuf.nano.CodedInputByteBufferNano input) + throws java.io.IOException { + return new VoicemailInfo().mergeFrom(input); + } +} |