DO NOT MERGE: Fix temporary black after setting a new image wallpaper

Sometimes see temporary black wallpaper after setting a new image
wallpaper because of:
- decode bitmap twice, results in long latency until drawing.
- the set wallpaper apis didn't sync with wallpaper rendering well.
Solutions:
- Only decode bitmap when necessary.
- Make set wallpaper apis wait for image wallpaper rendering finished.

Bug: 194080642
Test: see b/194080642#comment3
Test: atest WallpaperManagerServiceTests --iterations 50
Test: atest SystemUITests
Change-Id: I369eff5195571161cf286b3ec97175481b02eed7
This commit is contained in:
Ahan Wu
2021-08-22 22:56:34 -08:00
parent 6925a160d0
commit 7af300eea6
6 changed files with 196 additions and 28 deletions

View File

@@ -559,6 +559,53 @@ public class WallpaperManager {
return null; return null;
} }
public Rect peekWallpaperDimensions(Context context, boolean returnDefault, int userId) {
if (mService != null) {
try {
if (!mService.isWallpaperSupported(context.getOpPackageName())) {
return new Rect();
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
Rect dimensions = null;
synchronized (this) {
try {
Bundle params = new Bundle();
// Let's peek user wallpaper first.
ParcelFileDescriptor pfd = mService.getWallpaperWithFeature(
context.getOpPackageName(), context.getAttributionTag(), this,
FLAG_SYSTEM, params, userId);
if (pfd != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, options);
dimensions = new Rect(0, 0, options.outWidth, options.outHeight);
}
} catch (RemoteException ex) {
Log.w(TAG, "peek wallpaper dimensions failed", ex);
}
}
// If user wallpaper is unavailable, may be the default one instead.
if ((dimensions == null || dimensions.width() == 0 || dimensions.height() == 0)
&& returnDefault) {
InputStream is = openDefaultWallpaper(context, FLAG_SYSTEM);
if (is != null) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
dimensions = new Rect(0, 0, options.outWidth, options.outHeight);
} finally {
IoUtils.closeQuietly(is);
}
}
}
return dimensions;
}
void forgetLoadedWallpaper() { void forgetLoadedWallpaper() {
synchronized (this) { synchronized (this) {
mCachedWallpaper = null; mCachedWallpaper = null;
@@ -1038,6 +1085,17 @@ public class WallpaperManager {
return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId, hardware, cmProxy); return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId, hardware, cmProxy);
} }
/**
* Peek the dimensions of system wallpaper of the user without decoding it.
*
* @return the dimensions of system wallpaper
* @hide
*/
public Rect peekBitmapDimensions() {
return sGlobals.peekWallpaperDimensions(
mContext, true /* returnDefault */, mContext.getUserId());
}
/** /**
* Get an open, readable file descriptor to the given wallpaper image file. * Get an open, readable file descriptor to the given wallpaper image file.
* The caller is responsible for closing the file descriptor when done ingesting the file. * The caller is responsible for closing the file descriptor when done ingesting the file.

View File

@@ -96,6 +96,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -152,6 +153,7 @@ public abstract class WallpaperService extends Service {
private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050;
private static final int MSG_ZOOM = 10100; private static final int MSG_ZOOM = 10100;
private static final int MSG_SCALE_PREVIEW = 10110; private static final int MSG_SCALE_PREVIEW = 10110;
private static final int MSG_REPORT_SHOWN = 10150;
private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY, private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY,
Float.NEGATIVE_INFINITY); Float.NEGATIVE_INFINITY);
@@ -526,6 +528,35 @@ public abstract class WallpaperService extends Service {
return false; return false;
} }
/**
* This will be called in the end of {@link #updateSurface(boolean, boolean, boolean)}.
* If true is returned, the engine will not report shown until rendering finished is
* reported. Otherwise, the engine will report shown immediately right after redraw phase
* in {@link #updateSurface(boolean, boolean, boolean)}.
*
* @hide
*/
public boolean shouldWaitForEngineShown() {
return false;
}
/**
* Reports the rendering is finished, stops waiting, then invokes
* {@link IWallpaperEngineWrapper#reportShown()}.
*
* @hide
*/
public void reportEngineShown(boolean waitForEngineShown) {
if (mIWallpaperEngine.mShownReported) return;
Message message = mCaller.obtainMessage(MSG_REPORT_SHOWN);
if (!waitForEngineShown) {
mCaller.removeMessages(MSG_REPORT_SHOWN);
mCaller.sendMessage(message);
} else {
mCaller.sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(1));
}
}
/** /**
* Control whether this wallpaper will receive raw touch events * Control whether this wallpaper will receive raw touch events
* from the window manager as the user interacts with the window * from the window manager as the user interacts with the window
@@ -930,7 +961,7 @@ public abstract class WallpaperService extends Service {
void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
if (mDestroyed) { if (mDestroyed) {
Log.w(TAG, "Ignoring updateSurface: destroyed"); Log.w(TAG, "Ignoring updateSurface due to destroyed");
} }
boolean fixedSize = false; boolean fixedSize = false;
@@ -1197,7 +1228,6 @@ public abstract class WallpaperService extends Service {
+ this); + this);
onVisibilityChanged(false); onVisibilityChanged(false);
} }
} finally { } finally {
mIsCreating = false; mIsCreating = false;
mSurfaceCreated = true; mSurfaceCreated = true;
@@ -1207,7 +1237,7 @@ public abstract class WallpaperService extends Service {
processLocalColors(mPendingXOffset, mPendingXOffsetStep); processLocalColors(mPendingXOffset, mPendingXOffsetStep);
} }
reposition(); reposition();
mIWallpaperEngine.reportShown(); reportEngineShown(shouldWaitForEngineShown());
} }
} catch (RemoteException ex) { } catch (RemoteException ex) {
} }
@@ -2201,6 +2231,9 @@ public abstract class WallpaperService extends Service {
// Connection went away, nothing to do in here. // Connection went away, nothing to do in here.
} }
} break; } break;
case MSG_REPORT_SHOWN: {
reportShown();
} break;
default : default :
Log.w(TAG, "Unknown message type " + message.what); Log.w(TAG, "Unknown message type " + message.what);
} }

View File

@@ -120,15 +120,16 @@ public class ImageWallpaper extends WallpaperService {
@Override @Override
public void onCreate(SurfaceHolder surfaceHolder) { public void onCreate(SurfaceHolder surfaceHolder) {
Trace.beginSection("ImageWallpaper.Engine#onCreate");
mEglHelper = getEglHelperInstance(); mEglHelper = getEglHelperInstance();
// Deferred init renderer because we need to get wallpaper by display context. // Deferred init renderer because we need to get wallpaper by display context.
mRenderer = getRendererInstance(); mRenderer = getRendererInstance();
setFixedSizeAllowed(true); setFixedSizeAllowed(true);
updateSurfaceSize(); updateSurfaceSize();
mRenderer.setOnBitmapChanged(this::updateMiniBitmap); mRenderer.setOnBitmapChanged(this::updateMiniBitmap);
getDisplayContext().getSystemService(DisplayManager.class) getDisplayContext().getSystemService(DisplayManager.class)
.registerDisplayListener(this, mWorker.getThreadHandler()); .registerDisplayListener(this, mWorker.getThreadHandler());
Trace.endSection();
} }
@Override @Override
@@ -197,16 +198,23 @@ public class ImageWallpaper extends WallpaperService {
return true; return true;
} }
@Override
public boolean shouldWaitForEngineShown() {
return true;
}
@Override @Override
public void onDestroy() { public void onDestroy() {
getDisplayContext().getSystemService(DisplayManager.class) getDisplayContext().getSystemService(DisplayManager.class)
.unregisterDisplayListener(this); .unregisterDisplayListener(this);
mMiniBitmap = null; mMiniBitmap = null;
mWorker.getThreadHandler().post(() -> { mWorker.getThreadHandler().post(() -> {
Trace.beginSection("ImageWallpaper.Engine#onDestroy");
mRenderer.finish(); mRenderer.finish();
mRenderer = null; mRenderer = null;
mEglHelper.finish(); mEglHelper.finish();
mEglHelper = null; mEglHelper = null;
Trace.endSection();
}); });
} }
@@ -340,8 +348,10 @@ public class ImageWallpaper extends WallpaperService {
public void onSurfaceCreated(SurfaceHolder holder) { public void onSurfaceCreated(SurfaceHolder holder) {
if (mWorker == null) return; if (mWorker == null) return;
mWorker.getThreadHandler().post(() -> { mWorker.getThreadHandler().post(() -> {
Trace.beginSection("ImageWallpaper#onSurfaceCreated");
mEglHelper.init(holder, needSupportWideColorGamut()); mEglHelper.init(holder, needSupportWideColorGamut());
mRenderer.onSurfaceCreated(); mRenderer.onSurfaceCreated();
Trace.endSection();
}); });
} }
@@ -358,9 +368,11 @@ public class ImageWallpaper extends WallpaperService {
} }
private void drawFrame() { private void drawFrame() {
Trace.beginSection("ImageWallpaper#drawFrame");
preRender(); preRender();
requestRender(); requestRender();
postRender(); postRender();
Trace.endSection();
} }
public void preRender() { public void preRender() {
@@ -427,6 +439,7 @@ public class ImageWallpaper extends WallpaperService {
// This method should only be invoked from worker thread. // This method should only be invoked from worker thread.
Trace.beginSection("ImageWallpaper#postRender"); Trace.beginSection("ImageWallpaper#postRender");
scheduleFinishRendering(); scheduleFinishRendering();
reportEngineShown(false /* waitForEngineShown */);
Trace.endSection(); Trace.endSection();
} }

