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