From 248ba23ad28c1f8fb69904663204df08c5818700 Mon Sep 17 00:00:00 2001 From: James Dong Date: Sat, 28 Apr 2012 21:30:46 -0700 Subject: [PATCH] Fix a race condition in Camera API for handling focus In the case where a previous AF completion was outstanding but before the completion notification reached the application, the application cancelled this AF request, and then started a new AF request. Right after the new AF request, the AF completion notification for earlier AF request reached the application. The application could not tell the AF completion notification was meant for the cancelled AF, but thought the new AF was successfully completed. Subsequently, the application trid to take a picture, which failed as a result. The fix is to add an explicit lock in the Camera.java class to fix the race condition to synchornize autoFocus(), cancelAutoFocus() and the callback of the pending AF completion message. o related-to-bug: 6026480 Change-Id: I33d244d908ac066698e792f641ba88fe228b14a9 --- core/java/android/hardware/Camera.java | 40 ++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index eb0a0c6f4732a..5ed8dd12cca05 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -35,6 +35,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.StringTokenizer; +import java.util.concurrent.locks.ReentrantLock; /** * The Camera class is used to set image capture settings, start/stop preview, @@ -154,6 +155,7 @@ public class Camera { private boolean mOneShot; private boolean mWithBuffer; private boolean mFaceDetectionRunning = false; + private ReentrantLock mFocusLock = new ReentrantLock(); /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -746,8 +748,14 @@ public class Camera { return; case CAMERA_MSG_FOCUS: - if (mAutoFocusCallback != null) { - mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera); + mFocusLock.lock(); + try { + if (mAutoFocusCallback != null) { + boolean success = msg.arg1 == 0 ? false : true; + mAutoFocusCallback.onAutoFocus(success, mCamera); + } + } finally { + mFocusLock.unlock(); } return; @@ -872,8 +880,13 @@ public class Camera { */ public final void autoFocus(AutoFocusCallback cb) { - mAutoFocusCallback = cb; - native_autoFocus(); + mFocusLock.lock(); + try { + mAutoFocusCallback = cb; + native_autoFocus(); + } finally { + mFocusLock.unlock(); + } } private native final void native_autoFocus(); @@ -887,8 +900,14 @@ public class Camera { */ public final void cancelAutoFocus() { - mAutoFocusCallback = null; - native_cancelAutoFocus(); + mFocusLock.lock(); + try { + mAutoFocusCallback = null; + native_cancelAutoFocus(); + removePendingAFCompletionMessages(); + } finally { + mFocusLock.unlock(); + } } private native final void native_cancelAutoFocus(); @@ -3577,4 +3596,13 @@ public class Camera { return false; } }; + + /* + * At any time, there should be at most one pending auto focus completion + * message, but we simply remove all pending AF completion messages in + * the looper's queue. + */ + private void removePendingAFCompletionMessages() { + mEventHandler.removeMessages(CAMERA_MSG_FOCUS); + } }