Snap for 10447354 from e225f918a40322f7f16f007987d965dbcab56bee to mainline-wifi-release

Change-Id: Ic08acc5d954b01f5be98b1e9458b6092e7a9dcb8
diff --git a/src/com/android/calllogbackup/CallLogBackupAgent.java b/src/com/android/calllogbackup/CallLogBackupAgent.java
index 8b58f99..4bb55b0 100644
--- a/src/com/android/calllogbackup/CallLogBackupAgent.java
+++ b/src/com/android/calllogbackup/CallLogBackupAgent.java
@@ -21,6 +21,8 @@
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupRestoreEventLogger;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.database.Cursor;
@@ -108,6 +110,22 @@
 
     private static final String TAG = "CallLogBackupAgent";
 
+    /** Data types and errors used when reporting B&R success rate and errors.  */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    @VisibleForTesting
+    static final String CALLLOGS = "telecom_call_logs";
+
+    @BackupRestoreEventLogger.BackupRestoreError
+    static final String ERROR_UNEXPECTED_KEY = "unexpected_key";
+    @BackupRestoreEventLogger.BackupRestoreError
+    static final String ERROR_END_OEM_MARKER_NOT_FOUND = "end_oem_marker_not_found";
+    @BackupRestoreEventLogger.BackupRestoreError
+    static final String ERROR_READING_CALL_DATA = "error_reading_call_data";
+    @BackupRestoreEventLogger.BackupRestoreError
+    static final String ERROR_BACKUP_CALL_FAILED = "backup_call_failed";
+
+    private BackupRestoreEventLogger mLogger;
+
     /** Current version of CallLogBackup. Used to track the backup format. */
     @VisibleForTesting
     static final int VERSION = 1009;
@@ -151,6 +169,56 @@
         CallLog.Calls.IS_PHONE_ACCOUNT_MIGRATION_PENDING
     };
 
+    /**
+     * BackupRestoreEventLogger Dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface BackupRestoreEventLoggerProxy {
+        void logItemsBackedUp(String dataType, int count);
+        void logItemsBackupFailed(String dataType, int count, String error);
+        void logItemsRestored(String dataType, int count);
+        void logItemsRestoreFailed(String dataType, int count, String error);
+    }
+
+    private BackupRestoreEventLoggerProxy mBackupRestoreEventLoggerProxy =
+            new BackupRestoreEventLoggerProxy() {
+        @Override
+        public void logItemsBackedUp(String dataType, int count) {
+            mLogger.logItemsBackedUp(dataType, count);
+        }
+
+        @Override
+        public void logItemsBackupFailed(String dataType, int count, String error) {
+            mLogger.logItemsBackupFailed(dataType, count, error);
+        }
+
+        @Override
+        public void logItemsRestored(String dataType, int count) {
+            mLogger.logItemsRestored(dataType, count);
+        }
+
+        @Override
+        public void logItemsRestoreFailed(String dataType, int count, String error) {
+            mLogger.logItemsRestoreFailed(dataType, count, error);
+        }
+    };
+
+    /**
+     * Overrides BackupRestoreEventLogger dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setBackupRestoreEventLoggerProxy(BackupRestoreEventLoggerProxy proxy) {
+        mBackupRestoreEventLoggerProxy = proxy;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.d(TAG, "onCreate");
+        BackupManager backupManager = new BackupManager(getApplicationContext());
+        mLogger = backupManager.getBackupRestoreEventLogger(/* backupAgent */ this);
+    }
+
     /** ${inheritDoc} */
     @Override
     public void onBackup(ParcelFileDescriptor oldStateDescriptor, BackupDataOutput data,
@@ -205,6 +273,7 @@
             Call call = readCallFromData(data);
             if (call != null && call.type != Calls.VOICEMAIL_TYPE) {
                 writeCallToProvider(call);
+                mBackupRestoreEventLoggerProxy.logItemsRestored(CALLLOGS, /* count */ 1);
                 if (isDebug()) {
                     Log.d(TAG, "Restored call: " + call);
                 }
@@ -233,6 +302,7 @@
                 // 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);
+                mBackupRestoreEventLoggerProxy.logItemsBackedUp(CALLLOGS, /* count */ 1);
             }
         }
 
