Camera: ensure lock ordering (device->session)
am: b0025e1f68
Change-Id: Idcb9b48274ecfe9fe2f306ae201491d1e485d601
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user