Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c1edc4a8 authored by Beatrice Marchegiani's avatar Beatrice Marchegiani Committed by Android (Google) Code Review
Browse files

Merge "Add logic to persiste BMM events to a text file and print them in the...

Merge "Add logic to persiste BMM events to a text file and print them in the dumpsys function" into main
parents 834e9152 f1cc7cf1
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -133,6 +133,7 @@ import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.transport.TransportNotAvailableException;
import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorDumpsysUtils;
import com.android.server.backup.utils.BackupManagerMonitorEventSender;
import com.android.server.backup.utils.BackupObserverUtils;
import com.android.server.backup.utils.SparseArrayUtils;
@@ -142,6 +143,7 @@ import dalvik.annotation.optimization.NeverCompile;
import com.google.android.collect.Sets;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -150,6 +152,7 @@ import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
@@ -4161,6 +4164,7 @@ public class UserBackupManagerService {
                }
            }
            dumpInternal(pw);
            dumpBMMEvents(pw);
        } finally {
            Binder.restoreCallingIdentity(identityToken);
        }
@@ -4178,6 +4182,23 @@ public class UserBackupManagerService {
        }
    }

    private void dumpBMMEvents(PrintWriter pw) {
        BackupManagerMonitorDumpsysUtils bm =
                new BackupManagerMonitorDumpsysUtils();
        File events = bm.getBMMEventsFile();
        pw.println("START OF BACKUP MANAGER MONITOR EVENTS");
        try (BufferedReader reader = new BufferedReader(new FileReader(events))) {
            String line;
            while ((line = reader.readLine()) != null) {
                pw.println(line);
            }
        } catch (IOException e) {
            Slog.e(TAG, "IO Exception when reading BMM events from file: " + e);
            pw.println("IO Exception when reading BMM events from file");
        }
        pw.println("END OF BACKUP MANAGER MONITOR EVENTS");
    }

    @NeverCompile // Avoid size overhead of debugging code.
    private void dumpInternal(PrintWriter pw) {
        // Add prefix for only non-system users so that system user dumpsys is the same as before
+260 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.server.backup.utils;

import android.app.backup.BackupAnnotations;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupRestoreEventLogger;
import android.os.Bundle;
import android.os.Environment;
import android.util.Slog;

import com.android.internal.util.FastPrintWriter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;


/*
 * Util class to parse a BMM event and write it to a text file, to be the printed in
 * the backup dumpsys
 *
 * Note: this class is note thread safe
 */
public class BackupManagerMonitorDumpsysUtils {

    private static final String TAG = "BackupManagerMonitorDumpsysUtils";
    // Name of the subdirectory where the text file containing the BMM events will be stored.
    // Same as {@link UserBackupManagerFiles}
    private static final String BACKUP_PERSISTENT_DIR = "backup";

    /**
     * Parses the BackupManagerMonitor bundle for a RESTORE event in a series of strings that
     * will be persisted in a text file and printed in the dumpsys.
     *
     * If the evenntBundle passed is not a RESTORE event, return early
     *
     * Key information related to the event:
     * - Timestamp (HAS TO ALWAYS BE THE FIRST LINE OF EACH EVENT)
     * - Event ID
     * - Event Category
     * - Operation type
     * - Package name (can be null)
     * - Agent logs (if available)
     *
     * Example of formatting:
     * RESTORE Event: [2023-08-18 17:16:00.735] Agent - Agent logging results
     *     Package name: com.android.wallpaperbackup
     *     Agent Logs:
     *         Data Type: wlp_img_system
     *             Item restored: 0/1
     *             Agent Error - Category: no_wallpaper, Count: 1
     *         Data Type: wlp_img_lock
     *             Item restored: 0/1
     *             Agent Error - Category: no_wallpaper, Count: 1
     */
    public void parseBackupManagerMonitorRestoreEventForDumpsys(Bundle eventBundle) {
        if (eventBundle == null) {
            return;
        }

        if (!isOpTypeRestore(eventBundle)) {
            //We only log Restore events
            return;
        }

        if (!eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_ID)
                || !eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY)) {
            Slog.w(TAG, "Event id and category are not optional fields.");
            return;
        }
        File bmmEvents = getBMMEventsFile();

        try (FileOutputStream out = new FileOutputStream(bmmEvents, /*append*/ true);
            PrintWriter pw = new FastPrintWriter(out);) {

            int eventCategory = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
            int eventId = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);

            if (eventId == BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS &&
                    !hasAgentLogging(eventBundle)) {
                // Do not record an empty agent logging event
                return;
            }

            pw.println("RESTORE Event: [" + timestamp() + "] " +
                    getCategory(eventCategory) + " - " +
                    getId(eventId));

            if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME)) {
                pw.println("\tPackage name: "
                        + eventBundle.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME));
            }

            // TODO(b/296818666): add extras to the events
            addAgentLogsIfAvailable(eventBundle, pw);
        } catch (java.io.IOException e) {
            Slog.e(TAG, "IO Exception when writing BMM events to file: " + e);
        }

    }

    private boolean hasAgentLogging(Bundle eventBundle) {
        if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS)) {
            ArrayList<BackupRestoreEventLogger.DataTypeResult> agentLogs =
                    eventBundle.getParcelableArrayList(
                            BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS);

            return !agentLogs.isEmpty();
        }
        return false;
    }

    /**
     * Extracts agent logs from the BackupManagerMonitor event. These logs detail:
     * - the data type for the agent
     * - the count of successfully restored items
     * - the count of items that failed to restore
     * - the metadata associated with this datatype
     * - any errors
     */
    private void addAgentLogsIfAvailable(Bundle eventBundle, PrintWriter pw) {
        if (hasAgentLogging(eventBundle)) {
            pw.println("\tAgent Logs:");
            ArrayList<BackupRestoreEventLogger.DataTypeResult> agentLogs =
                    eventBundle.getParcelableArrayList(
                            BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS);
            for (BackupRestoreEventLogger.DataTypeResult result : agentLogs) {
                int totalItems = result.getFailCount() + result.getSuccessCount();
                pw.println("\t\tData Type: " + result.getDataType());
                pw.println("\t\t\tItem restored: " + result.getSuccessCount() + "/" +
                        totalItems);
                for (Map.Entry<String, Integer> entry : result.getErrors().entrySet()) {
                    pw.println("\t\t\tAgent Error - Category: " +
                            entry.getKey() + ", Count: " + entry.getValue());
                }
            }
        }
    }

    /*
     * Get the path of the text files which stores the BMM events
     */
    public File getBMMEventsFile() {
        File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
        File fname = new File(dataDir, "bmmevents.txt");
        return fname;
    }

    private String timestamp() {
        long currentTime = System.currentTimeMillis();
        Date date = new Date(currentTime);
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        return dateFormat.format(date);
    }

    private String getCategory(int code) {
        String category = switch (code) {
            case BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT -> "Transport";
            case BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT -> "Agent";
            case BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY ->
                    "Backup Manager Policy";
            default -> "Unknown category code: " + code;
        };
        return category;
    }

    private String getId(int code) {
        String id = switch (code) {
            case BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL -> "Full backup cancel";
            case BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY -> "Illegal key";
            case BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND -> "No data to send";
            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE -> "Package ineligible";
            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT ->
                    "Package key-value participant";
            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED -> "Package stopped";
            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND -> "Package not found";
            case BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED -> "Backup disabled";
            case BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED ->
                    "Device not provisioned";
            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT ->
                    "Package transport not present";
            case BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT -> "Error preflight";
            case BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT -> "Quota hit preflight";
            case BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP -> "Exception full backup";
            case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL ->
                    "Key-value backup cancel";
            case BackupManagerMonitor.LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE ->
                    "No restore metadata available";
            case BackupManagerMonitor.LOG_EVENT_ID_NO_PM_METADATA_RECEIVED ->
                    "No PM metadata received";
            case BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA ->
                    "PM agent has no metadata";
            case BackupManagerMonitor.LOG_EVENT_ID_LOST_TRANSPORT -> "Lost transport";
            case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_PRESENT -> "Package not present";
            case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER ->
                    "Restore version higher";
            case BackupManagerMonitor.LOG_EVENT_ID_APP_HAS_NO_AGENT -> "App has no agent";
            case BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH -> "Signature mismatch";
            case BackupManagerMonitor.LOG_EVENT_ID_CANT_FIND_AGENT -> "Can't find agent";
            case BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT ->
                    "Key-value restore timeout";
            case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION -> "Restore any version";
            case BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH -> "Versions match";
            case BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER ->
                    "Version of backup older";
            case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH ->
                    "Full restore signature mismatch";
            case BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT -> "System app no agent";
            case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE ->
                    "Full restore allow backup false";
            case BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED -> "APK not installed";
            case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK ->
                    "Cannot restore without APK";
            case BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE -> "Missing signature";
            case BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE ->
                    "Expected different package";
            case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION -> "Unknown version";
            case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_TIMEOUT -> "Full restore timeout";
            case BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST -> "Corrupt manifest";
            case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH ->
                    "Widget metadata mismatch";
            case BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION ->
                    "Widget unknown version";
            case BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES -> "No packages";
            case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL -> "Transport is null";
            case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED ->
                    "Transport non-incremental backup required";
            case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS -> "Agent logging results";
            default -> "Unknown log event ID: " + code;
        };
        return id;
    }

    private boolean isOpTypeRestore(Bundle eventBundle) {
        return switch (eventBundle.getInt(
                BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, -1)) {
            case BackupAnnotations.OperationType.RESTORE -> true;
            default -> false;
        };
    }
}
+39 −20
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;

