Merge "Camera2: Stop repeating request for abandoned output" into nyc-dev
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index b542339..5743b4d 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -921,7 +921,16 @@
int requestId = mRepeatingRequestId;
mRepeatingRequestId = REQUEST_ID_NONE;
- long lastFrameNumber = mRemoteDevice.cancelRequest(requestId);
+ long lastFrameNumber;
+ try {
+ lastFrameNumber = mRemoteDevice.cancelRequest(requestId);
+ } catch (IllegalArgumentException e) {
+ if (DEBUG) {
+ Log.v(TAG, "Repeating request was already stopped for request " + requestId);
+ }
+ // Repeating request was already stopped. Nothing more to do.
+ return;
+ }
checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
}
@@ -1686,6 +1695,24 @@
}
@Override
+ public void onRepeatingRequestError(long lastFrameNumber) {
+ if (DEBUG) {
+ Log.d(TAG, "Repeating request error received. Last frame number is " +
+ lastFrameNumber);
+ }
+
+ synchronized(mInterfaceLock) {
+ // Camera is already closed or no repeating request is present.
+ if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) {
+ return; // Camera already closed
+ }
+
+ checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
+ mRepeatingRequestId = REQUEST_ID_NONE;
+ }
+ }
+
+ @Override
public void onDeviceIdle() {
if (DEBUG) {
Log.d(TAG, "Camera now idle");
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index b0b94e3..e48bce1 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -76,6 +76,7 @@
void onBusy();
void onCaptureStarted(RequestHolder holder, long timestamp);
void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
+ void onRepeatingRequestError(long lastFrameNumber);
}
/**
@@ -201,6 +202,22 @@
}
/**
+ * Set repeating request error.
+ *
+ * <p>Repeating request has been stopped due to an error such as abandoned output surfaces.</p>
+ *
+ * @param lastFrameNumber Frame number of the last repeating request before it is stopped.
+ */
+ public synchronized void setRepeatingRequestError(final long lastFrameNumber) {
+ mCurrentHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCurrentListener.onRepeatingRequestError(lastFrameNumber);
+ }
+ });
+ }
+
+ /**
* Set the listener for state transition callbacks.
*
* @param handler handler on which to call the callbacks.
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index f99928a..acbf214 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -205,6 +205,7 @@
private static final int CAPTURE_STARTED = 2;
private static final int RESULT_RECEIVED = 3;
private static final int PREPARED = 4;
+ private static final int REPEATING_REQUEST_ERROR = 5;
private final HandlerThread mHandlerThread;
private Handler mHandler;
@@ -261,6 +262,15 @@
getHandler().sendMessage(msg);
}
+
+ @Override
+ public void onRepeatingRequestError(long lastFrameNumber) {
+ Message msg = getHandler().obtainMessage(REPEATING_REQUEST_ERROR,
+ /*arg1*/ (int) (lastFrameNumber & 0xFFFFFFFFL),
+ /*arg2*/ (int) ( (lastFrameNumber >> 32) & 0xFFFFFFFFL));
+ getHandler().sendMessage(msg);
+ }
+
@Override
public IBinder asBinder() {
// This is solely intended to be used for in-process binding.
@@ -311,6 +321,12 @@
mCallbacks.onPrepared(streamId);
break;
}
+ case REPEATING_REQUEST_ERROR: {
+ long lastFrameNumber = msg.arg2 & 0xFFFFFFFFL;
+ lastFrameNumber = (lastFrameNumber << 32) | (msg.arg1 & 0xFFFFFFFFL);
+ mCallbacks.onRepeatingRequestError(lastFrameNumber);
+ break;
+ }
default:
throw new IllegalArgumentException(
"Unknown callback message " + msg.what);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 6c95869..3e79118 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -242,6 +242,25 @@
}
});
}
+
+ @Override
+ public void onRepeatingRequestError(final long lastFrameNumber) {
+ mResultHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "doing onRepeatingRequestError callback.");
+ }
+ try {
+ mDeviceCallbacks.onRepeatingRequestError(lastFrameNumber);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Received remote exception during onRepeatingRequestError " +
+ "callback: ", e);
+ }
+ }
+ });
+ }
};
private final RequestThreadManager mRequestThreadManager;
@@ -397,8 +416,15 @@
"submitRequestList - Empty/null requests are not allowed");
}
- List<Long> surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
- getSurfaceIds(mConfiguredSurfaces);
+ List<Long> surfaceIds;
+
+ try {
+ surfaceIds = (mConfiguredSurfaces == null) ? new ArrayList<Long>() :
+ getSurfaceIds(mConfiguredSurfaces);
+ } catch (BufferQueueAbandonedException e) {
+ throw new ServiceSpecificException(BAD_VALUE,
+ "submitRequestList - configured surface is abandoned.");
+ }
// Make sure that there all requests have at least 1 surface; all surfaces are non-null
for (CaptureRequest request : requestList) {
@@ -674,12 +700,17 @@
LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
}
- static long getSurfaceId(Surface surface) {
+ static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
checkNotNull(surface);
- return nativeGetSurfaceId(surface);
+ try {
+ return nativeGetSurfaceId(surface);
+ } catch (IllegalArgumentException e) {
+ throw new BufferQueueAbandonedException();
+ }
}
- static List<Long> getSurfaceIds(SparseArray<Surface> surfaces) {
+ static List<Long> getSurfaceIds(SparseArray<Surface> surfaces)
+ throws BufferQueueAbandonedException {
if (surfaces == null) {
throw new NullPointerException("Null argument surfaces");
}
@@ -696,7 +727,8 @@
return surfaceIds;
}
- static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
+ static List<Long> getSurfaceIds(Collection<Surface> surfaces)
+ throws BufferQueueAbandonedException {
if (surfaces == null) {
throw new NullPointerException("Null argument surfaces");
}
@@ -713,7 +745,13 @@
}
static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
- long id = getSurfaceId(s);
+ long id = 0;
+ try {
+ id = getSurfaceId(s);
+ } catch (BufferQueueAbandonedException e) {
+ // If surface is abandoned, return false.
+ return false;
+ }
return ids.contains(id);
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestHolder.java b/core/java/android/hardware/camera2/legacy/RequestHolder.java
index 476c3de..98b761b 100644
--- a/core/java/android/hardware/camera2/legacy/RequestHolder.java
+++ b/core/java/android/hardware/camera2/legacy/RequestHolder.java
@@ -40,6 +40,7 @@
private final int mNumJpegTargets;
private final int mNumPreviewTargets;
private volatile boolean mFailed = false;
+ private boolean mOutputAbandoned = false;
private final Collection<Long> mJpegSurfaceIds;
@@ -266,4 +267,17 @@
return mFailed;
}
+ /**
+ * Mark at least one of this request's output surfaces is abandoned.
+ */
+ public void setOutputAbandoned() {
+ mOutputAbandoned = true;
+ }
+
+ /**
+ * Return if any of this request's output surface is abandoned.
+ */
+ public boolean isOutputAbandoned() {
+ return mOutputAbandoned;
+ }
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index a3fdd56..da62f54 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -710,6 +710,7 @@
break;
case MSG_SUBMIT_CAPTURE_REQUEST:
Handler handler = RequestThreadManager.this.mRequestThread.getHandler();
+ boolean anyRequestOutputAbandoned = false;
// Get the next burst from the request queue.
Pair<BurstHolder, Long> nextBurst = mRequestQueue.getNext();
@@ -910,7 +911,22 @@
if (!holder.requestFailed()) {
mDeviceState.setCaptureResult(holder, result);
}
+
+ if (holder.isOutputAbandoned()) {
+ anyRequestOutputAbandoned = true;
+ }
}
+
+ // Stop the repeating request if any of its output surfaces is abandoned.
+ if (anyRequestOutputAbandoned && nextBurst.first.isRepeating()) {
+ long lastFrameNumber = cancelRepeating(nextBurst.first.getRequestId());
+ if (DEBUG) {
+ Log.d(TAG, "Stopped repeating request. Last frame number is " +
+ lastFrameNumber);
+ }
+ mDeviceState.setRepeatingRequestError(lastFrameNumber);
+ }
+
if (DEBUG) {
long totalTime = SystemClock.elapsedRealtimeNanos() - startTime;
Log.d(TAG, "Capture request took " + totalTime + " ns");
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 70bc2fd..e0d3905 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -525,9 +525,16 @@
checkEglError("makeCurrent");
}
- private boolean swapBuffers(EGLSurface surface) {
+ private boolean swapBuffers(EGLSurface surface)
+ throws LegacyExceptionUtils.BufferQueueAbandonedException {
boolean result = EGL14.eglSwapBuffers(mEGLDisplay, surface);
- checkEglError("swapBuffers");
+ int error = EGL14.eglGetError();
+ if (error == EGL14.EGL_BAD_SURFACE) {
+ throw new LegacyExceptionUtils.BufferQueueAbandonedException();
+ } else if (error != EGL14.EGL_SUCCESS) {
+ throw new IllegalStateException("swapBuffers: EGL error: 0x" +
+ Integer.toHexString(error));
+ }
return result;
}
@@ -722,7 +729,14 @@
addGlTimestamp(timestamp);
}
- List<Long> targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
+ List<Long> targetSurfaceIds = new ArrayList();
+ try {
+ targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces);
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ Log.w(TAG, "Surface abandoned, dropping frame. ", e);
+ request.setOutputAbandoned();
+ }
+
for (EGLSurfaceHolder holder : mSurfaces) {
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
try{
@@ -737,6 +751,7 @@
swapBuffers(holder.eglSurface);
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
Log.w(TAG, "Surface abandoned, dropping frame. ", e);
+ request.setOutputAbandoned();
}
}
}
@@ -761,6 +776,7 @@
holder.width, holder.height, format);
} catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
Log.w(TAG, "Surface abandoned, dropping frame. ", e);
+ request.setOutputAbandoned();
}
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 9a0946e..bbc249f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -254,6 +254,15 @@
// TODO Auto-generated method stub
}
+
+ /*
+ * (non-Javadoc)
+ * @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError()
+ */
+ @Override
+ public void onRepeatingRequestError(long lastFrameNumber) {
+ // TODO Auto-generated method stub
+ }
}
@SmallTest
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 5c1d8a7..6c879b9 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -145,6 +145,15 @@
// TODO Auto-generated method stub
}
+
+ /*
+ * (non-Javadoc)
+ * @see android.hardware.camera2.ICameraDeviceCallbacks#onRepeatingRequestError()
+ */
+ @Override
+ public void onRepeatingRequestError(long lastFrameNumber) {
+ // TODO Auto-generated method stub
+ }
}
class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> {