Add backup logging to WallpaperBackupAgent

Bug: 268471749
Test: atest WallpaperEventLoggerTest WallpaperBackupAgentTest
Change-Id: I9e040383e64adc76cc308450c9c112c8dd5dbe32
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp
index d142f25..8acc508 100644
--- a/packages/WallpaperBackup/Android.bp
+++ b/packages/WallpaperBackup/Android.bp
@@ -42,7 +42,7 @@
     srcs: [
         // Include the app source code because the app runs as the system user on-device.
         "src/**/*.java",
-        "test/src/**/*.java"
+        "test/src/**/*.java",
     ],
     libs: [
         "android.test.base",
@@ -54,7 +54,8 @@
         "mockito-target-minus-junit4",
         "truth-prebuilt",
     ],
+    resource_dirs: ["test/res"],
     certificate: "platform",
     platform_apis: true,
-    test_suites: ["device-tests"]
+    test_suites: ["device-tests"],
 }
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index e549b61..6aca2fd 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -19,11 +19,18 @@
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.FLAG_SYSTEM;
 
+import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE;
+import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_METADATA;
+import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_WALLPAPER;
+import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_QUOTA_EXCEEDED;
+
 import android.app.AppGlobals;
 import android.app.WallpaperManager;
 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.BackupRestoreError;
 import android.app.backup.FullBackupDataOutput;
 import android.content.ComponentName;
 import android.content.Context;
@@ -103,6 +110,10 @@
     private boolean mQuotaExceeded;
 
     private WallpaperManager mWallpaperManager;
+    private WallpaperEventLogger mEventLogger;
+
+    private boolean mSystemHasLiveComponent;
+    private boolean mLockHasLiveComponent;
 
     @Override
     public void onCreate() {
@@ -117,6 +128,9 @@
         if (DEBUG) {
             Slog.v(TAG, "quota file " + mQuotaFile.getPath() + " exists=" + mQuotaExceeded);
         }
+
+        BackupManager backupManager = new BackupManager(getApplicationContext());
+        mEventLogger = new WallpaperEventLogger(backupManager, /* wallpaperAgent */ this);
     }
 
     @Override
@@ -149,11 +163,18 @@
                 Slog.v(TAG, "lockGen=" + lockGeneration + " : lockChanged=" + lockChanged);
             }
 
+            // Due to the way image vs live wallpaper backup logic is intermingled, for logging
+            // purposes first check if we have live components for each wallpaper to avoid
+            // over-reporting errors.
+            mSystemHasLiveComponent = mWallpaperManager.getWallpaperInfo(FLAG_SYSTEM) != null;
+            mLockHasLiveComponent = mWallpaperManager.getWallpaperInfo(FLAG_LOCK) != null;
+
             backupWallpaperInfoFile(/* sysOrLockChanged= */ sysChanged || lockChanged, data);
             backupSystemWallpaperFile(sharedPrefs, sysChanged, sysGeneration, data);
             backupLockWallpaperFileIfItExists(sharedPrefs, lockChanged, lockGeneration, data);
         } catch (Exception e) {
             Slog.e(TAG, "Unable to back up wallpaper", e);
+            mEventLogger.onBackupException(e);
         } finally {
             // Even if this time we had to back off on attempting to store the lock image
             // due to exceeding the data quota, try again next time.  This will alternate
@@ -170,6 +191,14 @@
 
         if (wallpaperInfoFd == null) {
             Slog.w(TAG, "Wallpaper metadata file doesn't exist");
+            // If we have live components, getting the file to back up somehow failed, so log it
+            // as an error.
+            if (mSystemHasLiveComponent) {
+                mEventLogger.onSystemLiveWallpaperBackupFailed(ERROR_NO_METADATA);
+            }
+            if (mLockHasLiveComponent) {
+                mEventLogger.onLockLiveWallpaperBackupFailed(ERROR_NO_METADATA);
+            }
             return;
         }
 
@@ -182,12 +211,22 @@
 
         if (DEBUG) Slog.v(TAG, "Storing wallpaper metadata");
         backupFile(infoStage, data);
+
+        // We've backed up the info file which contains the live component, so log it as success
+        if (mSystemHasLiveComponent) {
+            mEventLogger.onSystemLiveWallpaperBackedUp(
+                    mWallpaperManager.getWallpaperInfo(FLAG_SYSTEM));
+        }
+        if (mLockHasLiveComponent) {
+            mEventLogger.onLockLiveWallpaperBackedUp(mWallpaperManager.getWallpaperInfo(FLAG_LOCK));
+        }
     }
 
     private void backupSystemWallpaperFile(SharedPreferences sharedPrefs,
             boolean sysChanged, int sysGeneration, FullBackupDataOutput data) throws IOException {
         if (!mWallpaperManager.isWallpaperBackupEligible(FLAG_SYSTEM)) {
             Slog.d(TAG, "System wallpaper ineligible for backup");
+            logSystemImageErrorIfNoLiveComponent(ERROR_INELIGIBLE);
             return;
         }
 
@@ -197,6 +236,7 @@
 
         if (systemWallpaperImageFd == null) {
             Slog.w(TAG, "System wallpaper doesn't exist");
+            logSystemImageErrorIfNoLiveComponent(ERROR_NO_WALLPAPER);
             return;
         }
 
@@ -210,8 +250,17 @@
         if (DEBUG) Slog.v(TAG, "Storing system wallpaper image");
         backupFile(imageStage, data);
         sharedPrefs.edit().putInt(SYSTEM_GENERATION, sysGeneration).apply();
+        mEventLogger.onSystemImageWallpaperBackedUp();
     }
 