View File

@@ -102,7 +102,6 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer {
@Override @Override
public Size reportSurfaceSize() { public Size reportSurfaceSize() {
mTexture.use(null /* consumer */);
mSurfaceSize.set(mTexture.getTextureDimensions()); mSurfaceSize.set(mTexture.getTextureDimensions());
return new Size(mSurfaceSize.width(), mSurfaceSize.height()); return new Size(mSurfaceSize.width(), mSurfaceSize.height());
} }
@@ -124,6 +123,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer {
private final WallpaperManager mWallpaperManager; private final WallpaperManager mWallpaperManager;
private Bitmap mBitmap; private Bitmap mBitmap;
private boolean mWcgContent; private boolean mWcgContent;
private boolean mTextureUsed;
private WallpaperTexture(WallpaperManager wallpaperManager) { private WallpaperTexture(WallpaperManager wallpaperManager) {
mWallpaperManager = wallpaperManager; mWallpaperManager = wallpaperManager;
@@ -141,6 +141,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer {
mWallpaperManager.forgetLoadedWallpaper(); mWallpaperManager.forgetLoadedWallpaper();
if (mBitmap != null) { if (mBitmap != null) {
mDimensions.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); mDimensions.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
mTextureUsed = true;
} else { } else {
Log.w(TAG, "Can't get bitmap"); Log.w(TAG, "Can't get bitmap");
} }
@@ -171,6 +172,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer {
} }
private Rect getTextureDimensions() { private Rect getTextureDimensions() {
if (!mTextureUsed) {
mDimensions.set(mWallpaperManager.peekBitmapDimensions());
}
return mDimensions; return mDimensions;
} }

View File

@@ -208,7 +208,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
* wallpaper set and is created for the first time. The CLOSE_WRITE is triggered * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
* every time the wallpaper is changed. * every time the wallpaper is changed.
*/ */
private class WallpaperObserver extends FileObserver { class WallpaperObserver extends FileObserver {
final int mUserId; final int mUserId;
final WallpaperData mWallpaper; final WallpaperData mWallpaper;
@@ -226,7 +226,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG); mWallpaperLockFile = new File(mWallpaperDir, WALLPAPER_LOCK_ORIG);
} }
private WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) { WallpaperData dataForEvent(boolean sysChanged, boolean lockChanged) {
WallpaperData wallpaper = null; WallpaperData wallpaper = null;
synchronized (mLock) { synchronized (mLock) {
if (lockChanged) { if (lockChanged) {
@@ -309,9 +309,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
} }
wallpaper.imageWallpaperPending = false; wallpaper.imageWallpaperPending = false;
if (sysWallpaperChanged) { if (sysWallpaperChanged) {
IRemoteCallback.Stub callback = new IRemoteCallback.Stub() {
@Override
public void sendResult(Bundle data) throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "publish system wallpaper changed!");
}
notifyWallpaperChanged(wallpaper);
}
};
// If this was the system wallpaper, rebind... // If this was the system wallpaper, rebind...
bindWallpaperComponentLocked(mImageWallpaper, true, bindWallpaperComponentLocked(mImageWallpaper, true,
false, wallpaper, null); false, wallpaper, callback);
notifyColorsWhich |= FLAG_SYSTEM; notifyColorsWhich |= FLAG_SYSTEM;
} }
if (lockWallpaperChanged if (lockWallpaperChanged
@@ -331,15 +340,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
} }
saveSettingsLocked(wallpaper.userId); saveSettingsLocked(wallpaper.userId);
// Notify the client immediately if only lockscreen wallpaper changed.
// Publish completion *after* we've persisted the changes if (lockWallpaperChanged && !sysWallpaperChanged) {
if (wallpaper.setComplete != null) { notifyWallpaperChanged(wallpaper);
try {
wallpaper.setComplete.onWallpaperChanged();
} catch (RemoteException e) {
// if this fails we don't really care; the setting app may just
// have crashed and that sort of thing is a fact of life.
}
} }
} }
} }
@@ -353,6 +356,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
} }
} }
private void notifyWallpaperChanged(WallpaperData wallpaper) {
// Publish completion *after* we've persisted the changes
if (wallpaper.setComplete != null) {
try {
wallpaper.setComplete.onWallpaperChanged();
} catch (RemoteException e) {
// if this fails we don't really care; the setting app may just
// have crashed and that sort of thing is a fact of life.
}
}
}
private void notifyLockWallpaperChanged() { private void notifyLockWallpaperChanged() {
final IWallpaperManagerCallback cb = mKeyguardListener; final IWallpaperManagerCallback cb = mKeyguardListener;
if (cb != null) { if (cb != null) {
@@ -364,7 +379,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
} }
} }
private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) { void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
if (wallpaper.connection != null) { if (wallpaper.connection != null) {
wallpaper.connection.forEachDisplayConnector(connector -> { wallpaper.connection.forEachDisplayConnector(connector -> {
notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId); notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId);
@@ -568,7 +583,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
* Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
* for display. * for display.
*/ */
private void generateCrop(WallpaperData wallpaper) { void generateCrop(WallpaperData wallpaper) {
boolean success = false; boolean success = false;
// Only generate crop for default display. // Only generate crop for default display.
@@ -2834,7 +2849,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return false; return false;
} }
private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) { boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
if (DEBUG_LIVE) { if (DEBUG_LIVE) {
Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName); Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
@@ -3121,7 +3136,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return new JournaledFile(new File(base), new File(base + ".tmp")); return new JournaledFile(new File(base), new File(base + ".tmp"));
} }
private void saveSettingsLocked(int userId) { void saveSettingsLocked(int userId) {
JournaledFile journal = makeJournaledFile(userId); JournaledFile journal = makeJournaledFile(userId);
FileOutputStream fstream = null; FileOutputStream fstream = null;
try { try {
@@ -3270,7 +3285,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
* Important: this method loads settings to initialize the given user's wallpaper data if * Important: this method loads settings to initialize the given user's wallpaper data if
* there is no current in-memory state. * there is no current in-memory state.
*/ */
private WallpaperData getWallpaperSafeLocked(int userId, int which) { WallpaperData getWallpaperSafeLocked(int userId, int which) {
// We're setting either just system (work with the system wallpaper), // We're setting either just system (work with the system wallpaper),
// both (also work with the system wallpaper), or just the lock // both (also work with the system wallpaper), or just the lock
// wallpaper (update against the existing lock wallpaper if any). // wallpaper (update against the existing lock wallpaper if any).

View File

@@ -18,13 +18,18 @@ package com.android.server.wallpaper;
import static android.app.WallpaperManager.COMMAND_REAPPLY; import static android.app.WallpaperManager.COMMAND_REAPPLY;
import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.os.FileObserver.CLOSE_WRITE;
import static android.os.UserHandle.USER_SYSTEM;
import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wallpaper.WallpaperManagerService.WALLPAPER;
import static com.android.server.wallpaper.WallpaperManagerService.WALLPAPER_CROP;
import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -34,6 +39,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.junit.Assume.assumeThat; import static org.junit.Assume.assumeThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
@@ -41,6 +47,7 @@ import static org.mockito.Mockito.verify;
import android.app.AppGlobals; import android.app.AppGlobals;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.app.WallpaperColors;
import android.app.WallpaperManager; import android.app.WallpaperManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@@ -49,8 +56,10 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice; import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.graphics.Color;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.os.UserHandle; import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.service.wallpaper.IWallpaperConnection; import android.service.wallpaper.IWallpaperConnection;
import android.service.wallpaper.IWallpaperEngine; import android.service.wallpaper.IWallpaperEngine;
@@ -143,7 +152,6 @@ public class WallpaperManagerServiceTests {
sContext.getTestablePermissions().setPermission( sContext.getTestablePermissions().setPermission(
android.Manifest.permission.SET_WALLPAPER, android.Manifest.permission.SET_WALLPAPER,
PackageManager.PERMISSION_GRANTED); PackageManager.PERMISSION_GRANTED);
doNothing().when(sContext).sendBroadcastAsUser(any(), any());
//Wallpaper components //Wallpaper components
sWallpaperService = mock(IWallpaperConnection.Stub.class); sWallpaperService = mock(IWallpaperConnection.Stub.class);
@@ -180,6 +188,7 @@ public class WallpaperManagerServiceTests {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
sContext.addMockSystemService(DisplayManager.class, mDisplayManager); sContext.addMockSystemService(DisplayManager.class, mDisplayManager);
doNothing().when(sContext).sendBroadcastAsUser(any(), any());
final Display mockDisplay = mock(Display.class); final Display mockDisplay = mock(Display.class);
doReturn(DISPLAY_SIZE_DIMENSION).when(mockDisplay).getMaximumSizeDimension(); doReturn(DISPLAY_SIZE_DIMENSION).when(mockDisplay).getMaximumSizeDimension();
@@ -242,13 +251,13 @@ public class WallpaperManagerServiceTests {
*/ */
@Test @Test
public void testDataCorrectAfterBoot() { public void testDataCorrectAfterBoot() {
mService.switchUser(UserHandle.USER_SYSTEM, null); mService.switchUser(USER_SYSTEM, null);
final WallpaperData fallbackData = mService.mFallbackWallpaper; final WallpaperData fallbackData = mService.mFallbackWallpaper;
assertEquals("Fallback wallpaper component should be ImageWallpaper.", assertEquals("Fallback wallpaper component should be ImageWallpaper.",
sImageWallpaperComponentName, fallbackData.wallpaperComponent); sImageWallpaperComponentName, fallbackData.wallpaperComponent);
verifyLastWallpaperData(UserHandle.USER_SYSTEM, sDefaultWallpaperComponent); verifyLastWallpaperData(USER_SYSTEM, sDefaultWallpaperComponent);
verifyDisplayData(); verifyDisplayData();
} }
@@ -261,7 +270,7 @@ public class WallpaperManagerServiceTests {
assumeThat(sDefaultWallpaperComponent, assumeThat(sDefaultWallpaperComponent,
not(CoreMatchers.equalTo(sImageWallpaperComponentName))); not(CoreMatchers.equalTo(sImageWallpaperComponentName)));
final int testUserId = UserHandle.USER_SYSTEM; final int testUserId = USER_SYSTEM;
mService.switchUser(testUserId, null); mService.switchUser(testUserId, null);
verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent); verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
verifyCurrentSystemData(testUserId); verifyCurrentSystemData(testUserId);
@@ -281,7 +290,7 @@ public class WallpaperManagerServiceTests {
*/ */
@Test @Test
public void testSetCurrentComponent() throws Exception { public void testSetCurrentComponent() throws Exception {
final int testUserId = UserHandle.USER_SYSTEM; final int testUserId = USER_SYSTEM;
mService.switchUser(testUserId, null); mService.switchUser(testUserId, null);
verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent); verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
verifyCurrentSystemData(testUserId); verifyCurrentSystemData(testUserId);
@@ -387,6 +396,42 @@ public class WallpaperManagerServiceTests {
assertEquals(systemWallpaperData.primaryColors, shouldMatchSystem.primaryColors); assertEquals(systemWallpaperData.primaryColors, shouldMatchSystem.primaryColors);
} }
@Test
public void testWallpaperManagerCallbackInRightOrder() throws RemoteException {
WallpaperData wallpaper = new WallpaperData(
USER_SYSTEM, mService.getWallpaperDir(USER_SYSTEM), WALLPAPER, WALLPAPER_CROP);
wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
spyOn(wallpaper);
doReturn(wallpaper).when(mService).getWallpaperSafeLocked(wallpaper.userId, FLAG_SYSTEM);
doNothing().when(mService).switchWallpaper(any(), any());
doReturn(true).when(mService)
.bindWallpaperComponentLocked(any(), anyBoolean(), anyBoolean(), any(), any());
doNothing().when(mService).saveSettingsLocked(wallpaper.userId);
doNothing().when(mService).generateCrop(wallpaper);
// timestamps of {ACTION_WALLPAPER_CHANGED, onWallpaperColorsChanged}
final long[] timestamps = new long[2];
doAnswer(invocation -> timestamps[0] = SystemClock.elapsedRealtime())
.when(sContext).sendBroadcastAsUser(any(), any());
doAnswer(invocation -> timestamps[1] = SystemClock.elapsedRealtime())
.when(mService).notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
assertNull(wallpaper.wallpaperObserver);
mService.switchUser(wallpaper.userId, null);
assertNotNull(wallpaper.wallpaperObserver);
// We will call onEvent directly, so stop watching the file.
wallpaper.wallpaperObserver.stopWatching();
spyOn(wallpaper.wallpaperObserver);
doReturn(wallpaper).when(wallpaper.wallpaperObserver).dataForEvent(true, false);
wallpaper.wallpaperObserver.onEvent(CLOSE_WRITE, WALLPAPER);
// ACTION_WALLPAPER_CHANGED should be invoked before onWallpaperColorsChanged.
assertTrue(timestamps[1] > timestamps[0]);
}
// Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
// non-current user must not bind to wallpaper service. // non-current user must not bind to wallpaper service.
private void verifyNoConnectionBeforeLastUser(int lastUserId) { private void verifyNoConnectionBeforeLastUser(int lastUserId) {