From 7e9dec284128ef07024c85bc59f317f3e6726c23 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Wed, 13 Apr 2016 17:43:45 -0700 Subject: [PATCH] Reload LockscreenWallpaper and ImageWallpaper on background thread Fixes: 27148260 Change-Id: I426712024ec1a82fccd48154c65da31d3e610525 --- .../com/android/systemui/ImageWallpaper.java | 144 ++++++++++------ .../statusbar/phone/LockscreenWallpaper.java | 162 ++++++++++++------ 2 files changed, 197 insertions(+), 109 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 130628442dfb9..c72f5d2f18cdd 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -29,6 +29,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region.Op; import android.opengl.GLUtils; +import android.os.AsyncTask; import android.os.SystemProperties; import android.renderscript.Matrix4f; import android.service.wallpaper.WallpaperService; @@ -155,6 +156,8 @@ public class ImageWallpaper extends WallpaperService { private int mLastRequestedWidth = -1; private int mLastRequestedHeight = -1; + private AsyncTask mLoader; + private boolean mNeedsDrawAfterLoadingWallpaper; public DrawableEngine() { super(); @@ -184,10 +187,9 @@ public class ImageWallpaper extends WallpaperService { super.onCreate(surfaceHolder); mDefaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay(); - - updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo()); - setOffsetNotificationsEnabled(false); + + updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo(), false /* forDraw */); } @Override @@ -197,17 +199,19 @@ public class ImageWallpaper extends WallpaperService { mWallpaperManager.forgetLoadedWallpaper(); } - void updateSurfaceSize(SurfaceHolder surfaceHolder, DisplayInfo displayInfo) { + boolean updateSurfaceSize(SurfaceHolder surfaceHolder, DisplayInfo displayInfo, + boolean forDraw) { + boolean hasWallpaper = true; + // Load background image dimensions, if we haven't saved them yet if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) { // Need to load the image to get dimensions mWallpaperManager.forgetLoadedWallpaper(); - updateWallpaperLocked(); - if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) { - // Default to the display size if we can't find the dimensions - mBackgroundWidth = displayInfo.logicalWidth; - mBackgroundHeight = displayInfo.logicalHeight; + loadWallpaper(forDraw); + if (DEBUG) { + Log.d(TAG, "Reloading, redoing updateSurfaceSize later."); } + hasWallpaper = false; } // Force the wallpaper to cover the screen in both dimensions @@ -224,6 +228,7 @@ public class ImageWallpaper extends WallpaperService { } else { surfaceHolder.setSizeFromLayout(); } + return hasWallpaper; } @Override @@ -299,6 +304,7 @@ public class ImageWallpaper extends WallpaperService { } super.onSurfaceRedrawNeeded(holder); + mLastSurfaceHeight = mLastSurfaceWidth = -1; drawFrame(); } @@ -317,7 +323,9 @@ public class ImageWallpaper extends WallpaperService { // should change if (newRotation != mLastRotation) { // Update surface size (if necessary) - updateSurfaceSize(getSurfaceHolder(), displayInfo); + if (!updateSurfaceSize(getSurfaceHolder(), displayInfo, true /* forDraw */)) { + return; // had to reload wallpaper, will retry later + } mRotationAtLastSurfaceSizeUpdate = newRotation; mDisplayWidthAtLastSurfaceSizeUpdate = displayInfo.logicalWidth; mDisplayHeightAtLastSurfaceSizeUpdate = displayInfo.logicalHeight; @@ -339,8 +347,8 @@ public class ImageWallpaper extends WallpaperService { } mLastRotation = newRotation; - // Load bitmap if it is not yet loaded or if it was loaded at a different size - if (mBackground == null || surfaceDimensionsChanged) { + // Load bitmap if it is not yet loaded + if (mBackground == null) { if (DEBUG) { Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " + mBackground + ", " + @@ -349,20 +357,11 @@ public class ImageWallpaper extends WallpaperService { dw + ", " + dh); } mWallpaperManager.forgetLoadedWallpaper(); - updateWallpaperLocked(); - if (mBackground == null) { - if (DEBUG) { - Log.d(TAG, "Unable to load bitmap"); - } - return; - } + loadWallpaper(true /* needDraw */); if (DEBUG) { - if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) { - Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " + - dw + ", " + dh + ", " + mBackground.getWidth() + ", " + - mBackground.getHeight()); - } + Log.d(TAG, "Reloading, resuming draw later"); } + return; } // Center the scaled image @@ -422,36 +421,77 @@ public class ImageWallpaper extends WallpaperService { } } - private void updateWallpaperLocked() { - Throwable exception = null; - try { - mBackground = null; - mBackgroundWidth = -1; - mBackgroundHeight = -1; - mBackground = mWallpaperManager.getBitmap(); - mBackgroundWidth = mBackground.getWidth(); - mBackgroundHeight = mBackground.getHeight(); - } catch (RuntimeException e) { - exception = e; - } catch (OutOfMemoryError e) { - exception = e; - } - - if (exception != null) { - mBackground = null; - mBackgroundWidth = -1; - mBackgroundHeight = -1; - // Note that if we do fail at this, and the default wallpaper can't - // be loaded, we will go into a cycle. Don't do a build where the - // default wallpaper can't be loaded. - Log.w(TAG, "Unable to load wallpaper!", exception); - try { - mWallpaperManager.clear(); - } catch (IOException ex) { - // now we're really screwed. - Log.w(TAG, "Unable reset to default wallpaper!", ex); + /** + * Loads the wallpaper on background thread and schedules updating the surface frame, + * and if {@param needsDraw} is set also draws a frame. + * + * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to + * the active request). + */ + private void loadWallpaper(boolean needsDraw) { + mNeedsDrawAfterLoadingWallpaper |= needsDraw; + if (mLoader != null) { + if (DEBUG) { + Log.d(TAG, "Skipping loadWallpaper, already in flight "); } + return; } + mLoader = new AsyncTask() { + @Override + protected Bitmap doInBackground(Void... params) { + Throwable exception; + try { + return mWallpaperManager.getBitmap(); + } catch (RuntimeException | OutOfMemoryError e) { + exception = e; + } + + if (exception != null) { + // Note that if we do fail at this, and the default wallpaper can't + // be loaded, we will go into a cycle. Don't do a build where the + // default wallpaper can't be loaded. + Log.w(TAG, "Unable to load wallpaper!", exception); + try { + mWallpaperManager.clear(); + } catch (IOException ex) { + // now we're really screwed. + Log.w(TAG, "Unable reset to default wallpaper!", ex); + } + + try { + return mWallpaperManager.getBitmap(); + } catch (RuntimeException | OutOfMemoryError e) { + Log.w(TAG, "Unable to load default wallpaper!", e); + } + } + return null; + } + + @Override + protected void onPostExecute(Bitmap b) { + mBackground = null; + mBackgroundWidth = -1; + mBackgroundHeight = -1; + + if (b != null) { + mBackground = b; + mBackgroundWidth = mBackground.getWidth(); + mBackgroundHeight = mBackground.getHeight(); + } + + if (DEBUG) { + Log.d(TAG, "Wallpaper loaded: " + mBackground); + } + updateSurfaceSize(getSurfaceHolder(), getDefaultDisplayInfo(), + false /* forDraw */); + if (mNeedsDrawAfterLoadingWallpaper) { + drawFrame(); + } + + mLoader = null; + mNeedsDrawAfterLoadingWallpaper = false; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index 92f3585f43efe..c423b670e3b91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -27,7 +27,7 @@ import android.graphics.BitmapFactory; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.DrawableWrapper; -import android.os.Bundle; +import android.os.AsyncTask; import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -46,9 +46,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen private static final String TAG = "LockscreenWallpaper"; - private final Context mContext; private final PhoneStatusBar mBar; - private final IWallpaperManager mService; private final WallpaperManager mWallpaperManager; private final Handler mH; @@ -58,69 +56,75 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen // The user selected in the UI, or null if no user is selected or UI doesn't support selecting // users. private UserHandle mSelectedUser; + private AsyncTask mLoader; public LockscreenWallpaper(Context ctx, PhoneStatusBar bar, Handler h) { - mContext = ctx; mBar = bar; mH = h; - mService = IWallpaperManager.Stub.asInterface( - ServiceManager.getService(Context.WALLPAPER_SERVICE)); mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE); mCurrentUserId = ActivityManager.getCurrentUser(); + IWallpaperManager service = IWallpaperManager.Stub.asInterface( + ServiceManager.getService(Context.WALLPAPER_SERVICE)); try { - mService.setLockWallpaperCallback(this); + service.setLockWallpaperCallback(this); } catch (RemoteException e) { Log.e(TAG, "System dead?" + e); } } public Bitmap getBitmap() { - try { - if (mCached) { - return mCache; - } - if (!mService.isWallpaperSupported(mContext.getOpPackageName())) { - mCached = true; - mCache = null; - return null; - } - // Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK - // wallpaper. - final int lockWallpaperUserId = - mSelectedUser != null ? mSelectedUser.getIdentifier() : mCurrentUserId; - ParcelFileDescriptor fd = mService.getWallpaper(null, WallpaperManager.FLAG_LOCK, - new Bundle(), lockWallpaperUserId); - if (fd != null) { - try { - BitmapFactory.Options options = new BitmapFactory.Options(); - mCache = BitmapFactory.decodeFileDescriptor( - fd.getFileDescriptor(), null, options); - mCached = true; - return mCache; - } catch (OutOfMemoryError e) { - Log.w(TAG, "Can't decode file", e); - return null; - } finally { - IoUtils.closeQuietly(fd); - } - } else { - mCached = true; - if (mSelectedUser != null && mSelectedUser.getIdentifier() != mCurrentUserId) { - // When selected user is different from the current user, show the selected - // user's static wallpaper. - mCache = mWallpaperManager.getBitmapAsUser(mSelectedUser.getIdentifier()); - } else { - // When there is no selected user, or it's same as the current user, show the - // system (possibly dynamic) wallpaper for the selected user. - mCache = null; - } - return mCache; - } - } catch (RemoteException e) { - Log.e(TAG, "System dead?" + e); + if (mCached) { + return mCache; + } + if (!mWallpaperManager.isWallpaperSupported()) { + mCached = true; + mCache = null; return null; } + + LoaderResult result = loadBitmap(mCurrentUserId, mSelectedUser); + if (result.success) { + mCached = true; + mCache = result.bitmap; + } + return mCache; + } + + public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) { + // May be called on any thread - only use thread safe operations. + + // Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK + // wallpaper. + final int lockWallpaperUserId = + selectedUser != null ? selectedUser.getIdentifier() : currentUserId; + ParcelFileDescriptor fd = mWallpaperManager.getWallpaperFile( + WallpaperManager.FLAG_LOCK, lockWallpaperUserId); + + if (fd != null) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + return LoaderResult.success(BitmapFactory.decodeFileDescriptor( + fd.getFileDescriptor(), null, options)); + } catch (OutOfMemoryError e) { + Log.w(TAG, "Can't decode file", e); + return LoaderResult.fail(); + } finally { + IoUtils.closeQuietly(fd); + } + } else { + if (selectedUser != null && selectedUser.getIdentifier() != currentUserId) { + // When selected user is different from the current user, show the selected + // user's static wallpaper. + return LoaderResult.success( + mWallpaperManager.getBitmapAsUser(selectedUser.getIdentifier())); + + } else { + // When there is no selected user, or it's same as the current user, show the + // system (possibly dynamic) wallpaper for the selected user. + return LoaderResult.success(null); + } + } } public void setCurrentUser(int user) { @@ -135,14 +139,16 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen return; } mSelectedUser = selectedUser; - - mH.removeCallbacks(this); - mH.post(this); + postUpdateWallpaper(); } @Override public void onWallpaperChanged() { // Called on Binder thread. + postUpdateWallpaper(); + } + + private void postUpdateWallpaper() { mH.removeCallbacks(this); mH.post(this); } @@ -150,10 +156,52 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen @Override public void run() { // Called in response to onWallpaperChanged on the main thread. - mCached = false; - mCache = null; - getBitmap(); - mBar.updateMediaMetaData(true /* metaDataChanged */, true /* allowEnterAnimation */); + + if (mLoader != null) { + mLoader.cancel(false /* interrupt */); + } + + final int currentUser = mCurrentUserId; + final UserHandle selectedUser = mSelectedUser; + mLoader = new AsyncTask() { + @Override + protected LoaderResult doInBackground(Void... params) { + return loadBitmap(currentUser, selectedUser); + } + + @Override + protected void onPostExecute(LoaderResult result) { + super.onPostExecute(result); + if (isCancelled()) { + return; + } + if (result.success) { + mCached = true; + mCache = result.bitmap; + mBar.updateMediaMetaData( + true /* metaDataChanged */, true /* allowEnterAnimation */); + } + mLoader = null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private static class LoaderResult { + public final boolean success; + public final Bitmap bitmap; + + LoaderResult(boolean success, Bitmap bitmap) { + this.success = success; + this.bitmap = bitmap; + } + + static LoaderResult success(Bitmap b) { + return new LoaderResult(true, b); + } + + static LoaderResult fail() { + return new LoaderResult(false, null); + } } /**