+    private void logSystemImageErrorIfNoLiveComponent(@BackupRestoreError String error) {
+        if (mSystemHasLiveComponent) {
+            return;
+        }
+        mEventLogger.onSystemImageWallpaperBackupFailed(error);
+    }
+
+
     private void backupLockWallpaperFileIfItExists(SharedPreferences sharedPrefs,
             boolean lockChanged, int lockGeneration, FullBackupDataOutput data) throws IOException {
         final File lockImageStage = new File(getFilesDir(), LOCK_WALLPAPER_STAGE);
@@ -224,11 +273,13 @@
             }
             Slog.d(TAG, "No lockscreen wallpaper set, add nothing to backup");
             sharedPrefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply();
+            logLockImageErrorIfNoLiveComponent(ERROR_NO_WALLPAPER);
             return;
         }
 
         if (!mWallpaperManager.isWallpaperBackupEligible(FLAG_LOCK)) {
             Slog.d(TAG, "Lock screen wallpaper ineligible for backup");
+            logLockImageErrorIfNoLiveComponent(ERROR_INELIGIBLE);
             return;
         }
 
@@ -239,11 +290,13 @@
         // set, but we can't find it.
         if (lockWallpaperFd == null) {
             Slog.w(TAG, "Lock wallpaper doesn't exist");
+            logLockImageErrorIfNoLiveComponent(ERROR_NO_WALLPAPER);
             return;
         }
 
         if (mQuotaExceeded) {
             Slog.w(TAG, "Not backing up lock screen wallpaper. Quota was exceeded last time");
+            logLockImageErrorIfNoLiveComponent(ERROR_QUOTA_EXCEEDED);
             return;
         }
 
@@ -255,6 +308,14 @@
         if (DEBUG) Slog.v(TAG, "Storing lock wallpaper image");
         backupFile(lockImageStage, data);
         sharedPrefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply();
