summaryrefslogtreecommitdiffstats
path: root/services/camera/libcameraservice/device3
diff options
context:
space:
mode:
authorEino-Ville Talvala <etalvala@google.com>2013-09-06 09:32:43 -0700
committerEino-Ville Talvala <etalvala@google.com>2013-10-02 18:11:21 -0700
commitf1e98d857ec377f2c9b916073d40732e6ebb7ced (patch)
tree2a435e723f17c0c7b3e6db323d68be6cfb7d5c66 /services/camera/libcameraservice/device3
parentf05e50eb06d3f70e50fa7f44c1fd32128033b49d (diff)
downloadframeworks_av-f1e98d857ec377f2c9b916073d40732e6ebb7ced.zip
frameworks_av-f1e98d857ec377f2c9b916073d40732e6ebb7ced.tar.gz
frameworks_av-f1e98d857ec377f2c9b916073d40732e6ebb7ced.tar.bz2
Camera API 2, Device 2/3: Implement idle and shutter callbacks
- 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
Diffstat (limited to 'services/camera/libcameraservice/device3')
-rw-r--r--services/camera/libcameraservice/device3/Camera3Device.cpp539
-rw-r--r--services/camera/libcameraservice/device3/Camera3Device.h98
-rw-r--r--services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp72
-rw-r--r--services/camera/libcameraservice/device3/Camera3IOStreamBase.h1
-rw-r--r--services/camera/libcameraservice/device3/Camera3InputStream.cpp4
-rw-r--r--services/camera/libcameraservice/device3/Camera3OutputStream.cpp3
-rw-r--r--services/camera/libcameraservice/device3/Camera3Stream.cpp37
-rw-r--r--services/camera/libcameraservice/device3/Camera3Stream.h12
-rw-r--r--services/camera/libcameraservice/device3/Camera3StreamInterface.h10
-rw-r--r--services/camera/libcameraservice/device3/StatusTracker.cpp219
-rw-r--r--services/camera/libcameraservice/device3/StatusTracker.h130
11 files changed, 866 insertions, 259 deletions
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index b468eb3..ed6458c 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -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;
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 61caf13..6295c80 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -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
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 0850566..727a8c9 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -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) {
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 9432a59..fcb9d04 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -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:
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index c80f512..5aa9a3e 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -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(
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 35cb5ba..41328fc 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -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 {
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index a6872aa..6d2cf94 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -20,13 +20,18 @@
#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);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index b64fd86..6eeb721 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -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;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 4768536..c93ae15 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -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
index 0000000..ab5419f
--- /dev/null
+++ b/services/camera/libcameraservice/device3/StatusTracker.cpp
@@ -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
index 0000000..49cecb3
--- /dev/null
+++ b/services/camera/libcameraservice/device3/StatusTracker.h
@@ -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