diff options
| -rw-r--r-- | core/java/android/service/wallpaper/WallpaperService.java | 338 |
1 files changed, 199 insertions, 139 deletions
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 8f76a1831605..2887d1f85d21 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -61,6 +61,7 @@ import android.hardware.display.DisplayManager.DisplayListener; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; @@ -96,6 +97,7 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.window.ClientWindowFrames; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.HandlerCaller; import com.android.internal.view.BaseIWindow; @@ -104,9 +106,10 @@ import com.android.internal.view.BaseSurfaceHolder; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -166,11 +169,12 @@ public abstract class WallpaperService extends Service { private static final int MSG_RESIZE_PREVIEW = 10110; private static final int MSG_REPORT_SHOWN = 10150; 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 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 = SystemProperties.getBoolean("persist.debug.enable_wallpaper_dimming", true); @@ -180,6 +184,9 @@ public abstract class WallpaperService extends Service { private final ArrayList<Engine> mActiveEngines = new ArrayList<Engine>(); + private Handler mBackgroundHandler; + private HandlerThread mBackgroundThread; + static final class WallpaperCommand { String action; int x; @@ -198,14 +205,6 @@ public abstract class WallpaperService extends Service { */ public class Engine { 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. HandlerCaller mCaller; @@ -267,11 +266,34 @@ public abstract class WallpaperService extends Service { final Object mLock = new Object(); boolean mOffsetMessageEnqueued; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - float mPendingXOffset; - float mPendingYOffset; - float mPendingXOffsetStep; - float mPendingYOffsetStep; + @GuardedBy("mLock") + private float mPendingXOffset; + @GuardedBy("mLock") + 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; MotionEvent mPendingMove; boolean mIsInAmbientMode; @@ -280,12 +302,8 @@ public abstract class WallpaperService extends Service { private long mLastColorInvalidation; 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 Handler mHandler; - private Display mDisplay; private Context mDisplayContext; private int mDisplayState; @@ -825,7 +843,7 @@ public abstract class WallpaperService extends Service { + "was not established."); } mResetWindowPages = true; - processLocalColors(mPendingXOffset, mPendingXOffsetStep); + processLocalColors(); } catch (RemoteException e) { Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); } @@ -1361,10 +1379,9 @@ public abstract class WallpaperService extends Service { mIsCreating = false; mSurfaceCreated = true; if (redrawNeeded) { - resetWindowPages(); mSession.finishDrawing(mWindow, null /* postDrawTransaction */, Integer.MAX_VALUE); - processLocalColors(mPendingXOffset, mPendingXOffsetStep); + processLocalColors(); } reposition(); reportEngineShown(shouldWaitForEngineShown()); @@ -1509,7 +1526,7 @@ public abstract class WallpaperService extends Service { if (!mDestroyed) { mVisible = visible; reportVisibility(); - if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep); + if (mReportedVisible) processLocalColors(); } else { AnimationHandler.requestAnimatorsEnabled(visible, this); } @@ -1594,14 +1611,14 @@ public abstract class WallpaperService extends Service { } // setup local color extraction data - processLocalColors(xOffset, xOffsetStep); + processLocalColors(); } /** * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls. */ - private void processLocalColors(float xOffset, float xOffsetStep) { + private void processLocalColors() { if (mProcessLocalColorsPending.compareAndSet(false, true)) { final long now = mClockFunction.get(); final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp; @@ -1611,80 +1628,98 @@ public abstract class WallpaperService extends Service { mHandler.postDelayed(() -> { mLastProcessLocalColorsTimestamp = now + timeToWait; mProcessLocalColorsPending.set(false); - processLocalColorsInternal(xOffset, xOffsetStep); + processLocalColorsInternal(); }, 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 (DEBUG) { - Log.d(TAG, "processLocalColors " + xOffset + " of step " - + xOffsetStep); - } - //below is the default implementation - if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN - || !mSurfaceHolder.getSurface().isValid()) return; - int xCurrentPage; + float xOffset; + float xOffsetStep; + float wallpaperDimAmount; + int xPage; int xPages; - if (!validStep(xOffsetStep)) { + Set<RectF> areas; + EngineWindowPage current; + + synchronized (mLock) { + xOffset = mPendingXOffset; + xOffsetStep = mPendingXOffsetStep; + wallpaperDimAmount = mWallpaperDimAmount; + if (DEBUG) { - Log.w(TAG, "invalid offset step " + xOffsetStep); + Log.d(TAG, "processLocalColors " + xOffset + " of step " + + xOffsetStep); + } + if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN + || !mSurfaceHolder.getSurface().isValid()) return; + int xCurrentPage; + if (!validStep(xOffsetStep)) { + if (DEBUG) { + Log.w(TAG, "invalid offset step " + xOffsetStep); + } + xOffset = 0; + xOffsetStep = 1; + xCurrentPage = 0; + xPages = 1; + } else { + xPages = Math.round(1 / xOffsetStep) + 1; + xOffsetStep = (float) 1 / (float) xPages; + float shrink = (float) (xPages - 1) / (float) xPages; + xOffset *= shrink; + xCurrentPage = Math.round(xOffset / xOffsetStep); + } + if (DEBUG) { + Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage); + Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset); } - xOffset = 0; - xOffsetStep = 1; - xCurrentPage = 0; - xPages = 1; - } else { - xPages = Math.round(1 / xOffsetStep) + 1; - xOffsetStep = (float) 1 / (float) xPages; - float shrink = (float) (xPages - 1) / (float) xPages; - xOffset *= shrink; - xCurrentPage = Math.round(xOffset / xOffsetStep); - } - if (DEBUG) { - Log.d(TAG, "xPages " + xPages + " xPage " + xCurrentPage); - Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset); - } - float finalXOffsetStep = xOffsetStep; - float finalXOffset = xOffset; + float finalXOffsetStep = xOffsetStep; + float finalXOffset = xOffset; - Trace.beginSection("WallpaperService#processLocalColors"); - resetWindowPages(); - int xPage = xCurrentPage; - EngineWindowPage current; - if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { - mWindowPages = new EngineWindowPage[xPages]; - initWindowPages(mWindowPages, finalXOffsetStep); - } - if (mLocalColorsToAdd.size() != 0) { - for (RectF colorArea : mLocalColorsToAdd) { - if (!isValid(colorArea)) continue; - mLocalColorAreas.add(colorArea); - int colorPage = getRectFPage(colorArea, finalXOffsetStep); - EngineWindowPage currentPage = mWindowPages[colorPage]; - currentPage.setLastUpdateTime(0); - currentPage.removeColor(colorArea); + resetWindowPages(); + xPage = xCurrentPage; + if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { + mWindowPages = new EngineWindowPage[xPages]; + initWindowPages(mWindowPages, finalXOffsetStep); } - mLocalColorsToAdd.clear(); - } - if (xPage >= mWindowPages.length) { - if (DEBUG) { - Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage); - Log.e(TAG, "error on page " + xPage + " out of " + xPages); - Log.e(TAG, - "error on xOffsetStep " + finalXOffsetStep - + " xOffset " + finalXOffset); + if (mLocalColorsToAdd.size() != 0) { + for (RectF colorArea : mLocalColorsToAdd) { + if (!isValid(colorArea)) continue; + mLocalColorAreas.add(colorArea); + int colorPage = getRectFPage(colorArea, finalXOffsetStep); + EngineWindowPage currentPage = mWindowPages[colorPage]; + currentPage.setLastUpdateTime(0); + currentPage.removeColor(colorArea); + } + mLocalColorsToAdd.clear(); } - xPage = mWindowPages.length - 1; + if (xPage >= mWindowPages.length) { + if (DEBUG) { + Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage); + Log.e(TAG, "error on page " + xPage + " out of " + xPages); + Log.e(TAG, + "error on xOffsetStep " + finalXOffsetStep + + " xOffset " + finalXOffset); + } + xPage = mWindowPages.length - 1; + } + current = mWindowPages[xPage]; + areas = new HashSet<>(current.getAreas()); } - current = mWindowPages[xPage]; - updatePage(current, xPage, xPages, finalXOffsetStep); - Trace.endSection(); + updatePage(current, areas, xPage, xPages, wallpaperDimAmount); } + @GuardedBy("mLock") private void initWindowPages(EngineWindowPage[] windowPages, float step) { for (int i = 0; i < windowPages.length; i++) { windowPages[i] = new EngineWindowPage(); @@ -1701,16 +1736,16 @@ public abstract class WallpaperService extends Service { } } - void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages, - float xOffsetStep) { + void updatePage(EngineWindowPage currentPage, Set<RectF> areas, int pageIndx, int numPages, + float wallpaperDimAmount) { + // in case the clock is zero, we start with negative time long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION; long lapsed = current - currentPage.getLastUpdateTime(); // Always update the page when the last update time is <= 0 // This is important especially when the device first boots - if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) { - return; - } + if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return; + Surface surface = mSurfaceHolder.getSurface(); if (!surface.isValid()) return; boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y; @@ -1723,43 +1758,59 @@ public abstract class WallpaperService extends Service { Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); return; } + final String pixelCopySectionName = "WallpaperService#pixelCopy"; + final int pixelCopyCount = mPixelCopyCount++; + Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount); Bitmap screenShot = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); final Bitmap finalScreenShot = screenShot; - Trace.beginSection("WallpaperService#pixelCopy"); - PixelCopy.request(surface, screenShot, (res) -> { - Trace.endSection(); - if (DEBUG) Log.d(TAG, "result of pixel copy is " + res); - if (res != PixelCopy.SUCCESS) { - Bitmap lastBitmap = currentPage.getBitmap(); - // assign the last bitmap taken for now - currentPage.setBitmap(mLastScreenshot); - Bitmap lastScreenshot = mLastScreenshot; - if (lastScreenshot != null && !lastScreenshot.isRecycled() - && !Objects.equals(lastBitmap, lastScreenshot)) { - updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); + try { + // TODO(b/274427458) check if this can be done in the background. + PixelCopy.request(surface, screenShot, (res) -> { + Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount); + if (DEBUG) { + Log.d(TAG, "result of pixel copy is: " + + (res == PixelCopy.SUCCESS ? "SUCCESS" : "FAILURE")); } - } else { - mLastScreenshot = finalScreenShot; - // going to hold this lock for a while - currentPage.setBitmap(finalScreenShot); - currentPage.setLastUpdateTime(current); - updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); - } - }, mHandler); - + if (res != PixelCopy.SUCCESS) { + Bitmap lastBitmap = currentPage.getBitmap(); + // assign the last bitmap taken for now + currentPage.setBitmap(mLastScreenshot); + Bitmap lastScreenshot = mLastScreenshot; + if (lastScreenshot != null && !Objects.equals(lastBitmap, lastScreenshot)) { + updatePageColors( + currentPage, areas, pageIndx, numPages, wallpaperDimAmount); + } + } else { + mLastScreenshot = finalScreenShot; + currentPage.setBitmap(finalScreenShot); + currentPage.setLastUpdateTime(current); + 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"); + } } // locked by the passed page - private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages, - float xOffsetStep) { + private void updatePageColors(EngineWindowPage page, Set<RectF> areas, + int pageIndx, int numPages, float wallpaperDimAmount) { if (page.getBitmap() == null) return; + if (!mBackgroundHandler.getLooper().isCurrentThread()) { + throw new IllegalStateException( + "ProcessLocalColors should be called from the background thread"); + } Trace.beginSection("WallpaperService#updatePageColors"); if (DEBUG) { Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " + page.getAreas().size() + " and bitmap size of " + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); } - for (RectF area: page.getAreas()) { + for (RectF area: areas) { if (area == null) continue; RectF subArea = generateSubRect(area, pageIndx, numPages); Bitmap b = page.getBitmap(); @@ -1769,12 +1820,12 @@ public abstract class WallpaperService extends Service { int height = Math.round(b.getHeight() * subArea.height()); Bitmap target; try { - target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height); + target = Bitmap.createBitmap(b, x, y, width, height); } catch (Exception e) { Log.e(TAG, "Error creating page local color bitmap", e); continue; } - WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount); + WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount); target.recycle(); WallpaperColors currentColor = page.getColors(area); @@ -1791,12 +1842,14 @@ public abstract class WallpaperService extends Service { + " local color callback for area" + area + " for page " + pageIndx + " of " + numPages); } - try { - mConnection.onLocalWallpaperColorsChanged(area, color, - mDisplayContext.getDisplayId()); - } catch (RemoteException e) { - Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); - } + mHandler.post(() -> { + try { + mConnection.onLocalWallpaperColorsChanged(area, color, + mDisplayContext.getDisplayId()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); + } + }); } } Trace.endSection(); @@ -1822,16 +1875,17 @@ public abstract class WallpaperService extends Service { return new RectF(left, in.top, right, in.bottom); } + @GuardedBy("mLock") private void resetWindowPages() { if (supportsLocalColorExtraction()) return; if (!mResetWindowPages) return; mResetWindowPages = false; - mLastWindowPage = -1; for (int i = 0; i < mWindowPages.length; i++) { mWindowPages[i].setLastUpdateTime(0L); } } + @GuardedBy("mLock") private int getRectFPage(RectF area, float step) { if (!isValid(area)) return 0; if (!validStep(step)) return 0; @@ -1852,12 +1906,12 @@ public abstract class WallpaperService extends Service { if (DEBUG) { Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); } - mHandler.post(() -> { - mLocalColorsToAdd.addAll(regions); - processLocalColors(mPendingXOffset, mPendingYOffset); + mBackgroundHandler.post(() -> { + synchronized (mLock) { + mLocalColorsToAdd.addAll(regions); + } + processLocalColors(); }); - - } /** @@ -1867,16 +1921,18 @@ public abstract class WallpaperService extends Service { */ public void removeLocalColorsAreas(@NonNull List<RectF> regions) { if (supportsLocalColorExtraction()) return; - mHandler.post(() -> { - float step = mPendingXOffsetStep; - mLocalColorsToAdd.removeAll(regions); - mLocalColorAreas.removeAll(regions); - if (!validStep(step)) { - return; - } - for (int i = 0; i < mWindowPages.length; i++) { - for (int j = 0; j < regions.size(); j++) { - mWindowPages[i].removeArea(regions.get(j)); + mBackgroundHandler.post(() -> { + synchronized (mLock) { + float step = mPendingXOffsetStep; + mLocalColorsToAdd.removeAll(regions); + mLocalColorAreas.removeAll(regions); + if (!validStep(step)) { + return; + } + for (int i = 0; i < mWindowPages.length; i++) { + for (int j = 0; j < regions.size(); j++) { + mWindowPages[i].removeArea(regions.get(j)); + } } } }); @@ -1894,7 +1950,7 @@ public abstract class WallpaperService extends Service { } 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) { @@ -2498,6 +2554,9 @@ public abstract class WallpaperService extends Service { @Override public void onCreate() { Trace.beginSection("WPMS.onCreate"); + mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); super.onCreate(); Trace.endSection(); } @@ -2510,6 +2569,7 @@ public abstract class WallpaperService extends Service { mActiveEngines.get(i).detach(); } mActiveEngines.clear(); + mBackgroundThread.quitSafely(); Trace.endSection(); } |