Loading core/java/android/service/wallpaper/WallpaperService.java +199 −139 Original line number Original line Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.hardware.display.DisplayManager.DisplayListener; import android.os.Build; import android.os.Build; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IBinder; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.os.Message; Loading Loading @@ -96,6 +97,7 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerGlobal; import android.window.ClientWindowFrames; import android.window.ClientWindowFrames; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.HandlerCaller; import com.android.internal.os.HandlerCaller; import com.android.internal.view.BaseIWindow; import com.android.internal.view.BaseIWindow; Loading @@ -104,9 +106,10 @@ import com.android.internal.view.BaseSurfaceHolder; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; 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; Loading Loading @@ -166,11 +169,12 @@ public abstract class WallpaperService extends Service { private static final int MSG_RESIZE_PREVIEW = 10110; private static final int MSG_RESIZE_PREVIEW = 10110; private static final int MSG_REPORT_SHOWN = 10150; private static final int MSG_REPORT_SHOWN = 10150; private static final int MSG_UPDATE_DIMMING = 10200; private static final int MSG_UPDATE_DIMMING = 10200; private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY); /** limit calls to {@link Engine#onComputeColors} to at most once per second */ private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 1000; /** limit calls to {@link Engine#processLocalColorsInternal} to at most once per 2 seconds */ private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 2000; private static final boolean ENABLE_WALLPAPER_DIMMING = private static final boolean ENABLE_WALLPAPER_DIMMING = SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true); SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true); Loading @@ -180,6 +184,9 @@ public abstract class WallpaperService extends Service { private final ArrayList<Engine> mActiveEngines private final ArrayList<Engine> mActiveEngines = new ArrayList<Engine>(); = new ArrayList<Engine>(); private Handler mBackgroundHandler; private HandlerThread mBackgroundThread; static final class WallpaperCommand { static final class WallpaperCommand { String action; String action; int x; int x; Loading @@ -198,14 +205,6 @@ public abstract class WallpaperService extends Service { */ */ public class Engine { public class Engine { IWallpaperEngineWrapper mIWallpaperEngine; IWallpaperEngineWrapper mIWallpaperEngine; final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4); final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4); // 2D matrix [x][y] to represent a page of a portion of a window EngineWindowPage[] mWindowPages = new EngineWindowPage[0]; Bitmap mLastScreenshot; int mLastWindowPage = -1; private boolean mResetWindowPages; // Copies from mIWallpaperEngine. // Copies from mIWallpaperEngine. HandlerCaller mCaller; HandlerCaller mCaller; Loading Loading @@ -267,11 +266,34 @@ public abstract class WallpaperService extends Service { final Object mLock = new Object(); final Object mLock = new Object(); boolean mOffsetMessageEnqueued; boolean mOffsetMessageEnqueued; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) float mPendingXOffset; @GuardedBy("mLock") float mPendingYOffset; private float mPendingXOffset; float mPendingXOffsetStep; @GuardedBy("mLock") float mPendingYOffsetStep; private float mPendingYOffset; @GuardedBy("mLock") private float mPendingXOffsetStep; @GuardedBy("mLock") private float mPendingYOffsetStep; /** * local color extraction related fields. When a user calls `addLocalColorAreas` */ @GuardedBy("mLock") private final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4); @GuardedBy("mLock") private final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4); private long mLastProcessLocalColorsTimestamp; private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false); private int mPixelCopyCount = 0; // 2D matrix [x][y] to represent a page of a portion of a window @GuardedBy("mLock") private EngineWindowPage[] mWindowPages = new EngineWindowPage[0]; private Bitmap mLastScreenshot; private boolean mResetWindowPages; boolean mPendingSync; boolean mPendingSync; MotionEvent mPendingMove; MotionEvent mPendingMove; boolean mIsInAmbientMode; boolean mIsInAmbientMode; Loading @@ -280,12 +302,8 @@ public abstract class WallpaperService extends Service { private long mLastColorInvalidation; private long mLastColorInvalidation; private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; // used to throttle processLocalColors private long mLastProcessLocalColorsTimestamp; private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false); private final Supplier<Long> mClockFunction; private final Supplier<Long> mClockFunction; private final Handler mHandler; private final Handler mHandler; private Display mDisplay; private Display mDisplay; private Context mDisplayContext; private Context mDisplayContext; private int mDisplayState; private int mDisplayState; Loading Loading @@ -825,7 +843,7 @@ public abstract class WallpaperService extends Service { + "was not established."); + "was not established."); } } mResetWindowPages = true; mResetWindowPages = true; processLocalColors(mPendingXOffset, mPendingXOffsetStep); processLocalColors(); } catch (RemoteException e) { } catch (RemoteException e) { Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); } } Loading Loading @@ -1361,10 +1379,9 @@ public abstract class WallpaperService extends Service { mIsCreating = false; mIsCreating = false; mSurfaceCreated = true; mSurfaceCreated = true; if (redrawNeeded) { if (redrawNeeded) { resetWindowPages(); mSession.finishDrawing(mWindow, null /* postDrawTransaction */, mSession.finishDrawing(mWindow, null /* postDrawTransaction */, Integer.MAX_VALUE); Integer.MAX_VALUE); processLocalColors(mPendingXOffset, mPendingXOffsetStep); processLocalColors(); } } reposition(); reposition(); reportEngineShown(shouldWaitForEngineShown()); reportEngineShown(shouldWaitForEngineShown()); Loading Loading @@ -1509,7 +1526,7 @@ public abstract class WallpaperService extends Service { if (!mDestroyed) { if (!mDestroyed) { mVisible = visible; mVisible = visible; reportVisibility(); reportVisibility(); if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep); if (mReportedVisible) processLocalColors(); } else { } else { AnimationHandler.requestAnimatorsEnabled(visible, this); AnimationHandler.requestAnimatorsEnabled(visible, this); } } Loading Loading @@ -1594,14 +1611,14 @@ public abstract class WallpaperService extends Service { } } // setup local color extraction data // setup local color extraction data processLocalColors(xOffset, xOffsetStep); processLocalColors(); } } /** /** * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls. * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls. */ */ private void processLocalColors(float xOffset, float xOffsetStep) { private void processLocalColors() { if (mProcessLocalColorsPending.compareAndSet(false, true)) { if (mProcessLocalColorsPending.compareAndSet(false, true)) { final long now = mClockFunction.get(); final long now = mClockFunction.get(); final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp; final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp; Loading @@ -1611,23 +1628,41 @@ public abstract class WallpaperService extends Service { mHandler.postDelayed(() -> { mHandler.postDelayed(() -> { mLastProcessLocalColorsTimestamp = now + timeToWait; mLastProcessLocalColorsTimestamp = now + timeToWait; mProcessLocalColorsPending.set(false); mProcessLocalColorsPending.set(false); processLocalColorsInternal(xOffset, xOffsetStep); processLocalColorsInternal(); }, timeToWait); }, timeToWait); } } } } private void processLocalColorsInternal(float xOffset, float xOffsetStep) { /** // implemented by the wallpaper * Default implementation of the local color extraction. * This will take a screenshot of the whole wallpaper on the main thread. * Then, in a background thread, for each launcher page, for each area that needs color * extraction in this page, creates a sub-bitmap and call {@link WallpaperColors#fromBitmap} * to extract the colors. Every time a launcher page has been processed, call * {@link #notifyLocalColorsChanged} with the color and areas of this page. */ private void processLocalColorsInternal() { if (supportsLocalColorExtraction()) return; if (supportsLocalColorExtraction()) return; float xOffset; float xOffsetStep; float wallpaperDimAmount; int xPage; int xPages; Set<RectF> areas; EngineWindowPage current; synchronized (mLock) { xOffset = mPendingXOffset; xOffsetStep = mPendingXOffsetStep; wallpaperDimAmount = mWallpaperDimAmount; if (DEBUG) { if (DEBUG) { Log.d(TAG, "processLocalColors " + xOffset + " of step " Log.d(TAG, "processLocalColors " + xOffset + " of step " + xOffsetStep); + xOffsetStep); } } //below is the default implementation if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN || !mSurfaceHolder.getSurface().isValid()) return; || !mSurfaceHolder.getSurface().isValid()) return; int xCurrentPage; int xCurrentPage; int xPages; if (!validStep(xOffsetStep)) { if (!validStep(xOffsetStep)) { if (DEBUG) { if (DEBUG) { Log.w(TAG, "invalid offset step " + xOffsetStep); Log.w(TAG, "invalid offset step " + xOffsetStep); Loading @@ -1651,10 +1686,8 @@ public abstract class WallpaperService extends Service { float finalXOffsetStep = xOffsetStep; float finalXOffsetStep = xOffsetStep; float finalXOffset = xOffset; float finalXOffset = xOffset; Trace.beginSection("WallpaperService#processLocalColors"); resetWindowPages(); resetWindowPages(); int xPage = xCurrentPage; xPage = xCurrentPage; EngineWindowPage current; if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { mWindowPages = new EngineWindowPage[xPages]; mWindowPages = new EngineWindowPage[xPages]; initWindowPages(mWindowPages, finalXOffsetStep); initWindowPages(mWindowPages, finalXOffsetStep); Loading @@ -1681,10 +1714,12 @@ public abstract class WallpaperService extends Service { xPage = mWindowPages.length - 1; xPage = mWindowPages.length - 1; } } current = mWindowPages[xPage]; current = mWindowPages[xPage]; updatePage(current, xPage, xPages, finalXOffsetStep); areas = new HashSet<>(current.getAreas()); Trace.endSection(); } updatePage(current, areas, xPage, xPages, wallpaperDimAmount); } } @GuardedBy("mLock") private void initWindowPages(EngineWindowPage[] windowPages, float step) { private void initWindowPages(EngineWindowPage[] windowPages, float step) { for (int i = 0; i < windowPages.length; i++) { for (int i = 0; i < windowPages.length; i++) { windowPages[i] = new EngineWindowPage(); windowPages[i] = new EngineWindowPage(); Loading @@ -1701,16 +1736,16 @@ public abstract class WallpaperService extends Service { } } } } void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages, void updatePage(EngineWindowPage currentPage, Set<RectF> areas, int pageIndx, int numPages, float xOffsetStep) { float wallpaperDimAmount) { // in case the clock is zero, we start with negative time // in case the clock is zero, we start with negative time long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION; long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION; long lapsed = current - currentPage.getLastUpdateTime(); long lapsed = current - currentPage.getLastUpdateTime(); // Always update the page when the last update time is <= 0 // Always update the page when the last update time is <= 0 // This is important especially when the device first boots // This is important especially when the device first boots if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) { if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return; return; } Surface surface = mSurfaceHolder.getSurface(); Surface surface = mSurfaceHolder.getSurface(); if (!surface.isValid()) return; if (!surface.isValid()) return; boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y; boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y; Loading @@ -1723,43 +1758,59 @@ public abstract class WallpaperService extends Service { Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); return; return; } } final String pixelCopySectionName = "WallpaperService#pixelCopy"; final int pixelCopyCount = mPixelCopyCount++; Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount); Bitmap screenShot = Bitmap.createBitmap(width, height, Bitmap screenShot = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Bitmap.Config.ARGB_8888); final Bitmap finalScreenShot = screenShot; final Bitmap finalScreenShot = screenShot; Trace.beginSection("WallpaperService#pixelCopy"); try { // TODO(b/274427458) check if this can be done in the background. PixelCopy.request(surface, screenShot, (res) -> { PixelCopy.request(surface, screenShot, (res) -> { Trace.endSection(); Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount); if (DEBUG) Log.d(TAG, "result of pixel copy is " + res); if (DEBUG) { Log.d(TAG, "result of pixel copy is: " + (res == PixelCopy.SUCCESS ? "SUCCESS" : "FAILURE")); } if (res != PixelCopy.SUCCESS) { if (res != PixelCopy.SUCCESS) { Bitmap lastBitmap = currentPage.getBitmap(); Bitmap lastBitmap = currentPage.getBitmap(); // assign the last bitmap taken for now // assign the last bitmap taken for now currentPage.setBitmap(mLastScreenshot); currentPage.setBitmap(mLastScreenshot); Bitmap lastScreenshot = mLastScreenshot; Bitmap lastScreenshot = mLastScreenshot; if (lastScreenshot != null && !lastScreenshot.isRecycled() if (lastScreenshot != null && !Objects.equals(lastBitmap, lastScreenshot)) { && !Objects.equals(lastBitmap, lastScreenshot)) { updatePageColors( updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); currentPage, areas, pageIndx, numPages, wallpaperDimAmount); } } } else { } else { mLastScreenshot = finalScreenShot; mLastScreenshot = finalScreenShot; // going to hold this lock for a while currentPage.setBitmap(finalScreenShot); currentPage.setBitmap(finalScreenShot); currentPage.setLastUpdateTime(current); currentPage.setLastUpdateTime(current); updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); updatePageColors( currentPage, areas, pageIndx, numPages, wallpaperDimAmount); } }, mBackgroundHandler); } catch (IllegalArgumentException e) { // this can potentially happen if the surface is invalidated right between the // surface.isValid() check and the PixelCopy operation. // in this case, stop: we'll compute colors on the next processLocalColors call. Log.w(TAG, "Cancelling processLocalColors: exception caught during PixelCopy"); } } }, mHandler); } } // locked by the passed page // locked by the passed page private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages, private void updatePageColors(EngineWindowPage page, Set<RectF> areas, float xOffsetStep) { int pageIndx, int numPages, float wallpaperDimAmount) { if (page.getBitmap() == null) return; if (page.getBitmap() == null) return; if (!mBackgroundHandler.getLooper().isCurrentThread()) { throw new IllegalStateException( "ProcessLocalColors should be called from the background thread"); } Trace.beginSection("WallpaperService#updatePageColors"); Trace.beginSection("WallpaperService#updatePageColors"); if (DEBUG) { if (DEBUG) { Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " + page.getAreas().size() + " and bitmap size of " + page.getAreas().size() + " and bitmap size of " + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); } } for (RectF area: page.getAreas()) { for (RectF area: areas) { if (area == null) continue; if (area == null) continue; RectF subArea = generateSubRect(area, pageIndx, numPages); RectF subArea = generateSubRect(area, pageIndx, numPages); Bitmap b = page.getBitmap(); Bitmap b = page.getBitmap(); Loading @@ -1769,12 +1820,12 @@ public abstract class WallpaperService extends Service { int height = Math.round(b.getHeight() * subArea.height()); int height = Math.round(b.getHeight() * subArea.height()); Bitmap target; Bitmap target; try { try { target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height); target = Bitmap.createBitmap(b, x, y, width, height); } catch (Exception e) { } catch (Exception e) { Log.e(TAG, "Error creating page local color bitmap", e); Log.e(TAG, "Error creating page local color bitmap", e); continue; continue; } } WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount); WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount); target.recycle(); target.recycle(); WallpaperColors currentColor = page.getColors(area); WallpaperColors currentColor = page.getColors(area); Loading @@ -1791,12 +1842,14 @@ public abstract class WallpaperService extends Service { + " local color callback for area" + area + " for page " + pageIndx + " local color callback for area" + area + " for page " + pageIndx + " of " + numPages); + " of " + numPages); } } mHandler.post(() -> { try { try { mConnection.onLocalWallpaperColorsChanged(area, color, mConnection.onLocalWallpaperColorsChanged(area, color, mDisplayContext.getDisplayId()); mDisplayContext.getDisplayId()); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); } } }); } } } } Trace.endSection(); Trace.endSection(); Loading @@ -1822,16 +1875,17 @@ public abstract class WallpaperService extends Service { return new RectF(left, in.top, right, in.bottom); return new RectF(left, in.top, right, in.bottom); } } @GuardedBy("mLock") private void resetWindowPages() { private void resetWindowPages() { if (supportsLocalColorExtraction()) return; if (supportsLocalColorExtraction()) return; if (!mResetWindowPages) return; if (!mResetWindowPages) return; mResetWindowPages = false; mResetWindowPages = false; mLastWindowPage = -1; for (int i = 0; i < mWindowPages.length; i++) { for (int i = 0; i < mWindowPages.length; i++) { mWindowPages[i].setLastUpdateTime(0L); mWindowPages[i].setLastUpdateTime(0L); } } } } @GuardedBy("mLock") private int getRectFPage(RectF area, float step) { private int getRectFPage(RectF area, float step) { if (!isValid(area)) return 0; if (!isValid(area)) return 0; if (!validStep(step)) return 0; if (!validStep(step)) return 0; Loading @@ -1852,12 +1906,12 @@ public abstract class WallpaperService extends Service { if (DEBUG) { if (DEBUG) { Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); } } mHandler.post(() -> { mBackgroundHandler.post(() -> { synchronized (mLock) { mLocalColorsToAdd.addAll(regions); mLocalColorsToAdd.addAll(regions); processLocalColors(mPendingXOffset, mPendingYOffset); } processLocalColors(); }); }); } } /** /** Loading @@ -1867,7 +1921,8 @@ public abstract class WallpaperService extends Service { */ */ public void removeLocalColorsAreas(@NonNull List<RectF> regions) { public void removeLocalColorsAreas(@NonNull List<RectF> regions) { if (supportsLocalColorExtraction()) return; if (supportsLocalColorExtraction()) return; mHandler.post(() -> { mBackgroundHandler.post(() -> { synchronized (mLock) { float step = mPendingXOffsetStep; float step = mPendingXOffsetStep; mLocalColorsToAdd.removeAll(regions); mLocalColorsToAdd.removeAll(regions); mLocalColorAreas.removeAll(regions); mLocalColorAreas.removeAll(regions); Loading @@ -1879,6 +1934,7 @@ public abstract class WallpaperService extends Service { mWindowPages[i].removeArea(regions.get(j)); mWindowPages[i].removeArea(regions.get(j)); } } } } } }); }); } } Loading @@ -1894,7 +1950,7 @@ public abstract class WallpaperService extends Service { } } private boolean validStep(float step) { private boolean validStep(float step) { return !PROHIBITED_STEPS.contains(step) && step > 0. && step <= 1.; return !Float.isNaN(step) && step > 0f && step <= 1f; } } void doCommand(WallpaperCommand cmd) { void doCommand(WallpaperCommand cmd) { Loading Loading @@ -2498,6 +2554,9 @@ public abstract class WallpaperService extends Service { @Override @Override public void onCreate() { public void onCreate() { Trace.beginSection("WPMS.onCreate"); Trace.beginSection("WPMS.onCreate"); mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); super.onCreate(); super.onCreate(); Trace.endSection(); Trace.endSection(); } } Loading @@ -2510,6 +2569,7 @@ public abstract class WallpaperService extends Service { mActiveEngines.get(i).detach(); mActiveEngines.get(i).detach(); } } mActiveEngines.clear(); mActiveEngines.clear(); mBackgroundThread.quitSafely(); Trace.endSection(); Trace.endSection(); } } Loading Loading
core/java/android/service/wallpaper/WallpaperService.java +199 −139 Original line number Original line Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.hardware.display.DisplayManager.DisplayListener; import android.os.Build; import android.os.Build; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IBinder; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.os.Message; Loading Loading @@ -96,6 +97,7 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerGlobal; import android.window.ClientWindowFrames; import android.window.ClientWindowFrames; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.HandlerCaller; import com.android.internal.os.HandlerCaller; import com.android.internal.view.BaseIWindow; import com.android.internal.view.BaseIWindow; Loading @@ -104,9 +106,10 @@ import com.android.internal.view.BaseSurfaceHolder; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.List; import java.util.Objects; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; 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; Loading Loading @@ -166,11 +169,12 @@ public abstract class WallpaperService extends Service { private static final int MSG_RESIZE_PREVIEW = 10110; private static final int MSG_RESIZE_PREVIEW = 10110; private static final int MSG_REPORT_SHOWN = 10150; private static final int MSG_REPORT_SHOWN = 10150; private static final int MSG_UPDATE_DIMMING = 10200; private static final int MSG_UPDATE_DIMMING = 10200; private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY); /** limit calls to {@link Engine#onComputeColors} to at most once per second */ private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 1000; /** limit calls to {@link Engine#processLocalColorsInternal} to at most once per 2 seconds */ private static final int PROCESS_LOCAL_COLORS_INTERVAL_MS = 2000; private static final boolean ENABLE_WALLPAPER_DIMMING = private static final boolean ENABLE_WALLPAPER_DIMMING = SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true); SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true); Loading @@ -180,6 +184,9 @@ public abstract class WallpaperService extends Service { private final ArrayList<Engine> mActiveEngines private final ArrayList<Engine> mActiveEngines = new ArrayList<Engine>(); = new ArrayList<Engine>(); private Handler mBackgroundHandler; private HandlerThread mBackgroundThread; static final class WallpaperCommand { static final class WallpaperCommand { String action; String action; int x; int x; Loading @@ -198,14 +205,6 @@ public abstract class WallpaperService extends Service { */ */ public class Engine { public class Engine { IWallpaperEngineWrapper mIWallpaperEngine; IWallpaperEngineWrapper mIWallpaperEngine; final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4); final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4); // 2D matrix [x][y] to represent a page of a portion of a window EngineWindowPage[] mWindowPages = new EngineWindowPage[0]; Bitmap mLastScreenshot; int mLastWindowPage = -1; private boolean mResetWindowPages; // Copies from mIWallpaperEngine. // Copies from mIWallpaperEngine. HandlerCaller mCaller; HandlerCaller mCaller; Loading Loading @@ -267,11 +266,34 @@ public abstract class WallpaperService extends Service { final Object mLock = new Object(); final Object mLock = new Object(); boolean mOffsetMessageEnqueued; boolean mOffsetMessageEnqueued; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) float mPendingXOffset; @GuardedBy("mLock") float mPendingYOffset; private float mPendingXOffset; float mPendingXOffsetStep; @GuardedBy("mLock") float mPendingYOffsetStep; private float mPendingYOffset; @GuardedBy("mLock") private float mPendingXOffsetStep; @GuardedBy("mLock") private float mPendingYOffsetStep; /** * local color extraction related fields. When a user calls `addLocalColorAreas` */ @GuardedBy("mLock") private final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4); @GuardedBy("mLock") private final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4); private long mLastProcessLocalColorsTimestamp; private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false); private int mPixelCopyCount = 0; // 2D matrix [x][y] to represent a page of a portion of a window @GuardedBy("mLock") private EngineWindowPage[] mWindowPages = new EngineWindowPage[0]; private Bitmap mLastScreenshot; private boolean mResetWindowPages; boolean mPendingSync; boolean mPendingSync; MotionEvent mPendingMove; MotionEvent mPendingMove; boolean mIsInAmbientMode; boolean mIsInAmbientMode; Loading @@ -280,12 +302,8 @@ public abstract class WallpaperService extends Service { private long mLastColorInvalidation; private long mLastColorInvalidation; private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; // used to throttle processLocalColors private long mLastProcessLocalColorsTimestamp; private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false); private final Supplier<Long> mClockFunction; private final Supplier<Long> mClockFunction; private final Handler mHandler; private final Handler mHandler; private Display mDisplay; private Display mDisplay; private Context mDisplayContext; private Context mDisplayContext; private int mDisplayState; private int mDisplayState; Loading Loading @@ -825,7 +843,7 @@ public abstract class WallpaperService extends Service { + "was not established."); + "was not established."); } } mResetWindowPages = true; mResetWindowPages = true; processLocalColors(mPendingXOffset, mPendingXOffsetStep); processLocalColors(); } catch (RemoteException e) { } catch (RemoteException e) { Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); } } Loading Loading @@ -1361,10 +1379,9 @@ public abstract class WallpaperService extends Service { mIsCreating = false; mIsCreating = false; mSurfaceCreated = true; mSurfaceCreated = true; if (redrawNeeded) { if (redrawNeeded) { resetWindowPages(); mSession.finishDrawing(mWindow, null /* postDrawTransaction */, mSession.finishDrawing(mWindow, null /* postDrawTransaction */, Integer.MAX_VALUE); Integer.MAX_VALUE); processLocalColors(mPendingXOffset, mPendingXOffsetStep); processLocalColors(); } } reposition(); reposition(); reportEngineShown(shouldWaitForEngineShown()); reportEngineShown(shouldWaitForEngineShown()); Loading Loading @@ -1509,7 +1526,7 @@ public abstract class WallpaperService extends Service { if (!mDestroyed) { if (!mDestroyed) { mVisible = visible; mVisible = visible; reportVisibility(); reportVisibility(); if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep); if (mReportedVisible) processLocalColors(); } else { } else { AnimationHandler.requestAnimatorsEnabled(visible, this); AnimationHandler.requestAnimatorsEnabled(visible, this); } } Loading Loading @@ -1594,14 +1611,14 @@ public abstract class WallpaperService extends Service { } } // setup local color extraction data // setup local color extraction data processLocalColors(xOffset, xOffsetStep); processLocalColors(); } } /** /** * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls. * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls. */ */ private void processLocalColors(float xOffset, float xOffsetStep) { private void processLocalColors() { if (mProcessLocalColorsPending.compareAndSet(false, true)) { if (mProcessLocalColorsPending.compareAndSet(false, true)) { final long now = mClockFunction.get(); final long now = mClockFunction.get(); final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp; final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp; Loading @@ -1611,23 +1628,41 @@ public abstract class WallpaperService extends Service { mHandler.postDelayed(() -> { mHandler.postDelayed(() -> { mLastProcessLocalColorsTimestamp = now + timeToWait; mLastProcessLocalColorsTimestamp = now + timeToWait; mProcessLocalColorsPending.set(false); mProcessLocalColorsPending.set(false); processLocalColorsInternal(xOffset, xOffsetStep); processLocalColorsInternal(); }, timeToWait); }, timeToWait); } } } } private void processLocalColorsInternal(float xOffset, float xOffsetStep) { /** // implemented by the wallpaper * Default implementation of the local color extraction. * This will take a screenshot of the whole wallpaper on the main thread. * Then, in a background thread, for each launcher page, for each area that needs color * extraction in this page, creates a sub-bitmap and call {@link WallpaperColors#fromBitmap} * to extract the colors. Every time a launcher page has been processed, call * {@link #notifyLocalColorsChanged} with the color and areas of this page. */ private void processLocalColorsInternal() { if (supportsLocalColorExtraction()) return; if (supportsLocalColorExtraction()) return; float xOffset; float xOffsetStep; float wallpaperDimAmount; int xPage; int xPages; Set<RectF> areas; EngineWindowPage current; synchronized (mLock) { xOffset = mPendingXOffset; xOffsetStep = mPendingXOffsetStep; wallpaperDimAmount = mWallpaperDimAmount; if (DEBUG) { if (DEBUG) { Log.d(TAG, "processLocalColors " + xOffset + " of step " Log.d(TAG, "processLocalColors " + xOffset + " of step " + xOffsetStep); + xOffsetStep); } } //below is the default implementation if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN || !mSurfaceHolder.getSurface().isValid()) return; || !mSurfaceHolder.getSurface().isValid()) return; int xCurrentPage; int xCurrentPage; int xPages; if (!validStep(xOffsetStep)) { if (!validStep(xOffsetStep)) { if (DEBUG) { if (DEBUG) { Log.w(TAG, "invalid offset step " + xOffsetStep); Log.w(TAG, "invalid offset step " + xOffsetStep); Loading @@ -1651,10 +1686,8 @@ public abstract class WallpaperService extends Service { float finalXOffsetStep = xOffsetStep; float finalXOffsetStep = xOffsetStep; float finalXOffset = xOffset; float finalXOffset = xOffset; Trace.beginSection("WallpaperService#processLocalColors"); resetWindowPages(); resetWindowPages(); int xPage = xCurrentPage; xPage = xCurrentPage; EngineWindowPage current; if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { mWindowPages = new EngineWindowPage[xPages]; mWindowPages = new EngineWindowPage[xPages]; initWindowPages(mWindowPages, finalXOffsetStep); initWindowPages(mWindowPages, finalXOffsetStep); Loading @@ -1681,10 +1714,12 @@ public abstract class WallpaperService extends Service { xPage = mWindowPages.length - 1; xPage = mWindowPages.length - 1; } } current = mWindowPages[xPage]; current = mWindowPages[xPage]; updatePage(current, xPage, xPages, finalXOffsetStep); areas = new HashSet<>(current.getAreas()); Trace.endSection(); } updatePage(current, areas, xPage, xPages, wallpaperDimAmount); } } @GuardedBy("mLock") private void initWindowPages(EngineWindowPage[] windowPages, float step) { private void initWindowPages(EngineWindowPage[] windowPages, float step) { for (int i = 0; i < windowPages.length; i++) { for (int i = 0; i < windowPages.length; i++) { windowPages[i] = new EngineWindowPage(); windowPages[i] = new EngineWindowPage(); Loading @@ -1701,16 +1736,16 @@ public abstract class WallpaperService extends Service { } } } } void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages, void updatePage(EngineWindowPage currentPage, Set<RectF> areas, int pageIndx, int numPages, float xOffsetStep) { float wallpaperDimAmount) { // in case the clock is zero, we start with negative time // in case the clock is zero, we start with negative time long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION; long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION; long lapsed = current - currentPage.getLastUpdateTime(); long lapsed = current - currentPage.getLastUpdateTime(); // Always update the page when the last update time is <= 0 // Always update the page when the last update time is <= 0 // This is important especially when the device first boots // This is important especially when the device first boots if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) { if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return; return; } Surface surface = mSurfaceHolder.getSurface(); Surface surface = mSurfaceHolder.getSurface(); if (!surface.isValid()) return; if (!surface.isValid()) return; boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y; boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y; Loading @@ -1723,43 +1758,59 @@ public abstract class WallpaperService extends Service { Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); return; return; } } final String pixelCopySectionName = "WallpaperService#pixelCopy"; final int pixelCopyCount = mPixelCopyCount++; Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount); Bitmap screenShot = Bitmap.createBitmap(width, height, Bitmap screenShot = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Bitmap.Config.ARGB_8888); final Bitmap finalScreenShot = screenShot; final Bitmap finalScreenShot = screenShot; Trace.beginSection("WallpaperService#pixelCopy"); try { // TODO(b/274427458) check if this can be done in the background. PixelCopy.request(surface, screenShot, (res) -> { PixelCopy.request(surface, screenShot, (res) -> { Trace.endSection(); Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount); if (DEBUG) Log.d(TAG, "result of pixel copy is " + res); if (DEBUG) { Log.d(TAG, "result of pixel copy is: " + (res == PixelCopy.SUCCESS ? "SUCCESS" : "FAILURE")); } if (res != PixelCopy.SUCCESS) { if (res != PixelCopy.SUCCESS) { Bitmap lastBitmap = currentPage.getBitmap(); Bitmap lastBitmap = currentPage.getBitmap(); // assign the last bitmap taken for now // assign the last bitmap taken for now currentPage.setBitmap(mLastScreenshot); currentPage.setBitmap(mLastScreenshot); Bitmap lastScreenshot = mLastScreenshot; Bitmap lastScreenshot = mLastScreenshot; if (lastScreenshot != null && !lastScreenshot.isRecycled() if (lastScreenshot != null && !Objects.equals(lastBitmap, lastScreenshot)) { && !Objects.equals(lastBitmap, lastScreenshot)) { updatePageColors( updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); currentPage, areas, pageIndx, numPages, wallpaperDimAmount); } } } else { } else { mLastScreenshot = finalScreenShot; mLastScreenshot = finalScreenShot; // going to hold this lock for a while currentPage.setBitmap(finalScreenShot); currentPage.setBitmap(finalScreenShot); currentPage.setLastUpdateTime(current); currentPage.setLastUpdateTime(current); updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); updatePageColors( currentPage, areas, pageIndx, numPages, wallpaperDimAmount); } }, mBackgroundHandler); } catch (IllegalArgumentException e) { // this can potentially happen if the surface is invalidated right between the // surface.isValid() check and the PixelCopy operation. // in this case, stop: we'll compute colors on the next processLocalColors call. Log.w(TAG, "Cancelling processLocalColors: exception caught during PixelCopy"); } } }, mHandler); } } // locked by the passed page // locked by the passed page private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages, private void updatePageColors(EngineWindowPage page, Set<RectF> areas, float xOffsetStep) { int pageIndx, int numPages, float wallpaperDimAmount) { if (page.getBitmap() == null) return; if (page.getBitmap() == null) return; if (!mBackgroundHandler.getLooper().isCurrentThread()) { throw new IllegalStateException( "ProcessLocalColors should be called from the background thread"); } Trace.beginSection("WallpaperService#updatePageColors"); Trace.beginSection("WallpaperService#updatePageColors"); if (DEBUG) { if (DEBUG) { Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " + page.getAreas().size() + " and bitmap size of " + page.getAreas().size() + " and bitmap size of " + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); } } for (RectF area: page.getAreas()) { for (RectF area: areas) { if (area == null) continue; if (area == null) continue; RectF subArea = generateSubRect(area, pageIndx, numPages); RectF subArea = generateSubRect(area, pageIndx, numPages); Bitmap b = page.getBitmap(); Bitmap b = page.getBitmap(); Loading @@ -1769,12 +1820,12 @@ public abstract class WallpaperService extends Service { int height = Math.round(b.getHeight() * subArea.height()); int height = Math.round(b.getHeight() * subArea.height()); Bitmap target; Bitmap target; try { try { target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height); target = Bitmap.createBitmap(b, x, y, width, height); } catch (Exception e) { } catch (Exception e) { Log.e(TAG, "Error creating page local color bitmap", e); Log.e(TAG, "Error creating page local color bitmap", e); continue; continue; } } WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount); WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount); target.recycle(); target.recycle(); WallpaperColors currentColor = page.getColors(area); WallpaperColors currentColor = page.getColors(area); Loading @@ -1791,12 +1842,14 @@ public abstract class WallpaperService extends Service { + " local color callback for area" + area + " for page " + pageIndx + " local color callback for area" + area + " for page " + pageIndx + " of " + numPages); + " of " + numPages); } } mHandler.post(() -> { try { try { mConnection.onLocalWallpaperColorsChanged(area, color, mConnection.onLocalWallpaperColorsChanged(area, color, mDisplayContext.getDisplayId()); mDisplayContext.getDisplayId()); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); } } }); } } } } Trace.endSection(); Trace.endSection(); Loading @@ -1822,16 +1875,17 @@ public abstract class WallpaperService extends Service { return new RectF(left, in.top, right, in.bottom); return new RectF(left, in.top, right, in.bottom); } } @GuardedBy("mLock") private void resetWindowPages() { private void resetWindowPages() { if (supportsLocalColorExtraction()) return; if (supportsLocalColorExtraction()) return; if (!mResetWindowPages) return; if (!mResetWindowPages) return; mResetWindowPages = false; mResetWindowPages = false; mLastWindowPage = -1; for (int i = 0; i < mWindowPages.length; i++) { for (int i = 0; i < mWindowPages.length; i++) { mWindowPages[i].setLastUpdateTime(0L); mWindowPages[i].setLastUpdateTime(0L); } } } } @GuardedBy("mLock") private int getRectFPage(RectF area, float step) { private int getRectFPage(RectF area, float step) { if (!isValid(area)) return 0; if (!isValid(area)) return 0; if (!validStep(step)) return 0; if (!validStep(step)) return 0; Loading @@ -1852,12 +1906,12 @@ public abstract class WallpaperService extends Service { if (DEBUG) { if (DEBUG) { Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); } } mHandler.post(() -> { mBackgroundHandler.post(() -> { synchronized (mLock) { mLocalColorsToAdd.addAll(regions); mLocalColorsToAdd.addAll(regions); processLocalColors(mPendingXOffset, mPendingYOffset); } processLocalColors(); }); }); } } /** /** Loading @@ -1867,7 +1921,8 @@ public abstract class WallpaperService extends Service { */ */ public void removeLocalColorsAreas(@NonNull List<RectF> regions) { public void removeLocalColorsAreas(@NonNull List<RectF> regions) { if (supportsLocalColorExtraction()) return; if (supportsLocalColorExtraction()) return; mHandler.post(() -> { mBackgroundHandler.post(() -> { synchronized (mLock) { float step = mPendingXOffsetStep; float step = mPendingXOffsetStep; mLocalColorsToAdd.removeAll(regions); mLocalColorsToAdd.removeAll(regions); mLocalColorAreas.removeAll(regions); mLocalColorAreas.removeAll(regions); Loading @@ -1879,6 +1934,7 @@ public abstract class WallpaperService extends Service { mWindowPages[i].removeArea(regions.get(j)); mWindowPages[i].removeArea(regions.get(j)); } } } } } }); }); } } Loading @@ -1894,7 +1950,7 @@ public abstract class WallpaperService extends Service { } } private boolean validStep(float step) { private boolean validStep(float step) { return !PROHIBITED_STEPS.contains(step) && step > 0. && step <= 1.; return !Float.isNaN(step) && step > 0f && step <= 1f; } } void doCommand(WallpaperCommand cmd) { void doCommand(WallpaperCommand cmd) { Loading Loading @@ -2498,6 +2554,9 @@ public abstract class WallpaperService extends Service { @Override @Override public void onCreate() { public void onCreate() { Trace.beginSection("WPMS.onCreate"); Trace.beginSection("WPMS.onCreate"); mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); super.onCreate(); super.onCreate(); Trace.endSection(); Trace.endSection(); } } Loading @@ -2510,6 +2569,7 @@ public abstract class WallpaperService extends Service { mActiveEngines.get(i).detach(); mActiveEngines.get(i).detach(); } } mActiveEngines.clear(); mActiveEngines.clear(); mBackgroundThread.quitSafely(); Trace.endSection(); Trace.endSection(); } } Loading