Camera3Device: Don't time out on long exposures

 - Calculate expected duration based on request settings instead
   of using a fixed timeout for shutdown
 - Ensure the timeouts are always at least 5 seconds anyway

Test: Camera CTS passes with a long-exposure device
Bug: 38212789
Change-Id: I6b154710314cdc84b223f1a9f39fead9262ce27b
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 0807c0a..16fcfb1 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -251,9 +251,11 @@
                 SET_ERR_L("Can't stop streaming");
                 // Continue to close device even in case of error
             } else {
-                res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+                nsecs_t maxExpectedDuration = getExpectedInFlightDurationLocked();
+                res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
                 if (res != OK) {
-                    SET_ERR_L("Timeout waiting for HAL to drain");
+                    SET_ERR_L("Timeout waiting for HAL to drain (% " PRIi64 " ns)",
+                            maxExpectedDuration);
                     // Continue to close device even in case of error
                 }
             }
@@ -309,6 +311,7 @@
 
     {
         Mutex::Autolock l(mLock);
+        mExpectedInflightDuration = 0;
         mInterface->clear();
         mOutputStreams.clear();
         mInputStream.clear();
@@ -1555,8 +1558,11 @@
             return INVALID_OPERATION;
     }
 
-    ALOGV("%s: Camera %s: Waiting until idle", __FUNCTION__, mId.string());
-    status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+    nsecs_t maxExpectedDuration = getExpectedInFlightDurationLocked();
+
+    ALOGV("%s: Camera %s: Waiting until idle (%" PRIi64 "ns)", __FUNCTION__, mId.string(),
+            maxExpectedDuration);
+    status_t res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
     if (res != OK) {
         SET_ERR_L("Error waiting for HAL to drain: %s (%d)", strerror(-res),
                 res);
@@ -1576,11 +1582,13 @@
     mRequestThread->setPaused(true);
     mPauseStateNotify = true;
 
-    ALOGV("%s: Camera %s: Internal wait until idle", __FUNCTION__, mId.string());
-    status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+    nsecs_t maxExpectedDuration = getExpectedInFlightDurationLocked();
+    ALOGV("%s: Camera %s: Internal wait until idle (% " PRIi64 " ns)", __FUNCTION__, mId.string(),
+          maxExpectedDuration);
+    status_t res = waitUntilStateThenRelock(/*active*/ false, maxExpectedDuration);
     if (res != OK) {
         SET_ERR_L("Can't idle device in %f seconds!",
-                kShutdownTimeout/1e9);
+                maxExpectedDuration/1e9);
     }
 
     return res;
@@ -2347,13 +2355,13 @@
 
 status_t Camera3Device::registerInFlight(uint32_t frameNumber,
         int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
-        bool hasAppCallback) {
+        bool hasAppCallback, nsecs_t maxExpectedDuration) {
     ATRACE_CALL();
     Mutex::Autolock l(mInFlightLock);
 
     ssize_t res;
     res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
-            hasAppCallback));
+            hasAppCallback, maxExpectedDuration));
     if (res < 0) return res;
 
     if (mInFlightMap.size() == 1) {
@@ -2364,6 +2372,9 @@
         }
     }
 
+    Mutex::Autolock ml(mLock);
+    mExpectedInflightDuration += maxExpectedDuration;
+
     return OK;
 }
 
@@ -2384,6 +2395,7 @@
 }
 
 void Camera3Device::removeInFlightMapEntryLocked(int idx) {
+    nsecs_t duration = mInFlightMap.valueAt(idx).maxExpectedDuration;
     mInFlightMap.removeItemsAt(idx, 1);
 
     // Indicate idle inFlightMap to the status tracker
@@ -2394,6 +2406,8 @@
             mStatusTracker->markComponentIdle(mInFlightStatusId, Fence::NO_FENCE);
         }
     }
+    Mutex::Autolock l(mLock);
+    mExpectedInflightDuration -= duration;
 }
 
 void Camera3Device::removeInFlightRequestIfReadyLocked(int idx) {
@@ -3913,6 +3927,42 @@
     return true;
 }
 
