Merge "[Magnifier-25] Fix race condition after #dismiss"
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 6427965..eb2af60 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -89,6 +89,9 @@
NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
// Rectangle defining the view surface area we pixel copy content from.
private final Rect mPixelCopyRequestRect = new Rect();
+ // Lock to synchronize between the UI thread and the thread that handles pixel copy results.
+ // Only sync mWindow writes from UI thread with mWindow reads from sPixelCopyHandlerThread.
+ private final Object mLock = new Object();
/**
* Initializes a magnifier.
@@ -170,9 +173,12 @@
if (xPosInView != mPrevPosInView.x || yPosInView != mPrevPosInView.y) {
if (mWindow == null) {
- mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
- getValidViewSurface(), mWindowWidth, mWindowHeight, mWindowElevation,
- Handler.getMain() /* draw the magnifier on the UI thread */, mCallback);
+ synchronized (mLock) {
+ mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
+ getValidViewSurface(), mWindowWidth, mWindowHeight, mWindowElevation,
+ Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
+ mCallback);
+ }
}
performPixelCopy(startX, startY, true /* update window position */);
mPrevPosInView.x = xPosInView;
@@ -200,8 +206,10 @@
*/
public void dismiss() {
if (mWindow != null) {
- mWindow.destroy();
- mWindow = null;
+ synchronized (mLock) {
+ mWindow.destroy();
+ mWindow = null;
+ }
}
}
@@ -281,19 +289,22 @@
clampedStartYInSurface + mBitmapHeight);
final int windowCoordsX = mWindowCoords.x;
final int windowCoordsY = mWindowCoords.y;
+ final InternalPopupWindow currentWindowInstance = mWindow;
final Bitmap bitmap =
Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
PixelCopy.request(surface, mPixelCopyRequestRect, bitmap,
result -> {
- synchronized (mWindow.mLock) {
- if (mWindow != null) {
- if (updateWindowPosition) {
- // TODO: pull the position update outside #performPixelCopy
- mWindow.setContentPositionForNextDraw(windowCoordsX, windowCoordsY);
- }
- mWindow.updateContent(bitmap);
+ synchronized (mLock) {
+ if (mWindow != currentWindowInstance) {
+ // The magnifier was dismissed (and maybe shown again) in the meantime.
+ return;
}
+ if (updateWindowPosition) {
+ // TODO: pull the position update outside #performPixelCopy
+ mWindow.setContentPositionForNextDraw(windowCoordsX, windowCoordsY);
+ }
+ mWindow.updateContent(bitmap);
}
},
sPixelCopyHandlerThread.getThreadHandler());
@@ -337,7 +348,7 @@
// Members below describe the state of the magnifier. Reads/writes to them
// have to be synchronized between the UI thread and the thread that handles
// the pixel copy results. This is the purpose of mLock.
- private final Object mLock = new Object();
+ private final Object mLock;
// Whether a magnifier frame draw is currently pending in the UI thread queue.
private boolean mFrameDrawScheduled;
// The content bitmap.
@@ -353,8 +364,9 @@
InternalPopupWindow(final Context context, final Display display,
final Surface parentSurface,
final int width, final int height, final float elevation,
- final Handler handler, final Callback callback) {
+ final Handler handler, final Object lock, final Callback callback) {
mDisplay = display;
+ mLock = lock;
mCallback = callback;
mContentWidth = width;