Loading core/java/android/app/IWallpaperManager.aidl +10 −4 Original line number Diff line number Diff line Loading @@ -35,9 +35,15 @@ interface IWallpaperManager { * 'which' is some combination of: * FLAG_SET_SYSTEM * FLAG_SET_LOCK * * A 'null' cropHint rectangle is explicitly permitted as a sentinel for "whatever * the source image's bounding rect is." * * The completion callback's "onWallpaperChanged()" method is invoked when the * new wallpaper content is ready to display. */ ParcelFileDescriptor setWallpaper(String name, in String callingPackage, out Bundle extras, int which); in Rect cropHint, out Bundle extras, int which, IWallpaperManagerCallback completion); /** * Set the live wallpaper. This only affects the system wallpaper. Loading core/java/android/app/WallpaperManager.java +43 −3 Original line number Diff line number Diff line Loading @@ -64,6 +64,8 @@ import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Provides access to the system wallpaper. With WallpaperManager, you can Loading Loading @@ -770,18 +772,26 @@ public class WallpaperManager { return 0; } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { Resources resources = mContext.getResources(); /* Set the wallpaper to the default values */ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( "res:" + resources.getResourceName(resid), mContext.getOpPackageName(), result, which); mContext.getOpPackageName(), null, result, which, completion); if (fd != null) { FileOutputStream fos = null; boolean ok = false; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); copyStreamToWallpaperFile(resources.openRawResource(resid), fos); // The 'close()' is the trigger for any server-side image manipulation, // so we must do that before waiting for completion. fos.close(); completion.waitForCompletion(); } finally { // Might be redundant but completion shouldn't wait unless the write // succeeded; this is a fallback if it threw past the close+wait. IoUtils.closeQuietly(fos); } } Loading Loading @@ -876,14 +886,17 @@ public class WallpaperManager { return 0; } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), result, which); mContext.getOpPackageName(), visibleCropHint, result, which, completion); if (fd != null) { FileOutputStream fos = null; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos); fos.close(); completion.waitForCompletion(); } finally { IoUtils.closeQuietly(fos); } Loading Loading @@ -990,14 +1003,17 @@ public class WallpaperManager { return 0; } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), result, which); mContext.getOpPackageName(), visibleCropHint, result, which, completion); if (fd != null) { FileOutputStream fos = null; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); copyStreamToWallpaperFile(bitmapData, fos); fos.close(); completion.waitForCompletion(); } finally { IoUtils.closeQuietly(fos); } Loading Loading @@ -1385,4 +1401,28 @@ public class WallpaperManager { return null; } // Private completion callback for setWallpaper() synchronization private class WallpaperSetCompletion extends IWallpaperManagerCallback.Stub { final CountDownLatch mLatch; public WallpaperSetCompletion() { mLatch = new CountDownLatch(1); } public void waitForCompletion() { try { mLatch.await(30, TimeUnit.SECONDS); } catch (InterruptedException e) { // This might be legit: the crop may take a very long time. Don't sweat // it in that case; we are okay with display lagging behind in order to // keep the caller from locking up indeterminately. } } @Override public void onWallpaperChanged() throws RemoteException { mLatch.countDown(); } } } services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +181 −17 Original line number Diff line number Diff line Loading @@ -42,6 +42,9 @@ import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; Loading Loading @@ -71,6 +74,7 @@ import android.view.Display; import android.view.IWindowManager; import android.view.WindowManager; import java.io.BufferedOutputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; Loading @@ -96,7 +100,7 @@ import libcore.io.IoUtils; public class WallpaperManagerService extends IWallpaperManager.Stub { static final String TAG = "WallpaperManagerService"; static final boolean DEBUG = false; static final boolean DEBUG = true; final Object mLock = new Object[0]; Loading @@ -106,7 +110,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { */ static final long MIN_WALLPAPER_CRASH_TIME = 10000; static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128; static final String WALLPAPER = "wallpaper"; static final String WALLPAPER = "wallpaper_orig"; static final String WALLPAPER_CROP = "wallpaper"; static final String WALLPAPER_INFO = "wallpaper_info.xml"; /** Loading @@ -120,6 +125,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { final WallpaperData mWallpaper; final File mWallpaperDir; final File mWallpaperFile; final File mWallpaperCropFile; final File mWallpaperInfoFile; public WallpaperObserver(WallpaperData wallpaper) { Loading @@ -128,6 +134,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { mWallpaperDir = getWallpaperDir(wallpaper.userId); mWallpaper = wallpaper; mWallpaperFile = new File(mWallpaperDir, WALLPAPER); mWallpaperCropFile = new File(mWallpaperDir, WALLPAPER_CROP); mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO); } Loading @@ -136,8 +143,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (path == null) { return; } final boolean written = (event == CLOSE_WRITE || event == MOVED_TO); final File changedFile = new File(mWallpaperDir, path); synchronized (mLock) { File changedFile = new File(mWallpaperDir, path); if (mWallpaperFile.equals(changedFile) || mWallpaperInfoFile.equals(changedFile)) { // changing the wallpaper means we'll need to back up the new one Loading @@ -148,12 +157,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } if (mWallpaperFile.equals(changedFile)) { notifyCallbacksLocked(mWallpaper); final boolean written = (event == CLOSE_WRITE || event == MOVED_TO); if (mWallpaper.wallpaperComponent == null || event != CLOSE_WRITE // includes the MOVED_TO case || mWallpaper.imageWallpaperPending) { if (written) { // The image source has finished writing the source image, // so we now produce the crop rect (in the background), and // only publish the new displayable (sub)image as a result // of that work. generateCrop(mWallpaper); mWallpaper.imageWallpaperPending = false; if (mWallpaper.setComplete != null) { try { mWallpaper.setComplete.onWallpaperChanged(); } catch (RemoteException e) { // if this fails we don't really care; the setting app may just // have crashed and that sort of thing is a fact of life. } } bindWallpaperComponentLocked(mImageWallpaper, true, false, mWallpaper, null); Loading @@ -163,6 +183,84 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } } } /** * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped * for display. */ private void generateCrop(WallpaperData wallpaper) { boolean success = false; boolean needCrop = false; // Analyse the source; needed in multiple cases BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options); // Legacy case uses an empty crop rect here, so we just preserve the // source image verbatim if (!wallpaper.cropHint.isEmpty()) { // ...clamp the crop rect to the measured bounds... wallpaper.cropHint.right = Math.min(wallpaper.cropHint.right, options.outWidth); wallpaper.cropHint.bottom = Math.min(wallpaper.cropHint.bottom, options.outHeight); // ...and don't bother cropping if what we're left with is identity needCrop = (options.outHeight >= wallpaper.cropHint.height() && options.outWidth >= wallpaper.cropHint.width()); } if (!needCrop) { // Simple case: the nominal crop is at least as big as the source image, // so we take the whole thing and just copy the image file directly. if (DEBUG) { Slog.v(TAG, "Null crop of new wallpaper; copying"); } success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile); if (!success) { wallpaper.cropFile.delete(); // TODO: fall back to default wallpaper in this case } } else { // Fancy case: the crop is a subrect of the source FileOutputStream f = null; BufferedOutputStream bos = null; try { BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance( wallpaper.wallpaperFile.getAbsolutePath(), false); Bitmap cropped = decoder.decodeRegion(wallpaper.cropHint, null); decoder.recycle(); if (cropped == null) { Slog.e(TAG, "Could not decode new wallpaper"); } else { f = new FileOutputStream(wallpaper.cropFile); bos = new BufferedOutputStream(f, 32*1024); cropped.compress(Bitmap.CompressFormat.PNG, 90, bos); bos.flush(); // don't rely on the implicit flush-at-close when noting success success = true; } } catch (IOException e) { if (DEBUG) { Slog.e(TAG, "I/O error decoding crop: " + e.getMessage()); } } finally { IoUtils.closeQuietly(bos); IoUtils.closeQuietly(f); } } if (!success) { Slog.e(TAG, "Unable to apply new wallpaper"); wallpaper.cropFile.delete(); } if (wallpaper.cropFile.exists()) { boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile()); if (DEBUG) { Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon); } } } final Context mContext; final IWindowManager mIWindowManager; Loading Loading @@ -191,13 +289,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { int userId; File wallpaperFile; final File wallpaperFile; final File cropFile; /** * Client is currently writing a new image wallpaper. */ boolean imageWallpaperPending; /** * Callback once the set + crop is finished */ IWallpaperManagerCallback setComplete; /** * Resource name if using a picture from the wallpaper gallery */ Loading Loading @@ -232,11 +336,26 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { int width = -1; int height = -1; /** * The crop hint supplied for displaying a subset of the source image */ final Rect cropHint = new Rect(0, 0, 0, 0); final Rect padding = new Rect(0, 0, 0, 0); WallpaperData(int userId) { this.userId = userId; wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER); cropFile = new File(getWallpaperDir(userId), WALLPAPER_CROP); } // Only called in single-threaded boot sequence mode boolean ensureCropExists() { // if the crop file is not present, copy over the source image to use verbatim if (!cropFile.exists()) { return FileUtils.copyFile(wallpaperFile, cropFile); } return true; } } Loading Loading @@ -524,6 +643,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { public void systemRunning() { if (DEBUG) Slog.v(TAG, "systemReady"); WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM); if (!wallpaper.ensureCropExists()) { clearWallpaperLocked(false, UserHandle.USER_SYSTEM, null); } switchWallpaper(wallpaper, null); wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper); wallpaper.wallpaperObserver.startWatching(); Loading Loading @@ -602,6 +724,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { onStoppingUser(userId); File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER); wallpaperFile.delete(); File cropFile = new File(getWallpaperDir(userId), WALLPAPER_CROP); cropFile.delete(); File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO); wallpaperInfoFile.delete(); } Loading Loading @@ -653,9 +777,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper == null) { return; } File f = new File(getWallpaperDir(userId), WALLPAPER); if (f.exists()) { f.delete(); if (wallpaper.wallpaperFile.exists()) { wallpaper.wallpaperFile.delete(); wallpaper.cropFile.delete(); } final long ident = Binder.clearCallingIdentity(); try { Loading Loading @@ -844,11 +968,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { outParams.putInt("height", wallpaper.height); } wallpaper.callbacks.register(cb); File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER); if (!f.exists()) { if (!wallpaper.cropFile.exists()) { return null; } return ParcelFileDescriptor.open(f, MODE_READ_ONLY); return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY); } catch (FileNotFoundException e) { /* Shouldn't happen as we check to see if the file exists */ Slog.w(TAG, "Error getting wallpaper", e); Loading @@ -869,8 +992,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } @Override public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Bundle extras, int which) { public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Rect cropHint, Bundle extras, int which, IWallpaperManagerCallback completion) { checkPermission(android.Manifest.permission.SET_WALLPAPER); if (which == 0) { Loading @@ -881,6 +1004,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { return null; } // "null" means the no-op crop, preserving the full input image if (cropHint == null) { cropHint = new Rect(0, 0, 0, 0); } else { if (cropHint.isEmpty() || cropHint.left < 0 || cropHint.top < 0) { return null; } } synchronized (mLock) { if (DEBUG) Slog.v(TAG, "setWallpaper"); int userId = UserHandle.getCallingUserId(); Loading @@ -890,6 +1024,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras); if (pfd != null) { wallpaper.imageWallpaperPending = true; wallpaper.setComplete = completion; wallpaper.cropHint.set(cropHint); } return pfd; } finally { Loading Loading @@ -1196,6 +1332,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); out.attribute(null, "width", Integer.toString(wallpaper.width)); out.attribute(null, "height", Integer.toString(wallpaper.height)); out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left)); out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top)); out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right)); out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom)); if (wallpaper.padding.left != 0) { out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left)); } Loading @@ -1208,6 +1350,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper.padding.bottom != 0) { out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom)); } out.attribute(null, "name", wallpaper.name); if (wallpaper.wallpaperComponent != null && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) { Loading Loading @@ -1304,6 +1447,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width")); wallpaper.height = Integer.parseInt(parser .getAttributeValue(null, "height")); wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0); wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0); wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0); wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0); wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0); wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0); wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0); Loading @@ -1322,6 +1469,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (DEBUG) { Slog.v(TAG, "mWidth:" + wallpaper.width); Slog.v(TAG, "mHeight:" + wallpaper.height); Slog.v(TAG, "cropRect:" + wallpaper.cropHint); Slog.v(TAG, "mName:" + wallpaper.name); Slog.v(TAG, "mNextWallpaperComponent:" + wallpaper.nextWallpaperComponent); Loading @@ -1348,6 +1496,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (!success) { wallpaper.width = -1; wallpaper.height = -1; wallpaper.cropHint.set(0, 0, 0, 0); wallpaper.padding.set(0, 0, 0, 0); wallpaper.name = ""; } else { Loading @@ -1368,6 +1517,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper.height < baseSize) { wallpaper.height = baseSize; } // and crop, if not previously specified if (wallpaper.cropHint.width() <= 0 || wallpaper.cropHint.height() <= 0) { wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height); } } private int getMaximumSizeDimension() { Loading Loading @@ -1431,6 +1585,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } // Restore the named resource bitmap to both source + crop files boolean restoreNamedResourceLocked(WallpaperData wallpaper) { if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) { String resName = wallpaper.name.substring(4); Loading @@ -1456,6 +1611,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { int resId = -1; InputStream res = null; FileOutputStream fos = null; FileOutputStream cos = null; try { Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED); Resources r = c.getResources(); Loading @@ -1469,13 +1625,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { res = r.openRawResource(resId); if (wallpaper.wallpaperFile.exists()) { wallpaper.wallpaperFile.delete(); wallpaper.cropFile.delete(); } fos = new FileOutputStream(wallpaper.wallpaperFile); cos = new FileOutputStream(wallpaper.cropFile); byte[] buffer = new byte[32768]; int amt; while ((amt=res.read(buffer)) > 0) { fos.write(buffer, 0, amt); cos.write(buffer, 0, amt); } // mWallpaperObserver will notice the close and send the change broadcast Loading @@ -1491,8 +1650,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { IoUtils.closeQuietly(res); if (fos != null) { FileUtils.sync(fos); IoUtils.closeQuietly(fos); } if (cos != null) { FileUtils.sync(cos); } IoUtils.closeQuietly(fos); IoUtils.closeQuietly(cos); } } } Loading Loading @@ -1520,6 +1683,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { pw.print(wallpaper.width); pw.print(" mHeight="); pw.println(wallpaper.height); pw.print(" mCropHint="); pw.println(wallpaper.cropHint); pw.print(" mPadding="); pw.println(wallpaper.padding); pw.print(" mName="); pw.println(wallpaper.name); pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent); Loading Loading
core/java/android/app/IWallpaperManager.aidl +10 −4 Original line number Diff line number Diff line Loading @@ -35,9 +35,15 @@ interface IWallpaperManager { * 'which' is some combination of: * FLAG_SET_SYSTEM * FLAG_SET_LOCK * * A 'null' cropHint rectangle is explicitly permitted as a sentinel for "whatever * the source image's bounding rect is." * * The completion callback's "onWallpaperChanged()" method is invoked when the * new wallpaper content is ready to display. */ ParcelFileDescriptor setWallpaper(String name, in String callingPackage, out Bundle extras, int which); in Rect cropHint, out Bundle extras, int which, IWallpaperManagerCallback completion); /** * Set the live wallpaper. This only affects the system wallpaper. Loading
core/java/android/app/WallpaperManager.java +43 −3 Original line number Diff line number Diff line Loading @@ -64,6 +64,8 @@ import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Provides access to the system wallpaper. With WallpaperManager, you can Loading Loading @@ -770,18 +772,26 @@ public class WallpaperManager { return 0; } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { Resources resources = mContext.getResources(); /* Set the wallpaper to the default values */ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper( "res:" + resources.getResourceName(resid), mContext.getOpPackageName(), result, which); mContext.getOpPackageName(), null, result, which, completion); if (fd != null) { FileOutputStream fos = null; boolean ok = false; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); copyStreamToWallpaperFile(resources.openRawResource(resid), fos); // The 'close()' is the trigger for any server-side image manipulation, // so we must do that before waiting for completion. fos.close(); completion.waitForCompletion(); } finally { // Might be redundant but completion shouldn't wait unless the write // succeeded; this is a fallback if it threw past the close+wait. IoUtils.closeQuietly(fos); } } Loading Loading @@ -876,14 +886,17 @@ public class WallpaperManager { return 0; } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), result, which); mContext.getOpPackageName(), visibleCropHint, result, which, completion); if (fd != null) { FileOutputStream fos = null; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos); fos.close(); completion.waitForCompletion(); } finally { IoUtils.closeQuietly(fos); } Loading Loading @@ -990,14 +1003,17 @@ public class WallpaperManager { return 0; } final Bundle result = new Bundle(); final WallpaperSetCompletion completion = new WallpaperSetCompletion(); try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null, mContext.getOpPackageName(), result, which); mContext.getOpPackageName(), visibleCropHint, result, which, completion); if (fd != null) { FileOutputStream fos = null; try { fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); copyStreamToWallpaperFile(bitmapData, fos); fos.close(); completion.waitForCompletion(); } finally { IoUtils.closeQuietly(fos); } Loading Loading @@ -1385,4 +1401,28 @@ public class WallpaperManager { return null; } // Private completion callback for setWallpaper() synchronization private class WallpaperSetCompletion extends IWallpaperManagerCallback.Stub { final CountDownLatch mLatch; public WallpaperSetCompletion() { mLatch = new CountDownLatch(1); } public void waitForCompletion() { try { mLatch.await(30, TimeUnit.SECONDS); } catch (InterruptedException e) { // This might be legit: the crop may take a very long time. Don't sweat // it in that case; we are okay with display lagging behind in order to // keep the caller from locking up indeterminately. } } @Override public void onWallpaperChanged() throws RemoteException { mLatch.countDown(); } } }
services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +181 −17 Original line number Diff line number Diff line Loading @@ -42,6 +42,9 @@ import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; Loading Loading @@ -71,6 +74,7 @@ import android.view.Display; import android.view.IWindowManager; import android.view.WindowManager; import java.io.BufferedOutputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; Loading @@ -96,7 +100,7 @@ import libcore.io.IoUtils; public class WallpaperManagerService extends IWallpaperManager.Stub { static final String TAG = "WallpaperManagerService"; static final boolean DEBUG = false; static final boolean DEBUG = true; final Object mLock = new Object[0]; Loading @@ -106,7 +110,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { */ static final long MIN_WALLPAPER_CRASH_TIME = 10000; static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128; static final String WALLPAPER = "wallpaper"; static final String WALLPAPER = "wallpaper_orig"; static final String WALLPAPER_CROP = "wallpaper"; static final String WALLPAPER_INFO = "wallpaper_info.xml"; /** Loading @@ -120,6 +125,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { final WallpaperData mWallpaper; final File mWallpaperDir; final File mWallpaperFile; final File mWallpaperCropFile; final File mWallpaperInfoFile; public WallpaperObserver(WallpaperData wallpaper) { Loading @@ -128,6 +134,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { mWallpaperDir = getWallpaperDir(wallpaper.userId); mWallpaper = wallpaper; mWallpaperFile = new File(mWallpaperDir, WALLPAPER); mWallpaperCropFile = new File(mWallpaperDir, WALLPAPER_CROP); mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO); } Loading @@ -136,8 +143,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (path == null) { return; } final boolean written = (event == CLOSE_WRITE || event == MOVED_TO); final File changedFile = new File(mWallpaperDir, path); synchronized (mLock) { File changedFile = new File(mWallpaperDir, path); if (mWallpaperFile.equals(changedFile) || mWallpaperInfoFile.equals(changedFile)) { // changing the wallpaper means we'll need to back up the new one Loading @@ -148,12 +157,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } if (mWallpaperFile.equals(changedFile)) { notifyCallbacksLocked(mWallpaper); final boolean written = (event == CLOSE_WRITE || event == MOVED_TO); if (mWallpaper.wallpaperComponent == null || event != CLOSE_WRITE // includes the MOVED_TO case || mWallpaper.imageWallpaperPending) { if (written) { // The image source has finished writing the source image, // so we now produce the crop rect (in the background), and // only publish the new displayable (sub)image as a result // of that work. generateCrop(mWallpaper); mWallpaper.imageWallpaperPending = false; if (mWallpaper.setComplete != null) { try { mWallpaper.setComplete.onWallpaperChanged(); } catch (RemoteException e) { // if this fails we don't really care; the setting app may just // have crashed and that sort of thing is a fact of life. } } bindWallpaperComponentLocked(mImageWallpaper, true, false, mWallpaper, null); Loading @@ -163,6 +183,84 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } } } /** * Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped * for display. */ private void generateCrop(WallpaperData wallpaper) { boolean success = false; boolean needCrop = false; // Analyse the source; needed in multiple cases BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options); // Legacy case uses an empty crop rect here, so we just preserve the // source image verbatim if (!wallpaper.cropHint.isEmpty()) { // ...clamp the crop rect to the measured bounds... wallpaper.cropHint.right = Math.min(wallpaper.cropHint.right, options.outWidth); wallpaper.cropHint.bottom = Math.min(wallpaper.cropHint.bottom, options.outHeight); // ...and don't bother cropping if what we're left with is identity needCrop = (options.outHeight >= wallpaper.cropHint.height() && options.outWidth >= wallpaper.cropHint.width()); } if (!needCrop) { // Simple case: the nominal crop is at least as big as the source image, // so we take the whole thing and just copy the image file directly. if (DEBUG) { Slog.v(TAG, "Null crop of new wallpaper; copying"); } success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile); if (!success) { wallpaper.cropFile.delete(); // TODO: fall back to default wallpaper in this case } } else { // Fancy case: the crop is a subrect of the source FileOutputStream f = null; BufferedOutputStream bos = null; try { BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance( wallpaper.wallpaperFile.getAbsolutePath(), false); Bitmap cropped = decoder.decodeRegion(wallpaper.cropHint, null); decoder.recycle(); if (cropped == null) { Slog.e(TAG, "Could not decode new wallpaper"); } else { f = new FileOutputStream(wallpaper.cropFile); bos = new BufferedOutputStream(f, 32*1024); cropped.compress(Bitmap.CompressFormat.PNG, 90, bos); bos.flush(); // don't rely on the implicit flush-at-close when noting success success = true; } } catch (IOException e) { if (DEBUG) { Slog.e(TAG, "I/O error decoding crop: " + e.getMessage()); } } finally { IoUtils.closeQuietly(bos); IoUtils.closeQuietly(f); } } if (!success) { Slog.e(TAG, "Unable to apply new wallpaper"); wallpaper.cropFile.delete(); } if (wallpaper.cropFile.exists()) { boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile()); if (DEBUG) { Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon); } } } final Context mContext; final IWindowManager mIWindowManager; Loading Loading @@ -191,13 +289,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { int userId; File wallpaperFile; final File wallpaperFile; final File cropFile; /** * Client is currently writing a new image wallpaper. */ boolean imageWallpaperPending; /** * Callback once the set + crop is finished */ IWallpaperManagerCallback setComplete; /** * Resource name if using a picture from the wallpaper gallery */ Loading Loading @@ -232,11 +336,26 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { int width = -1; int height = -1; /** * The crop hint supplied for displaying a subset of the source image */ final Rect cropHint = new Rect(0, 0, 0, 0); final Rect padding = new Rect(0, 0, 0, 0); WallpaperData(int userId) { this.userId = userId; wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER); cropFile = new File(getWallpaperDir(userId), WALLPAPER_CROP); } // Only called in single-threaded boot sequence mode boolean ensureCropExists() { // if the crop file is not present, copy over the source image to use verbatim if (!cropFile.exists()) { return FileUtils.copyFile(wallpaperFile, cropFile); } return true; } } Loading Loading @@ -524,6 +643,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { public void systemRunning() { if (DEBUG) Slog.v(TAG, "systemReady"); WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM); if (!wallpaper.ensureCropExists()) { clearWallpaperLocked(false, UserHandle.USER_SYSTEM, null); } switchWallpaper(wallpaper, null); wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper); wallpaper.wallpaperObserver.startWatching(); Loading Loading @@ -602,6 +724,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { onStoppingUser(userId); File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER); wallpaperFile.delete(); File cropFile = new File(getWallpaperDir(userId), WALLPAPER_CROP); cropFile.delete(); File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO); wallpaperInfoFile.delete(); } Loading Loading @@ -653,9 +777,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper == null) { return; } File f = new File(getWallpaperDir(userId), WALLPAPER); if (f.exists()) { f.delete(); if (wallpaper.wallpaperFile.exists()) { wallpaper.wallpaperFile.delete(); wallpaper.cropFile.delete(); } final long ident = Binder.clearCallingIdentity(); try { Loading Loading @@ -844,11 +968,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { outParams.putInt("height", wallpaper.height); } wallpaper.callbacks.register(cb); File f = new File(getWallpaperDir(wallpaperUserId), WALLPAPER); if (!f.exists()) { if (!wallpaper.cropFile.exists()) { return null; } return ParcelFileDescriptor.open(f, MODE_READ_ONLY); return ParcelFileDescriptor.open(wallpaper.cropFile, MODE_READ_ONLY); } catch (FileNotFoundException e) { /* Shouldn't happen as we check to see if the file exists */ Slog.w(TAG, "Error getting wallpaper", e); Loading @@ -869,8 +992,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } @Override public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Bundle extras, int which) { public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Rect cropHint, Bundle extras, int which, IWallpaperManagerCallback completion) { checkPermission(android.Manifest.permission.SET_WALLPAPER); if (which == 0) { Loading @@ -881,6 +1004,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { return null; } // "null" means the no-op crop, preserving the full input image if (cropHint == null) { cropHint = new Rect(0, 0, 0, 0); } else { if (cropHint.isEmpty() || cropHint.left < 0 || cropHint.top < 0) { return null; } } synchronized (mLock) { if (DEBUG) Slog.v(TAG, "setWallpaper"); int userId = UserHandle.getCallingUserId(); Loading @@ -890,6 +1024,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras); if (pfd != null) { wallpaper.imageWallpaperPending = true; wallpaper.setComplete = completion; wallpaper.cropHint.set(cropHint); } return pfd; } finally { Loading Loading @@ -1196,6 +1332,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); out.attribute(null, "width", Integer.toString(wallpaper.width)); out.attribute(null, "height", Integer.toString(wallpaper.height)); out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left)); out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top)); out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right)); out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom)); if (wallpaper.padding.left != 0) { out.attribute(null, "paddingLeft", Integer.toString(wallpaper.padding.left)); } Loading @@ -1208,6 +1350,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper.padding.bottom != 0) { out.attribute(null, "paddingBottom", Integer.toString(wallpaper.padding.bottom)); } out.attribute(null, "name", wallpaper.name); if (wallpaper.wallpaperComponent != null && !wallpaper.wallpaperComponent.equals(mImageWallpaper)) { Loading Loading @@ -1304,6 +1447,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width")); wallpaper.height = Integer.parseInt(parser .getAttributeValue(null, "height")); wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0); wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0); wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0); wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0); wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0); wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0); wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0); Loading @@ -1322,6 +1469,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (DEBUG) { Slog.v(TAG, "mWidth:" + wallpaper.width); Slog.v(TAG, "mHeight:" + wallpaper.height); Slog.v(TAG, "cropRect:" + wallpaper.cropHint); Slog.v(TAG, "mName:" + wallpaper.name); Slog.v(TAG, "mNextWallpaperComponent:" + wallpaper.nextWallpaperComponent); Loading @@ -1348,6 +1496,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (!success) { wallpaper.width = -1; wallpaper.height = -1; wallpaper.cropHint.set(0, 0, 0, 0); wallpaper.padding.set(0, 0, 0, 0); wallpaper.name = ""; } else { Loading @@ -1368,6 +1517,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { if (wallpaper.height < baseSize) { wallpaper.height = baseSize; } // and crop, if not previously specified if (wallpaper.cropHint.width() <= 0 || wallpaper.cropHint.height() <= 0) { wallpaper.cropHint.set(0, 0, wallpaper.width, wallpaper.height); } } private int getMaximumSizeDimension() { Loading Loading @@ -1431,6 +1585,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { } } // Restore the named resource bitmap to both source + crop files boolean restoreNamedResourceLocked(WallpaperData wallpaper) { if (wallpaper.name.length() > 4 && "res:".equals(wallpaper.name.substring(0, 4))) { String resName = wallpaper.name.substring(4); Loading @@ -1456,6 +1611,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { int resId = -1; InputStream res = null; FileOutputStream fos = null; FileOutputStream cos = null; try { Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED); Resources r = c.getResources(); Loading @@ -1469,13 +1625,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { res = r.openRawResource(resId); if (wallpaper.wallpaperFile.exists()) { wallpaper.wallpaperFile.delete(); wallpaper.cropFile.delete(); } fos = new FileOutputStream(wallpaper.wallpaperFile); cos = new FileOutputStream(wallpaper.cropFile); byte[] buffer = new byte[32768]; int amt; while ((amt=res.read(buffer)) > 0) { fos.write(buffer, 0, amt); cos.write(buffer, 0, amt); } // mWallpaperObserver will notice the close and send the change broadcast Loading @@ -1491,8 +1650,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { IoUtils.closeQuietly(res); if (fos != null) { FileUtils.sync(fos); IoUtils.closeQuietly(fos); } if (cos != null) { FileUtils.sync(cos); } IoUtils.closeQuietly(fos); IoUtils.closeQuietly(cos); } } } Loading Loading @@ -1520,6 +1683,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { pw.print(wallpaper.width); pw.print(" mHeight="); pw.println(wallpaper.height); pw.print(" mCropHint="); pw.println(wallpaper.cropHint); pw.print(" mPadding="); pw.println(wallpaper.padding); pw.print(" mName="); pw.println(wallpaper.name); pw.print(" mWallpaperComponent="); pw.println(wallpaper.wallpaperComponent); Loading