+        mEventLogger.onLockImageWallpaperBackedUp();
+    }
+
+    private void logLockImageErrorIfNoLiveComponent(@BackupRestoreError String error) {
+        if (mLockHasLiveComponent) {
+            return;
+        }
+        mEventLogger.onLockImageWallpaperBackupFailed(error);
     }
 
     /**
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java
new file mode 100644
index 0000000..64944b3
--- /dev/null
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java
@@ -0,0 +1,139 @@
+/*
+ * 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.wallpaperbackup;
+
+import android.annotation.Nullable;
+import android.app.WallpaperInfo;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupRestoreEventLogger;
+import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType;
+import android.app.backup.BackupRestoreEventLogger.BackupRestoreError;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Log backup / restore related events using {@link BackupRestoreEventLogger}.
+ */
+public class WallpaperEventLogger {
+    /* Static image used as system (or home) screen wallpaper */
+    @BackupRestoreDataType
+    @VisibleForTesting
+    static final String WALLPAPER_IMG_SYSTEM = "wlp_img_system";
+
+    /* Static image used as lock screen wallpaper */
+    @BackupRestoreDataType
+    @VisibleForTesting
+    static final String WALLPAPER_IMG_LOCK = "wlp_img_lock";
+
+    /* Live component used as system (or home) screen wallpaper */
+    @BackupRestoreDataType
+    @VisibleForTesting
+    static final String WALLPAPER_LIVE_SYSTEM = "wlp_live_system";
+
+    /* Live component used as lock screen wallpaper */
+    @BackupRestoreDataType
+    @VisibleForTesting
+    static final String WALLPAPER_LIVE_LOCK = "wlp_live_lock";
+
+    @BackupRestoreError
+    static final String ERROR_INELIGIBLE = "ineligible";
+    @BackupRestoreError
+    static final String ERROR_NO_METADATA = "no_metadata";
+    @BackupRestoreError
+    static final String ERROR_NO_WALLPAPER = "no_wallpaper";
+    @BackupRestoreError
+    static final String ERROR_QUOTA_EXCEEDED = "quota_exceeded";
+
+    private final BackupRestoreEventLogger mLogger;
+
+    private final Set<String> mProcessedDataTypes = new HashSet<>();
+
+    WallpaperEventLogger(BackupManager backupManager, WallpaperBackupAgent wallpaperAgent) {
+        mLogger = backupManager.getBackupRestoreEventLogger(/* backupAgent */ wallpaperAgent);
+    }
+
+    void onSystemImageWallpaperBackedUp() {
+        logBackupSuccessInternal(WALLPAPER_IMG_SYSTEM, /* liveComponentWallpaperInfo */ null);
+    }
+
+    void onLockImageWallpaperBackedUp() {
+        logBackupSuccessInternal(WALLPAPER_IMG_LOCK, /* liveComponentWallpaperInfo */ null);
+    }
+
+    void onSystemLiveWallpaperBackedUp(WallpaperInfo wallpaperInfo) {
+        logBackupSuccessInternal(WALLPAPER_LIVE_SYSTEM, wallpaperInfo);
+    }
+
+    void onLockLiveWallpaperBackedUp(WallpaperInfo wallpaperInfo) {
+        logBackupSuccessInternal(WALLPAPER_LIVE_LOCK, wallpaperInfo);
+    }
+
+    void onSystemImageWallpaperBackupFailed(@BackupRestoreError String error) {
+        logBackupFailureInternal(WALLPAPER_IMG_SYSTEM, error);
+    }
+
+    void onLockImageWallpaperBackupFailed(@BackupRestoreError String error) {
+        logBackupFailureInternal(WALLPAPER_IMG_LOCK, error);
+    }
+
+    void onSystemLiveWallpaperBackupFailed(@BackupRestoreError String error) {
+        logBackupFailureInternal(WALLPAPER_LIVE_SYSTEM, error);
+    }
+
+    void onLockLiveWallpaperBackupFailed(@BackupRestoreError String error) {
+        logBackupFailureInternal(WALLPAPER_LIVE_LOCK, error);
+    }
+
+
+    /**
+     * Called when the whole backup flow is interrupted by an exception.
+     */
+    void onBackupException(Exception exception) {
+        String error = exception.getClass().getName();
+        if (!mProcessedDataTypes.contains(WALLPAPER_IMG_SYSTEM) && !mProcessedDataTypes.contains(
+                WALLPAPER_LIVE_SYSTEM)) {
+            mLogger.logItemsBackupFailed(WALLPAPER_IMG_SYSTEM, /* count */ 1, error);
+        }
+        if (!mProcessedDataTypes.contains(WALLPAPER_IMG_LOCK) && !mProcessedDataTypes.contains(
+                WALLPAPER_LIVE_LOCK)) {
+            mLogger.logItemsBackupFailed(WALLPAPER_IMG_LOCK, /* count */ 1, error);
+        }
+    }
+
+    private void logBackupSuccessInternal(@BackupRestoreDataType String which,
+            @Nullable WallpaperInfo liveComponentWallpaperInfo) {
+        mLogger.logItemsBackedUp(which, /* count */ 1);
+        logLiveWallpaperNameIfPresent(which, liveComponentWallpaperInfo);
+        mProcessedDataTypes.add(which);
+    }
+
+    private void logBackupFailureInternal(@BackupRestoreDataType String which,
+            @BackupRestoreError String error) {
+        mLogger.logItemsBackupFailed(which, /* count */ 1, error);
+        mProcessedDataTypes.add(which);
+    }
+
+    private void logLiveWallpaperNameIfPresent(@BackupRestoreDataType String wallpaperType,
+            WallpaperInfo wallpaperInfo) {
+        if (wallpaperInfo != null) {
+            mLogger.logBackupMetadata(wallpaperType, wallpaperInfo.getComponent().getClassName());
+        }
+    }
+}
diff --git a/packages/WallpaperBackup/test/AndroidManifest.xml b/packages/WallpaperBackup/test/AndroidManifest.xml
index 44ab1b6..eb1e98b 100644
--- a/packages/WallpaperBackup/test/AndroidManifest.xml
+++ b/packages/WallpaperBackup/test/AndroidManifest.xml
@@ -4,6 +4,21 @@
 
     <application android:label="WallpaperBackup Tests">
         <uses-library android:name="android.test.runner" />