import java.util.List;
@@ -54,9 +55,17 @@ public class BackupManagerMonitorEventSender {
     */
    private static final int AGENT_LOGGER_RESULTS_TIMEOUT_MILLIS = 500;
    @Nullable private IBackupManagerMonitor mMonitor;

    private final BackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtils;
    public BackupManagerMonitorEventSender(@Nullable IBackupManagerMonitor monitor) {
        mMonitor = monitor;
        mBackupManagerMonitorDumpsysUtils = new BackupManagerMonitorDumpsysUtils();
    }

    @VisibleForTesting
    BackupManagerMonitorEventSender(@Nullable IBackupManagerMonitor monitor,
            BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils) {
        mMonitor = monitor;
        mBackupManagerMonitorDumpsysUtils = backupManagerMonitorDumpsysUtils;
    }

    public void setMonitor(IBackupManagerMonitor monitor) {
@@ -82,7 +91,6 @@ public class BackupManagerMonitorEventSender {
            PackageInfo pkg,
            int category,
            Bundle extras) {
        if (mMonitor != null) {
        try {
            Bundle bundle = new Bundle();
            bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, id);
@@ -97,8 +105,20 @@ public class BackupManagerMonitorEventSender {
            }
            if (extras != null) {
                bundle.putAll(extras);
                if (extras.containsKey(EXTRA_LOG_OPERATION_TYPE) &&
                        extras.getInt(EXTRA_LOG_OPERATION_TYPE) == OperationType.RESTORE){
                    mBackupManagerMonitorDumpsysUtils
                            .parseBackupManagerMonitorRestoreEventForDumpsys(bundle);
                }
            }

            if (mMonitor != null) {
                mMonitor.onEvent(bundle);
            } else {
                if (DEBUG) {
                    Slog.w(TAG, "backup manager monitor is null unable to send event");
                }
            }
        } catch (RemoteException e) {
            mMonitor = null;
            if (DEBUG) {
@@ -106,7 +126,6 @@ public class BackupManagerMonitorEventSender {
            }
        }
    }
    }

    /**
     * Extracts logging results from the provided {@code agent} and notifies the {@code monitor}
@@ -120,7 +139,7 @@ public class BackupManagerMonitorEventSender {
     */
    public void monitorAgentLoggingResults(PackageInfo pkg, IBackupAgent agent) {
        if (mMonitor == null) {
            return;
            Slog.i(TAG, "backup manager monitor is null unable to send event"+pkg);
        }

        try {
+84 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.server.backup.utils;

import static org.junit.Assert.assertTrue;

import android.app.backup.BackupManagerMonitor;
import android.os.Bundle;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;

public class BackupManagerMonitorDumpsysUtilsTest {
    private File mTempFile;
    private TestBackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtils;
    @Rule
    public TemporaryFolder tmp = new TemporaryFolder();

    @Before
    public void setUp() throws Exception {
        mTempFile = tmp.newFile("testbmmevents.txt");
        mBackupManagerMonitorDumpsysUtils = new TestBackupManagerMonitorDumpsysUtils();
    }


    @Test
    public void parseBackupManagerMonitorEventForDumpsys_bundleIsNull_noLogsWrittenToFile()
            throws Exception {
        mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(null);

        assertTrue(mTempFile.length() == 0);

    }

    @Test
    public void parseBackupManagerMonitorEventForDumpsys_missingID_noLogsWrittenToFile()
            throws Exception {
        Bundle event = new Bundle();
        event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, 1);
        mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);

        assertTrue(mTempFile.length() == 0);
    }

    @Test
    public void parseBackupManagerMonitorEventForDumpsys_missingCategory_noLogsWrittenToFile()
            throws Exception {
        Bundle event = new Bundle();
        event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, 1);
        mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);

        assertTrue(mTempFile.length() == 0);
    }

    private class TestBackupManagerMonitorDumpsysUtils
            extends BackupManagerMonitorDumpsysUtils {
        TestBackupManagerMonitorDumpsysUtils() {
            super();
        }

        @Override
        public File getBMMEventsFile() {
            return mTempFile;
        }
    }
}
+58 −5
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

