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 */ 16 package android.hardware.camera2.impl; 17 18 import android.hardware.camera2.CameraAccessException; 19 import android.hardware.camera2.CameraCaptureSession; 20 import android.hardware.camera2.CameraDevice; 21 import android.hardware.camera2.CaptureRequest; 22 import android.hardware.camera2.ICameraDeviceUser; 23 import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher; 24 import android.hardware.camera2.dispatch.BroadcastDispatcher; 25 import android.hardware.camera2.dispatch.DuckTypingDispatcher; 26 import android.hardware.camera2.dispatch.HandlerDispatcher; 27 import android.hardware.camera2.dispatch.InvokeDispatcher; 28 import android.hardware.camera2.params.OutputConfiguration; 29 import android.hardware.camera2.utils.TaskDrainer; 30 import android.hardware.camera2.utils.TaskSingleDrainer; 31 import android.os.Handler; 32 import android.util.Log; 33 import android.view.Surface; 34 35 import java.util.Arrays; 36 import java.util.List; 37 38 import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler; 39 import static com.android.internal.util.Preconditions.*; 40 41 public 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 */ CameraCaptureSessionImpl(int id, Surface input, CameraCaptureSession.StateCallback callback, Handler stateHandler, android.hardware.camera2.impl.CameraDeviceImpl deviceImpl, Handler deviceStateHandler, boolean configureSuccess)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 getDevice()135 public CameraDevice getDevice() { 136 return mDeviceImpl; 137 } 138 139 @Override prepare(Surface surface)140 public void prepare(Surface surface) throws CameraAccessException { 141 mDeviceImpl.prepare(surface); 142 } 143 144 @Override prepare(int maxCount, Surface surface)145 public void prepare(int maxCount, Surface surface) throws CameraAccessException { 146 mDeviceImpl.prepare(maxCount, surface); 147 } 148 149 @Override tearDown(Surface surface)150 public void tearDown(Surface surface) throws CameraAccessException { 151 mDeviceImpl.tearDown(surface); 152 } 153 154 @Override finalizeOutputConfigurations( List<OutputConfiguration> outputConfigs)155 public void finalizeOutputConfigurations( 156 List<OutputConfiguration> outputConfigs) throws CameraAccessException { 157 mDeviceImpl.finalizeOutputConfigs(outputConfigs); 158 } 159 160 @Override capture(CaptureRequest request, CaptureCallback callback, Handler handler)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 captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)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 setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler)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 setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)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 stopRepeating()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 abortCaptures()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 isReprocessable()310 public boolean isReprocessable() { 311 return mInput != null; 312 } 313 314 @Override getInputSurface()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 replaceSessionClose()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 close()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 isAborting()426 public boolean isAborting() { 427 return mAborting; 428 } 429 430 /** 431 * Post calls into a CameraCaptureSession.StateCallback to the user-specified {@code handler}. 432 */ createUserStateCallbackProxy(Handler handler, StateCallback callback)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") createCaptureCallbackProxy( Handler handler, CaptureCallback callback)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 getDeviceStateCallback()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 finalize()655 protected void finalize() throws Throwable { 656 try { 657 close(); 658 } finally { 659 super.finalize(); 660 } 661 } 662 checkNotClosed()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 */ addPendingSequence(int sequenceId)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 */ finishPendingSequence(int sequenceId)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 onDrained()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 onDrained()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 onDrained()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