+        <service android:name="com.android.wallpaperbackup.utils.TestWallpaperService"
+                 android:enabled="true"
+                 android:directBootAware="true"
+                 android:label="Test wallpaper"
+                 android:permission="android.permission.BIND_WALLPAPER"
+                 android:exported="true">
+
+            <intent-filter>
+                <action android:name="android.service.wallpaper.WallpaperService"/>
+            </intent-filter>
+
+            <!-- Link to XML that defines the wallpaper info. -->
+            <meta-data android:name="android.service.wallpaper"
+                       android:resource="@xml/livewallpaper"/>
+        </service>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/packages/WallpaperBackup/test/res/xml/livewallpaper.xml b/packages/WallpaperBackup/test/res/xml/livewallpaper.xml
new file mode 100644
index 0000000..c6fbe2bda
--- /dev/null
+++ b/packages/WallpaperBackup/test/res/xml/livewallpaper.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<wallpaper/>
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 20dd5165..89459f6 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -23,22 +23,40 @@
 import static com.android.wallpaperbackup.WallpaperBackupAgent.LOCK_WALLPAPER_STAGE;
 import static com.android.wallpaperbackup.WallpaperBackupAgent.SYSTEM_WALLPAPER_STAGE;
 import static com.android.wallpaperbackup.WallpaperBackupAgent.WALLPAPER_INFO_STAGE;
+import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE;
+import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_WALLPAPER;
+import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_QUOTA_EXCEEDED;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_LOCK;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_SYSTEM;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_LOCK;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_SYSTEM;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
+import android.app.backup.BackupAnnotations;
+import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
 import android.app.backup.FullBackupDataOutput;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.service.wallpaper.WallpaperService;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -69,12 +87,18 @@
     private static final int TEST_SYSTEM_WALLPAPER_ID = 1;
     private static final int TEST_LOCK_WALLPAPER_ID = 2;
     private static final int NO_LOCK_WALLPAPER_ID = -1;
+    // An arbitrary user.
+    private static final UserHandle USER_HANDLE = new UserHandle(15);
 
