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