@@ -339,6 +409,8 @@
         try {
             callId = Integer.parseInt(data.getKey());
         } catch (NumberFormatException e) {
+            mBackupRestoreEventLoggerProxy.logItemsRestoreFailed(
+                    CALLLOGS, /* count */ 1, ERROR_UNEXPECTED_KEY);
             Log.e(TAG, "Unexpected key found in restore: " + data.getKey());
             return null;
         }
@@ -374,6 +446,8 @@
 
                 int marker = dataInput.readInt();
                 if (marker != END_OEM_DATA_MARKER) {
+                    mBackupRestoreEventLoggerProxy.logItemsRestoreFailed(CALLLOGS, /* count */ 1,
+                            ERROR_END_OEM_MARKER_NOT_FOUND);
                     Log.e(TAG, "Did not find END-OEM marker for call " + call.id);
                     // The marker does not match the expected value, ignore this call completely.
                     return null;
@@ -432,6 +506,8 @@
             }
             return call;
         } catch (IOException e) {
+            mBackupRestoreEventLoggerProxy.logItemsRestoreFailed(
+                    CALLLOGS, /* count */ 1, ERROR_READING_CALL_DATA);
             Log.e(TAG, "Error reading call data for " + callId, e);
             return null;
         }
@@ -566,10 +642,14 @@
             output.writeEntityHeader(Integer.toString(call.id), baos.size());
             output.writeEntityData(baos.toByteArray(), baos.size());
 
+            mBackupRestoreEventLoggerProxy.logItemsBackedUp(CALLLOGS, /* count */ 1);
+
             if (isDebug()) {
                 Log.d(TAG, "Wrote call to backup: " + call + " with byte array: " + baos);
             }
-        } catch (IOException e) {
+        } catch (Exception e) {
+            mBackupRestoreEventLoggerProxy.logItemsBackupFailed(
+                    CALLLOGS, /* count */ 1, ERROR_BACKUP_CALL_FAILED);
             Log.e(TAG, "Failed to backup call: " + call, e);
         }
     }
diff --git a/tests/src/com/android/calllogbackup/CallLogBackupAgentTest.java b/tests/src/com/android/calllogbackup/CallLogBackupAgentTest.java
index 4567e40..de681e5 100644
--- a/tests/src/com/android/calllogbackup/CallLogBackupAgentTest.java
+++ b/tests/src/com/android/calllogbackup/CallLogBackupAgentTest.java
@@ -16,6 +16,8 @@
 
 package com.android.calllogbackup;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.eq;
@@ -41,6 +43,7 @@
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.EOFException;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -57,11 +60,38 @@
     static final String TEST_PHONE_ACCOUNT_HANDLE_SUB_ID = "666";
     static final int TEST_PHONE_ACCOUNT_HANDLE_SUB_ID_INT = 666;
     static final String TEST_PHONE_ACCOUNT_HANDLE_ICC_ID = "891004234814455936F";
+
+    public int backupRestoreLoggerSuccessCount = 0;
+    public int backupRestoreLoggerFailCount = 0;
+
     @Mock DataInput mDataInput;
     @Mock DataOutput mDataOutput;
     @Mock BackupDataOutput mBackupDataOutput;
     @Mock Cursor mCursor;
 
+    private CallLogBackupAgent.BackupRestoreEventLoggerProxy mBackupRestoreEventLoggerProxy =
+            new CallLogBackupAgent.BackupRestoreEventLoggerProxy() {
+        @Override
+        public void logItemsBackedUp(String dataType, int count) {
+            backupRestoreLoggerSuccessCount += count;
+        }
+
+        @Override
+        public void logItemsBackupFailed(String dataType, int count, String error) {
+            backupRestoreLoggerFailCount += count;
+        }
+
+        @Override
+        public void logItemsRestored(String dataType, int count) {
+            backupRestoreLoggerSuccessCount += count;
+        }
+
+        @Override
+        public void logItemsRestoreFailed(String dataType, int count, String error) {
+            backupRestoreLoggerFailCount += count;
+        }
+    };
+
     CallLogBackupAgent mCallLogBackupAgent;
 
     MockitoHelper mMockitoHelper = new MockitoHelper();
@@ -77,6 +107,7 @@
         MockitoAnnotations.initMocks(this);
 
         mCallLogBackupAgent = new CallLogBackupAgent();
