Merge "Add backup logging to WallpaperBackupAgent" into udc-dev
This commit is contained in:
@@ -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"],
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
17
packages/WallpaperBackup/test/res/xml/livewallpaper.xml
Normal file
17
packages/WallpaperBackup/test/res/xml/livewallpaper.xml
Normal file
@@ -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/>
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user