OSDN Git Service

Camera API 2, Device 2/3: Implement idle and shutter callbacks
authorEino-Ville Talvala <etalvala@google.com>
Fri, 6 Sep 2013 16:32:43 +0000 (09:32 -0700)
committerEino-Ville Talvala <etalvala@google.com>
Thu, 3 Oct 2013 01:11:21 +0000 (18:11 -0700)
- Update callback Binder interface
- Rename frameId to be requestId to be consistent and disambiguate
  from frameNumber.
- Implement shutter callback from HAL2/3 notify()
  - Add in-flight tracking to HAL2
  - Add requestId to in-flight tracking
  - Report requestId from shutter callback
- Implement idle callback from HAL3 process_capture_result
  - Add new idle tracker thread
  - Update all idle waiting to use the tracker
  - Add reporting from request thread, all streams to tracker
  - Remove existing idle waiting infrastructure

Bug: 10549462
Change-Id: I867bfc248e3848c50e71527e3561fe92dc037958

35 files changed:
camera/IProCameraCallbacks.cpp
camera/ProCamera.cpp
camera/camera2/ICameraDeviceCallbacks.cpp
camera/tests/ProCameraTests.cpp
include/camera/IProCameraCallbacks.h
include/camera/ProCamera.h
include/camera/camera2/ICameraDeviceCallbacks.h
services/camera/libcameraservice/Android.mk
services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
services/camera/libcameraservice/api1/client2/CaptureSequencer.h
services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
services/camera/libcameraservice/api1/client2/ZslProcessor.h
services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
services/camera/libcameraservice/api1/client2/ZslProcessor3.h
services/camera/libcameraservice/api2/CameraDeviceClient.cpp
services/camera/libcameraservice/api2/CameraDeviceClient.h
services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
services/camera/libcameraservice/api_pro/ProCamera2Client.h
services/camera/libcameraservice/common/Camera2ClientBase.cpp
services/camera/libcameraservice/common/Camera2ClientBase.h
services/camera/libcameraservice/common/CameraDeviceBase.h
services/camera/libcameraservice/common/FrameProcessorBase.h
services/camera/libcameraservice/device2/Camera2Device.cpp
services/camera/libcameraservice/device2/Camera2Device.h
services/camera/libcameraservice/device3/Camera3Device.cpp
services/camera/libcameraservice/device3/Camera3Device.h
services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
services/camera/libcameraservice/device3/Camera3IOStreamBase.h
services/camera/libcameraservice/device3/Camera3InputStream.cpp
services/camera/libcameraservice/device3/Camera3OutputStream.cpp
services/camera/libcameraservice/device3/Camera3Stream.cpp
services/camera/libcameraservice/device3/Camera3Stream.h
services/camera/libcameraservice/device3/Camera3StreamInterface.h
services/camera/libcameraservice/device3/StatusTracker.cpp [new file with mode: 0644]
services/camera/libcameraservice/device3/StatusTracker.h [new file with mode: 0644]

index 0fdb85a..bd3d420 100644 (file)
@@ -67,11 +67,11 @@ public:
                            IBinder::FLAG_ONEWAY);
     }
 