+        mCallLogBackupAgent.setBackupRestoreEventLoggerProxy(mBackupRestoreEventLoggerProxy);
     }
 
     @Override
@@ -178,9 +209,45 @@
 
         mCallLogBackupAgent.runBackup(state, mBackupDataOutput, calls);
 
+        // Ensure the {@link BackupRestoreEventLogger} is not notified as no calls were backed up:
+        assertEquals(backupRestoreLoggerSuccessCount, 0);
+        assertEquals(backupRestoreLoggerFailCount, 0);
+
         Mockito.verifyNoMoreInteractions(mBackupDataOutput);
     }
 
+    public void testRunBackup_OneNewCall_ErrorAddingCall() throws Exception {
+        CallLogBackupState state = new CallLogBackupState();
+        state.version = CallLogBackupAgent.VERSION;
+        state.callIds = new TreeSet<>();
+        List<Call> calls = new LinkedList<>();
+        calls.add(makeCall(101, 0L, 0L, "555-5555"));
+
+        // Throw an exception when the call is added to the backup:
+        when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt()))
+                .thenThrow(IOException.class);
+        mCallLogBackupAgent.runBackup(state, mBackupDataOutput, calls);
+
+        // Ensure the {@link BackupRestoreEventLogger} is informed of the failed backed up call:
+        assertEquals(backupRestoreLoggerSuccessCount, 0);
+        assertEquals(backupRestoreLoggerFailCount, 1);
+    }
+
+    public void testRunBackup_OneNewCall_NullBackupDataOutput() throws Exception {
+        CallLogBackupState state = new CallLogBackupState();
+        state.version = CallLogBackupAgent.VERSION;
+        state.callIds = new TreeSet<>();
+        List<Call> calls = new LinkedList<>();
+        calls.add(makeCall(101, 0L, 0L, "555-5555"));
+
+        // Invoke runBackup() with a null value for BackupDataOutput causing an exception:
+        mCallLogBackupAgent.runBackup(state, null, calls);
+
+        // Ensure the {@link BackupRestoreEventLogger} is informed of the failed backed up call:
+        assertEquals(backupRestoreLoggerSuccessCount, 0);
+        assertEquals(backupRestoreLoggerFailCount, 1);
+    }
+
     public void testRunBackup_OneNewCall() throws Exception {
         CallLogBackupState state = new CallLogBackupState();
         state.version = CallLogBackupAgent.VERSION;
@@ -189,6 +256,10 @@
         calls.add(makeCall(101, 0L, 0L, "555-5555"));
         mCallLogBackupAgent.runBackup(state, mBackupDataOutput, calls);
 
+        // Ensure the {@link BackupRestoreEventLogger} is informed of the backed up call:
+        assertEquals(backupRestoreLoggerSuccessCount, 1);
+        assertEquals(backupRestoreLoggerFailCount, 0);
+
         verify(mBackupDataOutput).writeEntityHeader(eq("101"), Matchers.anyInt());
         verify(mBackupDataOutput).writeEntityData((byte[]) Matchers.any(), Matchers.anyInt());
     }
@@ -274,6 +345,10 @@
 
         mCallLogBackupAgent.runBackup(state, mBackupDataOutput, calls);
 
+        // Ensure the {@link BackupRestoreEventLogger} is informed of the 2 backed up calls:
+        assertEquals(backupRestoreLoggerSuccessCount, 2);
+        assertEquals(backupRestoreLoggerFailCount, 0);
+
         InOrder inOrder = Mockito.inOrder(mBackupDataOutput);
         inOrder.verify(mBackupDataOutput).writeEntityHeader(eq("101"), Matchers.anyInt());
         inOrder.verify(mBackupDataOutput).
@@ -296,6 +371,10 @@
 
         mCallLogBackupAgent.runBackup(state, mBackupDataOutput, calls);
 
+        // Ensure the {@link BackupRestoreEventLogger} is informed of the 2 backed up calls:
+        assertEquals(backupRestoreLoggerSuccessCount, 2);
+        assertEquals(backupRestoreLoggerFailCount, 0);
+
         InOrder inOrder = Mockito.inOrder(mBackupDataOutput);
         inOrder.verify(mBackupDataOutput).writeEntityHeader(eq("102"), Matchers.anyInt());
         inOrder.verify(mBackupDataOutput).