From 1722028514ca6582a917e78a05f0594d82b2d4ea Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Mon, 4 Nov 2019 11:35:49 -0800 Subject: [PATCH 1/8] Prevent system uid component from running in an isolated app process Bug: 140055304 Test: Manual Change-Id: I5a1618fab529cb0300d4a8e9c7762ee218ca09eb Merged-In: I5a1618fab529cb0300d4a8e9c7762ee218ca09eb (cherry picked from commit 84f2b227b0a0e74157508f143c1ef44ef0f0be50) --- services/core/java/com/android/server/am/ProcessList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 838e7d3c02086..fe29a36ec45f3 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1329,7 +1329,7 @@ public final class ProcessList { final int procCount = procs.size(); for (int i = 0; i < procCount; i++) { final int procUid = procs.keyAt(i); - if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) { + if (!UserHandle.isCore(procUid) || !UserHandle.isSameUser(procUid, uid)) { // Don't use an app process or different user process for system component. continue; } From f7add6e51872f161a72874d515a85fec4eafedd6 Mon Sep 17 00:00:00 2001 From: Ahan Wu Date: Thu, 3 Oct 2019 12:36:59 +0800 Subject: [PATCH 2/8] Validate wallpaper dimension while generating crop If dimensions of cropped wallpaper exceed max texture size that GPU can support, it will cause sysui keep crashing because it can not initialize egl with an appropriate surface. 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: Icdc6292191d1e966c52640f4c1a5a154dfba45d3 (cherry picked from commit 32c6a7c691b0d91085c1ed13fe6f1c473c94b4c8) --- .../android/server/wallpaper/GLHelper.java | 148 ++++++++++++++++++ .../wallpaper/WallpaperManagerService.java | 114 ++++++++++---- 2 files changed, 232 insertions(+), 30 deletions(-) create mode 100644 services/core/java/com/android/server/wallpaper/GLHelper.java diff --git a/services/core/java/com/android/server/wallpaper/GLHelper.java b/services/core/java/com/android/server/wallpaper/GLHelper.java new file mode 100644 index 0000000000000..1d733f53f0550 --- /dev/null +++ b/services/core/java/com/android/server/wallpaper/GLHelper.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wallpaper; + +import static android.opengl.EGL14.EGL_ALPHA_SIZE; +import static android.opengl.EGL14.EGL_BLUE_SIZE; +import static android.opengl.EGL14.EGL_CONFIG_CAVEAT; +import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION; +import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY; +import static android.opengl.EGL14.EGL_DEPTH_SIZE; +import static android.opengl.EGL14.EGL_GREEN_SIZE; +import static android.opengl.EGL14.EGL_HEIGHT; +import static android.opengl.EGL14.EGL_NONE; +import static android.opengl.EGL14.EGL_NO_CONTEXT; +import static android.opengl.EGL14.EGL_NO_DISPLAY; +import static android.opengl.EGL14.EGL_NO_SURFACE; +import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT; +import static android.opengl.EGL14.EGL_RED_SIZE; +import static android.opengl.EGL14.EGL_RENDERABLE_TYPE; +import static android.opengl.EGL14.EGL_STENCIL_SIZE; +import static android.opengl.EGL14.EGL_WIDTH; +import static android.opengl.EGL14.eglChooseConfig; +import static android.opengl.EGL14.eglCreateContext; +import static android.opengl.EGL14.eglCreatePbufferSurface; +import static android.opengl.EGL14.eglDestroyContext; +import static android.opengl.EGL14.eglDestroySurface; +import static android.opengl.EGL14.eglGetDisplay; +import static android.opengl.EGL14.eglGetError; +import static android.opengl.EGL14.eglInitialize; +import static android.opengl.EGL14.eglMakeCurrent; +import static android.opengl.EGL14.eglTerminate; +import static android.opengl.GLES20.GL_MAX_TEXTURE_SIZE; +import static android.opengl.GLES20.glGetIntegerv; + +import android.opengl.EGLConfig; +import android.opengl.EGLContext; +import android.opengl.EGLDisplay; +import android.opengl.EGLSurface; +import android.opengl.GLUtils; +import android.os.SystemProperties; +import android.util.Log; + +class GLHelper { + private static final String TAG = GLHelper.class.getSimpleName(); + private static final int sMaxTextureSize; + + static { + int maxTextureSize = SystemProperties.getInt("sys.max_texture_size", 0); + sMaxTextureSize = maxTextureSize > 0 ? maxTextureSize : retrieveTextureSizeFromGL(); + } + + private static int retrieveTextureSizeFromGL() { + try { + String err; + + // Before we can retrieve info from GL, + // we have to create EGLContext, EGLConfig and EGLDisplay first. + // We will fail at querying info from GL once one of above failed. + // When this happens, we will use defValue instead. + EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (eglDisplay == null || eglDisplay == EGL_NO_DISPLAY) { + err = "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError()); + throw new RuntimeException(err); + } + + if (!eglInitialize(eglDisplay, null, 0 /* majorOffset */, null, 1 /* minorOffset */)) { + err = "eglInitialize failed: " + GLUtils.getEGLErrorString(eglGetError()); + throw new RuntimeException(err); + } + + EGLConfig eglConfig = null; + int[] configsCount = new int[1]; + EGLConfig[] configs = new EGLConfig[1]; + int[] configSpec = new int[] { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, 0, + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_NONE + }; + + if (!eglChooseConfig(eglDisplay, configSpec, 0 /* attrib_listOffset */, + configs, 0 /* configOffset */, 1 /* config_size */, + configsCount, 0 /* num_configOffset */)) { + err = "eglChooseConfig failed: " + GLUtils.getEGLErrorString(eglGetError()); + throw new RuntimeException(err); + } else if (configsCount[0] > 0) { + eglConfig = configs[0]; + } + + if (eglConfig == null) { + throw new RuntimeException("eglConfig not initialized!"); + } + + int[] attr_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + EGLContext eglContext = eglCreateContext( + eglDisplay, eglConfig, EGL_NO_CONTEXT, attr_list, 0 /* offset */); + + if (eglContext == null || eglContext == EGL_NO_CONTEXT) { + err = "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()); + throw new RuntimeException(err); + } + + // We create a push buffer temporarily for querying info from GL. + int[] attrs = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}; + EGLSurface eglSurface = + eglCreatePbufferSurface(eglDisplay, eglConfig, attrs, 0 /* offset */); + eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); + + // Now, we are ready to query the info from GL. + int[] maxSize = new int[1]; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0 /* offset */); + + // We have got the info we want, release all egl resources. + eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(eglDisplay, eglSurface); + eglDestroyContext(eglDisplay, eglContext); + eglTerminate(eglDisplay); + return maxSize[0]; + } catch (RuntimeException e) { + Log.w(TAG, "Retrieve from GL failed", e); + return Integer.MAX_VALUE; + } + } + + static int getMaxTextureSize() { + return sMaxTextureSize; + } +} + diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index b0f1e5d69be40..991c09a97bf5e 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -134,6 +134,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private static final boolean DEBUG = false; private static final boolean DEBUG_LIVE = true; + // This 100MB limitation is defined in RecordingCanvas. + private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; + public static class Lifecycle extends SystemService { private IWallpaperManagerService mService; @@ -572,7 +575,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Only generate crop for default display. final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY); - Rect cropHint = new Rect(wallpaper.cropHint); + final Rect cropHint = new Rect(wallpaper.cropHint); + final DisplayInfo displayInfo = new DisplayInfo(); + mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo); if (DEBUG) { Slog.v(TAG, "Generating crop for new wallpaper(s): 0x" @@ -618,12 +623,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } // scale if the crop height winds up not matching the recommended metrics - needScale = (wpData.mHeight != cropHint.height()); + needScale = wpData.mHeight != cropHint.height() + || cropHint.height() > GLHelper.getMaxTextureSize() + || cropHint.width() > GLHelper.getMaxTextureSize(); //make sure screen aspect ratio is preserved if width is scaled under screen size if (needScale) { - final DisplayInfo displayInfo = new DisplayInfo(); - mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo); final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height(); final int newWidth = (int) (cropHint.width() * scaleByHeight); if (newWidth < displayInfo.logicalWidth) { @@ -644,14 +649,29 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (!needCrop && !needScale) { // Simple case: the nominal crop fits what we want, so we take // the whole thing and just copy the image file directly. - if (DEBUG) { - Slog.v(TAG, "Null crop of new wallpaper; copying"); + + // TODO: It is not accurate to estimate bitmap size without decoding it, + // may be we can try to remove this optimized way in the future, + // that means, we will always go into the 'else' block. + + // This is just a quick estimation, may be smaller than it is. + long estimateSize = options.outWidth * options.outHeight * 4; + + // A bitmap over than MAX_BITMAP_SIZE will make drawBitmap() fail. + // Please see: RecordingCanvas#throwIfCannotDraw. + if (estimateSize < MAX_BITMAP_SIZE) { + success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile); } - success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile); + if (!success) { wallpaper.cropFile.delete(); // TODO: fall back to default wallpaper in this case } + + if (DEBUG) { + Slog.v(TAG, "Null crop of new wallpaper, estimate size=" + + estimateSize + ", success=" + success); + } } else { // Fancy case: crop and scale. First, we decode and scale down if appropriate. FileOutputStream f = null; @@ -665,49 +685,78 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // We calculate the largest power-of-two under the actual ratio rather than // just let the decode take care of it because we also want to remap where the // cropHint rectangle lies in the decoded [super]rect. - final BitmapFactory.Options scaler; final int actualScale = cropHint.height() / wpData.mHeight; int scale = 1; - while (2*scale < actualScale) { + while (2 * scale <= actualScale) { scale *= 2; } - if (scale > 1) { - scaler = new BitmapFactory.Options(); - scaler.inSampleSize = scale; + options.inSampleSize = scale; + options.inJustDecodeBounds = false; + + final Rect estimateCrop = new Rect(cropHint); + estimateCrop.scale(1f / options.inSampleSize); + final float hRatio = (float) wpData.mHeight / estimateCrop.height(); + final int destHeight = (int) (estimateCrop.height() * hRatio); + final int destWidth = (int) (estimateCrop.width() * hRatio); + + // We estimated an invalid crop, try to adjust the cropHint to get a valid one. + if (destWidth > GLHelper.getMaxTextureSize()) { + int newHeight = (int) (wpData.mHeight / hRatio); + int newWidth = (int) (wpData.mWidth / hRatio); + if (DEBUG) { - Slog.v(TAG, "Downsampling cropped rect with scale " + scale); + Slog.v(TAG, "Invalid crop dimensions, trying to adjust."); } - } else { - scaler = null; + + estimateCrop.set(cropHint); + estimateCrop.left += (cropHint.width() - newWidth) / 2; + estimateCrop.top += (cropHint.height() - newHeight) / 2; + estimateCrop.right = estimateCrop.left + newWidth; + estimateCrop.bottom = estimateCrop.top + newHeight; + cropHint.set(estimateCrop); + estimateCrop.scale(1f / options.inSampleSize); } - Bitmap cropped = decoder.decodeRegion(cropHint, scaler); + + // We've got the safe cropHint; now we want to scale it properly to + // the desired rectangle. + // That's a height-biased operation: make it fit the hinted height. + final int safeHeight = (int) (estimateCrop.height() * hRatio); + final int safeWidth = (int) (estimateCrop.width() * hRatio); + + if (DEBUG) { + Slog.v(TAG, "Decode parameters:"); + Slog.v(TAG, " cropHint=" + cropHint + ", estimateCrop=" + estimateCrop); + Slog.v(TAG, " down sampling=" + options.inSampleSize + + ", hRatio=" + hRatio); + Slog.v(TAG, " dest=" + destWidth + "x" + destHeight); + Slog.v(TAG, " safe=" + safeWidth + "x" + safeHeight); + Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize()); + } + + Bitmap cropped = decoder.decodeRegion(cropHint, options); decoder.recycle(); if (cropped == null) { Slog.e(TAG, "Could not decode new wallpaper"); } else { - // We've got the extracted crop; now we want to scale it properly to - // the desired rectangle. That's a height-biased operation: make it - // fit the hinted height, and accept whatever width we end up with. - cropHint.offsetTo(0, 0); - cropHint.right /= scale; // adjust by downsampling factor - cropHint.bottom /= scale; - final float heightR = - ((float) wpData.mHeight) / ((float) cropHint.height()); - if (DEBUG) { - Slog.v(TAG, "scale " + heightR + ", extracting " + cropHint); - } - final int destWidth = (int)(cropHint.width() * heightR); + // We are safe to create final crop with safe dimensions now. final Bitmap finalCrop = Bitmap.createScaledBitmap(cropped, - destWidth, wpData.mHeight, true); + safeWidth, safeHeight, true); if (DEBUG) { Slog.v(TAG, "Final extract:"); Slog.v(TAG, " dims: w=" + wpData.mWidth + " h=" + wpData.mHeight); - Slog.v(TAG, " out: w=" + finalCrop.getWidth() + Slog.v(TAG, " out: w=" + finalCrop.getWidth() + " h=" + finalCrop.getHeight()); } + // A bitmap over than MAX_BITMAP_SIZE will make drawBitmap() fail. + // Please see: RecordingCanvas#throwIfCannotDraw. + if (finalCrop.getByteCount() > MAX_BITMAP_SIZE) { + throw new RuntimeException( + "Too large bitmap, limit=" + MAX_BITMAP_SIZE); + } + f = new FileOutputStream(wallpaper.cropFile); bos = new BufferedOutputStream(f, 32*1024); finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos); @@ -1981,6 +2030,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (!isWallpaperSupported(callingPackage)) { return; } + + // Make sure both width and height are not larger than max texture size. + width = Math.min(width, GLHelper.getMaxTextureSize()); + height = Math.min(height, GLHelper.getMaxTextureSize()); + synchronized (mLock) { int userId = UserHandle.getCallingUserId(); WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); From bfeaf4e4a824d467cf755a3f7b47bd10cda42286 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Mon, 23 Sep 2019 11:54:56 -0700 Subject: [PATCH 3/8] Do not register lift when sensor is not present Bug: 141284056 Fixes: 142365481 Test: force snesor to return null Change-Id: I45583e4f1c06ec7088a7d6335597a8671555f339 (cherry picked from commit 3b745ddf4a0105ee01b42f5024be4e0370a89b6b) (cherry picked from commit 0a410d3a6c4d7cff0d617e501ecc14e61219a358) --- .../android/systemui/statusbar/phone/KeyguardLiftController.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index f4635d1270a8a..f7b8a2e29129f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -67,6 +67,9 @@ class KeyguardLiftController constructor( } private fun updateListeningState() { + if (pickupSensor == null) { + return + } val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && !statusBarStateController.isDozing From 8412b9f24a75b9d070dec92da755cd27ead13306 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 1 Oct 2019 12:06:05 -0700 Subject: [PATCH 4/8] Dismissed keyguard gone runnables when aborting Previously those actions could linger around and lead to a variety of bugs. Also fixed an issue when gutsManager calls were overlapping to protect against these issues Fixes: 130885521 Test: atest SystemUITests Test: see bug description Change-Id: Iffe37e6d48bbc9c26ba92f362d7b67bf743c7c28 (cherry picked from commit 300dcfb7c3ebcfcba2e76de6ce16f2b0309e2e6d) --- .../notification/row/NotificationGuts.java | 8 +++--- .../row/NotificationGutsManager.java | 7 ++++-- .../phone/StatusBarKeyguardViewManager.java | 17 ++++++++++++- .../StatusBarKeyguardViewManagerTest.java | 25 +++++++++++++++++++ 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java index 4700baae8fabc..18d436ff7659e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java @@ -421,7 +421,7 @@ public class NotificationGuts extends FrameLayout { } /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */ - private static class AnimateCloseListener extends AnimatorListenerAdapter { + private class AnimateCloseListener extends AnimatorListenerAdapter { final View mView; private final GutsContent mGutsContent; @@ -433,8 +433,10 @@ public class NotificationGuts extends FrameLayout { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - mView.setVisibility(View.GONE); - mGutsContent.onFinishedClosing(); + if (!isExposed()) { + mView.setVisibility(View.GONE); + mGutsContent.onFinishedClosing(); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 8f7671a5dd96d..719ec3215d427 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -100,6 +100,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx protected String mKeyToRemoveOnGutsClosed; private StatusBar mStatusBar; + private Runnable mOpenRunnable; @Inject public NotificationGutsManager( @@ -343,6 +344,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls, int x, int y, boolean resetMenu) { if (mNotificationGutsExposed != null) { + mNotificationGutsExposed.removeCallbacks(mOpenRunnable); mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force); } if (resetMenu) { @@ -445,7 +447,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx // ensure that it's laid but not visible until actually laid out guts.setVisibility(View.INVISIBLE); // Post to ensure the the guts are properly laid out. - guts.post(new Runnable() { + mOpenRunnable = new Runnable() { @Override public void run() { if (row.getWindowToken() == null) { @@ -470,7 +472,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mListContainer.onHeightChanged(row, true /* needsAnimation */); mGutsMenuItem = menuItem; } - }); + }; + guts.post(mOpenRunnable); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 0c47d1468a7f9..68ee8bbbb69bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -62,6 +62,8 @@ import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; import java.io.PrintWriter; import java.util.ArrayList; +import androidx.annotation.VisibleForTesting; + /** * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done, @@ -161,6 +163,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mLastLockVisible; private OnDismissAction mAfterKeyguardGoneAction; + private Runnable mKeyguardGoneCancelAction; private final ArrayList mAfterKeyguardGoneRunnables = new ArrayList<>(); // Dismiss action to be launched when we stop dozing or the keyguard is gone. @@ -328,10 +331,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return false; } - private void hideBouncer(boolean destroyView) { + @VisibleForTesting + void hideBouncer(boolean destroyView) { if (mBouncer == null) { return; } + if (mShowing) { + // If we were showing the bouncer and then aborting, we need to also clear out any + // potential actions unless we actually unlocked. + mAfterKeyguardGoneAction = null; + if (mKeyguardGoneCancelAction != null) { + mKeyguardGoneCancelAction.run(); + mKeyguardGoneCancelAction = null; + } + } mBouncer.hide(destroyView); cancelPendingWakeupAction(); } @@ -364,6 +377,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncer.showWithDismissAction(r, cancelAction); } else { mAfterKeyguardGoneAction = r; + mKeyguardGoneCancelAction = cancelAction; mBouncer.show(false /* resetSecuritySelection */); } } @@ -671,6 +685,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mAfterKeyguardGoneAction.onDismiss(); mAfterKeyguardGoneAction = null; } + mKeyguardGoneCancelAction = null; for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) { mAfterKeyguardGoneRunnables.get(i).run(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 63f653b0b303f..0da0e7647707d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -221,6 +221,31 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { verify(mStatusBar, never()).animateKeyguardUnoccluding(); } + @Test + public void testHiding_cancelsGoneRunnable() { + OnDismissAction action = mock(OnDismissAction.class); + Runnable cancelAction = mock(Runnable.class); + mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, + true /* afterKeyguardGone */); + + mStatusBarKeyguardViewManager.hideBouncer(true); + mStatusBarKeyguardViewManager.hide(0, 30); + verify(action, never()).onDismiss(); + verify(cancelAction).run(); + } + + @Test + public void testHiding_doesntCancelWhenShowing() { + OnDismissAction action = mock(OnDismissAction.class); + Runnable cancelAction = mock(Runnable.class); + mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, + true /* afterKeyguardGone */); + + mStatusBarKeyguardViewManager.hide(0, 30); + verify(action).onDismiss(); + verify(cancelAction, never()).run(); + } + private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager { public TestableStatusBarKeyguardViewManager(Context context, From 18ed45f835485d8d64708540d318707eeb215c1c Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 3 Oct 2019 13:20:08 -0700 Subject: [PATCH 5/8] Ensure SKP serialization occurs on RenderThread Instead of doing lazy serialization of SKP on the background executor serialize to a byte[] immediately at callback invocation. This ensures no potential for later mutations, race conditions, or wrong-thread issues at the expense of potentially impacting app rendering performance. However it seems preferable for a debug-only tool to be a slow instead of very crashy. Bug: 141772764 Test: test app Change-Id: I3316d49970b96f1c59bb0a28ff7335db608e539e (cherry picked from commit 8d0da1a6c5271d7f803b665d6a0784a2ca74d3c8) --- core/java/android/view/ViewDebug.java | 101 +++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 12 deletions(-) diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index a390db2e9644c..1ee33660cc62a 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -868,6 +868,94 @@ public class ViewDebug { return null; } + private static class StreamingPictureCallbackHandler implements AutoCloseable, + HardwareRenderer.PictureCapturedCallback, Runnable { + private final HardwareRenderer mRenderer; + private final Callable mCallback; + private final Executor mExecutor; + private final ReentrantLock mLock = new ReentrantLock(false); + private final ArrayDeque mQueue = new ArrayDeque<>(3); + private final ByteArrayOutputStream mByteStream = new ByteArrayOutputStream(); + private boolean mStopListening; + private Thread mRenderThread; + + private StreamingPictureCallbackHandler(HardwareRenderer renderer, + Callable callback, Executor executor) { + mRenderer = renderer; + mCallback = callback; + mExecutor = executor; + mRenderer.setPictureCaptureCallback(this); + } + + @Override + public void close() { + mLock.lock(); + mStopListening = true; + mLock.unlock(); + mRenderer.setPictureCaptureCallback(null); + } + + @Override + public void onPictureCaptured(Picture picture) { + mLock.lock(); + if (mStopListening) { + mLock.unlock(); + mRenderer.setPictureCaptureCallback(null); + return; + } + if (mRenderThread == null) { + mRenderThread = Thread.currentThread(); + } + boolean needsInvoke = true; + if (mQueue.size() == 3) { + mQueue.removeLast(); + needsInvoke = false; + } + picture.writeToStream(mByteStream); + mQueue.add(mByteStream.toByteArray()); + mByteStream.reset(); + mLock.unlock(); + + if (needsInvoke) { + mExecutor.execute(this); + } + } + + @Override + public void run() { + mLock.lock(); + final byte[] picture = mQueue.poll(); + final boolean isStopped = mStopListening; + mLock.unlock(); + if (Thread.currentThread() == mRenderThread) { + close(); + throw new IllegalStateException( + "ViewDebug#startRenderingCommandsCapture must be given an executor that " + + "invokes asynchronously"); + } + if (isStopped) { + return; + } + OutputStream stream = null; + try { + stream = mCallback.call(); + } catch (Exception ex) { + Log.w("ViewDebug", "Aborting rendering commands capture " + + "because callback threw exception", ex); + } + if (stream != null) { + try { + stream.write(picture); + } catch (IOException ex) { + Log.w("ViewDebug", "Aborting rendering commands capture " + + "due to IOException writing to output stream", ex); + } + } else { + close(); + } + } + } + /** * Begins capturing the entire rendering commands for the view tree referenced by the given * view. The view passed may be any View in the tree as long as it is attached. That is, @@ -913,18 +1001,7 @@ public class ViewDebug { } final HardwareRenderer renderer = attachInfo.mThreadedRenderer; if (renderer != null) { - return new PictureCallbackHandler(renderer, (picture -> { - try { - OutputStream stream = callback.call(); - if (stream != null) { - picture.writeToStream(stream); - return true; - } - } catch (Exception ex) { - // fall through - } - return false; - }), executor); + return new StreamingPictureCallbackHandler(renderer, callback, executor); } return null; } From fab928923c8255626049e6f459105d2e4c715384 Mon Sep 17 00:00:00 2001 From: Patrick Baumann Date: Thu, 10 Oct 2019 15:50:28 -0700 Subject: [PATCH 6/8] Use KNOWN_PACKAGES when shared lib consumers This change ensures we find ALL known packages that could be consuming a shared library, not only currently installed ones. Without this check, the system may get into a state in which we have currently uninstalled but on-device apps that depend on a shared library that does not exist on device. This change also leaves static shared library packages on device even if it's not installed for any of the remaining users as it could still be used, but marked uninstalled for users in which it is consumed. Bug: 141413692 Bug: 142083996 Test: Manual; attempt to remove shared lib after marking its consumer uninstalled. Test: atest StaticSharedLibsHostTests Change-Id: Id4e37c3e4d3ea3ad5fddae5d2c7305e56f50eeea (cherry picked from commit 34b504c32ff054265630662fa6fa57158cbcdc6c) --- .../java/com/android/server/pm/PackageManagerService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index aae48239a0cc5..703c17acd6cef 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18626,7 +18626,7 @@ public class PackageManagerService extends IPackageManager.Stub continue; } List libClientPackages = getPackagesUsingSharedLibraryLPr( - libraryInfo, 0, currUserId); + libraryInfo, MATCH_KNOWN_PACKAGES, currUserId); if (!ArrayUtils.isEmpty(libClientPackages)) { Slog.w(TAG, "Not removing package " + pkg.manifestPackageName + " hosting lib " + libraryInfo.getName() + " version " @@ -23652,8 +23652,9 @@ public class PackageManagerService extends IPackageManager.Stub continue; } final String packageName = ps.pkg.packageName; - // Skip over if system app - if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { + // Skip over if system app or static shared library + if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 + || !TextUtils.isEmpty(ps.pkg.staticSharedLibName)) { continue; } if (DEBUG_CLEAN_APKS) { From 859ead528cd09f6fdf3a85df390745054058d12c Mon Sep 17 00:00:00 2001 From: Patrick Baumann Date: Wed, 6 Nov 2019 10:36:39 -0800 Subject: [PATCH 7/8] DO NOT MERGE: Fixes NPE when preparing app data during init When deleting an unused static shared library on Q, the user manager was fetched via mContext.getSystemService. At this time during boot, the service wasn't registered and so null was returned. This has already been addressed in R with a move to injecting dependencies in the PackageManagerService constructor. Bug: 142083996 Bug: 141413692 Test: manual; remove static dependency on eng Q build and reboot Change-Id: I8ae4e331d09b4734c54cdc6887b273705dce88b1 (cherry picked from commit 46e6a0b1ff34d3a0a8fc006ba86677780195a1ff) --- .../java/com/android/server/pm/PackageManagerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 703c17acd6cef..dd8cb4dba0a71 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -22967,9 +22967,9 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.writeKernelMappingLPr(ps); } - final UserManager um = mContext.getSystemService(UserManager.class); + final UserManagerService um = sUserManager; UserManagerInternal umInternal = getUserManagerInternal(); - for (UserInfo user : um.getUsers()) { + for (UserInfo user : um.getUsers(false /* excludeDying */)) { final int flags; if (umInternal.isUserUnlockingOrUnlocked(user.id)) { flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; From 818d8813f7bbab13ebccf5ab291fe3de8df5157e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 12 Nov 2019 15:33:22 -0800 Subject: [PATCH 8/8] Actually use mContext directly I40e95d72c63f864131f0028c47f121463d0f6dff (Use mContext directly) lost a needed line in a rebase Test: Forced commit to return exception, checked that device reboots Bug: 138952436 Merged-In: Idb7cb9a5b7c4c0d237ece9716932a260956a0109 Change-Id: I8e7484631cd76965e660544a1af9f567dc5d3a34 (cherry picked from commit c5fc5fe3266a7b01f689e2c981babadca5360836) --- .../core/java/com/android/server/am/ActivityManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 078fcef0f88b8..c2482ef77a7a0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5262,7 +5262,7 @@ public class ActivityManagerService extends IActivityManager.Stub storageManager.commitChanges(); } catch (Exception e) { PowerManager pm = (PowerManager) - mInjector.getContext().getSystemService(Context.POWER_SERVICE); + mContext.getSystemService(Context.POWER_SERVICE); pm.reboot("Checkpoint commit failed"); }