161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon/* 261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * Copyright (C) 2015 The Android Open Source Project 361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * 461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * Licensed under the Apache License, Version 2.0 (the "License"); 561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * you may not use this file except in compliance with the License. 661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * You may obtain a copy of the License at 761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * 861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * http://www.apache.org/licenses/LICENSE-2.0 961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * 1061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * Unless required by applicable law or agreed to in writing, software 1161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * distributed under the License is distributed on an "AS IS" BASIS, 1261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * See the License for the specific language governing permissions and 1461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * limitations under the License 1561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon */ 1661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 1761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonpackage com.android.providers.calllogbackup; 1861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 1961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.app.backup.BackupAgent; 2061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.app.backup.BackupDataInput; 2161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.app.backup.BackupDataOutput; 2261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.content.ComponentName; 2361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.content.ContentResolver; 2461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.database.Cursor; 2561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.os.ParcelFileDescriptor; 2661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.os.UserHandle; 2761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.os.UserManager; 2861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.provider.CallLog; 2961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.provider.CallLog.Calls; 3061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.telecom.PhoneAccountHandle; 3161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport android.util.Log; 3261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 3361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport com.android.internal.annotations.VisibleForTesting; 3461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 3561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.BufferedOutputStream; 3661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.ByteArrayInputStream; 3761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.ByteArrayOutputStream; 3861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.DataInput; 3961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.DataInputStream; 4061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.DataOutput; 4161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.DataOutputStream; 4261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.EOFException; 4361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.FileInputStream; 4461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.FileOutputStream; 4561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.io.IOException; 4661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.util.LinkedList; 4761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.util.List; 4861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.util.SortedSet; 4961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonimport java.util.TreeSet; 5061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 5161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon/** 5261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon * Call log backup agent. 5361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon */ 5461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordonpublic class CallLogBackupAgent extends BackupAgent { 5561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 5661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @VisibleForTesting 5761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon static class CallLogBackupState { 5861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon int version; 5961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon SortedSet<Integer> callIds; 6061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 6161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 6261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @VisibleForTesting 6361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon static class Call { 6461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon int id; 6561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon long date; 6661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon long duration; 6761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon String number; 6861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon int type; 6961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon int numberPresentation; 7061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon String accountComponentName; 7161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon String accountId; 7261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon String accountAddress; 7361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Long dataUsage; 7461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon int features; 7561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 7661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @Override 7761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon public String toString() { 7861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (isDebug()) { 7961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return "[" + id + ", account: [" + accountComponentName + " : " + accountId + 8061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon "]," + number + ", " + date + "]"; 8161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } else { 8261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return "[" + id + "]"; 8361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 8461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 8561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 8661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 87f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon static class OEMData { 88f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon String namespace; 89f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon byte[] bytes; 90f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 91f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon public OEMData(String namespace, byte[] bytes) { 92f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon this.namespace = namespace; 93f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon this.bytes = bytes == null ? ZERO_BYTE_ARRAY : bytes; 94f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon } 95f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon } 96f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 9761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private static final String TAG = "CallLogBackupAgent"; 9861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 9961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon /** Current version of CallLogBackup. Used to track the backup format. */ 10061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @VisibleForTesting 101f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon static final int VERSION = 1002; 10261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon /** Version indicating that there exists no previous backup entry. */ 10361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @VisibleForTesting 10461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon static final int VERSION_NO_PREVIOUS_STATE = 0; 10561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 106f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon static final String NO_OEM_NAMESPACE = "no-oem-namespace"; 107f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 108f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon static final byte[] ZERO_BYTE_ARRAY = new byte[0]; 109f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 110f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon static final int END_OEM_DATA_MARKER = 0x60061E; 111f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 11261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private static final String[] CALL_LOG_PROJECTION = new String[] { 11361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls._ID, 11461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.DATE, 11561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.DURATION, 11661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.NUMBER, 11761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.TYPE, 11861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.COUNTRY_ISO, 11961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.GEOCODED_LOCATION, 12061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.NUMBER_PRESENTATION, 12161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME, 12261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.PHONE_ACCOUNT_ID, 12361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.PHONE_ACCOUNT_ADDRESS, 12461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.DATA_USAGE, 12561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.FEATURES 12661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon }; 12761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 12861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon /** ${inheritDoc} */ 12961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @Override 13061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon public void onBackup(ParcelFileDescriptor oldStateDescriptor, BackupDataOutput data, 13161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon ParcelFileDescriptor newStateDescriptor) throws IOException { 13261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 13361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Get the list of the previous calls IDs which were backed up. 13461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon DataInputStream dataInput = new DataInputStream( 13561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon new FileInputStream(oldStateDescriptor.getFileDescriptor())); 13661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon final CallLogBackupState state; 13761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon try { 13861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon state = readState(dataInput); 13961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } finally { 14061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon dataInput.close(); 14161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 14261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 14361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Run the actual backup of data 14461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon runBackup(state, data, getAllCallLogEntries()); 14561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 14661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Rewrite the backup state. 14761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon DataOutputStream dataOutput = new DataOutputStream(new BufferedOutputStream( 14861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon new FileOutputStream(newStateDescriptor.getFileDescriptor()))); 14961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon try { 15061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon writeState(dataOutput, state); 15161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } finally { 15261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon dataOutput.close(); 15361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 15461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 15561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 15661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon /** ${inheritDoc} */ 15761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @Override 15861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) 15961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon throws IOException { 16061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (isDebug()) { 16161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.d(TAG, "Performing Restore"); 16261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 16361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 16461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon while (data.readNextHeader()) { 16561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Call call = readCallFromData(data); 16661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (call != null) { 16761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon writeCallToProvider(call); 16861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (isDebug()) { 16961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.d(TAG, "Restored call: " + call); 17061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 17161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 17261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 17361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 17461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 17561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @VisibleForTesting 17661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon void runBackup(CallLogBackupState state, BackupDataOutput data, Iterable<Call> calls) { 17761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon SortedSet<Integer> callsToRemove = new TreeSet<>(state.callIds); 17861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 17961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Loop through all the call log entries to identify: 18061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // (1) new calls 18161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // (2) calls which have been deleted. 18261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon for (Call call : calls) { 18361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (!state.callIds.contains(call.id)) { 18461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 18561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (isDebug()) { 18661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.d(TAG, "Adding call to backup: " + call); 18761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 18861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 18961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // This call new (not in our list from the last backup), lets back it up. 19061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon addCallToBackup(data, call); 19161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon state.callIds.add(call.id); 19261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } else { 19361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // This call still exists in the current call log so delete it from the 19461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // "callsToRemove" set since we want to keep it. 19561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon callsToRemove.remove(call.id); 19661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 19761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 19861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 19961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Remove calls which no longer exist in the set. 20061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon for (Integer i : callsToRemove) { 20161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (isDebug()) { 20261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.d(TAG, "Removing call from backup: " + i); 20361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 20461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 20561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon removeCallFromBackup(data, i); 20661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon state.callIds.remove(i); 20761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 20861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 20961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 21061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private Iterable<Call> getAllCallLogEntries() { 21161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon List<Call> calls = new LinkedList<>(); 21261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 21361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // We use the API here instead of querying ContactsDatabaseHelper directly because 21461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // CallLogProvider has special locks in place for sychronizing when to read. Using the APIs 21561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // gives us that for free. 21661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon ContentResolver resolver = getContentResolver(); 21761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Cursor cursor = resolver.query( 21861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLog.Calls.CONTENT_URI, CALL_LOG_PROJECTION, null, null, null); 21961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (cursor != null) { 22061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon try { 22161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon while (cursor.moveToNext()) { 22261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Call call = readCallFromCursor(cursor); 22361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (call != null) { 22461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon calls.add(call); 22561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 22661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 22761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } finally { 22861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon cursor.close(); 22961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 23061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 23161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 23261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return calls; 23361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 23461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 23561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private void writeCallToProvider(Call call) { 23661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Long dataUsage = call.dataUsage == 0 ? null : call.dataUsage; 23761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 238755cc64c7ff675cbc6ee930b2afc690eab8e35dfSantos Cordon PhoneAccountHandle handle = null; 239755cc64c7ff675cbc6ee930b2afc690eab8e35dfSantos Cordon if (call.accountComponentName != null && call.accountId != null) { 240755cc64c7ff675cbc6ee930b2afc690eab8e35dfSantos Cordon handle = new PhoneAccountHandle( 241755cc64c7ff675cbc6ee930b2afc690eab8e35dfSantos Cordon ComponentName.unflattenFromString(call.accountComponentName), call.accountId); 242755cc64c7ff675cbc6ee930b2afc690eab8e35dfSantos Cordon } 24361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Calls.addCall(null /* CallerInfo */, this, call.number, call.numberPresentation, call.type, 24461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.features, handle, call.date, (int) call.duration, 245a172ae826f3e53c637d239a354acd0a0dd9f40beRoshan Pius dataUsage, true /* addForAllUsers */, true /* is_read */); 24661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 24761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 24861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @VisibleForTesting 24961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLogBackupState readState(DataInput dataInput) throws IOException { 25061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon CallLogBackupState state = new CallLogBackupState(); 25161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon state.callIds = new TreeSet<>(); 25261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 25361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon try { 25461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Read the version. 25561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon state.version = dataInput.readInt(); 25661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 25761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (state.version >= 1) { 25861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Read the size. 25961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon int size = dataInput.readInt(); 26061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 26161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Read all of the call IDs. 26261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon for (int i = 0; i < size; i++) { 26361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon state.callIds.add(dataInput.readInt()); 26461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 26561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 26661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } catch (EOFException e) { 26761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon state.version = VERSION_NO_PREVIOUS_STATE; 26861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 26961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 27061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return state; 27161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 27261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 27361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @VisibleForTesting 27461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon void writeState(DataOutput dataOutput, CallLogBackupState state) 27561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon throws IOException { 27661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // Write version first of all 27761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon dataOutput.writeInt(VERSION); 27861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 27961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // [Version 1] 28061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon // size + callIds 28161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon dataOutput.writeInt(state.callIds.size()); 28261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon for (Integer i : state.callIds) { 28361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon dataOutput.writeInt(i); 28461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 28561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 28661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 28761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon @VisibleForTesting 28861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Call readCallFromData(BackupDataInput data) { 28961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon final int callId; 29061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon try { 29161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon callId = Integer.parseInt(data.getKey()); 29261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } catch (NumberFormatException e) { 29361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.e(TAG, "Unexpected key found in restore: " + data.getKey()); 29461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return null; 29561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 29661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 29761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon try { 29861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon byte [] byteArray = new byte[data.getDataSize()]; 29961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.readEntityData(byteArray, 0, byteArray.length); 30061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon DataInputStream dataInput = new DataInputStream(new ByteArrayInputStream(byteArray)); 30161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 30261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Call call = new Call(); 30361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.id = callId; 30461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 30561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon int version = dataInput.readInt(); 30661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (version >= 1) { 30761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.date = dataInput.readLong(); 30861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.duration = dataInput.readLong(); 30961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.number = readString(dataInput); 31061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.type = dataInput.readInt(); 31161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.numberPresentation = dataInput.readInt(); 31261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.accountComponentName = readString(dataInput); 31361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.accountId = readString(dataInput); 31461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.accountAddress = readString(dataInput); 31561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.dataUsage = dataInput.readLong(); 31661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.features = dataInput.readInt(); 31761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 31861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 319f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon if (version >= 1002) { 320f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon String namespace = dataInput.readUTF(); 321f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon int length = dataInput.readInt(); 322f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon byte[] buffer = new byte[length]; 323f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon dataInput.read(buffer); 324f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon readOEMDataForCall(call, new OEMData(namespace, buffer)); 325f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 326f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon int marker = dataInput.readInt(); 327f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon if (marker != END_OEM_DATA_MARKER) { 328f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon Log.e(TAG, "Did not find END-OEM marker for call " + call.id); 329f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // The marker does not match the expected value, ignore this call completely. 330f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon return null; 331f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon } 332f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon } 333f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 33461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return call; 33561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } catch (IOException e) { 33661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.e(TAG, "Error reading call data for " + callId, e); 33761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return null; 33861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 33961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 34061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 34161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private Call readCallFromCursor(Cursor cursor) { 34261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Call call = new Call(); 34361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.id = cursor.getInt(cursor.getColumnIndex(CallLog.Calls._ID)); 34461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.date = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE)); 34561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.duration = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DURATION)); 34661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER)); 34761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.type = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE)); 34861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.numberPresentation = 34961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon cursor.getInt(cursor.getColumnIndex(CallLog.Calls.NUMBER_PRESENTATION)); 35061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.accountComponentName = 35161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon cursor.getString(cursor.getColumnIndex(CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME)); 35261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.accountId = 35361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon cursor.getString(cursor.getColumnIndex(CallLog.Calls.PHONE_ACCOUNT_ID)); 35461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.accountAddress = 35561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon cursor.getString(cursor.getColumnIndex(CallLog.Calls.PHONE_ACCOUNT_ADDRESS)); 35661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.dataUsage = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATA_USAGE)); 35761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon call.features = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.FEATURES)); 35861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return call; 35961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 36061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 36161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private void addCallToBackup(BackupDataOutput output, Call call) { 36261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon ByteArrayOutputStream baos = new ByteArrayOutputStream(); 36361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon DataOutputStream data = new DataOutputStream(baos); 36461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 36561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon try { 36661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeInt(VERSION); 36761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeLong(call.date); 36861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeLong(call.duration); 36961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon writeString(data, call.number); 37061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeInt(call.type); 37161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeInt(call.numberPresentation); 37261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon writeString(data, call.accountComponentName); 37361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon writeString(data, call.accountId); 37461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon writeString(data, call.accountAddress); 37561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeLong(call.dataUsage == null ? 0 : call.dataUsage); 37661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeInt(call.features); 377f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 378f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon OEMData oemData = getOEMDataForCall(call); 379f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon data.writeUTF(oemData.namespace); 380f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon data.writeInt(oemData.bytes.length); 381f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon data.write(oemData.bytes); 3825fe9573b5dca5de8d789459eedb1df49497f3ae3Roshan Pius data.writeInt(END_OEM_DATA_MARKER); 383f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 38461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.flush(); 38561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 38661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon output.writeEntityHeader(Integer.toString(call.id), baos.size()); 38761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon output.writeEntityData(baos.toByteArray(), baos.size()); 38861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 38961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (isDebug()) { 39061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.d(TAG, "Wrote call to backup: " + call + " with byte array: " + baos); 39161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 39261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } catch (IOException e) { 39361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.e(TAG, "Failed to backup call: " + call, e); 39461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 39561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 39661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 397f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon /** 398f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * Allows OEMs to provide proprietary data to backup along with the rest of the call log 399f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * data. Because there is no way to provide a Backup Transport implementation 400f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * nor peek into the data format of backup entries without system-level permissions, it is 401f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * not possible (at the time of this writing) to write CTS tests for this piece of code. 402f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * It is, therefore, important that if you alter this portion of code that you 403f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * test backup and restore of call log is working as expected; ideally this would be tested by 404f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * backing up and restoring between two different Android phone devices running M+. 405f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon */ 406f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon private OEMData getOEMDataForCall(Call call) { 407f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon return new OEMData(NO_OEM_NAMESPACE, ZERO_BYTE_ARRAY); 408f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 409f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // OEMs that want to add their own proprietary data to call log backup should replace the 410f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // code above with their own namespace and add any additional data they need. 411f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // Versioning and size-prefixing the data should be done here as needed. 412f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // 413f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // Example: 414f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 415f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon /* 416f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon ByteArrayOutputStream baos = new ByteArrayOutputStream(); 417f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon DataOutputStream data = new DataOutputStream(baos); 418f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 419f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon String customData1 = "Generic OEM"; 420f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon int customData2 = 42; 421f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 422f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // Write a version for the data 423f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon data.writeInt(OEM_DATA_VERSION); 424f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 425f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // Write the data and flush 426f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon data.writeUTF(customData1); 427f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon data.writeInt(customData2); 428f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon data.flush(); 429f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 430f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon String oemNamespace = "com.oem.namespace"; 431f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon return new OEMData(oemNamespace, baos.toByteArray()); 432f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon */ 433f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon } 434f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 435f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon /** 436f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * Allows OEMs to read their own proprietary data when doing a call log restore. It is important 437f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * that the implementation verify the namespace of the data matches their expected value before 438f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * attempting to read the data or else you may risk reading invalid data. 439f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * 440f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon * See {@link #getOEMDataForCall} for information concerning proper testing of this code. 441f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon */ 442f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon private void readOEMDataForCall(Call call, OEMData oemData) { 443f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // OEMs that want to read proprietary data from a call log restore should do so here. 444f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // Before reading from the data, an OEM should verify that the data matches their 445f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // expected namespace. 446f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // 447f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // Example: 448f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 449f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon /* 450f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon if ("com.oem.expected.namespace".equals(oemData.namespace)) { 451f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon ByteArrayInputStream bais = new ByteArrayInputStream(oemData.bytes); 452f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon DataInputStream data = new DataInputStream(bais); 453f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 454f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // Check against this version as we read data. 455f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon int version = data.readInt(); 456f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon String customData1 = data.readUTF(); 457f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon int customData2 = data.readInt(); 458f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon // do something with data 459f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon } 460f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon */ 461f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon } 462f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 463f128d20ada7474e5714f81533b8f77e915315c1aSantos Cordon 46461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private void writeString(DataOutputStream data, String str) throws IOException { 46561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (str == null) { 46661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeBoolean(false); 46761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } else { 46861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeBoolean(true); 46961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon data.writeUTF(str); 47061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 47161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 47261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 47361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private String readString(DataInputStream data) throws IOException { 47461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon if (data.readBoolean()) { 47561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return data.readUTF(); 47661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } else { 47761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return null; 47861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 47961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 48061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 48161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private void removeCallFromBackup(BackupDataOutput output, int callId) { 48261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon try { 48361cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon output.writeEntityHeader(Integer.toString(callId), -1); 48461cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } catch (IOException e) { 48561cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon Log.e(TAG, "Failed to remove call: " + callId, e); 48661cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 48761cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 48861cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon 48961cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon private static boolean isDebug() { 49061cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon return Log.isLoggable(TAG, Log.DEBUG); 49161cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon } 49261cc930dd36a8d9efa02195fa7f55bbe87209788Santos Cordon} 493