@@ -63,26 +64,40 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class BackupManagerMonitorEventSenderTest {
    @Mock private IBackupManagerMonitor mMonitorMock;
    @Mock private BackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtilsMock;

    private BackupManagerMonitorEventSender mBackupManagerMonitorEventSender;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(mMonitorMock);
        mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(mMonitorMock,
                mBackupManagerMonitorDumpsysUtilsMock);
    }

    @Test
    public void monitorEvent_monitorIsNull_returnsNull() throws Exception {
        mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(/* monitor */ null);
    public void monitorEvent_monitorIsNull_sendBundleToDumpsys() throws Exception {
        Bundle extras = new Bundle();
        extras.putInt(EXTRA_LOG_OPERATION_TYPE, OperationType.RESTORE);
        mBackupManagerMonitorEventSender.setMonitor(null);
        mBackupManagerMonitorEventSender.monitorEvent(0, null, 0, extras);
        IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();

        verify(mBackupManagerMonitorDumpsysUtilsMock).parseBackupManagerMonitorRestoreEventForDumpsys(any(
                Bundle.class));
    }

    @Test
    public void monitorEvent_monitorIsNull_doNotCallOnEvent() throws Exception {
        mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(null);
        mBackupManagerMonitorEventSender.monitorEvent(0, null, 0, null);
        IBackupManagerMonitor monitor = mBackupManagerMonitorEventSender.getMonitor();

        assertThat(monitor).isNull();
        verify(mMonitorMock, never()).onEvent(any(Bundle.class));
    }

    @Test
    public void monitorEvent_monitorOnEventThrows_returnsNull() throws Exception {
    public void monitorEvent_monitorOnEventThrows_setsMonitorToNull() throws Exception {
        doThrow(new RemoteException()).when(mMonitorMock).onEvent(any(Bundle.class));

        mBackupManagerMonitorEventSender.monitorEvent(0, null, 0, null);
@@ -92,6 +107,14 @@ public class BackupManagerMonitorEventSenderTest {
        assertThat(monitor).isNull();
    }

    @Test
    public void monitorEvent_extrasAreNull_doNotSendBundleToDumpsys() throws Exception {
        mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, null);

        verify(mBackupManagerMonitorDumpsysUtilsMock, never())
                .parseBackupManagerMonitorRestoreEventForDumpsys(any(Bundle.class));
    }

    @Test
    public void monitorEvent_packageAndExtrasAreNull_fillsBundleCorrectly() throws Exception {
        mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, null);
@@ -161,6 +184,36 @@ public class BackupManagerMonitorEventSenderTest {
        assertThat(eventBundle.getString("key2")).isEqualTo("value2");
    }

    @Test
    public void monitorEvent_eventOpTypeIsRestore_sendBundleToDumpsys() throws Exception {
        Bundle extras = new Bundle();
        extras.putInt(EXTRA_LOG_OPERATION_TYPE, OperationType.RESTORE);
        mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, extras);

        verify(mBackupManagerMonitorDumpsysUtilsMock).parseBackupManagerMonitorRestoreEventForDumpsys(any(
                Bundle.class));
    }

    @Test
    public void monitorEvent_eventOpTypeIsBackup_doNotSendBundleToDumpsys() throws Exception {
        Bundle extras = new Bundle();
        extras.putInt(EXTRA_LOG_OPERATION_TYPE, OperationType.BACKUP);
        mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, extras);

        verify(mBackupManagerMonitorDumpsysUtilsMock, never())
                .parseBackupManagerMonitorRestoreEventForDumpsys(any(Bundle.class));
    }

    @Test
    public void monitorEvent_eventOpTypeIsUnknown_doNotSendBundleToDumpsys() throws Exception {
        Bundle extras = new Bundle();
        extras.putInt(EXTRA_LOG_OPERATION_TYPE, OperationType.UNKNOWN);
        mBackupManagerMonitorEventSender.monitorEvent(1, null, 2, extras);

        verify(mBackupManagerMonitorDumpsysUtilsMock, never())
                .parseBackupManagerMonitorRestoreEventForDumpsys(any(Bundle.class));
    }

    @Test
    public void monitorAgentLoggingResults_onBackup_fillsBundleCorrectly() throws Exception {
        PackageInfo packageInfo = new PackageInfo();