From caa1a65588ae75484cb1314df4a266bfd698a4cf Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Thu, 11 Nov 2021 17:20:15 -0800 Subject: [PATCH] Camera: Try to recover from failing stopRepeating requests In case the camera client disconnects from an output surface referenced by an active repeating request, a race could occur between the client and camera service. Camera service will notify the client about the failed repeating request and will reset the request id internally. During this time period the client can also try to call "stopRepeating" which will trigger an IAE, reset the client repeating request id value and return immediately. Once the "onRepeatingRequestError" gets scheduled or unblocked, the framework will just ignore it since it doesn't have any information about an active repeating request. The framework will be unable to track the last frame id in the repeating sequence. The sequence will never complete and the 'onClosed' callback will not get triggered as well. To mitigate this, cache the request id in case of IAE during "stopRepeating" and try to resume the last frame id sequence tracking once "onRepeatingRequestError" arrives. Bug: 205895636 Test: Camera CTS Change-Id: I5da4a82d156227568aaebe74a5e0b2400de1d01e --- .../camera2/impl/CameraDeviceImpl.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 88649392c23cd..9b19fc4d3ef2a 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -110,6 +110,11 @@ public class CameraDeviceImpl extends CameraDevice private int mRepeatingRequestId = REQUEST_ID_NONE; // Latest repeating request list's types private int[] mRepeatingRequestTypes; + + // Cache failed requests to process later in case of a repeating error callback + private int mFailedRepeatingRequestId = REQUEST_ID_NONE; + private int[] mFailedRepeatingRequestTypes; + // Map stream IDs to input/output configurations private SimpleEntry mConfiguredInput = new SimpleEntry<>(REQUEST_ID_NONE, null); @@ -1326,16 +1331,25 @@ public class CameraDeviceImpl extends CameraDevice int requestId = mRepeatingRequestId; mRepeatingRequestId = REQUEST_ID_NONE; + mFailedRepeatingRequestId = REQUEST_ID_NONE; int[] requestTypes = mRepeatingRequestTypes; mRepeatingRequestTypes = null; + mFailedRepeatingRequestTypes = null; long lastFrameNumber; try { lastFrameNumber = mRemoteDevice.cancelRequest(requestId); } catch (IllegalArgumentException e) { if (DEBUG) { - Log.v(TAG, "Repeating request was already stopped for request " + requestId); + Log.v(TAG, "Repeating request was already stopped for request " + + requestId); } + // Cache request id and request types in case of a race with + // "onRepeatingRequestError" which may no yet be scheduled on another thread + // or blocked by us. + mFailedRepeatingRequestId = requestId; + mFailedRepeatingRequestTypes = requestTypes; + // Repeating request was already stopped. Nothing more to do. return; } @@ -1965,7 +1979,17 @@ public class CameraDeviceImpl extends CameraDevice synchronized(mInterfaceLock) { // Camera is already closed or no repeating request is present. if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) { - return; // Camera already closed + if ((mFailedRepeatingRequestId == repeatingRequestId) && + (mFailedRepeatingRequestTypes != null) && (mRemoteDevice != null)) { + Log.v(TAG, "Resuming stop of failed repeating request with id: " + + mFailedRepeatingRequestId); + + checkEarlyTriggerSequenceCompleteLocked(mFailedRepeatingRequestId, + lastFrameNumber, mFailedRepeatingRequestTypes); + mFailedRepeatingRequestId = REQUEST_ID_NONE; + mFailedRepeatingRequestTypes = null; + } + return; } // Redirect device callback to the offline session in case we are in the middle