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