-    void onResultReceived(int32_t frameId, camera_metadata* result) {
+    void onResultReceived(int32_t requestId, camera_metadata* result) {
         ALOGV("onResultReceived");
         Parcel data, reply;
         data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
-        data.writeInt32(frameId);
+        data.writeInt32(requestId);
         CameraMetadata::writeToParcel(data, result);
         remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
     }
@@ -107,10 +107,10 @@ status_t BnProCameraCallbacks::onTransact(
         case RESULT_RECEIVED: {
             ALOGV("RESULT_RECEIVED");
             CHECK_INTERFACE(IProCameraCallbacks, data, reply);
-            int32_t frameId = data.readInt32();
+            int32_t requestId = data.readInt32();
             camera_metadata_t *result = NULL;
             CameraMetadata::readFromParcel(data, &result);
-            onResultReceived(frameId, result);
+            onResultReceived(requestId, result);
             return NO_ERROR;
             break;
         }
index 577c760..ba5a48c 100644 (file)
@@ -90,8 +90,8 @@ void ProCamera::onLockStatusChanged(
     }
 }
 
-void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) {
-    ALOGV("%s: frameId = %d, result = %p", __FUNCTION__, frameId, result);
+void ProCamera::onResultReceived(int32_t requestId, camera_metadata* result) {
+    ALOGV("%s: requestId = %d, result = %p", __FUNCTION__, requestId, result);
 
     sp<ProCameraListener> listener;
     {
@@ -112,7 +112,7 @@ void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) {
     result = tmp.release();
 
     if (listener != NULL) {
-        listener->onResultReceived(frameId, result);
+        listener->onResultReceived(requestId, result);
     } else {
         free_camera_metadata(result);
     }
index 3cec1f4..613358a 100644 (file)
@@ -32,7 +32,9 @@
 namespace android {
 
 enum {
-    NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
+    CAMERA_ERROR = IBinder::FIRST_CALL_TRANSACTION,
+    CAMERA_IDLE,
+    CAPTURE_STARTED,
     RESULT_RECEIVED,
 };
 
@@ -44,19 +46,37 @@ public:
     {
     }
 
-    // generic callback from camera service to app
-    void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
+    void onDeviceError(CameraErrorCode errorCode)
     {
-        ALOGV("notifyCallback");
+        ALOGV("onDeviceError");
         Parcel data, reply;
         data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
-        data.writeInt32(msgType);
-        data.writeInt32(ext1);
-        data.writeInt32(ext2);
-        remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
+        data.writeInt32(static_cast<int32_t>(errorCode));
+        remote()->transact(CAMERA_ERROR, data, &reply, IBinder::FLAG_ONEWAY);
         data.writeNoException();
     }
 
+    void onDeviceIdle()
+    {
+        ALOGV("onDeviceIdle");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
+        remote()->transact(CAMERA_IDLE, data, &reply, IBinder::FLAG_ONEWAY);
+        data.writeNoException();
+    }
+
+    void onCaptureStarted(int32_t requestId, int64_t timestamp)
+    {
+        ALOGV("onCaptureStarted");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraDeviceCallbacks::getInterfaceDescriptor());
+        data.writeInt32(requestId);
+        data.writeInt64(timestamp);
+        remote()->transact(CAPTURE_STARTED, data, &reply, IBinder::FLAG_ONEWAY);
+        data.writeNoException();
+    }
+
+
     void onResultReceived(int32_t requestId, const CameraMetadata& result) {
         ALOGV("onResultReceived");
         Parcel data, reply;
@@ -79,18 +99,33 @@ status_t BnCameraDeviceCallbacks::onTransact(
 {
     ALOGV("onTransact - code = %d", code);
     switch(code) {
-        case NOTIFY_CALLBACK: {
-            ALOGV("NOTIFY_CALLBACK");
+        case CAMERA_ERROR: {
+            ALOGV("onDeviceError");
+            CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
+            CameraErrorCode errorCode =
+                    static_cast<CameraErrorCode>(data.readInt32());
+            onDeviceError(errorCode);
+            data.readExceptionCode();
+            return NO_ERROR;
+        } break;
+        case CAMERA_IDLE: {
+            ALOGV("onDeviceIdle");
             CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
-            int32_t msgType = data.readInt32();
-            int32_t ext1 = data.readInt32();
-            int32_t ext2 = data.readInt32();
-            notifyCallback(msgType, ext1, ext2);
+            onDeviceIdle();
+            data.readExceptionCode();
+            return NO_ERROR;
+        } break;
+        case CAPTURE_STARTED: {
+            ALOGV("onCaptureStarted");
+            CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
+            int32_t requestId = data.readInt32();
+            int64_t timestamp = data.readInt64();
+            onCaptureStarted(requestId, timestamp);
             data.readExceptionCode();
             return NO_ERROR;
         } break;
         case RESULT_RECEIVED: {
-            ALOGV("RESULT_RECEIVED");
+            ALOGV("onResultReceived");
             CHECK_INTERFACE(ICameraDeviceCallbacks, data, reply);
             int32_t requestId = data.readInt32();
             CameraMetadata result;
@@ -102,8 +137,7 @@ status_t BnCameraDeviceCallbacks::onTransact(
             onResultReceived(requestId, result);
             data.readExceptionCode();
             return NO_ERROR;
-            break;
-        }
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
index e9aa99d..1f5867a 100644 (file)
@@ -284,9 +284,9 @@ protected:
         }
     }
 
-    virtual void onResultReceived(int32_t frameId,
+    virtual void onResultReceived(int32_t requestId,
                                   camera_metadata* request) {
-        dout << "Result received frameId = " << frameId
+        dout << "Result received requestId = " << requestId
              << ", requestPtr = " << (void*)request << std::endl;
         QueueEvent(RESULT_RECEIVED);
         free_camera_metadata(request);
@@ -1276,4 +1276,3 @@ TEST_F(ProCameraTest, ServiceListenersFunctional) {
 }
 }
 }
-
index c774698..e8abb89 100644 (file)
@@ -51,7 +51,7 @@ public:
     /** Missing by design: implementation is client-side in ProCamera.cpp **/
     // virtual void onBufferReceived(int streamId,
     //                               const CpuConsumer::LockedBufer& buf);
-    virtual void            onResultReceived(int32_t frameId,
+    virtual void            onResultReceived(int32_t requestId,
                                              camera_metadata* result) = 0;
 };
 
index d9ee662..83a3028 100644 (file)
@@ -252,7 +252,7 @@ protected:
     virtual void        onLockStatusChanged(
                                 IProCameraCallbacks::LockStatus newLockStatus);
 
-    virtual void        onResultReceived(int32_t frameId,
+    virtual void        onResultReceived(int32_t requestId,
                                          camera_metadata* result);
 private:
     ProCamera(int cameraId);
index 041fa65..8dac4f2 100644 (file)
@@ -35,13 +35,27 @@ class ICameraDeviceCallbacks : public IInterface
 public:
     DECLARE_META_INTERFACE(CameraDeviceCallbacks);
 
+    /**
+     * Error codes for CAMERA_MSG_ERROR
+     */
+    enum CameraErrorCode {
+        ERROR_CAMERA_DISCONNECTED = 0,
+        ERROR_CAMERA_DEVICE = 1,
+        ERROR_CAMERA_SERVICE = 2
+    };
+
+    // One way
+    virtual void            onDeviceError(CameraErrorCode errorCode) = 0;
+
+    // One way
+    virtual void            onDeviceIdle() = 0;
+
     // One way
-    virtual void            notifyCallback(int32_t msgType,
-                                           int32_t ext1,
-                                           int32_t ext2) = 0;
+    virtual void            onCaptureStarted(int32_t requestId,
+                                             int64_t timestamp) = 0;
 
     // One way
-    virtual void            onResultReceived(int32_t frameId,
+    virtual void            onResultReceived(int32_t requestId,
                                              const CameraMetadata& result) = 0;
 };
 
index d659ebb..d23f8b9 100644 (file)
@@ -33,6 +33,7 @@ LOCAL_SRC_FILES:=               \
     device3/Camera3InputStream.cpp \
     device3/Camera3OutputStream.cpp \
     device3/Camera3ZslStream.cpp \
+    device3/StatusTracker.cpp \
     gui/RingBufferConsumer.cpp \
 
 LOCAL_SHARED_LIBRARIES:= \
index ca3198f..1a1b27b 100644 (file)
@@ -103,12 +103,12 @@ void CaptureSequencer::notifyAutoExposure(uint8_t newState, int triggerId) {
     }
 }
 
-void CaptureSequencer::onFrameAvailable(int32_t frameId,
+void CaptureSequencer::onFrameAvailable(int32_t requestId,
         const CameraMetadata &frame) {
     ALOGV("%s: Listener found new frame", __FUNCTION__);
     ATRACE_CALL();
     Mutex::Autolock l(mInputMutex);
-    mNewFrameId = frameId;
+    mNewFrameId = requestId;
     mNewFrame = frame;
     if (!mNewFrameReceived) {
         mNewFrameReceived = true;
index 7ad461a..e1e6201 100644 (file)
@@ -62,7 +62,7 @@ class CaptureSequencer:
     void notifyAutoExposure(uint8_t newState, int triggerId);
 
     // Notifications from the frame processor
-    virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+    virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
 
     // Notifications from the JPEG processor
     void onCaptureAvailable(nsecs_t timestamp, sp<MemoryBase> captureBuffer);
index 08ab357..4207ba9 100644 (file)
@@ -71,7 +71,7 @@ void ZslProcessor::onFrameAvailable() {
     }
 }
 
-void ZslProcessor::onFrameAvailable(int32_t /*frameId*/,
+void ZslProcessor::onFrameAvailable(int32_t /*requestId*/,
         const CameraMetadata &frame) {
     Mutex::Autolock l(mInputMutex);
     camera_metadata_ro_entry_t entry;
index 5fb178f..6d3cb85 100644 (file)
@@ -54,7 +54,7 @@ class ZslProcessor:
     // From mZslConsumer
     virtual void onFrameAvailable();
     // From FrameProcessor
-    virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+    virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
 
     virtual void onBufferReleased(buffer_handle_t *handle);
 
index 3e05091..776ebe2 100644 (file)
@@ -61,7 +61,7 @@ ZslProcessor3::~ZslProcessor3() {
     deleteStream();
 }
 
-void ZslProcessor3::onFrameAvailable(int32_t /*frameId*/,
+void ZslProcessor3::onFrameAvailable(int32_t /*requestId*/,
                                      const CameraMetadata &frame) {
     Mutex::Autolock l(mInputMutex);
     camera_metadata_ro_entry_t entry;
index 35b85f5..d2f8322 100644 (file)
@@ -51,7 +51,7 @@ class ZslProcessor3 :
     ~ZslProcessor3();
 
     // From FrameProcessor
-    virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame);
+    virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame);
 
     /**
      ****************************************
index 76d44bf..72126c1 100644 (file)
@@ -45,14 +45,6 @@ CameraDeviceClientBase::CameraDeviceClientBase(
                 cameraId, cameraFacing, clientPid, clientUid, servicePid),
     mRemoteCallback(remoteCallback) {
 }
-void CameraDeviceClientBase::notifyError() {
-    // Thread safe. Don't bother locking.
-    sp<ICameraDeviceCallbacks> remoteCb = mRemoteCallback;
-
-    if (remoteCb != 0) {
-        remoteCb->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0);
-    }
-}
 
 // Interface used by CameraService
 
@@ -164,7 +156,6 @@ status_t CameraDeviceClient::submitRequest(sp<CaptureRequest> request,
     metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0],
                     outputStreamIds.size());
 
-    // TODO: @hide ANDROID_REQUEST_ID, or use another request token
     int32_t requestId = mRequestIdCounter++;
     metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1);
     ALOGV("%s: Camera %d: Submitting request with ID %d",
@@ -501,6 +492,34 @@ status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
     return dumpDevice(fd, args);
 }
 
+
+void CameraDeviceClient::notifyError() {
+    // Thread safe. Don't bother locking.
+    sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+
+    if (remoteCb != 0) {
+        remoteCb->onDeviceError(ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE);
+    }
+}
+
+void CameraDeviceClient::notifyIdle() {
+    // Thread safe. Don't bother locking.
+    sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+
+    if (remoteCb != 0) {
+        remoteCb->onDeviceIdle();
+    }
+}
+
+void CameraDeviceClient::notifyShutter(int requestId,
+        nsecs_t timestamp) {
+    // Thread safe. Don't bother locking.
+    sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+    if (remoteCb != 0) {
+        remoteCb->onCaptureStarted(requestId, timestamp);
+    }
+}
+
 // TODO: refactor the code below this with IProCameraUser.
 // it's 100% copy-pasted, so lets not change it right now to make it easier.
 
@@ -532,8 +551,8 @@ void CameraDeviceClient::detachDevice() {
 }
 
 /** Device-related methods */
-void CameraDeviceClient::onFrameAvailable(int32_t frameId,
-                                        const CameraMetadata& frame) {
+void CameraDeviceClient::onFrameAvailable(int32_t requestId,
+        const CameraMetadata& frame) {
     ATRACE_CALL();
     ALOGV("%s", __FUNCTION__);
 
@@ -541,7 +560,7 @@ void CameraDeviceClient::onFrameAvailable(int32_t frameId,
     sp<ICameraDeviceCallbacks> remoteCb = mRemoteCallback;
     if (remoteCb != NULL) {
         ALOGV("%s: frame = %p ", __FUNCTION__, &frame);
-        remoteCb->onResultReceived(frameId, frame);
+        remoteCb->onResultReceived(requestId, frame);
     }
 }
 
index b490924..b9c16aa 100644 (file)
@@ -45,8 +45,6 @@ protected:
             uid_t clientUid,
             int servicePid);
 
-    virtual void notifyError();
-
     sp<ICameraDeviceCallbacks> mRemoteCallback;
 };
 
@@ -112,11 +110,19 @@ public:
     virtual status_t      dump(int fd, const Vector<String16>& args);
 
     /**
+     * Device listener interface
+     */
+
+    virtual void notifyIdle();
+    virtual void notifyError();
+    virtual void notifyShutter(int requestId, nsecs_t timestamp);
+
+    /**
      * Interface used by independent components of CameraDeviceClient.
      */
 protected:
     /** FilteredListener implementation **/
-    virtual void          onFrameAvailable(int32_t frameId,
+    virtual void          onFrameAvailable(int32_t requestId,
                                            const CameraMetadata& frame);
     virtual void          detachDevice();
 
index 2b583e5..1a7a7a7 100644 (file)
@@ -374,7 +374,7 @@ void ProCamera2Client::detachDevice() {
 }
 
 /** Device-related methods */
-void ProCamera2Client::onFrameAvailable(int32_t frameId,
+void ProCamera2Client::onFrameAvailable(int32_t requestId,
                                         const CameraMetadata& frame) {
     ATRACE_CALL();
     ALOGV("%s", __FUNCTION__);
@@ -386,7 +386,7 @@ void ProCamera2Client::onFrameAvailable(int32_t frameId,
         CameraMetadata tmp(frame);
         camera_metadata_t* meta = tmp.release();
         ALOGV("%s: meta = %p ", __FUNCTION__, meta);
-        mRemoteCallback->onResultReceived(frameId, meta);
+        mRemoteCallback->onResultReceived(requestId, meta);
         tmp.acquire(meta);
     }
 
index 0bf6784..8a0f547 100644 (file)
@@ -97,7 +97,7 @@ public:
 
 protected:
     /** FilteredListener implementation **/
-    virtual void          onFrameAvailable(int32_t frameId,
+    virtual void          onFrameAvailable(int32_t requestId,
                                            const CameraMetadata& frame);
     virtual void          detachDevice();
 
index e808bf3..2d1253f 100644 (file)
@@ -226,13 +226,18 @@ void Camera2ClientBase<TClientBase>::notifyError(int errorCode, int arg1,
 }
 
 template <typename TClientBase>
-void Camera2ClientBase<TClientBase>::notifyShutter(int frameNumber,
+void Camera2ClientBase<TClientBase>::notifyIdle() {
+    ALOGV("Camera device is now idle");
+}
+
+template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyShutter(int requestId,
                                                    nsecs_t timestamp) {
-    (void)frameNumber;
+    (void)requestId;
     (void)timestamp;
 
-    ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__,
-          frameNumber, timestamp);
+    ALOGV("%s: Shutter notification for request id %d at time %lld",
+            __FUNCTION__, requestId, timestamp);
 }
 
 template <typename TClientBase>
@@ -244,13 +249,6 @@ void Camera2ClientBase<TClientBase>::notifyAutoFocus(uint8_t newState,
     ALOGV("%s: Autofocus state now %d, last trigger %d",
           __FUNCTION__, newState, triggerId);
 
-    typename SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
-    if (l.mRemoteCallback != 0) {
-        l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS_MOVE, 1, 0);
-    }
-    if (l.mRemoteCallback != 0) {
-        l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS, 1, 0);
-    }
 }
 
 template <typename TClientBase>
index d23197c..61e44f0 100644 (file)
@@ -62,7 +62,8 @@ public:
      */
 
     virtual void          notifyError(int errorCode, int arg1, int arg2);
-    virtual void          notifyShutter(int frameNumber, nsecs_t timestamp);
+    virtual void          notifyIdle();
+    virtual void          notifyShutter(int requestId, nsecs_t timestamp);
     virtual void          notifyAutoFocus(uint8_t newState, int triggerId);
     virtual void          notifyAutoExposure(uint8_t newState, int triggerId);
     virtual void          notifyAutoWhitebalance(uint8_t newState,
index ebbd4ea..e80abf1 100644 (file)
@@ -138,9 +138,18 @@ class CameraDeviceBase : public virtual RefBase {
      */
     class NotificationListener {
       public:
-        // Refer to the Camera2 HAL definition for notification definitions
+        // The set of notifications is a merge of the notifications required for
+        // API1 and API2.
+
+        // Required for API 1 and 2
         virtual void notifyError(int errorCode, int arg1, int arg2) = 0;
-        virtual void notifyShutter(int frameNumber, nsecs_t timestamp) = 0;
+
+        // Required only for API2
+        virtual void notifyIdle() = 0;
+        virtual void notifyShutter(int requestId,
+                nsecs_t timestamp) = 0;
+
+        // Required only for API1
         virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0;
         virtual void notifyAutoExposure(uint8_t newState, int triggerId) = 0;
         virtual void notifyAutoWhitebalance(uint8_t newState,
@@ -165,12 +174,14 @@ class CameraDeviceBase : public virtual RefBase {
     /**
      * Wait for a new frame to be produced, with timeout in nanoseconds.
      * Returns TIMED_OUT when no frame produced within the specified duration
+     * May be called concurrently to most methods, except for getNextFrame
      */
     virtual status_t waitForNextFrame(nsecs_t timeout) = 0;
 
     /**
      * Get next metadata frame from the frame queue. Returns NULL if the queue
      * is empty; caller takes ownership of the metadata buffer.
+     * May be called concurrently to most methods, except for waitForNextFrame
      */
     virtual status_t getNextFrame(CameraMetadata *frame) = 0;
 
index 1e46beb..f96caff 100644 (file)
@@ -39,7 +39,7 @@ class FrameProcessorBase: public Thread {
     virtual ~FrameProcessorBase();
 
     struct FilteredListener: virtual public RefBase {
-        virtual void onFrameAvailable(int32_t frameId,
+        virtual void onFrameAvailable(int32_t requestId,
                                       const CameraMetadata &frame) = 0;
     };
 
index fe2cd77..2bc1a8a 100644 (file)
@@ -464,8 +464,10 @@ void Camera2Device::notificationCallback(int32_t msg_type,
                 listener->notifyError(ext1, ext2, ext3);
                 break;
             case CAMERA2_MSG_SHUTTER: {
-                nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 );
-                listener->notifyShutter(ext1, timestamp);
+                // TODO: Only needed for camera2 API, which is unsupported
+                // by HAL2 directly.
+                // nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 );
+                // listener->notifyShutter(requestId, timestamp);
                 break;
             }
             case CAMERA2_MSG_AUTOFOCUS:
index 2aa22a2..1f53c56 100644 (file)
@@ -28,6 +28,10 @@ namespace android {
 
 /**
  * CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_2_0
+ *
+ * TODO for camera2 API implementation:
+ * Does not produce notifyShutter / notifyIdle callbacks to NotificationListener
+ * Use waitUntilDrained for idle.
  */
 class Camera2Device: public CameraDeviceBase {
   public:
index b468eb3..ed6458c 100644 (file)
@@ -82,6 +82,7 @@ int Camera3Device::getId() const {
 status_t Camera3Device::initialize(camera_module_t *module)
 {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
     ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId);
@@ -159,9 +160,20 @@ status_t Camera3Device::initialize(camera_module_t *module)
         }
     }
 
+    /** Start up status tracker thread */
+    mStatusTracker = new StatusTracker(this);
+    res = mStatusTracker->run(String8::format("C3Dev-%d-Status", mId).string());
+    if (res != OK) {
+        SET_ERR_L("Unable to start status tracking thread: %s (%d)",
+                strerror(-res), res);
+        device->common.close(&device->common);
+        mStatusTracker.clear();
+        return res;
+    }
+
     /** Start up request queue thread */
 
-    mRequestThread = new RequestThread(this, device);
+    mRequestThread = new RequestThread(this, mStatusTracker, device);
     res = mRequestThread->run(String8::format("C3Dev-%d-ReqQueue", mId).string());
     if (res != OK) {
         SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -175,81 +187,130 @@ status_t Camera3Device::initialize(camera_module_t *module)
 
     mDeviceInfo = info.static_camera_characteristics;
     mHal3Device = device;
-    mStatus = STATUS_IDLE;
+    mStatus = STATUS_UNCONFIGURED;
     mNextStreamId = 0;
     mNeedConfig = true;
+    mPauseStateNotify = false;
 
     return OK;
 }
 
 status_t Camera3Device::disconnect() {
     ATRACE_CALL();
-    Mutex::Autolock l(mLock);
+    Mutex::Autolock il(mInterfaceLock);
 
     ALOGV("%s: E", __FUNCTION__);
 
     status_t res = OK;
-    if (mStatus == STATUS_UNINITIALIZED) return res;
 
-    if (mStatus == STATUS_ACTIVE ||
-            (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
-        res = mRequestThread->clearRepeatingRequests();
-        if (res != OK) {
-            SET_ERR_L("Can't stop streaming");
-            // Continue to close device even in case of error
-        } else {
-            res = waitUntilDrainedLocked();
+    {
+        Mutex::Autolock l(mLock);
+        if (mStatus == STATUS_UNINITIALIZED) return res;
+
+        if (mStatus == STATUS_ACTIVE ||
+                (mStatus == STATUS_ERROR && mRequestThread != NULL)) {
+            res = mRequestThread->clearRepeatingRequests();
             if (res != OK) {
-                SET_ERR_L("Timeout waiting for HAL to drain");
+                SET_ERR_L("Can't stop streaming");
                 // Continue to close device even in case of error
+            } else {
+                res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+                if (res != OK) {
+                    SET_ERR_L("Timeout waiting for HAL to drain");
+                    // Continue to close device even in case of error
+                }
             }
         }
+
+        if (mStatus == STATUS_ERROR) {
+            CLOGE("Shutting down in an error state");
+        }
+
+        if (mStatusTracker != NULL) {
+            mStatusTracker->requestExit();
+        }
+
+        if (mRequestThread != NULL) {
+            mRequestThread->requestExit();
+        }
+
+        mOutputStreams.clear();
+        mInputStream.clear();
     }
-    assert(mStatus == STATUS_IDLE || mStatus == STATUS_ERROR);
 
-    if (mStatus == STATUS_ERROR) {
-        CLOGE("Shutting down in an error state");
+    // Joining done without holding mLock, otherwise deadlocks may ensue
+    // as the threads try to access parent state
+    if (mRequestThread != NULL && mStatus != STATUS_ERROR) {
+        // HAL may be in a bad state, so waiting for request thread
+        // (which may be stuck in the HAL processCaptureRequest call)
+        // could be dangerous.
+        mRequestThread->join();
     }
 
-    if (mRequestThread != NULL) {
-        mRequestThread->requestExit();
+    if (mStatusTracker != NULL) {
+        mStatusTracker->join();
     }
 
-    mOutputStreams.clear();
-    mInputStream.clear();
+    {
+        Mutex::Autolock l(mLock);
 
-    if (mRequestThread != NULL) {
-        if (mStatus != STATUS_ERROR) {
-            // HAL may be in a bad state, so waiting for request thread
-            // (which may be stuck in the HAL processCaptureRequest call)
-            // could be dangerous.
-            mRequestThread->join();
-        }
         mRequestThread.clear();
-    }
+        mStatusTracker.clear();
 
-    if (mHal3Device != NULL) {
-        mHal3Device->common.close(&mHal3Device->common);
-        mHal3Device = NULL;
-    }
+        if (mHal3Device != NULL) {
+            mHal3Device->common.close(&mHal3Device->common);
+            mHal3Device = NULL;
+        }
 
-    mStatus = STATUS_UNINITIALIZED;
+        mStatus = STATUS_UNINITIALIZED;
+    }
 
     ALOGV("%s: X", __FUNCTION__);
     return res;
 }
 
+// For dumping/debugging only -
+// try to acquire a lock a few times, eventually give up to proceed with
+// debug/dump operations
+bool Camera3Device::tryLockSpinRightRound(Mutex& lock) {
+    bool gotLock = false;
+    for (size_t i = 0; i < kDumpLockAttempts; ++i) {
+        if (lock.tryLock() == NO_ERROR) {
+            gotLock = true;
+            break;
+        } else {
+            usleep(kDumpSleepDuration);
+        }
+    }
+    return gotLock;
+}
+
 status_t Camera3Device::dump(int fd, const Vector<String16> &args) {
     ATRACE_CALL();
     (void)args;
+
+    // Try to lock, but continue in case of failure (to avoid blocking in
+    // deadlocks)
+    bool gotInterfaceLock = tryLockSpinRightRound(mInterfaceLock);
+    bool gotLock = tryLockSpinRightRound(mLock);
+
+    ALOGW_IF(!gotInterfaceLock,
+            "Camera %d: %s: Unable to lock interface lock, proceeding anyway",
+            mId, __FUNCTION__);
+    ALOGW_IF(!gotLock,
+            "Camera %d: %s: Unable to lock main lock, proceeding anyway",
+            mId, __FUNCTION__);
+
     String8 lines;
 
     const char *status =
             mStatus == STATUS_ERROR         ? "ERROR" :
             mStatus == STATUS_UNINITIALIZED ? "UNINITIALIZED" :
-            mStatus == STATUS_IDLE          ? "IDLE" :
+            mStatus == STATUS_UNCONFIGURED  ? "UNCONFIGURED" :
+            mStatus == STATUS_CONFIGURED    ? "CONFIGURED" :
             mStatus == STATUS_ACTIVE        ? "ACTIVE" :
             "Unknown";
+
     lines.appendFormat("    Device status: %s\n", status);
     if (mStatus == STATUS_ERROR) {
         lines.appendFormat("    Error cause: %s\n", mErrorCause.string());
@@ -285,7 +346,7 @@ status_t Camera3Device::dump(int fd, const Vector<String16> &args) {
         lines = String8("    Last request sent:\n");
         write(fd, lines.string(), lines.size());
 
-        CameraMetadata lastRequest = getLatestRequest();
+        CameraMetadata lastRequest = getLatestRequestLocked();
         lastRequest.dump(fd, /*verbosity*/2, /*indentation*/6);
     }
 
@@ -295,6 +356,9 @@ status_t Camera3Device::dump(int fd, const Vector<String16> &args) {
         mHal3Device->ops->dump(mHal3Device, fd);
     }
 
+    if (gotLock) mLock.unlock();
+    if (gotInterfaceLock) mInterfaceLock.unlock();
+
     return OK;
 }
 
@@ -311,6 +375,8 @@ const CameraMetadata& Camera3Device::info() const {
 
 status_t Camera3Device::capture(CameraMetadata &request) {
     ATRACE_CALL();
+    status_t res;
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
     // TODO: take ownership of the request
@@ -322,7 +388,9 @@ status_t Camera3Device::capture(CameraMetadata &request) {
         case STATUS_UNINITIALIZED:
             CLOGE("Device not initialized");
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+            // May be lazily configuring streams, will check during setup
+        case STATUS_CONFIGURED:
         case STATUS_ACTIVE:
             // OK
             break;
@@ -337,12 +405,23 @@ status_t Camera3Device::capture(CameraMetadata &request) {
         return BAD_VALUE;
     }
 
-    return mRequestThread->queueRequest(newRequest);
+    res = mRequestThread->queueRequest(newRequest);
+    if (res == OK) {
+        waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+        if (res != OK) {
+            SET_ERR_L("Can't transition to active in %f seconds!",
+                    kActiveTimeout/1e9);
+        }
+        ALOGV("Camera %d: Capture request enqueued", mId);
+    }
+    return res;
 }
 
 
 status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) {
     ATRACE_CALL();
+    status_t res;
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
     switch (mStatus) {
@@ -352,7 +431,9 @@ status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) {
         case STATUS_UNINITIALIZED:
             CLOGE("Device not initialized");
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+            // May be lazily configuring streams, will check during setup
+        case STATUS_CONFIGURED:
         case STATUS_ACTIVE:
             // OK
             break;
@@ -370,7 +451,16 @@ status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) {
     RequestList newRepeatingRequests;
     newRepeatingRequests.push_back(newRepeatingRequest);
 
-    return mRequestThread->setRepeatingRequests(newRepeatingRequests);
+    res = mRequestThread->setRepeatingRequests(newRepeatingRequests);
+    if (res == OK) {
+        waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+        if (res != OK) {
+            SET_ERR_L("Can't transition to active in %f seconds!",
+                    kActiveTimeout/1e9);
+        }
+        ALOGV("Camera %d: Repeating request set", mId);
+    }
+    return res;
 }
 
 
@@ -378,12 +468,16 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
         const CameraMetadata &request) {
     status_t res;
 
-    if (mStatus == STATUS_IDLE) {
+    if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
         res = configureStreamsLocked();
         if (res != OK) {
             SET_ERR_L("Can't set up streams: %s (%d)", strerror(-res), res);
             return NULL;
         }
+        if (mStatus == STATUS_UNCONFIGURED) {
+            CLOGE("No streams configured");
+            return NULL;
+        }
     }
 
     sp<CaptureRequest> newRequest = createCaptureRequest(request);
@@ -392,6 +486,7 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked(
 
 status_t Camera3Device::clearStreamingRequest() {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
     switch (mStatus) {
@@ -401,7 +496,8 @@ status_t Camera3Device::clearStreamingRequest() {
         case STATUS_UNINITIALIZED:
             CLOGE("Device not initialized");
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+        case STATUS_CONFIGURED:
         case STATUS_ACTIVE:
             // OK
             break;
@@ -409,12 +505,13 @@ status_t Camera3Device::clearStreamingRequest() {
             SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
-
+    ALOGV("Camera %d: Clearing repeating request", mId);
     return mRequestThread->clearRepeatingRequests();
 }
 
 status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
 
     return mRequestThread->waitUntilRequestProcessed(requestId, timeout);
 }
@@ -422,7 +519,10 @@ status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t time
 status_t Camera3Device::createInputStream(
         uint32_t width, uint32_t height, int format, int *id) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
+    ALOGV("Camera %d: Creating new input stream %d: %d x %d, format %d",
+            mId, mNextStreamId, width, height, format);
 
     status_t res;
     bool wasActive = false;
@@ -434,26 +534,24 @@ status_t Camera3Device::createInputStream(
         case STATUS_UNINITIALIZED:
             ALOGE("%s: Device not initialized", __FUNCTION__);
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+        case STATUS_CONFIGURED:
             // OK
             break;
         case STATUS_ACTIVE:
             ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
-            mRequestThread->setPaused(true);
-            res = waitUntilDrainedLocked();
+            res = internalPauseAndWaitLocked();
             if (res != OK) {
-                ALOGE("%s: Can't pause captures to reconfigure streams!",
-                        __FUNCTION__);
-                mStatus = STATUS_ERROR;
+                SET_ERR_L("Can't pause captures to reconfigure streams!");
                 return res;
             }
             wasActive = true;
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("%s: Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
-    assert(mStatus == STATUS_IDLE);
+    assert(mStatus != STATUS_ACTIVE);
 
     if (mInputStream != 0) {
         ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__);
@@ -462,6 +560,7 @@ status_t Camera3Device::createInputStream(
 
     sp<Camera3InputStream> newStream = new Camera3InputStream(mNextStreamId,
                 width, height, format);
+    newStream->setStatusTracker(mStatusTracker);
 
     mInputStream = newStream;
 
@@ -476,9 +575,10 @@ status_t Camera3Device::createInputStream(
                     __FUNCTION__, mNextStreamId, strerror(-res), res);
             return res;
         }
-        mRequestThread->setPaused(false);
+        internalResumeLocked();
     }
 
+    ALOGV("Camera %d: Created input stream", mId);
     return OK;
 }
 
@@ -490,7 +590,10 @@ status_t Camera3Device::createZslStream(
             int *id,
             sp<Camera3ZslStream>* zslStream) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
+    ALOGV("Camera %d: Creating ZSL stream %d: %d x %d, depth %d",
+            mId, mNextStreamId, width, height, depth);
 
     status_t res;
     bool wasActive = false;
@@ -502,26 +605,24 @@ status_t Camera3Device::createZslStream(
         case STATUS_UNINITIALIZED:
             ALOGE("%s: Device not initialized", __FUNCTION__);
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+        case STATUS_CONFIGURED:
             // OK
             break;
         case STATUS_ACTIVE:
             ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
-            mRequestThread->setPaused(true);
-            res = waitUntilDrainedLocked();
+            res = internalPauseAndWaitLocked();
             if (res != OK) {
-                ALOGE("%s: Can't pause captures to reconfigure streams!",
-                        __FUNCTION__);
-                mStatus = STATUS_ERROR;
+                SET_ERR_L("Can't pause captures to reconfigure streams!");
                 return res;
             }
             wasActive = true;
             break;
         default:
-            ALOGE("%s: Unexpected status: %d", __FUNCTION__, mStatus);
+            SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
-    assert(mStatus == STATUS_IDLE);
+    assert(mStatus != STATUS_ACTIVE);
 
     if (mInputStream != 0) {
         ALOGE("%s: Cannot create more than 1 input stream", __FUNCTION__);
@@ -530,6 +631,7 @@ status_t Camera3Device::createZslStream(
 
     sp<Camera3ZslStream> newStream = new Camera3ZslStream(mNextStreamId,
                 width, height, depth);
+    newStream->setStatusTracker(mStatusTracker);
 
     res = mOutputStreams.add(mNextStreamId, newStream);
     if (res < 0) {
@@ -551,16 +653,20 @@ status_t Camera3Device::createZslStream(
                     __FUNCTION__, mNextStreamId, strerror(-res), res);
             return res;
         }
-        mRequestThread->setPaused(false);
+        internalResumeLocked();
     }
 
+    ALOGV("Camera %d: Created ZSL stream", mId);
     return OK;
 }
 
 status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
         uint32_t width, uint32_t height, int format, size_t size, int *id) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
+    ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, size %d",
+            mId, mNextStreamId, width, height, format, size);
 
     status_t res;
     bool wasActive = false;
@@ -572,16 +678,15 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
         case STATUS_UNINITIALIZED:
             CLOGE("Device not initialized");
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+        case STATUS_CONFIGURED:
             // OK
             break;
         case STATUS_ACTIVE:
             ALOGV("%s: Stopping activity to reconfigure streams", __FUNCTION__);
-            mRequestThread->setPaused(true);
-            res = waitUntilDrainedLocked();
+            res = internalPauseAndWaitLocked();
             if (res != OK) {
-                ALOGE("%s: Can't pause captures to reconfigure streams!",
-                        __FUNCTION__);
+                SET_ERR_L("Can't pause captures to reconfigure streams!");
                 return res;
             }
             wasActive = true;
@@ -590,7 +695,7 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
             SET_ERR_L("Unexpected status: %d", mStatus);
             return INVALID_OPERATION;
     }
-    assert(mStatus == STATUS_IDLE);
+    assert(mStatus != STATUS_ACTIVE);
 
     sp<Camera3OutputStream> newStream;
     if (format == HAL_PIXEL_FORMAT_BLOB) {
@@ -600,6 +705,7 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
         newStream = new Camera3OutputStream(mNextStreamId, consumer,
                 width, height, format);
     }
+    newStream->setStatusTracker(mStatusTracker);
 
     res = mOutputStreams.add(mNextStreamId, newStream);
     if (res < 0) {
@@ -619,9 +725,9 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
                     mNextStreamId, strerror(-res), res);
             return res;
         }
-        mRequestThread->setPaused(false);
+        internalResumeLocked();
     }
-
+    ALOGV("Camera %d: Created new stream", mId);
     return OK;
 }
 
@@ -637,6 +743,7 @@ status_t Camera3Device::createReprocessStreamFromStream(int outputId, int *id) {
 status_t Camera3Device::getStreamInfo(int id,
         uint32_t *width, uint32_t *height, uint32_t *format) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
     switch (mStatus) {
@@ -646,7 +753,8 @@ status_t Camera3Device::getStreamInfo(int id,
         case STATUS_UNINITIALIZED:
             CLOGE("Device not initialized!");
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+        case STATUS_CONFIGURED:
         case STATUS_ACTIVE:
             // OK
             break;
@@ -671,6 +779,7 @@ status_t Camera3Device::getStreamInfo(int id,
 status_t Camera3Device::setStreamTransform(int id,
         int transform) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
     switch (mStatus) {
@@ -680,7 +789,8 @@ status_t Camera3Device::setStreamTransform(int id,
         case STATUS_UNINITIALIZED:
             CLOGE("Device not initialized");
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+        case STATUS_CONFIGURED:
         case STATUS_ACTIVE:
             // OK
             break;
@@ -701,6 +811,7 @@ status_t Camera3Device::setStreamTransform(int id,
 
 status_t Camera3Device::deleteStream(int id) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
     status_t res;
 
@@ -708,7 +819,7 @@ status_t Camera3Device::deleteStream(int id) {
 
     // CameraDevice semantics require device to already be idle before
     // deleteStream is called, unlike for createStream.
-    if (mStatus != STATUS_IDLE) {
+    if (mStatus == STATUS_ACTIVE) {
         ALOGV("%s: Camera %d: Device not idle", __FUNCTION__, mId);
         return -EBUSY;
     }
@@ -752,6 +863,7 @@ status_t Camera3Device::createDefaultRequest(int templateId,
         CameraMetadata *request) {
     ATRACE_CALL();
     ALOGV("%s: for template %d", __FUNCTION__, templateId);
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
     switch (mStatus) {
@@ -761,7 +873,8 @@ status_t Camera3Device::createDefaultRequest(int templateId,
         case STATUS_UNINITIALIZED:
             CLOGE("Device is not initialized!");
             return INVALID_OPERATION;
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
+        case STATUS_CONFIGURED:
         case STATUS_ACTIVE:
             // OK
             break;
@@ -787,61 +900,88 @@ status_t Camera3Device::createDefaultRequest(int templateId,
 
 status_t Camera3Device::waitUntilDrained() {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
-    return waitUntilDrainedLocked();
-}
-
-status_t Camera3Device::waitUntilDrainedLocked() {
-    ATRACE_CALL();
-    status_t res;
-
     switch (mStatus) {
         case STATUS_UNINITIALIZED:
-        case STATUS_IDLE:
+        case STATUS_UNCONFIGURED:
             ALOGV("%s: Already idle", __FUNCTION__);
             return OK;
+        case STATUS_CONFIGURED:
+            // To avoid race conditions, check with tracker to be sure
         case STATUS_ERROR:
         case STATUS_ACTIVE:
-            // Need to shut down
+            // Need to verify shut down
             break;
         default:
             SET_ERR_L("Unexpected status: %d",mStatus);
             return INVALID_OPERATION;
     }
 
-    if (mRequestThread != NULL) {
-        res = mRequestThread->waitUntilPaused(kShutdownTimeout);
-        if (res != OK) {
-            SET_ERR_L("Can't stop request thread in %f seconds!",
-                    kShutdownTimeout/1e9);
-            return res;
-        }
-    }
-    if (mInputStream != NULL) {
-        res = mInputStream->waitUntilIdle(kShutdownTimeout);
-        if (res != OK) {
-            SET_ERR_L("Can't idle input stream %d in %f seconds!",
-                    mInputStream->getId(), kShutdownTimeout/1e9);
-            return res;
-        }
+    ALOGV("%s: Camera %d: Waiting until idle", __FUNCTION__, mId);
+    status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+    return res;
+}
+
+// Pause to reconfigure
+status_t Camera3Device::internalPauseAndWaitLocked() {
+    mRequestThread->setPaused(true);
+    mPauseStateNotify = true;
+
+    ALOGV("%s: Camera %d: Internal wait until idle", __FUNCTION__, mId);
+    status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+    if (res != OK) {
+        SET_ERR_L("Can't idle device in %f seconds!",
+                kShutdownTimeout/1e9);
     }
-    for (size_t i = 0; i < mOutputStreams.size(); i++) {
-        res = mOutputStreams.editValueAt(i)->waitUntilIdle(kShutdownTimeout);
-        if (res != OK) {
-            SET_ERR_L("Can't idle output stream %d in %f seconds!",
-                    mOutputStreams.keyAt(i), kShutdownTimeout/1e9);
-            return res;
-        }
+
+    return res;
+}
+
+// Resume after internalPauseAndWaitLocked
+status_t Camera3Device::internalResumeLocked() {
+    status_t res;
+
+    mRequestThread->setPaused(false);
+
+    res = waitUntilStateThenRelock(/*active*/ true, kActiveTimeout);
+    if (res != OK) {
+        SET_ERR_L("Can't transition to active in %f seconds!",
+                kActiveTimeout/1e9);
     }
+    mPauseStateNotify = false;
+    return OK;
+}
 
-    if (mStatus != STATUS_ERROR) {
-        mStatus = STATUS_IDLE;
+status_t Camera3Device::waitUntilStateThenRelock(bool active,
+        nsecs_t timeout) {
+    status_t res = OK;
+    if (active == (mStatus == STATUS_ACTIVE)) {
+        // Desired state already reached
+        return res;
     }
 
-    return OK;
+    bool stateSeen = false;
+    do {
+        mRecentStatusUpdates.clear();
+
+        res = mStatusChanged.waitRelative(mLock, timeout);
+        if (res != OK) break;
+
+        // Check state change history during wait
+        for (size_t i = 0; i < mRecentStatusUpdates.size(); i++) {
+            if (active == (mRecentStatusUpdates[i] == STATUS_ACTIVE) ) {
+                stateSeen = true;
+                break;
+            }
+        }
+    } while (!stateSeen);
+
+    return res;
 }
 
+
 status_t Camera3Device::setNotifyCallback(NotificationListener *listener) {
     ATRACE_CALL();
     Mutex::Autolock l(mOutputLock);
@@ -893,6 +1033,7 @@ status_t Camera3Device::getNextFrame(CameraMetadata *frame) {
 
 status_t Camera3Device::triggerAutofocus(uint32_t id) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
 
     ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id);
     // Mix-in this trigger into the next request and only the next request.
@@ -913,6 +1054,7 @@ status_t Camera3Device::triggerAutofocus(uint32_t id) {
 
 status_t Camera3Device::triggerCancelAutofocus(uint32_t id) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
 
     ALOGV("%s: Triggering cancel autofocus, id %d", __FUNCTION__, id);
     // Mix-in this trigger into the next request and only the next request.
@@ -933,6 +1075,7 @@ status_t Camera3Device::triggerCancelAutofocus(uint32_t id) {
 
 status_t Camera3Device::triggerPrecaptureMetering(uint32_t id) {
     ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
 
     ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id);
     // Mix-in this trigger into the next request and only the next request.
@@ -963,7 +1106,7 @@ status_t Camera3Device::pushReprocessBuffer(int reprocessStreamId,
 status_t Camera3Device::flush() {
     ATRACE_CALL();
     ALOGV("%s: Camera %d: Flushing all requests", __FUNCTION__, mId);
-
+    Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
     mRequestThread->clear();
@@ -971,6 +1114,41 @@ status_t Camera3Device::flush() {
 }
 
 /**
+ * Methods called by subclasses
+ */
+
+void Camera3Device::notifyStatus(bool idle) {
+    {
+        // Need mLock to safely update state and synchronize to current
+        // state of methods in flight.
+        Mutex::Autolock l(mLock);
+        // We can get various system-idle notices from the status tracker
+        // while starting up. Only care about them if we've actually sent
+        // in some requests recently.
+        if (mStatus != STATUS_ACTIVE && mStatus != STATUS_CONFIGURED) {
+            return;
+        }
+        ALOGV("%s: Camera %d: Now %s", __FUNCTION__, mId,
+                idle ? "idle" : "active");
+        mStatus = idle ? STATUS_CONFIGURED : STATUS_ACTIVE;
+        mRecentStatusUpdates.add(mStatus);
+        mStatusChanged.signal();
+
+        // Skip notifying listener if we're doing some user-transparent
+        // state changes
+        if (mPauseStateNotify) return;
+    }
+    NotificationListener *listener;
+    {
+        Mutex::Autolock l(mOutputLock);
+        listener = mListener;
+    }
+    if (idle && listener != NULL) {
+        listener->notifyIdle();
+    }
+}
+
+/**
  * Camera3Device private methods
  */
 
@@ -1046,18 +1224,18 @@ status_t Camera3Device::configureStreamsLocked() {
     ATRACE_CALL();
     status_t res;
 
-    if (mStatus != STATUS_IDLE) {
+    if (mStatus != STATUS_UNCONFIGURED && mStatus != STATUS_CONFIGURED) {
         CLOGE("Not idle");
         return INVALID_OPERATION;
     }
 
     if (!mNeedConfig) {
         ALOGV("%s: Skipping config, no stream changes", __FUNCTION__);
-        mStatus = STATUS_ACTIVE;
         return OK;
     }
 
     // Start configuring the streams
+    ALOGV("%s: Camera %d: Starting stream configuration", __FUNCTION__, mId);
 
     camera3_stream_configuration config;
 
@@ -1139,11 +1317,18 @@ status_t Camera3Device::configureStreamsLocked() {
     // across configure_streams() calls
     mRequestThread->configurationComplete();
 
-    // Finish configuring the streams lazily on first reference
+    // Update device state
 
-    mStatus = STATUS_ACTIVE;
     mNeedConfig = false;
 
+    if (config.num_streams > 0) {
+        mStatus = STATUS_CONFIGURED;
+    } else {
+        mStatus = STATUS_UNCONFIGURED;
+    }
+
+    ALOGV("%s: Camera %d: Stream configuration complete", __FUNCTION__, mId);
+
     return OK;
 }
 
@@ -1190,12 +1375,12 @@ void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) {
  */
 
 status_t Camera3Device::registerInFlight(int32_t frameNumber,
-        int32_t numBuffers) {
+        int32_t requestId, int32_t numBuffers) {
     ATRACE_CALL();
     Mutex::Autolock l(mInFlightLock);
 
     ssize_t res;
-    res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers));
+    res = mInFlightMap.add(frameNumber, InFlightRequest(requestId, numBuffers));
     if (res < 0) return res;
 
     return OK;
@@ -1378,12 +1563,17 @@ void Camera3Device::notify(const camera3_notify_msg *msg) {
                 mNextShutterFrameNumber++;
             }
 
+            int32_t requestId = -1;
+
             // Set timestamp for the request in the in-flight tracking
+            // and get the request ID to send upstream
             {
                 Mutex::Autolock l(mInFlightLock);
                 idx = mInFlightMap.indexOfKey(frameNumber);
                 if (idx >= 0) {
-                    mInFlightMap.editValueAt(idx).captureTimestamp = timestamp;
+                    InFlightRequest &r = mInFlightMap.editValueAt(idx);
+                    r.captureTimestamp = timestamp;
+                    requestId = r.requestId;
                 }
             }
             if (idx < 0) {
@@ -1391,11 +1581,11 @@ void Camera3Device::notify(const camera3_notify_msg *msg) {
                         frameNumber);
                 break;
             }
-            ALOGVV("Camera %d: %s: Shutter fired for frame %d at %lld",
-                    mId, __FUNCTION__, frameNumber, timestamp);
+            ALOGVV("Camera %d: %s: Shutter fired for frame %d (id %d) at %lld",
+                    mId, __FUNCTION__, frameNumber, requestId, timestamp);
             // Call listener, if any
             if (listener != NULL) {
-                listener->notifyShutter(frameNumber, timestamp);
+                listener->notifyShutter(requestId, timestamp);
             }
             break;
         }
@@ -1405,40 +1595,15 @@ void Camera3Device::notify(const camera3_notify_msg *msg) {
     }
 }
 
-CameraMetadata Camera3Device::getLatestRequest() {
+CameraMetadata Camera3Device::getLatestRequestLocked() {
     ALOGV("%s", __FUNCTION__);
 
-    bool locked = false;
-
-    /**
-     * Why trylock instead of autolock?
-     *
-     * We want to be able to call this function from
-     * dumpsys, which often happens during deadlocks.
-     */
-    for (size_t i = 0; i < kDumpLockAttempts; ++i) {
-        if (mLock.tryLock() == NO_ERROR) {
-            locked = true;
-            break;
-        } else {
-            usleep(kDumpSleepDuration);
-        }
-    }
-
-    if (!locked) {
-        ALOGW("%s: Possible deadlock detected", __FUNCTION__);
-    }
-
     CameraMetadata retVal;
 
     if (mRequestThread != NULL) {
         retVal = mRequestThread->getLatestRequest();
     }
 
-    if (locked) {
-        mLock.unlock();
-    }
-
     return retVal;
 }
 
@@ -1447,9 +1612,11 @@ CameraMetadata Camera3Device::getLatestRequest() {
  */
 
 Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
+        sp<StatusTracker> statusTracker,
         camera3_device_t *hal3Device) :
         Thread(false),
         mParent(parent),
+        mStatusTracker(statusTracker),
         mHal3Device(hal3Device),
         mId(getId(parent)),
         mReconfigured(false),
@@ -1457,6 +1624,7 @@ Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
         mPaused(true),
         mFrameNumber(0),
         mLatestRequestId(NAME_NOT_FOUND) {
+    mStatusId = statusTracker->addComponent();
 }
 
 void Camera3Device::RequestThread::configurationComplete() {
@@ -1562,19 +1730,6 @@ void Camera3Device::RequestThread::setPaused(bool paused) {
     mDoPauseSignal.signal();
 }
 
-status_t Camera3Device::RequestThread::waitUntilPaused(nsecs_t timeout) {
-    ATRACE_CALL();
-    status_t res;
-    Mutex::Autolock l(mPauseLock);
-    while (!mPaused) {
-        res = mPausedSignal.waitRelative(mPauseLock, timeout);
-        if (res == TIMED_OUT) {
-            return res;
-        }
-    }
-    return OK;
-}
-
 status_t Camera3Device::RequestThread::waitUntilRequestProcessed(
         int32_t requestId, nsecs_t timeout) {
     Mutex::Autolock l(mLatestRequestMutex);
@@ -1591,7 +1746,13 @@ status_t Camera3Device::RequestThread::waitUntilRequestProcessed(
     return OK;
 }
 
-
+void Camera3Device::RequestThread::requestExit() {
+    // Call parent to set up shutdown
+    Thread::requestExit();
+    // The exit from any possible waits
+    mDoPauseSignal.signal();
+    mRequestSignal.signal();
+}
 
 bool Camera3Device::RequestThread::threadLoop() {
 
@@ -1613,6 +1774,18 @@ bool Camera3Device::RequestThread::threadLoop() {
     camera3_capture_request_t request = camera3_capture_request_t();
     Vector<camera3_stream_buffer_t> outputBuffers;
 
+    // Get the request ID, if any
+    int requestId;
+    camera_metadata_entry_t requestIdEntry =
+            nextRequest->mSettings.find(ANDROID_REQUEST_ID);
+    if (requestIdEntry.count > 0) {
+        requestId = requestIdEntry.data.i32[0];
+    } else {
+        ALOGW("%s: Did not have android.request.id set in the request",
+                __FUNCTION__);
+        requestId = NAME_NOT_FOUND;
+    }
+
     // Insert any queued triggers (before metadata is locked)
     int32_t triggerCount;
     res = insertTriggers(nextRequest);
@@ -1713,7 +1886,7 @@ bool Camera3Device::RequestThread::threadLoop() {
         return false;
     }
 
-    res = parent->registerInFlight(request.frame_number,
+    res = parent->registerInFlight(request.frame_number, requestId,
             request.num_output_buffers);
     if (res != OK) {
         SET_ERR("RequestThread: Unable to register new in-flight request:"
@@ -1762,16 +1935,7 @@ bool Camera3Device::RequestThread::threadLoop() {
     {
         Mutex::Autolock al(mLatestRequestMutex);
 
-        camera_metadata_entry_t requestIdEntry =
-                nextRequest->mSettings.find(ANDROID_REQUEST_ID);
-        if (requestIdEntry.count > 0) {
-            mLatestRequestId = requestIdEntry.data.i32[0];
-        } else {
-            ALOGW("%s: Did not have android.request.id set in the request",
-                  __FUNCTION__);
-            mLatestRequestId = NAME_NOT_FOUND;
-        }
-
+        mLatestRequestId = requestId;
         mLatestRequestSignal.signal();
     }
 
@@ -1790,8 +1954,6 @@ bool Camera3Device::RequestThread::threadLoop() {
         }
     }
 
-
-
     return true;
 }
 
@@ -1849,12 +2011,17 @@ sp<Camera3Device::CaptureRequest>
 
         res = mRequestSignal.waitRelative(mRequestLock, kRequestTimeout);
 
-        if (res == TIMED_OUT) {
-            // Signal that we're paused by starvation
+        if ((mRequestQueue.empty() && mRepeatingRequests.empty()) ||
+                exitPending()) {
             Mutex::Autolock pl(mPauseLock);
             if (mPaused == false) {
+                ALOGV("%s: RequestThread: Going idle", __FUNCTION__);
                 mPaused = true;
-                mPausedSignal.signal();
+                // Let the tracker know
+                sp<StatusTracker> statusTracker = mStatusTracker.promote();
+                if (statusTracker != 0) {
+                    statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+                }
             }
             // Stop waiting for now and let thread management happen
             return NULL;
@@ -1874,6 +2041,13 @@ sp<Camera3Device::CaptureRequest>
     // update internal pause state (capture/setRepeatingRequest unpause
     // directly).
     Mutex::Autolock pl(mPauseLock);
+    if (mPaused) {
+        ALOGV("%s: RequestThread: Unpaused", __FUNCTION__);
+        sp<StatusTracker> statusTracker = mStatusTracker.promote();
+        if (statusTracker != 0) {
+            statusTracker->markComponentActive(mStatusId);
+        }
+    }
     mPaused = false;
 
     // Check if we've reconfigured since last time, and reset the preview
@@ -1890,13 +2064,18 @@ bool Camera3Device::RequestThread::waitIfPaused() {
     status_t res;
     Mutex::Autolock l(mPauseLock);
     while (mDoPause) {
-        // Signal that we're paused by request
         if (mPaused == false) {
             mPaused = true;
-            mPausedSignal.signal();
+            ALOGV("%s: RequestThread: Paused", __FUNCTION__);
+            // Let the tracker know
+            sp<StatusTracker> statusTracker = mStatusTracker.promote();
+            if (statusTracker != 0) {
+                statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+            }
         }
+
         res = mDoPauseSignal.waitRelative(mPauseLock, kRequestTimeout);
-        if (res == TIMED_OUT) {
+        if (res == TIMED_OUT || exitPending()) {
             return true;
         }
     }
@@ -1909,8 +2088,16 @@ void Camera3Device::RequestThread::unpauseForNewRequests() {
     // With work to do, mark thread as unpaused.
     // If paused by request (setPaused), don't resume, to avoid
     // extra signaling/waiting overhead to waitUntilPaused
+    mRequestSignal.signal();
     Mutex::Autolock p(mPauseLock);
     if (!mDoPause) {
+        ALOGV("%s: RequestThread: Going active", __FUNCTION__);
+        if (mPaused) {
+            sp<StatusTracker> statusTracker = mStatusTracker.promote();
+            if (statusTracker != 0) {
+                statusTracker->markComponentActive(mStatusId);
+            }
+        }
         mPaused = false;
     }
 }
index 61caf13..6295c80 100644 (file)
@@ -26,6 +26,7 @@
 #include <hardware/camera3.h>
 
 #include "common/CameraDeviceBase.h"
+#include "device3/StatusTracker.h"
 
 /**
  * Function pointer types with C calling convention to
@@ -126,29 +127,47 @@ class Camera3Device :
 
     virtual status_t flush();
 
+    // Methods called by subclasses
+    void             notifyStatus(bool idle); // updates from StatusTracker
+
   private:
     static const size_t        kDumpLockAttempts  = 10;
     static const size_t        kDumpSleepDuration = 100000; // 0.10 sec
     static const size_t        kInFlightWarnLimit = 20;
     static const nsecs_t       kShutdownTimeout   = 5000000000; // 5 sec
+    static const nsecs_t       kActiveTimeout     = 500000000;  // 500 ms
     struct                     RequestTrigger;
 
+    // A lock to enforce serialization on the input/configure side
+    // of the public interface.
+    // Only locked by public methods inherited from CameraDeviceBase.
+    // Not locked by methods guarded by mOutputLock, since they may act
+    // concurrently to the input/configure side of the interface.
+    // Must be locked before mLock if both will be locked by a method
+    Mutex                      mInterfaceLock;
+
+    // The main lock on internal state
     Mutex                      mLock;
 
+    // Camera device ID
+    const int                  mId;
+
     /**** Scope for mLock ****/
 
-    const int                  mId;
     camera3_device_t          *mHal3Device;
 
     CameraMetadata             mDeviceInfo;
     vendor_tag_query_ops_t     mVendorTagOps;
 
-    enum {
+    enum Status {
         STATUS_ERROR,
         STATUS_UNINITIALIZED,
-        STATUS_IDLE,
+        STATUS_UNCONFIGURED,
+        STATUS_CONFIGURED,
         STATUS_ACTIVE
     }                          mStatus;
+    Vector<Status>             mRecentStatusUpdates;
+    Condition                  mStatusChanged;
 
     // Tracking cause of fatal errors when in STATUS_ERROR
     String8                    mErrorCause;
@@ -162,6 +181,10 @@ class Camera3Device :
     int                        mNextStreamId;
     bool                       mNeedConfig;
 
+    // Whether to send state updates upstream
+    // Pause when doing transparent reconfiguration
+    bool                       mPauseStateNotify;
+
     // Need to hold on to stream references until configure completes.
     Vector<sp<camera3::Camera3StreamInterface> > mDeletedStreams;
 
@@ -181,13 +204,34 @@ class Camera3Device :
      *
      * Takes mLock.
      */
-    virtual CameraMetadata getLatestRequest();
+    virtual CameraMetadata getLatestRequestLocked();
+
+    /**
+     * Pause processing and flush everything, but don't tell the clients.
+     * This is for reconfiguring outputs transparently when according to the
+     * CameraDeviceBase interface we shouldn't need to.
+     * Must be called with mLock and mInterfaceLock both held.
+     */
+    status_t internalPauseAndWaitLocked();
 
     /**
-     * Lock-held version of waitUntilDrained. Will transition to IDLE on
-     * success.
+     * Resume work after internalPauseAndWaitLocked()
+     * Must be called with mLock and mInterfaceLock both held.
      */
-    status_t           waitUntilDrainedLocked();
+    status_t internalResumeLocked();
+
+    /**
+     * Wait until status tracker tells us we've transitioned to the target state
+     * set, which is either ACTIVE when active==true or IDLE (which is any
+     * non-ACTIVE state) when active==false.
+     *
+     * Needs to be called with mLock and mInterfaceLock held.  This means there
+     * can ever only be one waiter at most.
+     *
+     * During the wait mLock is released.
+     *
+     */
+    status_t waitUntilStateThenRelock(bool active, nsecs_t timeout);
 
     /**
      * Do common work for setting up a streaming or single capture request.
@@ -217,6 +261,12 @@ class Camera3Device :
     void               setErrorStateLocked(const char *fmt, ...);
     void               setErrorStateLockedV(const char *fmt, va_list args);
 
+    /**
+     * Debugging trylock/spin method
+     * Try to acquire a lock a few times with sleeps between before giving up.
+     */
+    bool               tryLockSpinRightRound(Mutex& lock);
+
     struct RequestTrigger {
         // Metadata tag number, e.g. android.control.aePrecaptureTrigger
         uint32_t metadataTag;
@@ -242,6 +292,7 @@ class Camera3Device :
       public:
 
         RequestThread(wp<Camera3Device> parent,
+                sp<camera3::StatusTracker> statusTracker,
                 camera3_device_t *hal3Device);
 
         /**
@@ -279,13 +330,6 @@ class Camera3Device :
         void     setPaused(bool paused);
 
         /**
-         * Wait until thread is paused, either due to setPaused(true)
-         * or due to lack of input requests. Returns TIMED_OUT in case
-         * the thread does not pause within the timeout.
-         */
-        status_t waitUntilPaused(nsecs_t timeout);
-
-        /**
          * Wait until thread processes the capture request with settings'
          * android.request.id == requestId.
          *
@@ -295,6 +339,12 @@ class Camera3Device :
         status_t waitUntilRequestProcessed(int32_t requestId, nsecs_t timeout);
 
         /**
+         * Shut down the thread. Shutdown is asynchronous, so thread may
+         * still be running once this method returns.
+         */
+        virtual void requestExit();
+
+        /**
          * Get the latest request that was sent to the HAL
          * with process_capture_request.
          */
@@ -339,9 +389,12 @@ class Camera3Device :
         void               setErrorState(const char *fmt, ...);
 
         wp<Camera3Device>  mParent;
+        wp<camera3::StatusTracker>  mStatusTracker;
         camera3_device_t  *mHal3Device;
 
-        const int          mId;
+        const int          mId;       // The camera ID
+        int                mStatusId; // The RequestThread's component ID for
+                                      // status tracking
 
         Mutex              mRequestLock;
         Condition          mRequestSignal;
@@ -381,6 +434,8 @@ class Camera3Device :
      */
 
     struct InFlightRequest {
+        // android.request.id for the request
+        int     requestId;
         // Set by notify() SHUTTER call.
         nsecs_t captureTimestamp;
         // Set by process_capture_result call with valid metadata
@@ -389,13 +444,16 @@ class Camera3Device :
         // buffers
         int     numBuffersLeft;
 
+        // Default constructor needed by KeyedVector
         InFlightRequest() :
+                requestId(0),
                 captureTimestamp(0),
                 haveResultMetadata(false),
                 numBuffersLeft(0) {
         }
 
-        explicit InFlightRequest(int numBuffers) :
+        InFlightRequest(int id, int numBuffers) :
+                requestId(id),
                 captureTimestamp(0),
                 haveResultMetadata(false),
                 numBuffersLeft(numBuffers) {
@@ -407,7 +465,13 @@ class Camera3Device :
     Mutex                  mInFlightLock; // Protects mInFlightMap
     InFlightMap            mInFlightMap;
 
-    status_t registerInFlight(int32_t frameNumber, int32_t numBuffers);
+    status_t registerInFlight(int32_t frameNumber, int32_t requestId,
+            int32_t numBuffers);
+
+    /**
+     * Tracking for idle detection
+     */
+    sp<camera3::StatusTracker> mStatusTracker;
 
     /**
      * Output result queue and current HAL device 3A state
index 0850566..727a8c9 100644 (file)
@@ -23,7 +23,8 @@
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
-#include "Camera3IOStreamBase.h"
+#include "device3/Camera3IOStreamBase.h"
+#include "device3/StatusTracker.h"
 
 namespace android {
 
@@ -62,53 +63,6 @@ bool Camera3IOStreamBase::hasOutstandingBuffersLocked() const {
     return false;
 }
 
-status_t Camera3IOStreamBase::waitUntilIdle(nsecs_t timeout) {
-    status_t res;
-    {
-        Mutex::Autolock l(mLock);
-        while (mDequeuedBufferCount > 0) {
-            if (timeout != TIMEOUT_NEVER) {
-                nsecs_t startTime = systemTime();
-                res = mBufferReturnedSignal.waitRelative(mLock, timeout);
-                if (res == TIMED_OUT) {
-                    return res;
-                } else if (res != OK) {
-                    ALOGE("%s: Error waiting for outstanding buffers: %s (%d)",
-                            __FUNCTION__, strerror(-res), res);
-                    return res;
-                }
-                nsecs_t deltaTime = systemTime() - startTime;
-                if (timeout <= deltaTime) {
-                    timeout = 0;
-                } else {
-                    timeout -= deltaTime;
-                }
-            } else {
-                res = mBufferReturnedSignal.wait(mLock);
-                if (res != OK) {
-                    ALOGE("%s: Error waiting for outstanding buffers: %s (%d)",
-                            __FUNCTION__, strerror(-res), res);
-                    return res;
-                }
-            }
-        }
-    }
-
-    // No lock
-
-    unsigned int timeoutMs;
-    if (timeout == TIMEOUT_NEVER) {
-        timeoutMs = Fence::TIMEOUT_NEVER;
-    } else if (timeout == 0) {
-        timeoutMs = 0;
-    } else {
-        // Round up to wait at least 1 ms
-        timeoutMs = (timeout + 999999) / 1000000;
-    }
-
-    return mCombinedFence->wait(timeoutMs);
-}
-
 void Camera3IOStreamBase::dump(int fd, const Vector<String16> &args) const {
     (void) args;
     String8 lines;
@@ -190,6 +144,14 @@ void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer,
     buffer.release_fence = releaseFence;
     buffer.status = status;
 
+    // Inform tracker about becoming busy
+    if (mDequeuedBufferCount == 0 && mState != STATE_IN_CONFIG &&
+            mState != STATE_IN_RECONFIG) {
+        sp<StatusTracker> statusTracker = mStatusTracker.promote();
+        if (statusTracker != 0) {
+            statusTracker->markComponentActive(mStatusId);
+        }
+    }
     mDequeuedBufferCount++;
 }
 
@@ -253,12 +215,24 @@ status_t Camera3IOStreamBase::returnAnyBufferLocked(
     res = returnBufferCheckedLocked(buffer, timestamp, output,
                                     &releaseFence);
     if (res != OK) {
-        return res;
+        // NO_INIT means the buffer queue is abandoned, so to be resilient,
+        // still want to decrement in-flight counts.
+        if (res != NO_INIT) {
+            return res;
+        }
     }
 
     mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence);
 
     mDequeuedBufferCount--;
+    if (mDequeuedBufferCount == 0 && mState != STATE_IN_CONFIG &&
+            mState != STATE_IN_RECONFIG) {
+        sp<StatusTracker> statusTracker = mStatusTracker.promote();
+        if (statusTracker != 0) {
+            statusTracker->markComponentIdle(mStatusId, mCombinedFence);
+        }
+    }
+
     mBufferReturnedSignal.signal();
 
     if (output) {
index 9432a59..fcb9d04 100644 (file)
@@ -43,7 +43,6 @@ class Camera3IOStreamBase :
      * Camera3Stream interface
      */
 
-    virtual status_t waitUntilIdle(nsecs_t timeout);
     virtual void     dump(int fd, const Vector<String16> &args) const;
 
   protected:
index c80f512..5aa9a3e 100644 (file)
@@ -115,7 +115,6 @@ status_t Camera3InputStream::returnBufferCheckedLocked(
                 bufferFound = true;
                 bufferItem = tmp;
                 mBuffersInFlight.erase(it);
-                mDequeuedBufferCount--;
             }
         }
     }
@@ -148,12 +147,11 @@ status_t Camera3InputStream::returnBufferCheckedLocked(
     if (res != OK) {
         ALOGE("%s: Stream %d: Error releasing buffer back to buffer queue:"
                 " %s (%d)", __FUNCTION__, mId, strerror(-res), res);
-        return res;
     }
 
     *releaseFenceOut = releaseFence;
 
-    return OK;
+    return res;
 }
 
 status_t Camera3InputStream::returnInputBufferLocked(
index 35cb5ba..41328fc 100644 (file)
@@ -198,12 +198,11 @@ status_t Camera3OutputStream::returnBufferCheckedLocked(
     mLock.lock();
     if (res != OK) {
         close(anwReleaseFence);
-        return res;
     }
 
     *releaseFenceOut = releaseFence;
 
-    return OK;
+    return res;
 }
 
 void Camera3OutputStream::dump(int fd, const Vector<String16> &args) const {
index a6872aa..6d2cf94 100644 (file)
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
-#include "Camera3Stream.h"
+#include "device3/Camera3Stream.h"
+#include "device3/StatusTracker.h"
 
 namespace android {
 
 namespace camera3 {
 
 Camera3Stream::~Camera3Stream() {
+    sp<StatusTracker> statusTracker = mStatusTracker.promote();
+    if (statusTracker != 0 && mStatusId != StatusTracker::NO_STATUS_ID) {
+        statusTracker->removeComponent(mStatusId);
+    }
 }
 
 Camera3Stream* Camera3Stream::cast(camera3_stream *stream) {
@@ -44,7 +49,8 @@ Camera3Stream::Camera3Stream(int id,
     mId(id),
     mName(String8::format("Camera3Stream[%d]", id)),
     mMaxSize(maxSize),
-    mState(STATE_CONSTRUCTED) {
+    mState(STATE_CONSTRUCTED),
+    mStatusId(StatusTracker::NO_STATUS_ID) {
 
     camera3_stream::stream_type = type;
     camera3_stream::width = width;
@@ -119,6 +125,15 @@ camera3_stream* Camera3Stream::startConfiguration() {
         return NULL;
     }
 
+    // Stop tracking if currently doing so
+    if (mStatusId != StatusTracker::NO_STATUS_ID) {
+        sp<StatusTracker> statusTracker = mStatusTracker.promote();
+        if (statusTracker != 0) {
+            statusTracker->removeComponent(mStatusId);
+        }
+        mStatusId = StatusTracker::NO_STATUS_ID;
+    }
+
     if (mState == STATE_CONSTRUCTED) {
         mState = STATE_IN_CONFIG;
     } else { // mState == STATE_CONFIGURED
@@ -154,6 +169,12 @@ status_t Camera3Stream::finishConfiguration(camera3_device *hal3Device) {
             return INVALID_OPERATION;
     }
 
+    // Register for idle tracking
+    sp<StatusTracker> statusTracker = mStatusTracker.promote();
+    if (statusTracker != 0) {
+        mStatusId = statusTracker->addComponent();
+    }
+
     // Check if the stream configuration is unchanged, and skip reallocation if
     // so. As documented in hardware/camera3.h:configure_streams().
     if (mState == STATE_IN_RECONFIG &&
@@ -265,6 +286,18 @@ bool Camera3Stream::hasOutstandingBuffers() const {
     return hasOutstandingBuffersLocked();
 }
 
+status_t Camera3Stream::setStatusTracker(sp<StatusTracker> statusTracker) {
+    Mutex::Autolock l(mLock);
+    sp<StatusTracker> oldTracker = mStatusTracker.promote();
+    if (oldTracker != 0 && mStatusId != StatusTracker::NO_STATUS_ID) {
+        oldTracker->removeComponent(mStatusId);
+    }
+    mStatusId = StatusTracker::NO_STATUS_ID;
+    mStatusTracker = statusTracker;
+
+    return OK;
+}
+
 status_t Camera3Stream::disconnect() {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
index b64fd86..6eeb721 100644 (file)
@@ -190,12 +190,11 @@ class Camera3Stream :
     enum {
         TIMEOUT_NEVER = -1
     };
+
     /**
-     * Wait until the HAL is done with all of this stream's buffers, including
-     * signalling all release fences. Returns TIMED_OUT if the timeout is exceeded,
-     * OK on success. Pass in TIMEOUT_NEVER for timeout to indicate an indefinite wait.
+     * Set the status tracker to notify about idle transitions
      */
-    virtual status_t waitUntilIdle(nsecs_t timeout) = 0;
+    virtual status_t setStatusTracker(sp<StatusTracker> statusTracker);
 
     /**
      * Disconnect stream from its non-HAL endpoint. After this,
@@ -267,6 +266,11 @@ class Camera3Stream :
     // INVALID_OPERATION if they cannot be obtained.
     virtual status_t getEndpointUsage(uint32_t *usage) = 0;
 
+    // Tracking for idle state
+    wp<StatusTracker> mStatusTracker;
+    // Status tracker component ID
+    int mStatusId;
+
   private:
     uint32_t oldUsage;
     uint32_t oldMaxBuffers;
index 4768536..c93ae15 100644 (file)
@@ -26,6 +26,8 @@ namespace android {
 
 namespace camera3 {
 
+class StatusTracker;
+
 /**
  * An interface for managing a single stream of input and/or output data from
  * the camera device.
@@ -128,13 +130,11 @@ class Camera3StreamInterface : public virtual RefBase {
     enum {
         TIMEOUT_NEVER = -1
     };
+
     /**
-     * Wait until the HAL is done with all of this stream's buffers, including
-     * signalling all release fences. Returns TIMED_OUT if the timeout is
-     * exceeded, OK on success. Pass in TIMEOUT_NEVER for timeout to indicate
-     * an indefinite wait.
+     * Set the state tracker to use for signaling idle transitions.
      */
-    virtual status_t waitUntilIdle(nsecs_t timeout) = 0;
+    virtual status_t setStatusTracker(sp<StatusTracker> statusTracker) = 0;
 
     /**
      * Disconnect stream from its non-HAL endpoint. After this,
diff --git a/services/camera/libcameraservice/device3/StatusTracker.cpp b/services/camera/libcameraservice/device3/StatusTracker.cpp
new file mode 100644 (file)
index 0000000..ab5419f
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "Camera3-Status"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+// This is needed for stdint.h to define INT64_MAX in C++
+#define __STDC_LIMIT_MACROS
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <ui/Fence.h>
+
+#include "device3/StatusTracker.h"
+#include "device3/Camera3Device.h"
+
+namespace android {
+
+namespace camera3 {
+
+StatusTracker::StatusTracker(wp<Camera3Device> parent) :
+        mComponentsChanged(false),
+        mParent(parent),
+        mNextComponentId(0),
+        mIdleFence(new Fence()),
+        mDeviceState(IDLE) {
+}
+
+StatusTracker::~StatusTracker() {
+}
+
+int StatusTracker::addComponent() {
+    int id;
+    ssize_t err;
+    {
+        Mutex::Autolock l(mLock);
+        id = mNextComponentId++;
+        ALOGV("%s: Adding new component %d", __FUNCTION__, id);
+
+        err = mStates.add(id, IDLE);
+        ALOGE_IF(err < 0, "%s: Can't add new component %d: %s (%d)",
+                __FUNCTION__, id, strerror(-err), err);
+    }
+
+    if (err >= 0) {
+        Mutex::Autolock pl(mPendingLock);
+        mComponentsChanged = true;
+        mPendingChangeSignal.signal();
+    }
+
+    return err < 0 ? err : id;
+}
+
+void StatusTracker::removeComponent(int id) {
+    ssize_t idx;
+    {
+        Mutex::Autolock l(mLock);
+        ALOGV("%s: Removing component %d", __FUNCTION__, id);
+        idx = mStates.removeItem(id);
+    }
+
+    if (idx >= 0) {
+        Mutex::Autolock pl(mPendingLock);
+        mComponentsChanged = true;
+        mPendingChangeSignal.signal();
+    }
+
+    return;
+}
+
+
+void StatusTracker::markComponentIdle(int id, const sp<Fence>& componentFence) {
+    markComponent(id, IDLE, componentFence);
+}
+
+void StatusTracker::markComponentActive(int id) {
+    markComponent(id, ACTIVE, Fence::NO_FENCE);
+}
+
+void StatusTracker::markComponent(int id, ComponentState state,
+        const sp<Fence>& componentFence) {
+    ALOGV("%s: Component %d is now %s", __FUNCTION__, id,
+            state == IDLE ? "idle" : "active");
+    Mutex::Autolock l(mPendingLock);
+
+    StateChange newState = {
+        id,
+        state,
+        componentFence
+    };
+
+    mPendingChangeQueue.add(newState);
+    mPendingChangeSignal.signal();
+}
+
+void StatusTracker::requestExit() {
+    // First mark thread dead
+    Thread::requestExit();
+    // Then exit any waits
+    mPendingChangeSignal.signal();
+}
+
+StatusTracker::ComponentState StatusTracker::getDeviceStateLocked() {
+    for (size_t i = 0; i < mStates.size(); i++) {
+        if (mStates.valueAt(i) == ACTIVE) {
+            ALOGV("%s: Component %d not idle", __FUNCTION__,
+                    mStates.keyAt(i));
+            return ACTIVE;
+        }
+    }
+    // - If not yet signaled, getSignalTime returns INT64_MAX
+    // - If invalid fence or error, returns -1
+    // - Otherwise returns time of signalling.
+    // Treat -1 as 'signalled', since HAL may not be using fences, and want
+    // to be able to idle in case of errors.
+    nsecs_t signalTime = mIdleFence->getSignalTime();
+    bool fencesDone = signalTime != INT64_MAX;
+
+    ALOGV_IF(!fencesDone, "%s: Fences still to wait on", __FUNCTION__);
+
+    return fencesDone ? IDLE : ACTIVE;
+}
+
+bool StatusTracker::threadLoop() {
+    status_t res;
+
+    // Wait for state updates
+    {
+        Mutex::Autolock pl(mPendingLock);
+        while (mPendingChangeQueue.size() == 0 && !mComponentsChanged) {
+            res = mPendingChangeSignal.waitRelative(mPendingLock,
+                    kWaitDuration);
+            if (exitPending()) return false;
+            if (res != OK) {
+                if (res != TIMED_OUT) {
+                    ALOGE("%s: Error waiting on state changes: %s (%d)",
+                            __FUNCTION__, strerror(-res), res);
+                }
+                // TIMED_OUT is expected
+                break;
+            }
+        }
+    }
+
+    // After new pending states appear, or timeout, check if we're idle.  Even
+    // with timeout, need to check to account for fences that may still be
+    // clearing out
+    sp<Camera3Device> parent;
+    {
+        Mutex::Autolock pl(mPendingLock);
+        Mutex::Autolock l(mLock);
+
+        // Collect all pending state updates and see if the device
+        // collectively transitions between idle and active for each one
+
+        // First pass for changed components or fence completions
+        ComponentState prevState = getDeviceStateLocked();
+        if (prevState != mDeviceState) {
+            // Only collect changes to overall device state
+            mStateTransitions.add(prevState);
+        }
+        // For each pending component state update, check if we've transitioned
+        // to a new overall device state
+        for (size_t i = 0; i < mPendingChangeQueue.size(); i++) {
+            const StateChange &newState = mPendingChangeQueue[i];
+            ssize_t idx = mStates.indexOfKey(newState.id);
+            // Ignore notices for unknown components
+            if (idx >= 0) {
+                // Update single component state
+                mStates.replaceValueAt(idx, newState.state);
+                mIdleFence = Fence::merge(String8("idleFence"),
+                        mIdleFence, newState.fence);
+                // .. and see if overall device state has changed
+                ComponentState newState = getDeviceStateLocked();
+                if (newState != prevState) {
+                    mStateTransitions.add(newState);
+                }
+                prevState = newState;
+            }
+        }
+        mPendingChangeQueue.clear();
+        mComponentsChanged = false;
+
+        // Store final state after all pending state changes are done with
+
+        mDeviceState = prevState;
+        parent = mParent.promote();
+    }
+
+    // Notify parent for all intermediate transitions
+    if (mStateTransitions.size() > 0 && parent.get()) {
+        for (size_t i = 0; i < mStateTransitions.size(); i++) {
+            bool idle = (mStateTransitions[i] == IDLE);
+            ALOGV("Camera device is now %s", idle ? "idle" : "active");
+            parent->notifyStatus(idle);
+        }
+    }
+    mStateTransitions.clear();
+
+    return true;
+}
+
+} // namespace android
+
+} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/StatusTracker.h b/services/camera/libcameraservice/device3/StatusTracker.h
new file mode 100644 (file)
index 0000000..49cecb3
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA3_STATUSTRACKER_H
+#define ANDROID_SERVERS_CAMERA3_STATUSTRACKER_H
+
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/Mutex.h>
+#include <utils/Thread.h>
+#include <utils/KeyedVector.h>
+#include <hardware/camera3.h>
+
+#include "common/CameraDeviceBase.h"
+
+namespace android {
+
+class Camera3Device;
+class Fence;
+
+namespace camera3 {
+
+/**
+ * State tracking for idle and other collective state transitions.
+ * Collects idle notifications from different sources and calls the
+ * parent when all of them become idle.
+ *
+ * The parent is responsible for synchronizing the status updates with its
+ * internal state correctly, which means the notifyStatus call to the parent may
+ * block for a while.
+ */
+class StatusTracker: public Thread {
+  public:
+    StatusTracker(wp<Camera3Device> parent);
+    ~StatusTracker();
+
+    // An always-invalid component ID
+    static const int NO_STATUS_ID = -1;
+
+    // Add a component to track; returns non-negative unique ID for the new
+    // component on success, negative error code on failure.
+    // New components start in the idle state.
+    int addComponent();
+
+    // Remove existing component from idle tracking. Ignores unknown IDs
+    void removeComponent(int id);
+
+    // Set the state of a tracked component to be idle. Ignores unknown IDs; can
+    // accept a fence to wait on to complete idle.  The fence is merged with any
+    // previous fences given, which means they all must signal before the
+    // component is considered idle.
+    void markComponentIdle(int id, const sp<Fence>& componentFence);
+
+    // Set the state of a tracked component to be active. Ignores unknown IDs.
+    void markComponentActive(int id);
+
+    virtual void requestExit();
+  protected:
+
+    virtual bool threadLoop();
+
+  private:
+    enum ComponentState {
+        IDLE,
+        ACTIVE
+    };
+
+    void markComponent(int id, ComponentState state,
+            const sp<Fence>& componentFence);
+
+    // Guards mPendingChange, mPendingStates, mComponentsChanged
+    Mutex mPendingLock;
+
+    Condition mPendingChangeSignal;
+
+    struct StateChange {
+        int id;
+        ComponentState state;
+        sp<Fence> fence;
+    };
+    // A queue of yet-to-be-processed state changes to components
+    Vector<StateChange> mPendingChangeQueue;
+    bool mComponentsChanged;
+
+    wp<Camera3Device> mParent;
+
+    // Guards rest of internals. Must be locked after mPendingLock if both used.
+    Mutex mLock;
+
+    int mNextComponentId;
+
+    // Current component states
+    KeyedVector<int, ComponentState> mStates;
+    // Merged fence for all processed state changes
+    sp<Fence> mIdleFence;
+    // Current overall device state
+    ComponentState mDeviceState;
+
+    // Private to threadLoop
+
+    // Determine current overall device state
+    // We're IDLE iff
+    // - All components are currently IDLE
+    // - The merged fence for all component updates has signalled
+    ComponentState getDeviceStateLocked();
+
+    Vector<ComponentState> mStateTransitions;
+
+    static const nsecs_t kWaitDuration = 250000000LL; // 250 ms
+};
+
+} // namespace camera3
+
+} // namespace android
+
+#endif