• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Aucun tag

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

frameworks/base


Commit MetaInfo

Révision7d4f901951ad1554cac6d778d291435049532e00 (tree)
l'heure2020-01-08 08:03:53
AuteurAhan Wu <ahanwu@goog...>
CommiterVasyl Gello

Message de Log

DO NOT MERGE Validate wallpaper dimension while generating crop

If dimensions of cropped wallpaper image exceed max texture size that
GPU can support, it will cause ImageWallpaper keep crashing
because hwui crashes by invalid operation (0x502).

Bug: 120847476.
Test: Write a custom app to set a 8000x800 bitmap as wallpaper.
Test: The cropped file will be 29600x2960 and make sysui keep crashing.
Test: After applyed this cl, wallpaper will use fallback.
Test: Sysui will not keep crashing any more.
Change-Id: I8ed5931298c652a2230858cf62df3f6fcd345c5a
(cherry picked from commit f1e1f4f04d0165ed065637a4ba556583a7c79ef0)

Change Summary

Modification

--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/GLHelper.java
@@ -0,0 +1,148 @@
1+/*
2+ * Copyright (C) 2019 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+package com.android.server.wallpaper;
18+
19+import static android.opengl.EGL14.EGL_ALPHA_SIZE;
20+import static android.opengl.EGL14.EGL_BLUE_SIZE;
21+import static android.opengl.EGL14.EGL_CONFIG_CAVEAT;
22+import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
23+import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY;
24+import static android.opengl.EGL14.EGL_DEPTH_SIZE;
25+import static android.opengl.EGL14.EGL_GREEN_SIZE;
26+import static android.opengl.EGL14.EGL_HEIGHT;
27+import static android.opengl.EGL14.EGL_NONE;
28+import static android.opengl.EGL14.EGL_NO_CONTEXT;
29+import static android.opengl.EGL14.EGL_NO_DISPLAY;
30+import static android.opengl.EGL14.EGL_NO_SURFACE;
31+import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
32+import static android.opengl.EGL14.EGL_RED_SIZE;
33+import static android.opengl.EGL14.EGL_RENDERABLE_TYPE;
34+import static android.opengl.EGL14.EGL_STENCIL_SIZE;
35+import static android.opengl.EGL14.EGL_WIDTH;
36+import static android.opengl.EGL14.eglChooseConfig;
37+import static android.opengl.EGL14.eglCreateContext;
38+import static android.opengl.EGL14.eglCreatePbufferSurface;
39+import static android.opengl.EGL14.eglDestroyContext;
40+import static android.opengl.EGL14.eglDestroySurface;
41+import static android.opengl.EGL14.eglGetDisplay;
42+import static android.opengl.EGL14.eglGetError;
43+import static android.opengl.EGL14.eglInitialize;
44+import static android.opengl.EGL14.eglMakeCurrent;
45+import static android.opengl.EGL14.eglTerminate;
46+import static android.opengl.GLES20.GL_MAX_TEXTURE_SIZE;
47+import static android.opengl.GLES20.glGetIntegerv;
48+
49+import android.opengl.EGLConfig;
50+import android.opengl.EGLContext;
51+import android.opengl.EGLDisplay;
52+import android.opengl.EGLSurface;
53+import android.opengl.GLUtils;
54+import android.os.SystemProperties;
55+import android.util.Log;
56+
57+class GLHelper {
58+ private static final String TAG = GLHelper.class.getSimpleName();
59+ private static final int sMaxTextureSize;
60+
61+ static {
62+ int maxTextureSize = SystemProperties.getInt("sys.max_texture_size", 0);
63+ sMaxTextureSize = maxTextureSize > 0 ? maxTextureSize : retrieveTextureSizeFromGL();
64+ }
65+
66+ private static int retrieveTextureSizeFromGL() {
67+ try {
68+ String err;
69+
70+ // Before we can retrieve info from GL,
71+ // we have to create EGLContext, EGLConfig and EGLDisplay first.
72+ // We will fail at querying info from GL once one of above failed.
73+ // When this happens, we will use defValue instead.
74+ EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
75+ if (eglDisplay == null || eglDisplay == EGL_NO_DISPLAY) {
76+ err = "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError());
77+ throw new RuntimeException(err);
78+ }
79+
80+ if (!eglInitialize(eglDisplay, null, 0 /* majorOffset */, null, 1 /* minorOffset */)) {
81+ err = "eglInitialize failed: " + GLUtils.getEGLErrorString(eglGetError());
82+ throw new RuntimeException(err);
83+ }
84+
85+ EGLConfig eglConfig = null;
86+ int[] configsCount = new int[1];
87+ EGLConfig[] configs = new EGLConfig[1];
88+ int[] configSpec = new int[] {
89+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
90+ EGL_RED_SIZE, 8,
91+ EGL_GREEN_SIZE, 8,
92+ EGL_BLUE_SIZE, 8,
93+ EGL_ALPHA_SIZE, 0,
94+ EGL_DEPTH_SIZE, 0,
95+ EGL_STENCIL_SIZE, 0,
96+ EGL_CONFIG_CAVEAT, EGL_NONE,
97+ EGL_NONE
98+ };
99+
100+ if (!eglChooseConfig(eglDisplay, configSpec, 0 /* attrib_listOffset */,
101+ configs, 0 /* configOffset */, 1 /* config_size */,
102+ configsCount, 0 /* num_configOffset */)) {
103+ err = "eglChooseConfig failed: " + GLUtils.getEGLErrorString(eglGetError());
104+ throw new RuntimeException(err);
105+ } else if (configsCount[0] > 0) {
106+ eglConfig = configs[0];
107+ }
108+
109+ if (eglConfig == null) {
110+ throw new RuntimeException("eglConfig not initialized!");
111+ }
112+
113+ int[] attr_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
114+ EGLContext eglContext = eglCreateContext(
115+ eglDisplay, eglConfig, EGL_NO_CONTEXT, attr_list, 0 /* offset */);
116+
117+ if (eglContext == null || eglContext == EGL_NO_CONTEXT) {
118+ err = "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError());
119+ throw new RuntimeException(err);
120+ }
121+
122+ // We create a push buffer temporarily for querying info from GL.
123+ int[] attrs = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
124+ EGLSurface eglSurface =
125+ eglCreatePbufferSurface(eglDisplay, eglConfig, attrs, 0 /* offset */);
126+ eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
127+
128+ // Now, we are ready to query the info from GL.
129+ int[] maxSize = new int[1];
130+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0 /* offset */);
131+
132+ // We have got the info we want, release all egl resources.
133+ eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
134+ eglDestroySurface(eglDisplay, eglSurface);
135+ eglDestroyContext(eglDisplay, eglContext);
136+ eglTerminate(eglDisplay);
137+ return maxSize[0];
138+ } catch (RuntimeException e) {
139+ Log.w(TAG, "Retrieve from GL failed", e);
140+ return Integer.MAX_VALUE;
141+ }
142+ }
143+
144+ static int getMaxTextureSize() {
145+ return sMaxTextureSize;
146+ }
147+}
148+
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -115,6 +115,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
115115 static final String TAG = "WallpaperManagerService";
116116 static final boolean DEBUG = false;
117117
118+ // This 100MB limitation is defined in DisplayListCanvas.
119+ private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024;
120+
118121 public static class Lifecycle extends SystemService {
119122 private WallpaperManagerService mService;
120123
@@ -368,7 +371,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
368371 }
369372
370373 // scale if the crop height winds up not matching the recommended metrics
371- needScale = (wallpaper.height != cropHint.height());
374+ // also take care of invalid dimensions.
375+ needScale = wallpaper.height != cropHint.height()
376+ || cropHint.height() > GLHelper.getMaxTextureSize()
377+ || cropHint.width() > GLHelper.getMaxTextureSize();
372378
373379 if (DEBUG) {
374380 Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
@@ -380,14 +386,29 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
380386 if (!needCrop && !needScale) {
381387 // Simple case: the nominal crop fits what we want, so we take
382388 // the whole thing and just copy the image file directly.
383- if (DEBUG) {
384- Slog.v(TAG, "Null crop of new wallpaper; copying");
389+
390+ // TODO: It is not accurate to estimate bitmap size without decoding it,
391+ // may be we can try to remove this optimized way in the future,
392+ // that means, we will always go into the 'else' block.
393+
394+ // This is just a quick estimation, may be smaller than it is.
395+ long estimateSize = options.outWidth * options.outHeight * 4;
396+
397+ // A bitmap over than MAX_BITMAP_SIZE will make drawBitmap() fail.
398+ // Please see: DisplayListCanvas#throwIfCannotDraw.
399+ if (estimateSize < MAX_BITMAP_SIZE) {
400+ success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
385401 }
386- success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
402+
387403 if (!success) {
388404 wallpaper.cropFile.delete();
389405 // TODO: fall back to default wallpaper in this case
390406 }
407+
408+ if (DEBUG) {
409+ Slog.v(TAG, "Null crop of new wallpaper, estimate size=" + estimateSize
410+ + ", success=" + success);
411+ }
391412 } else {
392413 // Fancy case: crop and scale. First, we decode and scale down if appropriate.
393414 FileOutputStream f = null;
@@ -401,48 +422,78 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
401422 // We calculate the largest power-of-two under the actual ratio rather than
402423 // just let the decode take care of it because we also want to remap where the
403424 // cropHint rectangle lies in the decoded [super]rect.
404- final BitmapFactory.Options scaler;
405425 final int actualScale = cropHint.height() / wallpaper.height;
406426 int scale = 1;
407- while (2*scale < actualScale) {
427+ while (2*scale <= actualScale) {
408428 scale *= 2;
409429 }
410- if (scale > 1) {
411- scaler = new BitmapFactory.Options();
412- scaler.inSampleSize = scale;
430+ options.inSampleSize = scale;
431+ options.inJustDecodeBounds = false;
432+
433+ final Rect estimateCrop = new Rect(cropHint);
434+ estimateCrop.scale(1f / options.inSampleSize);
435+ final float hRatio = (float) wallpaper.height / estimateCrop.height();
436+ final int destHeight = (int) (estimateCrop.height() * hRatio);
437+ final int destWidth = (int) (estimateCrop.width() * hRatio);
438+
439+ // We estimated an invalid crop, try to adjust the cropHint to get a valid one.
440+ if (destWidth > GLHelper.getMaxTextureSize()) {
441+ int newHeight = (int) (wallpaper.height / hRatio);
442+ int newWidth = (int) (wallpaper.width / hRatio);
443+
413444 if (DEBUG) {
414- Slog.v(TAG, "Downsampling cropped rect with scale " + scale);
445+ Slog.v(TAG, "Invalid crop dimensions, trying to adjust.");
415446 }
416- } else {
417- scaler = null;
447+
448+ estimateCrop.set(cropHint);
449+ estimateCrop.left += (cropHint.width() - newWidth) / 2;
450+ estimateCrop.top += (cropHint.height() - newHeight) / 2;
451+ estimateCrop.right = estimateCrop.left + newWidth;
452+ estimateCrop.bottom = estimateCrop.top + newHeight;
453+ cropHint.set(estimateCrop);
454+ estimateCrop.scale(1f / options.inSampleSize);
455+ }
456+
457+ // We've got the safe cropHint; now we want to scale it properly to
458+ // the desired rectangle.
459+ // That's a height-biased operation: make it fit the hinted height.
460+ final int safeHeight = (int) (estimateCrop.height() * hRatio);
461+ final int safeWidth = (int) (estimateCrop.width() * hRatio);
462+
463+ if (DEBUG) {
464+ Slog.v(TAG, "Decode parameters:");
465+ Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop);
466+ Slog.v(TAG, " down sampling=" + options.inSampleSize
467+ + ", hRatio=" + hRatio);
468+ Slog.v(TAG, " dest=" + destWidth + "x" + destHeight);
469+ Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight);
470+ Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize());
418471 }
419- Bitmap cropped = decoder.decodeRegion(cropHint, scaler);
472+
473+ Bitmap cropped = decoder.decodeRegion(cropHint, options);
420474 decoder.recycle();
421475
422476 if (cropped == null) {
423477 Slog.e(TAG, "Could not decode new wallpaper");
424478 } else {
425- // We've got the extracted crop; now we want to scale it properly to
426- // the desired rectangle. That's a height-biased operation: make it
427- // fit the hinted height, and accept whatever width we end up with.
428- cropHint.offsetTo(0, 0);
429- cropHint.right /= scale; // adjust by downsampling factor
430- cropHint.bottom /= scale;
431- final float heightR = ((float)wallpaper.height) / ((float)cropHint.height());
432- if (DEBUG) {
433- Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint);
434- }
435- final int destWidth = (int)(cropHint.width() * heightR);
479+ // We are safe to create final crop with safe dimensions now.
436480 final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped,
437- destWidth, wallpaper.height, true);
481+ safeWidth, safeHeight, true);
438482 if (DEBUG) {
439483 Slog.v(TAG, "Final extract:");
440484 Slog.v(TAG, " dims: w=" + wallpaper.width
441485 + " h=" + wallpaper.height);
442- Slog.v(TAG, " out: w=" + finalCrop.getWidth()
486+ Slog.v(TAG, " out: w=" + finalCrop.getWidth()
443487 + " h=" + finalCrop.getHeight());
444488 }
445489
490+ // A bitmap over than MAX_BITMAP_SIZE will make drawBitmap() fail.
491+ // Please see: DisplayListCanvas#throwIfCannotDraw.
492+ if (finalCrop.getByteCount() > MAX_BITMAP_SIZE) {
493+ throw new RuntimeException(
494+ "Too large bitmap, limit=" + MAX_BITMAP_SIZE);
495+ }
496+
446497 f = new FileOutputStream(wallpaper.cropFile);
447498 bos = new BufferedOutputStream(f, 32*1024);
448499 finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
@@ -1234,6 +1285,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
12341285 if (!isWallpaperSupported(callingPackage)) {
12351286 return;
12361287 }
1288+
1289+ // Make sure both width and height are not larger than max texture size.
1290+ width = Math.min(width, GLHelper.getMaxTextureSize());
1291+ height = Math.min(height, GLHelper.getMaxTextureSize());
1292+
12371293 synchronized (mLock) {
12381294 int userId = UserHandle.getCallingUserId();
12391295 WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);