diff options
8 files changed, 708 insertions, 13 deletions
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp index d142f25c6a62..8acc5089f8bd 100644 --- a/packages/WallpaperBackup/Android.bp +++ b/packages/WallpaperBackup/Android.bp @@ -42,7 +42,7 @@ android_test { 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 @@ android_test { "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 e549b61ac491..6aca2fdc0f7f 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -19,11 +19,18 @@ package com.android.wallpaperbackup; 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 @@ public class WallpaperBackupAgent extends BackupAgent { private boolean mQuotaExceeded; private WallpaperManager mWallpaperManager; + private WallpaperEventLogger mEventLogger; + + private boolean mSystemHasLiveComponent; + private boolean mLockHasLiveComponent; @Override public void onCreate() { @@ -117,6 +128,9 @@ public class WallpaperBackupAgent extends BackupAgent { 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 @@ public class WallpaperBackupAgent extends BackupAgent { 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 @@ public class WallpaperBackupAgent extends BackupAgent { 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 @@ public class WallpaperBackupAgent extends BackupAgent { 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 @@ public class WallpaperBackupAgent extends BackupAgent { if (systemWallpaperImageFd == null) { Slog.w(TAG, "System wallpaper doesn't exist"); + logSystemImageErrorIfNoLiveComponent(ERROR_NO_WALLPAPER); return; } @@ -210,8 +250,17 @@ public class WallpaperBackupAgent extends BackupAgent { 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 @@ public class WallpaperBackupAgent extends BackupAgent { } 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 @@ public class WallpaperBackupAgent extends BackupAgent { // 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 @@ public class WallpaperBackupAgent extends BackupAgent { 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 000000000000..64944b3ff54f --- /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 44ab1b6d65ba..eb1e98b90808 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 000000000000..c6fbe2bda908 --- /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 20dd516503b8..89459f6e6772 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 android.os.ParcelFileDescriptor.MODE_READ_ONLY; 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 @@ public class WallpaperBackupAgentTest { 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 @@ public class WallpaperBackupAgentTest { 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 @@ public class WallpaperBackupAgentTest { 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 @@ public class WallpaperBackupAgentTest { 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 000000000000..3816a3ccc1eb --- /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 000000000000..cb8504132e45 --- /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(); + } +} |