Loading core/java/android/service/wallpaper/WallpaperService.java +46 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -107,6 +111,8 @@ public abstract class WallpaperService extends Service { 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>(); Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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"); Loading services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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()); } } Loading
core/java/android/service/wallpaper/WallpaperService.java +46 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -107,6 +111,8 @@ public abstract class WallpaperService extends Service { 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>(); Loading Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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"); Loading
services/tests/servicestests/src/com/android/server/wallpaper/WallpaperServiceTests.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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()); } }