-    @Mock private FullBackupDataOutput mOutput;
-    @Mock private WallpaperManager mWallpaperManager;
-    @Mock private Context mMockContext;
+    @Mock
+    private FullBackupDataOutput mOutput;
+    @Mock
+    private WallpaperManager mWallpaperManager;
+    @Mock
+    private Context mMockContext;
 
-    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+    @Rule
+    public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
 
     private ContextWithServiceOverrides mContext;
     private IsolatedWallpaperBackupAgent mWallpaperBackupAgent;
@@ -90,9 +114,10 @@
         mContext = new ContextWithServiceOverrides(ApplicationProvider.getApplicationContext());
         mContext.injectSystemService(WallpaperManager.class, mWallpaperManager);
 
-        mWallpaperBackupAgent = new IsolatedWallpaperBackupAgent(mTemporaryFolder.getRoot());
+        mWallpaperBackupAgent = new IsolatedWallpaperBackupAgent();
         mWallpaperBackupAgent.attach(mContext);
-        mWallpaperBackupAgent.onCreate();
+        mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD,
+                BackupAnnotations.OperationType.BACKUP);
 
         mWallpaperComponent = new ComponentName(TEST_WALLPAPER_PACKAGE, "");
     }
@@ -388,6 +413,185 @@
         verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
     }
 
