diff options
| -rw-r--r-- | core/java/android/service/wallpaper/WallpaperService.java | 53 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java | 68 |
2 files changed, 114 insertions, 7 deletions
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 65d66dc863d5..f548d3b77bf3 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -33,10 +33,12 @@ import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import android.util.MergedConfiguration; import android.view.Display; @@ -54,6 +56,7 @@ import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerGlobal; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.HandlerCaller; import com.android.internal.view.BaseIWindow; import com.android.internal.view.BaseSurfaceHolder; @@ -61,6 +64,7 @@ import com.android.internal.view.BaseSurfaceHolder; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.function.Supplier; /** * A wallpaper service is responsible for showing a live wallpaper behind @@ -106,7 +110,9 @@ public abstract class WallpaperService extends Service { private static final int MSG_WINDOW_MOVED = 10035; private static final int MSG_TOUCH_EVENT = 10040; private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; - + + private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; + private final ArrayList<Engine> mActiveEngines = new ArrayList<Engine>(); @@ -186,6 +192,11 @@ public abstract class WallpaperService extends Service { boolean mPendingSync; MotionEvent mPendingMove; + // Needed for throttling onComputeColors. + private long mLastColorInvalidation; + private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; + private Supplier<Long> mClockFunction = SystemClock::elapsedRealtime; + DisplayManager mDisplayManager; Display mDisplay; private int mDisplayState; @@ -551,18 +562,38 @@ public abstract class WallpaperService extends Service { * This will trigger a {@link #onComputeColors()} call. */ public void notifyColorsChanged() { + final long now = mClockFunction.get(); + final Handler mainHandler = Handler.getMain(); + if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) { + Log.w(TAG, "This call has been deferred. You should only call " + + "notifyColorsChanged() once every " + + (NOTIFY_COLORS_RATE_LIMIT_MS / 1000f) + " seconds."); + if (!mainHandler.hasCallbacks(mNotifyColorsChanged)) { + mainHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS); + } + return; + } + mLastColorInvalidation = now; + mainHandler.removeCallbacks(mNotifyColorsChanged); + try { - mConnection.onWallpaperColorsChanged(onComputeColors()); + final WallpaperColors newColors = onComputeColors(); + if (mConnection != null) { + mConnection.onWallpaperColorsChanged(newColors); + } else { + Log.w(TAG, "Can't notify system because wallpaper connection " + + "was not established."); + } } catch (RemoteException e) { - Log.w(TAG, "Can't invalidate wallpaper colors because " + - "wallpaper connection was lost", e); + Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); } } /** * Called by the system when it needs to know what colors the wallpaper is using. - * You might return null if no color information is available at the moment. In that case - * you might want to call {@link #notifyColorsChanged()} in a near future. + * You might return null if no color information is available at the moment. + * In that case you might want to call {@link #notifyColorsChanged()} when + * color information becomes available. * <p> * The simplest way of creating a {@link android.app.WallpaperColors} object is by using * {@link android.app.WallpaperColors#fromBitmap(Bitmap)} or @@ -631,6 +662,14 @@ public abstract class WallpaperService extends Service { } } + /** + * @hide + */ + @VisibleForTesting + public void setClockFunction(Supplier<Long> clockFunction) { + mClockFunction = clockFunction; + } + void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { if (mDestroyed) { Log.w(TAG, "Ignoring updateSurface: destroyed"); @@ -893,7 +932,7 @@ public abstract class WallpaperService extends Service { " w=" + mLayout.width + " h=" + mLayout.height); } } - + void attach(IWallpaperEngineWrapper wrapper) { if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); if (mDestroyed) { diff --git a/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java b/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java new file mode 100644 index 000000000000..0d29e8963728 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.wallpaper; + +import static org.junit.Assert.assertEquals; + +import android.app.WallpaperColors; +import android.os.SystemClock; +import android.service.wallpaper.WallpaperService; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class WallpaperServiceTests { + + @Test + public void testNotifyColorsChanged_rateLimit() throws Exception { + CountDownLatch eventCountdown = new CountDownLatch(2); + WallpaperService service = new WallpaperService() { + @Override + public Engine onCreateEngine() { + return new WallpaperService.Engine() { + @Override + public WallpaperColors onComputeColors() { + eventCountdown.countDown(); + return null; + } + }; + } + }; + WallpaperService.Engine engine = service.onCreateEngine(); + + // Called because it's the first time. + engine.notifyColorsChanged(); + assertEquals("OnComputeColors should have been called.", + 1, eventCountdown.getCount()); + + // Ignored since the call should be throttled. + engine.notifyColorsChanged(); + assertEquals("OnComputeColors should have been throttled.", + 1, eventCountdown.getCount()); + // Called after being deferred. + engine.setClockFunction(() -> SystemClock.elapsedRealtime() + 1500); + engine.notifyColorsChanged(); + assertEquals("OnComputeColors should have been deferred.", + 0, eventCountdown.getCount()); + } +} |