Reload LockscreenWallpaper and ImageWallpaper on background thread

Fixes: 27148260
Change-Id: I426712024ec1a82fccd48154c65da31d3e610525
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 1306284..c72f5d2 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.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 @@
 
         private int mLastRequestedWidth = -1;
         private int mLastRequestedHeight = -1;
+        private AsyncTask<Void, Void, Bitmap> mLoader;
+        private boolean mNeedsDrawAfterLoadingWallpaper;
 
         public DrawableEngine() {
             super();
@@ -184,10 +187,9 @@
             super.onCreate(surfaceHolder);
 
             mDefaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay();
-
-            updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo());
-
             setOffsetNotificationsEnabled(false);
+
+            updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo(), false /* forDraw */);
         }
 
         @Override
@@ -197,17 +199,19 @@
             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 @@
             } else {
                 surfaceHolder.setSizeFromLayout();
             }
+            return hasWallpaper;
         }
 
         @Override
@@ -299,6 +304,7 @@
             }
             super.onSurfaceRedrawNeeded(holder);
 
+            mLastSurfaceHeight = mLastSurfaceWidth = -1;
             drawFrame();
         }
 
@@ -317,7 +323,9 @@
                 // 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 @@
                 }
                 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 @@
                                 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 @@
             }
         }
 
-        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<Void, Void, Bitmap>() {
+                @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 92f3585..c423b67 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.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 @@
 
     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 @@
     // 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<Void, Void, LoaderResult> 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 @@
             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 @@
     @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<Void, Void, LoaderResult>() {
+            @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);
+        }
     }
 
     /**