+    @Test
+    public void testOnFullBackup_systemWallpaperImgSuccess_logsSuccess() throws Exception {
+        mockSystemWallpaperFileWithContents("system wallpaper");
+        mockCurrentWallpaperIds(TEST_SYSTEM_WALLPAPER_ID, NO_LOCK_WALLPAPER_ID);
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_SYSTEM,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getSuccessCount()).isEqualTo(1);
+    }
+
+    @Test
+    public void testOnFullBackup_systemWallpaperImgIneligible_logsFailure() throws Exception {
+        when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_SYSTEM))).thenReturn(false);
+        mockSystemWallpaperFileWithContents("system wallpaper");
+        mockCurrentWallpaperIds(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_SYSTEM,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getFailCount()).isEqualTo(1);
+        assertThat(result.getErrors()).containsKey(ERROR_INELIGIBLE);
+    }
+
+    @Test
+    public void testOnFullBackup_systemWallpaperImgMissing_logsFailure() throws Exception {
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_SYSTEM,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getFailCount()).isEqualTo(1);
+        assertThat(result.getErrors()).containsKey(ERROR_NO_WALLPAPER);
+    }
+
+    @Test
+    public void testOnFullBackup_systemWallpaperImgMissingButHasLiveComponent_logsLiveSuccess()
+            throws Exception {
+        mockWallpaperInfoFileWithContents("info file");
+        when(mWallpaperManager.getWallpaperInfo(anyInt())).thenReturn(getFakeWallpaperInfo());
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_LIVE_SYSTEM,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getSuccessCount()).isEqualTo(1);
+        assertThat(result.getMetadataHash()).isNotNull();
+    }
+
+    @Test
+    public void testOnFullBackup_systemWallpaperImgMissingButHasLiveComponent_logsNothingForImg()
+            throws Exception {
+        mockWallpaperInfoFileWithContents("info file");
+        when(mWallpaperManager.getWallpaperInfo(anyInt())).thenReturn(getFakeWallpaperInfo());
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_SYSTEM,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void testOnFullBackup_lockWallpaperImgSuccess_logsSuccess() throws Exception {
+        mockLockWallpaperFileWithContents("lock wallpaper");
+        mockCurrentWallpaperIds(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_LOCK,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getSuccessCount()).isEqualTo(1);
+    }
+
+    @Test
+    public void testOnFullBackup_lockWallpaperImgIneligible_logsFailure() throws Exception {
+        when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_LOCK))).thenReturn(false);
+        mockLockWallpaperFileWithContents("lock wallpaper");
+        mockCurrentWallpaperIds(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_LOCK,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getFailCount()).isEqualTo(1);
+        assertThat(result.getErrors()).containsKey(ERROR_INELIGIBLE);
+    }
+
+    @Test
+    public void testOnFullBackup_lockWallpaperImgMissing_logsFailure() throws Exception {
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_LOCK,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getFailCount()).isEqualTo(1);
+        assertThat(result.getErrors()).containsKey(ERROR_NO_WALLPAPER);
+    }
+
+    @Test
+    public void testOnFullBackup_lockWallpaperImgMissingButHasLiveComponent_logsLiveSuccess()
+            throws Exception {
+        mockWallpaperInfoFileWithContents("info file");
+        when(mWallpaperManager.getWallpaperInfo(anyInt())).thenReturn(getFakeWallpaperInfo());
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_LIVE_LOCK,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getSuccessCount()).isEqualTo(1);
+        assertThat(result.getMetadataHash()).isNotNull();
+    }
+
+    @Test
+    public void testOnFullBackup_lockWallpaperImgMissingButHasLiveComponent_logsNothingForImg()
+            throws Exception {
+        mockWallpaperInfoFileWithContents("info file");
+        when(mWallpaperManager.getWallpaperInfo(anyInt())).thenReturn(getFakeWallpaperInfo());
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_LOCK,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNull();
+    }
+
+
+    @Test
+    public void testOnFullBackup_exceptionThrown_logsException() throws Exception {
+        when(mWallpaperManager.isWallpaperBackupEligible(anyInt())).thenThrow(
+                new RuntimeException());
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_LOCK,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getFailCount()).isEqualTo(1);
+        assertThat(result.getErrors()).containsKey(RuntimeException.class.getName());
+    }
+
+    @Test
+    public void testOnFullBackup_lastBackupOverQuota_logsLockFailure() throws Exception {
+        mockSystemWallpaperFileWithContents("system wallpaper");
+        mockLockWallpaperFileWithContents("lock wallpaper");
+        mockCurrentWallpaperIds(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
+        markAgentAsOverQuota();
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_LOCK,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getFailCount()).isEqualTo(1);
+        assertThat(result.getErrors()).containsKey(ERROR_QUOTA_EXCEEDED);
+    }
+
+    @Test
+    public void testOnFullBackup_lastBackupOverQuota_logsSystemSuccess() throws Exception {
+        mockSystemWallpaperFileWithContents("system wallpaper");
+        mockLockWallpaperFileWithContents("lock wallpaper");
+        mockCurrentWallpaperIds(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
+        markAgentAsOverQuota();
+
+        mWallpaperBackupAgent.onFullBackup(mOutput);
+
+        DataTypeResult result = getLoggingResult(WALLPAPER_IMG_SYSTEM,
+                mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults());
+        assertThat(result).isNotNull();
+        assertThat(result.getSuccessCount()).isEqualTo(1);
+    }
+
     private void mockCurrentWallpaperIds(int systemWallpaperId, int lockWallpaperId) {
         when(mWallpaperManager.getWallpaperId(eq(FLAG_SYSTEM))).thenReturn(systemWallpaperId);
         when(mWallpaperManager.getWallpaperId(eq(FLAG_LOCK))).thenReturn(lockWallpaperId);
@@ -432,16 +636,41 @@
                 ParcelFileDescriptor.open(fakeLockWallpaperFile, MODE_READ_ONLY));
     }
 