+nsecs_t Camera3Device::RequestThread::calculateMaxExpectedDuration(const camera_metadata_t *request) {
+    nsecs_t maxExpectedDuration = kDefaultExpectedDuration;
+    camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
+    find_camera_metadata_ro_entry(request,
+            ANDROID_CONTROL_AE_MODE,
+            &e);
+    if (e.count == 0) return maxExpectedDuration;
+
+    switch (e.data.u8[0]) {
+        case ANDROID_CONTROL_AE_MODE_OFF:
+            find_camera_metadata_ro_entry(request,
+                    ANDROID_SENSOR_EXPOSURE_TIME,
+                    &e);
+            if (e.count > 0) {
+                maxExpectedDuration = e.data.i64[0];
+            }
+            find_camera_metadata_ro_entry(request,
+                    ANDROID_SENSOR_FRAME_DURATION,
+                    &e);
+            if (e.count > 0) {
+                maxExpectedDuration = std::max(e.data.i64[0], maxExpectedDuration);
+            }
+            break;
+        default:
+            find_camera_metadata_ro_entry(request,
+                    ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+                    &e);
+            if (e.count > 1) {
+                maxExpectedDuration = 1e9 / e.data.u8[0];
+            }
+            break;
+    }
+
+    return maxExpectedDuration;
+}
+
 bool Camera3Device::RequestThread::threadLoop() {
     ATRACE_CALL();
     status_t res;
@@ -4133,7 +4183,8 @@
         res = parent->registerInFlight(halRequest->frame_number,
                 totalNumBuffers, captureRequest->mResultExtras,
                 /*hasInput*/halRequest->input_buffer != NULL,
-                hasCallback);
+                hasCallback,
+                calculateMaxExpectedDuration(halRequest->settings));
         ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
                ", burstId = %" PRId32 ".",
                 __FUNCTION__,
@@ -4187,6 +4238,11 @@
     return false;
 }
 
+nsecs_t Camera3Device::getExpectedInFlightDurationLocked() {
+    return mExpectedInflightDuration > kMinInflightDuration ?
+            mExpectedInflightDuration : kMinInflightDuration;
+}
+
 void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) {
     if (mNextRequests.empty()) {
         return;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 0251d62..686a28b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -187,10 +187,11 @@
 
     static const size_t        kDumpLockAttempts  = 10;
     static const size_t        kDumpSleepDuration = 100000; // 0.10 sec
-    static const nsecs_t       kShutdownTimeout   = 5000000000; // 5 sec
     static const nsecs_t       kActiveTimeout     = 500000000;  // 500 ms
     static const size_t        kInFlightWarnLimit = 30;
     static const size_t        kInFlightWarnLimitHighSpeed = 256; // batch size 32 * pipe depth 8
+    static const nsecs_t       kDefaultExpectedDuration = 100000000; // 100 ms
+    static const nsecs_t       kMinInflightDuration = 5000000000; // 5 s
     // SCHED_FIFO priority for request submission thread in HFR mode
     static const int           kRequestThreadPriority = 1;
 
@@ -776,6 +777,9 @@
         // send request in mNextRequests to HAL in a batch. Return true = sucssess
         bool sendRequestsBatch();
 
+        // Calculate the expected maximum duration for a request
+        nsecs_t calculateMaxExpectedDuration(const camera_metadata_t *request);
+
         wp<Camera3Device>  mParent;
         wp<camera3::StatusTracker>  mStatusTracker;
         sp<HalInterface>   mInterface;
@@ -877,6 +881,11 @@
         // is not for constrained high speed recording, this flag will also be true.
         bool hasCallback;
 
+        // Maximum expected frame duration for this request.
+        // For manual captures, equal to the max of requested exposure time and frame duration
+        // For auto-exposure modes, equal to 1/(lower end of target FPS range)
+        nsecs_t maxExpectedDuration;
+
         // Default constructor needed by KeyedVector
         InFlightRequest() :
                 shutterTimestamp(0),
@@ -885,11 +894,12 @@
                 haveResultMetadata(false),
                 numBuffersLeft(0),
                 hasInputBuffer(false),
-                hasCallback(true) {
+                hasCallback(true),
+                maxExpectedDuration(kDefaultExpectedDuration) {
         }
 
         InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
-                bool hasAppCallback) :
+                bool hasAppCallback, nsecs_t maxDuration) :
                 shutterTimestamp(0),
                 sensorTimestamp(0),
                 requestStatus(OK),
@@ -897,20 +907,28 @@
                 numBuffersLeft(numBuffers),
                 resultExtras(extras),
                 hasInputBuffer(hasInput),
-                hasCallback(hasAppCallback) {
+                hasCallback(hasAppCallback),
+                maxExpectedDuration(maxDuration) {
         }
     };
 
     // Map from frame number to the in-flight request state
     typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap;
 
+    nsecs_t                mExpectedInflightDuration = 0;
     Mutex                  mInFlightLock; // Protects mInFlightMap
     InFlightMap            mInFlightMap;
     int                    mInFlightStatusId;
 
     status_t registerInFlight(uint32_t frameNumber,
             int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
-            bool callback);
+            bool callback, nsecs_t maxExpectedDuration);
+
+    /**
+     * Returns the maximum expected time it'll take for all currently in-flight
+     * requests to complete, based on their settings
+     */
+    nsecs_t getExpectedInFlightDurationLocked();
 
     /**
      * Tracking for idle detection