Camera: ensure lock ordering (device->session)

am: b0025e1f68

Change-Id: Idcb9b48274ecfe9fe2f306ae201491d1e485d601
This commit is contained in:
Yin-Chia Yeh
2016-08-11 00:39:26 +00:00
committed by android-build-merger

View File

@@ -286,23 +286,29 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
} }
@Override @Override
public synchronized void abortCaptures() throws CameraAccessException { public void abortCaptures() throws CameraAccessException {
checkNotClosed(); synchronized (this) {
checkNotClosed();
if (DEBUG) { if (DEBUG) {
Log.v(TAG, mIdString + "abortCaptures"); Log.v(TAG, mIdString + "abortCaptures");
}
if (mAborting) {
Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing");
return;
}
mAborting = true;
mAbortDrainer.taskStarted();
} }
if (mAborting) { synchronized (mDeviceImpl.mInterfaceLock) {
Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing"); synchronized (this) {
return; mDeviceImpl.flush();
// The next BUSY -> IDLE set of transitions will mark the end of the abort.
}
} }
mAborting = true;
mAbortDrainer.taskStarted();
mDeviceImpl.flush();
// The next BUSY -> IDLE set of transitions will mark the end of the abort.
} }
@Override @Override
@@ -330,78 +336,86 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
* @see CameraCaptureSession#close * @see CameraCaptureSession#close
*/ */
@Override @Override
public synchronized void replaceSessionClose() { public void replaceSessionClose() {
/* synchronized (this) {
* In order for creating new sessions to be fast, the new session should be created /*
* before the old session is closed. * In order for creating new sessions to be fast, the new session should be created
* * before the old session is closed.
* Otherwise the old session will always unconfigure if there is no new session to *
* replace it. * Otherwise the old session will always unconfigure if there is no new session to
* * replace it.
* Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt *
* to skip unconfigure if a new session is created before the captures are all drained, * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
* but this would introduce nondeterministic behavior. * to skip unconfigure if a new session is created before the captures are all drained,
*/ * but this would introduce nondeterministic behavior.
*/
if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose"); if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose");
// Set up fast shutdown. Possible alternative paths:
// - This session is active, so close() below starts the shutdown drain
// - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
// - This session is already closed and has executed the idle drain listener, and
// configureOutputsChecked(null) has already been called.
//
// Do not call configureOutputsChecked(null) going forward, since it would race with the
// configuration for the new session. If it was already called, then we don't care, since it
// won't get called again.
mSkipUnconfigure = true;
// Set up fast shutdown. Possible alternative paths:
// - This session is active, so close() below starts the shutdown drain
// - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener.
// - This session is already closed and has executed the idle drain listener, and
// configureOutputsChecked(null) has already been called.
//
// Do not call configureOutputsChecked(null) going forward, since it would race with the
// configuration for the new session. If it was already called, then we don't care,
// since it won't get called again.
mSkipUnconfigure = true;
}
close(); close();
} }
@Override @Override
public synchronized void close() { public void close() {
synchronized (this) {
if (mClosed) {
if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
return;
}
if (mClosed) { if (DEBUG) Log.v(TAG, mIdString + "close - first time");
if (DEBUG) Log.v(TAG, mIdString + "close - reentering");
return; mClosed = true;
} }
if (DEBUG) Log.v(TAG, mIdString + "close - first time"); synchronized (mDeviceImpl.mInterfaceLock) {
synchronized (this) {
/*
* Flush out any repeating request. Since camera is closed, no new requests
* can be queued, and eventually the entire request queue will be drained.
*
* If the camera device was already closed, short circuit and do nothing; since
* no more internal device callbacks will fire anyway.
*
* Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure
* the camera. Once that's done, fire #onClosed.
*/
try {
mDeviceImpl.stopRepeating();
} catch (IllegalStateException e) {
// OK: Camera device may already be closed, nothing else to do
mClosed = true; // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
// or just suppress the ISE only and rely onClosed.
// Also skip any of the draining work if this is already closed.
/* // Short-circuit; queue callback immediately and return
* Flush out any repeating request. Since camera is closed, no new requests mStateCallback.onClosed(this);
* can be queued, and eventually the entire request queue will be drained. return;
* } catch (CameraAccessException e) {
* If the camera device was already closed, short circuit and do nothing; since // OK: close does not throw checked exceptions.
* no more internal device callbacks will fire anyway. Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
*
* Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the
* camera. Once that's done, fire #onClosed.
*/
try {
mDeviceImpl.stopRepeating();
} catch (IllegalStateException e) {
// OK: Camera device may already be closed, nothing else to do
// TODO: Fire onClosed anytime we get the device onClosed or the ISE? // TODO: call onError instead of onClosed if this happens
// or just suppress the ISE only and rely onClosed. }
// Also skip any of the draining work if this is already closed. }
// Short-circuit; queue callback immediately and return
mStateCallback.onClosed(this);
return;
} catch (CameraAccessException e) {
// OK: close does not throw checked exceptions.
Log.e(TAG, mIdString + "Exception while stopping repeating: ", e);
// TODO: call onError instead of onClosed if this happens
} }
// If no sequences are pending, fire #onClosed immediately synchronized (this) {
mSequenceDrainer.beginDrain(); // If no sequences are pending, fire #onClosed immediately
mSequenceDrainer.beginDrain();
}
} }
/** /**