+    private WallpaperInfo getFakeWallpaperInfo() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+        Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+        intent.setPackage("com.android.wallpaperbackup.tests");
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> result = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
+        assertEquals(1, result.size());
+        ResolveInfo info = result.get(0);
+        return new WallpaperInfo(context, info);
+    }
+
+    private void markAgentAsOverQuota() throws Exception {
+        // Create over quota file to indicate the last backup was over quota
+        File quotaFile = new File(mContext.getFilesDir(), WallpaperBackupAgent.QUOTA_SENTINEL);
+        quotaFile.createNewFile();
+
+        // Now redo the setup of the agent to pick up the over quota
+        mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD,
+                BackupAnnotations.OperationType.BACKUP);
+    }
+
+    private static DataTypeResult getLoggingResult(String dataType, List<DataTypeResult> results) {
+        for (DataTypeResult result : results) {
+            if ((result.getDataType()).equals(dataType)) {
+                return result;
+            }
+        }
+        return null;
+    }
+
     private class IsolatedWallpaperBackupAgent extends WallpaperBackupAgent {
-        File mWallpaperBaseDirectory;
         List<File> mBackedUpFiles = new ArrayList<>();
         PackageMonitor mWallpaperPackageMonitor;
         boolean mIsDeviceInRestore = false;
 
-        IsolatedWallpaperBackupAgent(File wallpaperBaseDirectory) {
-            mWallpaperBaseDirectory = wallpaperBaseDirectory;
-        }
-
         @Override
         protected void backupFile(File file, FullBackupDataOutput data) {
             mBackedUpFiles.add(file);
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java
new file mode 100644
index 0000000..3816a3c
--- /dev/null
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.wallpaperbackup;
+
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_LOCK;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_SYSTEM;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_LOCK;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_SYSTEM;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.WallpaperInfo;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupRestoreEventLogger;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.service.wallpaper.WallpaperService;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.wallpaperbackup.utils.TestWallpaperService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class WallpaperEventLoggerTest {
+
+    @Mock
+    private BackupRestoreEventLogger mMockLogger;
+
+    @Mock
+    private BackupManager mMockBackupManager;
+
+    @Mock
+    private WallpaperBackupAgent mMockBackupAgent;
+
+    private static final String WALLPAPER_ERROR = "some_error";
+
+    private WallpaperEventLogger mWallpaperEventLogger;
+    private WallpaperInfo mWallpaperInfo;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mMockBackupAgent.getBackupRestoreEventLogger()).thenReturn(mMockLogger);
+        when(mMockBackupManager.getBackupRestoreEventLogger(any())).thenReturn(mMockLogger);
+
+        mWallpaperInfo = getWallpaperInfo();
+        mWallpaperEventLogger = new WallpaperEventLogger(mMockBackupManager, mMockBackupAgent);
+    }
+
+    @Test
+    public void onSystemImgWallpaperBackedUp_logsSuccess() {
+        mWallpaperEventLogger.onSystemImageWallpaperBackedUp();
+
+        verify(mMockLogger).logItemsBackedUp(eq(WALLPAPER_IMG_SYSTEM), eq(1));
+    }
+
+    @Test
+    public void onLockImgWallpaperBackedUp_logsSuccess() {
+        mWallpaperEventLogger.onLockImageWallpaperBackedUp();
+
+        verify(mMockLogger).logItemsBackedUp(eq(WALLPAPER_IMG_LOCK), eq(1));
+    }
+
+    @Test
+    public void onSystemLiveWallpaperBackedUp_logsSuccess() {
+        mWallpaperEventLogger.onSystemLiveWallpaperBackedUp(mWallpaperInfo);
+
+        verify(mMockLogger).logItemsBackedUp(eq(WALLPAPER_LIVE_SYSTEM), eq(1));
+    }
+
+    @Test
+    public void onLockLiveWallpaperBackedUp_logsSuccess() {
+        mWallpaperEventLogger.onLockLiveWallpaperBackedUp(mWallpaperInfo);
+
+        verify(mMockLogger).logItemsBackedUp(eq(WALLPAPER_LIVE_LOCK), eq(1));
+    }
+
+    @Test
+    public void onImgWallpaperBackedUp_nullInfo_doesNotLogMetadata() {
+        mWallpaperEventLogger.onSystemImageWallpaperBackedUp();
+
+        verify(mMockLogger, never()).logBackupMetadata(eq(WALLPAPER_IMG_SYSTEM), anyString());
+    }
+
+
+    @Test
+    public void onLiveWallpaperBackedUp_logsMetadata() {
+        mWallpaperEventLogger.onSystemLiveWallpaperBackedUp(mWallpaperInfo);
+
+        verify(mMockLogger).logBackupMetadata(eq(WALLPAPER_LIVE_SYSTEM),
+                eq(TestWallpaperService.class.getName()));
+    }
+
+
+    @Test
+    public void onSystemImgWallpaperBackupFailed_logsFail() {
+        mWallpaperEventLogger.onSystemImageWallpaperBackupFailed(WALLPAPER_ERROR);
+
+        verify(mMockLogger).logItemsBackupFailed(eq(WALLPAPER_IMG_SYSTEM), eq(1),
+                eq(WALLPAPER_ERROR));
+    }
+
+    @Test
+    public void onLockImgWallpaperBackupFailed_logsFail() {
+        mWallpaperEventLogger.onLockImageWallpaperBackupFailed(WALLPAPER_ERROR);
+
+        verify(mMockLogger).logItemsBackupFailed(eq(WALLPAPER_IMG_LOCK), eq(1),
+                eq(WALLPAPER_ERROR));
+    }
+
+
+    @Test
+    public void onSystemLiveWallpaperBackupFailed_logsFail() {
+        mWallpaperEventLogger.onSystemLiveWallpaperBackupFailed(WALLPAPER_ERROR);
+
+        verify(mMockLogger).logItemsBackupFailed(eq(WALLPAPER_LIVE_SYSTEM), eq(1),
+                eq(WALLPAPER_ERROR));
+    }
+
+    @Test
+    public void onLockLiveWallpaperBackupFailed_logsFail() {
+        mWallpaperEventLogger.onLockLiveWallpaperBackupFailed(WALLPAPER_ERROR);
+
+        verify(mMockLogger).logItemsBackupFailed(eq(WALLPAPER_LIVE_LOCK), eq(1),
+                eq(WALLPAPER_ERROR));
+    }
+
+
+    @Test
+    public void onWallpaperBackupException_someProcessed_doesNotLogErrorForProcessedType() {
+        mWallpaperEventLogger.onSystemImageWallpaperBackedUp();
+
+        mWallpaperEventLogger.onBackupException(new Exception());
+
+        verify(mMockLogger, never()).logItemsBackupFailed(eq(WALLPAPER_IMG_SYSTEM), anyInt(),
+                anyString());
+    }
+
+
+    @Test
+    public void onWallpaperBackupException_someProcessed_logsErrorForUnprocessedType() {
+        mWallpaperEventLogger.onSystemImageWallpaperBackedUp();
+
+        mWallpaperEventLogger.onBackupException(new Exception());
+
+        verify(mMockLogger).logItemsBackupFailed(eq(WALLPAPER_IMG_LOCK), eq(1),
+                eq(Exception.class.getName()));
+
+    }
+
+    @Test
+    public void onWallpaperBackupException_liveTypeProcessed_doesNotLogErrorForSameImgType() {
+        mWallpaperEventLogger.onSystemLiveWallpaperBackedUp(mWallpaperInfo);
+
+        mWallpaperEventLogger.onBackupException(new Exception());
+
+        verify(mMockLogger, never()).logItemsBackupFailed(eq(WALLPAPER_IMG_SYSTEM), anyInt(),
+                anyString());
+    }
+
+    private WallpaperInfo getWallpaperInfo() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+        Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+        intent.setPackage("com.android.wallpaperbackup.tests");
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> result = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
+        assertEquals(1, result.size());
+        ResolveInfo info = result.get(0);
+        return new WallpaperInfo(context, info);
+    }
+}
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/utils/TestWallpaperService.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/utils/TestWallpaperService.java
new file mode 100644
index 0000000..cb85041
--- /dev/null
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/utils/TestWallpaperService.java
@@ -0,0 +1,29 @@
+/*
+ * 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.wallpaperbackup.utils;
+
+import android.service.wallpaper.WallpaperService;
+
+/**
+ *  Empty wallpaper service used for wallpaper backup tests
+ */
+public class TestWallpaperService extends WallpaperService {
+    @Override
+    public Engine onCreateEngine() {
+        return new Engine();
+    }
+}