Replace BitmapRegionDecoder with ImageDecoder

1. generateCrop() couldn't handle super huge size because BitmapRegionDecoder
will occupy too much native heap during process, We replace old decoder
with ImageDecoder
2. Fix overflow problem in debug message
3. Add a record file to note if ImageDecoder work well

Bug: 204087139
Test: Manually set wallpaper, no PDoS observed.
Change-Id: I59cf3c21edce836ffbe9f24a08b3a8e077c5fcf3
Merged-In: I59cf3c21edce836ffbe9f24a08b3a8e077c5fcf3
(cherry picked from commit 08d14944212a7026218bde79091f0d1d48bc1c79)
Merged-In: I59cf3c21edce836ffbe9f24a08b3a8e077c5fcf3
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6fda72e..eddb5e9 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -59,8 +59,8 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
 import android.graphics.Color;
+import android.graphics.ImageDecoder;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.hardware.display.DisplayManager;
@@ -193,6 +193,8 @@
     static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
     static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
     static final String WALLPAPER_INFO = "wallpaper_info.xml";
+    private static final String RECORD_FILE = "decode_record";
+    private static final String RECORD_LOCK_FILE = "decode_lock_record";
 
     // All the various per-user state files we need to be aware of
     private static final String[] sPerUserFiles = new String[] {
@@ -674,8 +676,7 @@
                 }
 
                 if (DEBUG) {
-                    // This is just a quick estimation, may be smaller than it is.
-                    long estimateSize = options.outWidth * options.outHeight * 4;
+                    long estimateSize = (long) options.outWidth * options.outHeight * 4;
                     Slog.v(TAG, "Null crop of new wallpaper, estimate size="
                             + estimateSize + ", success=" + success);
                 }
@@ -684,9 +685,6 @@
                 FileOutputStream f = null;
                 BufferedOutputStream bos = null;
                 try {
-                    BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
-                            wallpaper.wallpaperFile.getAbsolutePath(), false);
-
                     // This actually downsamples only by powers of two, but that's okay; we do
                     // a proper scaling blit later.  This is to minimize transient RAM use.
                     // We calculate the largest power-of-two under the actual ratio rather than
@@ -740,8 +738,24 @@
                         Slog.v(TAG, "  maxTextureSize=" + GLHelper.getMaxTextureSize());
                     }
 
-                    Bitmap cropped = decoder.decodeRegion(cropHint, options);
-                    decoder.recycle();
+                    //Create a record file and will delete if ImageDecoder work well.
+                    final String recordName =
+                            (wallpaper.wallpaperFile.getName().equals(WALLPAPER)
+                                    ? RECORD_FILE : RECORD_LOCK_FILE);
+                    final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
+                    record.createNewFile();
+                    Slog.v(TAG, "record path =" + record.getPath()
+                            + ", record name =" + record.getName());
+
+                    final ImageDecoder.Source srcData =
+                            ImageDecoder.createSource(wallpaper.wallpaperFile);
+                    final int sampleSize = scale;
+                    Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
+                        decoder.setTargetSampleSize(sampleSize);
+                        decoder.setCrop(estimateCrop);
+                    });
+
+                    record.delete();
 
                     if (cropped == null) {
                         Slog.e(TAG, "Could not decode new wallpaper");
@@ -1779,6 +1793,7 @@
                     new UserSwitchObserver() {
                         @Override
                         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
+                            errorCheck(newUserId);
                             switchUser(newUserId, reply);
                         }
                     }, TAG);
@@ -1816,6 +1831,14 @@
 
     @Override
     public void onBootPhase(int phase) {
+        // If someone set too large jpg file as wallpaper, system_server may be killed by lmk in
+        // generateCrop(), so we create a file in generateCrop() before ImageDecoder starts working
+        // and delete this file after ImageDecoder finishing. If the specific file exists, that
+        // means ImageDecoder can't handle the original wallpaper file, in order to avoid
+        // system_server restart again and again and rescue party will trigger factory reset,
+        // so we reset default wallpaper in case system_server is trapped into a restart loop.
+        errorCheck(UserHandle.USER_SYSTEM);
+
         if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
             systemReady();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -1823,6 +1846,38 @@
         }
     }
 
+    private static final HashMap<Integer, String> sWallpaperType = new HashMap<Integer, String>() {
+        {
+            put(FLAG_SYSTEM, RECORD_FILE);
+            put(FLAG_LOCK, RECORD_LOCK_FILE);
+        }
+    };
+
+    private void errorCheck(int userID) {
+        sWallpaperType.forEach((type, filename) -> {
+            final File record = new File(getWallpaperDir(userID), filename);
+            if (record.exists()) {
+                Slog.w(TAG, "User:" + userID + ", wallpaper tyep = " + type
+                        + ", wallpaper fail detect!! reset to default wallpaper");
+                clearWallpaperData(userID, type);
+                record.delete();
+            }
+        });
+    }
+
+    private void clearWallpaperData(int userID, int wallpaperType) {
+        final WallpaperData wallpaper = new WallpaperData(userID, getWallpaperDir(userID),
+                (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER,
+                (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP);
+        if (wallpaper.sourceExists()) {
+            wallpaper.wallpaperFile.delete();
+        }
+        if (wallpaper.cropExists()) {
+            wallpaper.cropFile.delete();
+        }
+
+    }
+
     @Override
     public void onUnlockUser(final int userId) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);