CameraCaptureSessionImpl.java revision d24e49bba8225d846546612b647dde70ad71f1dc
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package android.hardware.camera2.impl; 17 18import android.hardware.camera2.CameraAccessException; 19import android.hardware.camera2.CameraCaptureSession; 20import android.hardware.camera2.CameraDevice; 21import android.hardware.camera2.CaptureRequest; 22import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher; 23import android.hardware.camera2.dispatch.BroadcastDispatcher; 24import android.hardware.camera2.dispatch.DuckTypingDispatcher; 25import android.hardware.camera2.dispatch.HandlerDispatcher; 26import android.hardware.camera2.dispatch.InvokeDispatcher; 27import android.hardware.camera2.utils.TaskDrainer; 28import android.hardware.camera2.utils.TaskSingleDrainer; 29import android.os.Handler; 30import android.util.Log; 31import android.view.Surface; 32 33import java.util.Arrays; 34import java.util.List; 35 36import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler; 37import static com.android.internal.util.Preconditions.*; 38 39public class CameraCaptureSessionImpl extends CameraCaptureSession { 40 private static final String TAG = "CameraCaptureSession"; 41 private static final boolean DEBUG = false; 42 43 /** Simple integer ID for session for debugging */ 44 private final int mId; 45 private final String mIdString; 46 47 /** Input surface configured by native camera framework based on user-specified configuration */ 48 private final Surface mInput; 49 /** User-specified set of surfaces used as the configuration outputs */ 50 private final List<Surface> mOutputs; 51 /** 52 * User-specified state callback, used for outgoing events; calls to this object will be 53 * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}. 54 */ 55 private final CameraCaptureSession.StateCallback mStateCallback; 56 /** User-specified state handler used for outgoing state callback events */ 57 private final Handler mStateHandler; 58 59 /** Internal camera device; used to translate calls into existing deprecated API */ 60 private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl; 61 /** Internal handler; used for all incoming events to preserve total order */ 62 private final Handler mDeviceHandler; 63 private final boolean mIsConstrainedHighSpeedSession; 64 65 /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */ 66 private final TaskDrainer<Integer> mSequenceDrainer; 67 /** Drain state transitions from ACTIVE -> IDLE */ 68 private final TaskSingleDrainer mIdleDrainer; 69 /** Drain state transitions from BUSY -> IDLE */ 70 private final TaskSingleDrainer mAbortDrainer; 71 72 /** This session is closed; all further calls will throw ISE */ 73 private boolean mClosed = false; 74 /** This session failed to be configured successfully */ 75 private final boolean mConfigureSuccess; 76 /** Do not unconfigure if this is set; another session will overwrite configuration */ 77 private boolean mSkipUnconfigure = false; 78 79 /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */ 80 private volatile boolean mAborting; 81 82 /** 83 * Create a new CameraCaptureSession. 84 * 85 * <p>The camera device must already be in the {@code IDLE} state when this is invoked. 86 * There must be no pending actions 87 * (e.g. no pending captures, no repeating requests, no flush).</p> 88 */ 89 CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs, 90 CameraCaptureSession.StateCallback callback, Handler stateHandler, 91 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, 92 Handler deviceStateHandler, boolean configureSuccess, boolean isConstrainedHighSpeed) { 93 if (outputs == null || outputs.isEmpty()) { 94 throw new IllegalArgumentException("outputs must be a non-null, non-empty list"); 95 } else if (callback == null) { 96 throw new IllegalArgumentException("callback must not be null"); 97 } 98 99 mIsConstrainedHighSpeedSession = isConstrainedHighSpeed; 100 mId = id; 101 mIdString = String.format("Session %d: ", mId); 102 103 // TODO: extra verification of outputs 104 mOutputs = outputs; 105 mInput = input; 106 mStateHandler = checkHandler(stateHandler); 107 mStateCallback = createUserStateCallbackProxy(mStateHandler, callback); 108 109 mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null"); 110 mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null"); 111 112 /* 113 * Use the same handler as the device's StateCallback for all the internal coming events 114 * 115 * This ensures total ordering between CameraDevice.StateCallback and 116 * CameraDeviceImpl.CaptureCallback events. 117 */ 118 mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(), 119 /*name*/"seq"); 120 mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(), 121 /*name*/"idle"); 122 mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(), 123 /*name*/"abort"); 124 125 // CameraDevice should call configureOutputs and have it finish before constructing us 126 127 if (configureSuccess) { 128 mStateCallback.onConfigured(this); 129 if (DEBUG) Log.v(TAG, mIdString + "Created session successfully"); 130 mConfigureSuccess = true; 131 } else { 132 mStateCallback.onConfigureFailed(this); 133 mClosed = true; // do not fire any other callbacks, do not allow any other work 134 Log.e(TAG, mIdString + "Failed to create capture session; configuration failed"); 135 mConfigureSuccess = false; 136 } 137 } 138 139 140 private boolean isConstrainedHighSpeedRequestList(List<CaptureRequest> requestList) { 141 checkCollectionNotEmpty(requestList, "High speed request list"); 142 for (CaptureRequest request : requestList) { 143 if (!request.isPartOfCRequestList()) { 144 return false; 145 } 146 } 147 return true; 148 } 149 150 /** 151 * If the session is constrained high speed session, it only accept constrained high speed 152 * request list. 153 */ 154 private void checkConstrainedHighSpeedRequestSanity(List<CaptureRequest> requestList) { 155 if (mIsConstrainedHighSpeedSession) { 156 if (!isConstrainedHighSpeedRequestList(requestList)) { 157 throw new IllegalArgumentException("It is only allowed to submit a constrained " 158 + "high speed request list to a constrained high speed session!!!"); 159 } 160 } 161 } 162 163 @Override 164 public CameraDevice getDevice() { 165 return mDeviceImpl; 166 } 167 168 @Override 169 public void prepare(Surface surface) throws CameraAccessException { 170 mDeviceImpl.prepare(surface); 171 } 172 173 @Override 174 public synchronized int capture(CaptureRequest request, CaptureCallback callback, 175 Handler handler) throws CameraAccessException { 176 if (request == null) { 177 throw new IllegalArgumentException("request must not be null"); 178 } else if (request.isReprocess() && !isReprocessable()) { 179 throw new IllegalArgumentException("this capture session cannot handle reprocess " + 180 "requests"); 181 } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { 182 throw new IllegalArgumentException("capture request was created for another session"); 183 } 184 if (mIsConstrainedHighSpeedSession) { 185 throw new UnsupportedOperationException("Constrained high speed session doesn't support" 186 + " this method"); 187 } 188 189 checkNotClosed(); 190 191 handler = checkHandler(handler, callback); 192 193 if (DEBUG) { 194 Log.v(TAG, mIdString + "capture - request " + request + ", callback " + callback + 195 " handler " + handler); 196 } 197 198 return addPendingSequence(mDeviceImpl.capture(request, 199 createCaptureCallbackProxy(handler, callback), mDeviceHandler)); 200 } 201 202 @Override 203 public synchronized int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, 204 Handler handler) throws CameraAccessException { 205 if (requests == null) { 206 throw new IllegalArgumentException("Requests must not be null"); 207 } else if (requests.isEmpty()) { 208 throw new IllegalArgumentException("Requests must have at least one element"); 209 } 210 211 checkConstrainedHighSpeedRequestSanity(requests); 212 213 for (CaptureRequest request : requests) { 214 if (request.isReprocess()) { 215 if (!isReprocessable()) { 216 throw new IllegalArgumentException("This capture session cannot handle " + 217 "reprocess requests"); 218 } else if (request.getReprocessableSessionId() != mId) { 219 throw new IllegalArgumentException("Capture request was created for another " + 220 "session"); 221 } 222 } 223 } 224 225 checkNotClosed(); 226 227 handler = checkHandler(handler, callback); 228 229 if (DEBUG) { 230 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); 231 Log.v(TAG, mIdString + "captureBurst - requests " + Arrays.toString(requestArray) + 232 ", callback " + callback + " handler " + handler); 233 } 234 235 return addPendingSequence(mDeviceImpl.captureBurst(requests, 236 createCaptureCallbackProxy(handler, callback), mDeviceHandler)); 237 } 238 239 @Override 240 public synchronized int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, 241 Handler handler) throws CameraAccessException { 242 if (request == null) { 243 throw new IllegalArgumentException("request must not be null"); 244 } else if (request.isReprocess()) { 245 throw new IllegalArgumentException("repeating reprocess requests are not supported"); 246 } 247 if (mIsConstrainedHighSpeedSession) { 248 throw new UnsupportedOperationException("Constrained high speed session doesn't support" 249 + " this method"); 250 } 251 252 checkNotClosed(); 253 254 handler = checkHandler(handler, callback); 255 256 if (DEBUG) { 257 Log.v(TAG, mIdString + "setRepeatingRequest - request " + request + ", callback " + 258 callback + " handler" + " " + handler); 259 } 260 261 return addPendingSequence(mDeviceImpl.setRepeatingRequest(request, 262 createCaptureCallbackProxy(handler, callback), mDeviceHandler)); 263 } 264 265 @Override 266 public synchronized int setRepeatingBurst(List<CaptureRequest> requests, 267 CaptureCallback callback, Handler handler) throws CameraAccessException { 268 if (requests == null) { 269 throw new IllegalArgumentException("requests must not be null"); 270 } else if (requests.isEmpty()) { 271 throw new IllegalArgumentException("requests must have at least one element"); 272 } 273 274 checkConstrainedHighSpeedRequestSanity(requests); 275 276 for (CaptureRequest r : requests) { 277 if (r.isReprocess()) { 278 throw new IllegalArgumentException("repeating reprocess burst requests are not " + 279 "supported"); 280 } 281 } 282 283 checkNotClosed(); 284 285 handler = checkHandler(handler, callback); 286 287 if (DEBUG) { 288 CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); 289 Log.v(TAG, mIdString + "setRepeatingBurst - requests " + Arrays.toString(requestArray) + 290 ", callback " + callback + " handler" + "" + handler); 291 } 292 293 return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests, 294 createCaptureCallbackProxy(handler, callback), mDeviceHandler)); 295 } 296 297 @Override 298 public synchronized void stopRepeating() throws CameraAccessException { 299 checkNotClosed(); 300 301 if (DEBUG) { 302 Log.v(TAG, mIdString + "stopRepeating"); 303 } 304 305 mDeviceImpl.stopRepeating(); 306 } 307 308 @Override 309 public synchronized void abortCaptures() throws CameraAccessException { 310 checkNotClosed(); 311 312 if (DEBUG) { 313 Log.v(TAG, mIdString + "abortCaptures"); 314 } 315 316 if (mAborting) { 317 Log.w(TAG, mIdString + "abortCaptures - Session is already aborting; doing nothing"); 318 return; 319 } 320 321 mAborting = true; 322 mAbortDrainer.taskStarted(); 323 324 mDeviceImpl.flush(); 325 // The next BUSY -> IDLE set of transitions will mark the end of the abort. 326 } 327 328 @Override 329 public boolean isReprocessable() { 330 return mInput != null; 331 } 332 333 @Override 334 public Surface getInputSurface() { 335 return mInput; 336 } 337 338 /** 339 * Replace this session with another session. 340 * 341 * <p>This is an optimization to avoid unconfiguring and then immediately having to 342 * reconfigure again.</p> 343 * 344 * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped. 345 * <p> 346 * 347 * <p>After this call completes, the session will not call any further methods on the camera 348 * device.</p> 349 * 350 * @see CameraCaptureSession#close 351 */ 352 synchronized void replaceSessionClose() { 353 /* 354 * In order for creating new sessions to be fast, the new session should be created 355 * before the old session is closed. 356 * 357 * Otherwise the old session will always unconfigure if there is no new session to 358 * replace it. 359 * 360 * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt 361 * to skip unconfigure if a new session is created before the captures are all drained, 362 * but this would introduce nondeterministic behavior. 363 */ 364 365 if (DEBUG) Log.v(TAG, mIdString + "replaceSessionClose"); 366 367 // Set up fast shutdown. Possible alternative paths: 368 // - This session is active, so close() below starts the shutdown drain 369 // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener. 370 // - This session is already closed and has executed the idle drain listener, and 371 // configureOutputsChecked(null) has already been called. 372 // 373 // Do not call configureOutputsChecked(null) going forward, since it would race with the 374 // configuration for the new session. If it was already called, then we don't care, since it 375 // won't get called again. 376 mSkipUnconfigure = true; 377 378 close(); 379 } 380 381 @Override 382 public synchronized void close() { 383 384 if (mClosed) { 385 if (DEBUG) Log.v(TAG, mIdString + "close - reentering"); 386 return; 387 } 388 389 if (DEBUG) Log.v(TAG, mIdString + "close - first time"); 390 391 mClosed = true; 392 393 /* 394 * Flush out any repeating request. Since camera is closed, no new requests 395 * can be queued, and eventually the entire request queue will be drained. 396 * 397 * If the camera device was already closed, short circuit and do nothing; since 398 * no more internal device callbacks will fire anyway. 399 * 400 * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the 401 * camera. Once that's done, fire #onClosed. 402 */ 403 try { 404 mDeviceImpl.stopRepeating(); 405 } catch (IllegalStateException e) { 406 // OK: Camera device may already be closed, nothing else to do 407 408 // TODO: Fire onClosed anytime we get the device onClosed or the ISE? 409 // or just suppress the ISE only and rely onClosed. 410 // Also skip any of the draining work if this is already closed. 411 412 // Short-circuit; queue callback immediately and return 413 mStateCallback.onClosed(this); 414 return; 415 } catch (CameraAccessException e) { 416 // OK: close does not throw checked exceptions. 417 Log.e(TAG, mIdString + "Exception while stopping repeating: ", e); 418 419 // TODO: call onError instead of onClosed if this happens 420 } 421 422 // If no sequences are pending, fire #onClosed immediately 423 mSequenceDrainer.beginDrain(); 424 } 425 426 /** 427 * Whether currently in mid-abort. 428 * 429 * <p>This is used by the implementation to set the capture failure 430 * reason, in lieu of more accurate error codes from the camera service. 431 * Unsynchronized to avoid deadlocks between simultaneous session->device, 432 * device->session calls.</p> 433 * 434 * <p>Package-private.</p> 435 */ 436 boolean isAborting() { 437 return mAborting; 438 } 439 440 /** 441 * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}. 442 */ 443 private StateCallback createUserStateCallbackProxy(Handler handler, StateCallback callback) { 444 InvokeDispatcher<StateCallback> userCallbackSink = new InvokeDispatcher<>(callback); 445 HandlerDispatcher<StateCallback> handlerPassthrough = 446 new HandlerDispatcher<>(userCallbackSink, handler); 447 448 return new CallbackProxies.SessionStateCallbackProxy(handlerPassthrough); 449 } 450 451 /** 452 * Forward callbacks from 453 * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback. 454 * 455 * <p>In particular, all calls are automatically split to go both to our own 456 * internal callback, and to the user-specified callback (by transparently posting 457 * to the user-specified handler).</p> 458 * 459 * <p>When a capture sequence finishes, update the pending checked sequences set.</p> 460 */ 461 @SuppressWarnings("deprecation") 462 private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy( 463 Handler handler, CaptureCallback callback) { 464 CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() { 465 @Override 466 public void onCaptureSequenceCompleted(CameraDevice camera, 467 int sequenceId, long frameNumber) { 468 finishPendingSequence(sequenceId); 469 } 470 471 @Override 472 public void onCaptureSequenceAborted(CameraDevice camera, 473 int sequenceId) { 474 finishPendingSequence(sequenceId); 475 } 476 }; 477 478 /* 479 * Split the calls from the device callback into local callback and the following chain: 480 * - replace the first CameraDevice arg with a CameraCaptureSession 481 * - duck type from device callback to session callback 482 * - then forward the call to a handler 483 * - then finally invoke the destination method on the session callback object 484 */ 485 if (callback == null) { 486 // OK: API allows the user to not specify a callback, and the handler may 487 // also be null in that case. Collapse whole dispatch chain to only call the local 488 // callback 489 return localCallback; 490 } 491 492 InvokeDispatcher<CameraDeviceImpl.CaptureCallback> localSink = 493 new InvokeDispatcher<>(localCallback); 494 495 InvokeDispatcher<CaptureCallback> userCallbackSink = 496 new InvokeDispatcher<>(callback); 497 HandlerDispatcher<CaptureCallback> handlerPassthrough = 498 new HandlerDispatcher<>(userCallbackSink, handler); 499 DuckTypingDispatcher<CameraDeviceImpl.CaptureCallback, CaptureCallback> duckToSession 500 = new DuckTypingDispatcher<>(handlerPassthrough, CaptureCallback.class); 501 ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureCallback, CameraCaptureSessionImpl> 502 replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession, 503 /*argumentIndex*/0, this); 504 505 BroadcastDispatcher<CameraDeviceImpl.CaptureCallback> broadcaster = 506 new BroadcastDispatcher<CameraDeviceImpl.CaptureCallback>( 507 replaceDeviceWithSession, 508 localSink); 509 510 return new CallbackProxies.DeviceCaptureCallbackProxy(broadcaster); 511 } 512 513 /** 514 * 515 * Create an internal state callback, to be invoked on the mDeviceHandler 516 * 517 * <p>It has a few behaviors: 518 * <ul> 519 * <li>Convert device state changes into session state changes. 520 * <li>Keep track of async tasks that the session began (idle, abort). 521 * </ul> 522 * </p> 523 * */ 524 CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() { 525 final CameraCaptureSession session = this; 526 527 return new CameraDeviceImpl.StateCallbackKK() { 528 private boolean mBusy = false; 529 private boolean mActive = false; 530 531 @Override 532 public void onOpened(CameraDevice camera) { 533 throw new AssertionError("Camera must already be open before creating a session"); 534 } 535 536 @Override 537 public void onDisconnected(CameraDevice camera) { 538 if (DEBUG) Log.v(TAG, mIdString + "onDisconnected"); 539 close(); 540 } 541 542 @Override 543 public void onError(CameraDevice camera, int error) { 544 // Should not be reached, handled by device code 545 Log.wtf(TAG, mIdString + "Got device error " + error); 546 } 547 548 @Override 549 public void onActive(CameraDevice camera) { 550 mIdleDrainer.taskStarted(); 551 mActive = true; 552 553 if (DEBUG) Log.v(TAG, mIdString + "onActive"); 554 mStateCallback.onActive(session); 555 } 556 557 @Override 558 public void onIdle(CameraDevice camera) { 559 boolean isAborting; 560 if (DEBUG) Log.v(TAG, mIdString + "onIdle"); 561 562 synchronized (session) { 563 isAborting = mAborting; 564 } 565 566 /* 567 * Check which states we transitioned through: 568 * 569 * (ACTIVE -> IDLE) 570 * (BUSY -> IDLE) 571 * 572 * Note that this is also legal: 573 * (ACTIVE -> BUSY -> IDLE) 574 * 575 * and mark those tasks as finished 576 */ 577 if (mBusy && isAborting) { 578 mAbortDrainer.taskFinished(); 579 580 synchronized (session) { 581 mAborting = false; 582 } 583 } 584 585 if (mActive) { 586 mIdleDrainer.taskFinished(); 587 } 588 589 mBusy = false; 590 mActive = false; 591 592 mStateCallback.onReady(session); 593 } 594 595 @Override 596 public void onBusy(CameraDevice camera) { 597 mBusy = true; 598 599 // TODO: Queue captures during abort instead of failing them 600 // since the app won't be able to distinguish the two actives 601 // Don't signal the application since there's no clean mapping here 602 if (DEBUG) Log.v(TAG, mIdString + "onBusy"); 603 } 604 605 @Override 606 public void onUnconfigured(CameraDevice camera) { 607 if (DEBUG) Log.v(TAG, mIdString + "onUnconfigured"); 608 } 609 610 @Override 611 public void onSurfacePrepared(Surface surface) { 612 if (DEBUG) Log.v(TAG, mIdString + "onPrepared"); 613 mStateCallback.onSurfacePrepared(session, surface); 614 } 615 616 }; 617 618 } 619 620 @Override 621 protected void finalize() throws Throwable { 622 try { 623 close(); 624 } finally { 625 super.finalize(); 626 } 627 } 628 629 private void checkNotClosed() { 630 if (mClosed) { 631 throw new IllegalStateException( 632 "Session has been closed; further changes are illegal."); 633 } 634 } 635 636 /** 637 * Notify the session that a pending capture sequence has just been queued. 638 * 639 * <p>During a shutdown/close, the session waits until all pending sessions are finished 640 * before taking any further steps to shut down itself.</p> 641 * 642 * @see #finishPendingSequence 643 */ 644 private int addPendingSequence(int sequenceId) { 645 mSequenceDrainer.taskStarted(sequenceId); 646 return sequenceId; 647 } 648 649 /** 650 * Notify the session that a pending capture sequence is now finished. 651 * 652 * <p>During a shutdown/close, once all pending sequences finish, it is safe to 653 * close the camera further by unconfiguring and then firing {@code onClosed}.</p> 654 */ 655 private void finishPendingSequence(int sequenceId) { 656 mSequenceDrainer.taskFinished(sequenceId); 657 } 658 659 private class SequenceDrainListener implements TaskDrainer.DrainListener { 660 @Override 661 public void onDrained() { 662 /* 663 * No repeating request is set; and the capture queue has fully drained. 664 * 665 * If no captures were queued to begin with, and an abort was queued, 666 * it's still possible to get another BUSY before the last IDLE. 667 * 668 * If the camera is already "IDLE" and no aborts are pending, 669 * then the drain immediately finishes. 670 */ 671 if (DEBUG) Log.v(TAG, mIdString + "onSequenceDrained"); 672 673 674 // Fire session close as soon as all sequences are complete. 675 // We may still need to unconfigure the device, but a new session might be created 676 // past this point, and notifications would then stop to this instance. 677 mStateCallback.onClosed(CameraCaptureSessionImpl.this); 678 679 // Fast path: A new capture session has replaced this one; don't wait for abort/idle 680 // as we won't get state updates any more anyway. 681 if (mSkipUnconfigure) { 682 return; 683 } 684 685 mAbortDrainer.beginDrain(); 686 } 687 } 688 689 private class AbortDrainListener implements TaskDrainer.DrainListener { 690 @Override 691 public void onDrained() { 692 if (DEBUG) Log.v(TAG, mIdString + "onAbortDrained"); 693 synchronized (CameraCaptureSessionImpl.this) { 694 /* 695 * Any queued aborts have now completed. 696 * 697 * It's now safe to wait to receive the final "IDLE" event, as the camera device 698 * will no longer again transition to "ACTIVE" by itself. 699 * 700 * If the camera is already "IDLE", then the drain immediately finishes. 701 */ 702 703 // Fast path: A new capture session has replaced this one; don't wait for idle 704 // as we won't get state updates any more anyway. 705 if (mSkipUnconfigure) { 706 return; 707 } 708 mIdleDrainer.beginDrain(); 709 } 710 } 711 } 712 713 private class IdleDrainListener implements TaskDrainer.DrainListener { 714 @Override 715 public void onDrained() { 716 if (DEBUG) Log.v(TAG, mIdString + "onIdleDrained"); 717 718 // Take device lock before session lock so that we can call back into device 719 // without causing a deadlock 720 synchronized (mDeviceImpl.mInterfaceLock) { 721 synchronized (CameraCaptureSessionImpl.this) { 722 /* 723 * The device is now IDLE, and has settled. It will not transition to 724 * ACTIVE or BUSY again by itself. 725 * 726 * It's now safe to unconfigure the outputs. 727 * 728 * This operation is idempotent; a session will not be closed twice. 729 */ 730 if (DEBUG) 731 Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " + 732 mSkipUnconfigure); 733 734 // Fast path: A new capture session has replaced this one; don't wait for idle 735 // as we won't get state updates any more anyway. 736 if (mSkipUnconfigure) { 737 return; 738 } 739 740 // Final slow path: unconfigure the camera, no session has replaced us and 741 // everything is idle. 742 try { 743 // begin transition to unconfigured 744 mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null, 745 /*isConstrainedHighSpeed*/false); 746 } catch (CameraAccessException e) { 747 // OK: do not throw checked exceptions. 748 Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e); 749 750 // TODO: call onError instead of onClosed if this happens 751 } catch (IllegalStateException e) { 752 // Camera is already closed, so nothing left to do 753 if (DEBUG) Log.v(TAG, mIdString + 754 "Camera was already closed or busy, skipping unconfigure"); 755 } 756 757 } 758 } 759 } 760 } 761 762 @Override 763 public boolean isConstrainedHighSpeed() { 764 return mIsConstrainedHighSpeedSession; 765 } 766 767} 768