CameraDeviceImpl.java revision bfbbee756663aeeb38706bb1bd4841dcd050f91b
1/* 2 * Copyright (C) 2013 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 17package android.hardware.camera2.impl; 18 19import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; 20 21import android.hardware.camera2.CameraAccessException; 22import android.hardware.camera2.CameraCaptureSession; 23import android.hardware.camera2.CameraCharacteristics; 24import android.hardware.camera2.CameraDevice; 25import android.hardware.camera2.CaptureRequest; 26import android.hardware.camera2.CaptureResult; 27import android.hardware.camera2.CaptureFailure; 28import android.hardware.camera2.ICameraDeviceCallbacks; 29import android.hardware.camera2.ICameraDeviceUser; 30import android.hardware.camera2.TotalCaptureResult; 31import android.hardware.camera2.params.OutputConfiguration; 32import android.hardware.camera2.utils.CameraBinderDecorator; 33import android.hardware.camera2.utils.CameraRuntimeException; 34import android.hardware.camera2.utils.LongParcelable; 35import android.os.Handler; 36import android.os.IBinder; 37import android.os.Looper; 38import android.os.RemoteException; 39import android.util.Log; 40import android.util.SparseArray; 41import android.view.Surface; 42 43import java.util.AbstractMap.SimpleEntry; 44import java.util.ArrayList; 45import java.util.HashMap; 46import java.util.HashSet; 47import java.util.Iterator; 48import java.util.List; 49import java.util.TreeSet; 50 51/** 52 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate 53 */ 54public class CameraDeviceImpl extends CameraDevice { 55 private final String TAG; 56 private final boolean DEBUG; 57 58 private static final int REQUEST_ID_NONE = -1; 59 60 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) 61 private ICameraDeviceUser mRemoteDevice; 62 63 // Lock to synchronize cross-thread access to device public interface 64 final Object mInterfaceLock = new Object(); // access from this class and Session only! 65 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); 66 67 private final StateCallback mDeviceCallback; 68 private volatile StateCallbackKK mSessionStateCallback; 69 private final Handler mDeviceHandler; 70 71 private volatile boolean mClosing = false; 72 private boolean mInError = false; 73 private boolean mIdle = true; 74 75 /** map request IDs to callback/request data */ 76 private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap = 77 new SparseArray<CaptureCallbackHolder>(); 78 79 private int mRepeatingRequestId = REQUEST_ID_NONE; 80 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>(); 81 // Map stream IDs to Surfaces 82 private final SparseArray<OutputConfiguration> mConfiguredOutputs = 83 new SparseArray<OutputConfiguration>(); 84 85 private final String mCameraId; 86 private final CameraCharacteristics mCharacteristics; 87 private final int mTotalPartialCount; 88 89 /** 90 * A list tracking request and its expected last frame. 91 * Updated when calling ICameraDeviceUser methods. 92 */ 93 private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>> 94 mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>(); 95 96 /** 97 * An object tracking received frame numbers. 98 * Updated when receiving callbacks from ICameraDeviceCallbacks. 99 */ 100 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker(); 101 102 private CameraCaptureSessionImpl mCurrentSession; 103 private int mNextSessionId = 0; 104 105 // Runnables for all state transitions, except error, which needs the 106 // error code argument 107 108 private final Runnable mCallOnOpened = new Runnable() { 109 @Override 110 public void run() { 111 StateCallbackKK sessionCallback = null; 112 synchronized(mInterfaceLock) { 113 if (mRemoteDevice == null) return; // Camera already closed 114 115 sessionCallback = mSessionStateCallback; 116 } 117 if (sessionCallback != null) { 118 sessionCallback.onOpened(CameraDeviceImpl.this); 119 } 120 mDeviceCallback.onOpened(CameraDeviceImpl.this); 121 } 122 }; 123 124 private final Runnable mCallOnUnconfigured = new Runnable() { 125 @Override 126 public void run() { 127 StateCallbackKK sessionCallback = null; 128 synchronized(mInterfaceLock) { 129 if (mRemoteDevice == null) return; // Camera already closed 130 131 sessionCallback = mSessionStateCallback; 132 } 133 if (sessionCallback != null) { 134 sessionCallback.onUnconfigured(CameraDeviceImpl.this); 135 } 136 } 137 }; 138 139 private final Runnable mCallOnActive = new Runnable() { 140 @Override 141 public void run() { 142 StateCallbackKK sessionCallback = null; 143 synchronized(mInterfaceLock) { 144 if (mRemoteDevice == null) return; // Camera already closed 145 146 sessionCallback = mSessionStateCallback; 147 } 148 if (sessionCallback != null) { 149 sessionCallback.onActive(CameraDeviceImpl.this); 150 } 151 } 152 }; 153 154 private final Runnable mCallOnBusy = new Runnable() { 155 @Override 156 public void run() { 157 StateCallbackKK sessionCallback = null; 158 synchronized(mInterfaceLock) { 159 if (mRemoteDevice == null) return; // Camera already closed 160 161 sessionCallback = mSessionStateCallback; 162 } 163 if (sessionCallback != null) { 164 sessionCallback.onBusy(CameraDeviceImpl.this); 165 } 166 } 167 }; 168 169 private final Runnable mCallOnClosed = new Runnable() { 170 private boolean mClosedOnce = false; 171 172 @Override 173 public void run() { 174 if (mClosedOnce) { 175 throw new AssertionError("Don't post #onClosed more than once"); 176 } 177 StateCallbackKK sessionCallback = null; 178 synchronized(mInterfaceLock) { 179 sessionCallback = mSessionStateCallback; 180 } 181 if (sessionCallback != null) { 182 sessionCallback.onClosed(CameraDeviceImpl.this); 183 } 184 mDeviceCallback.onClosed(CameraDeviceImpl.this); 185 mClosedOnce = true; 186 } 187 }; 188 189 private final Runnable mCallOnIdle = new Runnable() { 190 @Override 191 public void run() { 192 StateCallbackKK sessionCallback = null; 193 synchronized(mInterfaceLock) { 194 if (mRemoteDevice == null) return; // Camera already closed 195 196 sessionCallback = mSessionStateCallback; 197 } 198 if (sessionCallback != null) { 199 sessionCallback.onIdle(CameraDeviceImpl.this); 200 } 201 } 202 }; 203 204 private final Runnable mCallOnDisconnected = new Runnable() { 205 @Override 206 public void run() { 207 StateCallbackKK sessionCallback = null; 208 synchronized(mInterfaceLock) { 209 if (mRemoteDevice == null) return; // Camera already closed 210 211 sessionCallback = mSessionStateCallback; 212 } 213 if (sessionCallback != null) { 214 sessionCallback.onDisconnected(CameraDeviceImpl.this); 215 } 216 mDeviceCallback.onDisconnected(CameraDeviceImpl.this); 217 } 218 }; 219 220 public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler, 221 CameraCharacteristics characteristics) { 222 if (cameraId == null || callback == null || handler == null || characteristics == null) { 223 throw new IllegalArgumentException("Null argument given"); 224 } 225 mCameraId = cameraId; 226 mDeviceCallback = callback; 227 mDeviceHandler = handler; 228 mCharacteristics = characteristics; 229 230 final int MAX_TAG_LEN = 23; 231 String tag = String.format("CameraDevice-JV-%s", mCameraId); 232 if (tag.length() > MAX_TAG_LEN) { 233 tag = tag.substring(0, MAX_TAG_LEN); 234 } 235 TAG = tag; 236 DEBUG = Log.isLoggable(TAG, Log.DEBUG); 237 238 Integer partialCount = 239 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT); 240 if (partialCount == null) { 241 // 1 means partial result is not supported. 242 mTotalPartialCount = 1; 243 } else { 244 mTotalPartialCount = partialCount; 245 } 246 } 247 248 public CameraDeviceCallbacks getCallbacks() { 249 return mCallbacks; 250 } 251 252 public void setRemoteDevice(ICameraDeviceUser remoteDevice) { 253 synchronized(mInterfaceLock) { 254 // TODO: Move from decorator to direct binder-mediated exceptions 255 // If setRemoteFailure already called, do nothing 256 if (mInError) return; 257 258 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice); 259 260 mDeviceHandler.post(mCallOnOpened); 261 mDeviceHandler.post(mCallOnUnconfigured); 262 } 263 } 264 265 /** 266 * Call to indicate failed connection to a remote camera device. 267 * 268 * <p>This places the camera device in the error state and informs the callback. 269 * Use in place of setRemoteDevice() when startup fails.</p> 270 */ 271 public void setRemoteFailure(final CameraRuntimeException failure) { 272 int failureCode = StateCallback.ERROR_CAMERA_DEVICE; 273 boolean failureIsError = true; 274 275 switch (failure.getReason()) { 276 case CameraAccessException.CAMERA_IN_USE: 277 failureCode = StateCallback.ERROR_CAMERA_IN_USE; 278 break; 279 case CameraAccessException.MAX_CAMERAS_IN_USE: 280 failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE; 281 break; 282 case CameraAccessException.CAMERA_DISABLED: 283 failureCode = StateCallback.ERROR_CAMERA_DISABLED; 284 break; 285 case CameraAccessException.CAMERA_DISCONNECTED: 286 failureIsError = false; 287 break; 288 case CameraAccessException.CAMERA_ERROR: 289 failureCode = StateCallback.ERROR_CAMERA_DEVICE; 290 break; 291 default: 292 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason()); 293 break; 294 } 295 final int code = failureCode; 296 final boolean isError = failureIsError; 297 synchronized(mInterfaceLock) { 298 mInError = true; 299 mDeviceHandler.post(new Runnable() { 300 @Override 301 public void run() { 302 if (isError) { 303 mDeviceCallback.onError(CameraDeviceImpl.this, code); 304 } else { 305 mDeviceCallback.onDisconnected(CameraDeviceImpl.this); 306 } 307 } 308 }); 309 } 310 } 311 312 @Override 313 public String getId() { 314 return mCameraId; 315 } 316 317 public void configureOutputs(List<Surface> outputs) throws CameraAccessException { 318 // Leave this here for backwards compatibility with older code using this directly 319 ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size()); 320 for (Surface s : outputs) { 321 outputConfigs.add(new OutputConfiguration(s)); 322 } 323 configureOutputsChecked(outputConfigs); 324 } 325 326 /** 327 * Attempt to configure the outputs; the device goes to idle and then configures the 328 * new outputs if possible. 329 * 330 * <p>The configuration may gracefully fail, if there are too many outputs, if the formats 331 * are not supported, or if the sizes for that format is not supported. In this case this 332 * function will return {@code false} and the unconfigured callback will be fired.</p> 333 * 334 * <p>If the configuration succeeds (with 1 or more outputs), then the idle callback is fired. 335 * Unconfiguring the device always fires the idle callback.</p> 336 * 337 * @param outputs a list of one or more surfaces, or {@code null} to unconfigure 338 * @return whether or not the configuration was successful 339 * 340 * @throws CameraAccessException if there were any unexpected problems during configuration 341 */ 342 public boolean configureOutputsChecked(List<OutputConfiguration> outputs) 343 throws CameraAccessException { 344 // Treat a null input the same an empty list 345 if (outputs == null) { 346 outputs = new ArrayList<OutputConfiguration>(); 347 } 348 boolean success = false; 349 350 synchronized(mInterfaceLock) { 351 checkIfCameraClosedOrInError(); 352 // Streams to create 353 HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs); 354 // Streams to delete 355 List<Integer> deleteList = new ArrayList<Integer>(); 356 357 // Determine which streams need to be created, which to be deleted 358 for (int i = 0; i < mConfiguredOutputs.size(); ++i) { 359 int streamId = mConfiguredOutputs.keyAt(i); 360 OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i); 361 362 if (!outputs.contains(outConfig)) { 363 deleteList.add(streamId); 364 } else { 365 addSet.remove(outConfig); // Don't create a stream previously created 366 } 367 } 368 369 mDeviceHandler.post(mCallOnBusy); 370 stopRepeating(); 371 372 try { 373 waitUntilIdle(); 374 375 mRemoteDevice.beginConfigure(); 376 // Delete all streams first (to free up HW resources) 377 for (Integer streamId : deleteList) { 378 mRemoteDevice.deleteStream(streamId); 379 mConfiguredOutputs.delete(streamId); 380 } 381 382 // Add all new streams 383 for (OutputConfiguration outConfig : outputs) { 384 if (addSet.contains(outConfig)) { 385 int streamId = mRemoteDevice.createStream(outConfig); 386 mConfiguredOutputs.put(streamId, outConfig); 387 } 388 } 389 390 try { 391 mRemoteDevice.endConfigure(); 392 } 393 catch (IllegalArgumentException e) { 394 // OK. camera service can reject stream config if it's not supported by HAL 395 // This is only the result of a programmer misusing the camera2 api. 396 Log.w(TAG, "Stream configuration failed"); 397 return false; 398 } 399 400 success = true; 401 } catch (CameraRuntimeException e) { 402 if (e.getReason() == CAMERA_IN_USE) { 403 throw new IllegalStateException("The camera is currently busy." + 404 " You must wait until the previous operation completes."); 405 } 406 407 throw e.asChecked(); 408 } catch (RemoteException e) { 409 // impossible 410 return false; 411 } finally { 412 if (success && outputs.size() > 0) { 413 mDeviceHandler.post(mCallOnIdle); 414 } else { 415 // Always return to the 'unconfigured' state if we didn't hit a fatal error 416 mDeviceHandler.post(mCallOnUnconfigured); 417 } 418 } 419 } 420 421 return success; 422 } 423 424 @Override 425 public void createCaptureSession(List<Surface> outputs, 426 CameraCaptureSession.StateCallback callback, Handler handler) 427 throws CameraAccessException { 428 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 429 for (Surface surface : outputs) { 430 outConfigurations.add(new OutputConfiguration(surface)); 431 } 432 createCaptureSessionByOutputConfiguration(outConfigurations, callback, handler); 433 } 434 435 @Override 436 public void createCaptureSessionByOutputConfiguration( 437 List<OutputConfiguration> outputConfigurations, 438 CameraCaptureSession.StateCallback callback, Handler handler) 439 throws CameraAccessException { 440 synchronized(mInterfaceLock) { 441 if (DEBUG) { 442 Log.d(TAG, "createCaptureSession"); 443 } 444 445 checkIfCameraClosedOrInError(); 446 447 // Notify current session that it's going away, before starting camera operations 448 // After this call completes, the session is not allowed to call into CameraDeviceImpl 449 if (mCurrentSession != null) { 450 mCurrentSession.replaceSessionClose(); 451 } 452 453 // TODO: dont block for this 454 boolean configureSuccess = true; 455 CameraAccessException pendingException = null; 456 try { 457 // configure outputs and then block until IDLE 458 configureSuccess = configureOutputsChecked(outputConfigurations); 459 } catch (CameraAccessException e) { 460 configureSuccess = false; 461 pendingException = e; 462 if (DEBUG) { 463 Log.v(TAG, "createCaptureSession - failed with exception ", e); 464 } 465 } 466 467 List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size()); 468 for (OutputConfiguration config : outputConfigurations) { 469 outSurfaces.add(config.getSurface()); 470 } 471 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. 472 CameraCaptureSessionImpl newSession = 473 new CameraCaptureSessionImpl(mNextSessionId++, 474 outSurfaces, callback, handler, this, mDeviceHandler, 475 configureSuccess); 476 477 // TODO: wait until current session closes, then create the new session 478 mCurrentSession = newSession; 479 480 if (pendingException != null) { 481 throw pendingException; 482 } 483 484 mSessionStateCallback = mCurrentSession.getDeviceStateCallback(); 485 } 486 } 487 488 /** 489 * For use by backwards-compatibility code only. 490 */ 491 public void setSessionListener(StateCallbackKK sessionCallback) { 492 synchronized(mInterfaceLock) { 493 mSessionStateCallback = sessionCallback; 494 } 495 } 496 497 @Override 498 public CaptureRequest.Builder createCaptureRequest(int templateType) 499 throws CameraAccessException { 500 synchronized(mInterfaceLock) { 501 checkIfCameraClosedOrInError(); 502 503 CameraMetadataNative templatedRequest = new CameraMetadataNative(); 504 505 try { 506 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest); 507 } catch (CameraRuntimeException e) { 508 throw e.asChecked(); 509 } catch (RemoteException e) { 510 // impossible 511 return null; 512 } 513 514 CaptureRequest.Builder builder = 515 new CaptureRequest.Builder(templatedRequest); 516 517 return builder; 518 } 519 } 520 521 public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) 522 throws CameraAccessException { 523 if (DEBUG) { 524 Log.d(TAG, "calling capture"); 525 } 526 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 527 requestList.add(request); 528 return submitCaptureRequest(requestList, callback, handler, /*streaming*/false); 529 } 530 531 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, 532 Handler handler) throws CameraAccessException { 533 if (requests == null || requests.isEmpty()) { 534 throw new IllegalArgumentException("At least one request must be given"); 535 } 536 return submitCaptureRequest(requests, callback, handler, /*streaming*/false); 537 } 538 539 /** 540 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for 541 * starting and stopping repeating request and flushing. 542 * 543 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never 544 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered. 545 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair 546 * is added to the list mFrameNumberRequestPairs.</p> 547 * 548 * @param requestId the request ID of the current repeating request. 549 * 550 * @param lastFrameNumber last frame number returned from binder. 551 */ 552 private void checkEarlyTriggerSequenceComplete( 553 final int requestId, final long lastFrameNumber) { 554 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request 555 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately. 556 if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 557 final CaptureCallbackHolder holder; 558 int index = mCaptureCallbackMap.indexOfKey(requestId); 559 holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null; 560 if (holder != null) { 561 mCaptureCallbackMap.removeAt(index); 562 if (DEBUG) { 563 Log.v(TAG, String.format( 564 "remove holder for requestId %d, " 565 + "because lastFrame is %d.", 566 requestId, lastFrameNumber)); 567 } 568 } 569 570 if (holder != null) { 571 if (DEBUG) { 572 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because" 573 + " request did not reach HAL"); 574 } 575 576 Runnable resultDispatch = new Runnable() { 577 @Override 578 public void run() { 579 if (!CameraDeviceImpl.this.isClosed()) { 580 if (DEBUG) { 581 Log.d(TAG, String.format( 582 "early trigger sequence complete for request %d", 583 requestId)); 584 } 585 if (lastFrameNumber < Integer.MIN_VALUE 586 || lastFrameNumber > Integer.MAX_VALUE) { 587 throw new AssertionError(lastFrameNumber + " cannot be cast to int"); 588 } 589 holder.getCallback().onCaptureSequenceAborted( 590 CameraDeviceImpl.this, 591 requestId); 592 } 593 } 594 }; 595 holder.getHandler().post(resultDispatch); 596 } else { 597 Log.w(TAG, String.format( 598 "did not register callback to request %d", 599 requestId)); 600 } 601 } else { 602 mFrameNumberRequestPairs.add( 603 new SimpleEntry<Long, Integer>(lastFrameNumber, 604 requestId)); 605 // It is possible that the last frame has already arrived, so we need to check 606 // for sequence completion right away 607 checkAndFireSequenceComplete(); 608 } 609 } 610 611 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, 612 Handler handler, boolean repeating) throws CameraAccessException { 613 614 // Need a valid handler, or current thread needs to have a looper, if 615 // callback is valid 616 handler = checkHandler(handler, callback); 617 618 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 619 for (CaptureRequest request : requestList) { 620 if (request.getTargets().isEmpty()) { 621 throw new IllegalArgumentException( 622 "Each request must have at least one Surface target"); 623 } 624 625 for (Surface surface : request.getTargets()) { 626 if (surface == null) { 627 throw new IllegalArgumentException("Null Surface targets are not allowed"); 628 } 629 } 630 } 631 632 synchronized(mInterfaceLock) { 633 checkIfCameraClosedOrInError(); 634 int requestId; 635 636 if (repeating) { 637 stopRepeating(); 638 } 639 640 LongParcelable lastFrameNumberRef = new LongParcelable(); 641 try { 642 requestId = mRemoteDevice.submitRequestList(requestList, repeating, 643 /*out*/lastFrameNumberRef); 644 if (DEBUG) { 645 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber()); 646 } 647 } catch (CameraRuntimeException e) { 648 throw e.asChecked(); 649 } catch (RemoteException e) { 650 // impossible 651 return -1; 652 } 653 654 if (callback != null) { 655 mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback, 656 requestList, handler, repeating)); 657 } else { 658 if (DEBUG) { 659 Log.d(TAG, "Listen for request " + requestId + " is null"); 660 } 661 } 662 663 long lastFrameNumber = lastFrameNumberRef.getNumber(); 664 665 if (repeating) { 666 if (mRepeatingRequestId != REQUEST_ID_NONE) { 667 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 668 } 669 mRepeatingRequestId = requestId; 670 } else { 671 mFrameNumberRequestPairs.add( 672 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId)); 673 } 674 675 if (mIdle) { 676 mDeviceHandler.post(mCallOnActive); 677 } 678 mIdle = false; 679 680 return requestId; 681 } 682 } 683 684 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, 685 Handler handler) throws CameraAccessException { 686 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 687 requestList.add(request); 688 return submitCaptureRequest(requestList, callback, handler, /*streaming*/true); 689 } 690 691 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, 692 Handler handler) throws CameraAccessException { 693 if (requests == null || requests.isEmpty()) { 694 throw new IllegalArgumentException("At least one request must be given"); 695 } 696 return submitCaptureRequest(requests, callback, handler, /*streaming*/true); 697 } 698 699 public void stopRepeating() throws CameraAccessException { 700 701 synchronized(mInterfaceLock) { 702 checkIfCameraClosedOrInError(); 703 if (mRepeatingRequestId != REQUEST_ID_NONE) { 704 705 int requestId = mRepeatingRequestId; 706 mRepeatingRequestId = REQUEST_ID_NONE; 707 708 // Queue for deletion after in-flight requests finish 709 if (mCaptureCallbackMap.get(requestId) != null) { 710 mRepeatingRequestIdDeletedList.add(requestId); 711 } 712 713 try { 714 LongParcelable lastFrameNumberRef = new LongParcelable(); 715 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef); 716 long lastFrameNumber = lastFrameNumberRef.getNumber(); 717 718 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber); 719 720 } catch (CameraRuntimeException e) { 721 throw e.asChecked(); 722 } catch (RemoteException e) { 723 // impossible 724 return; 725 } 726 } 727 } 728 } 729 730 private void waitUntilIdle() throws CameraAccessException { 731 732 synchronized(mInterfaceLock) { 733 checkIfCameraClosedOrInError(); 734 735 if (mRepeatingRequestId != REQUEST_ID_NONE) { 736 throw new IllegalStateException("Active repeating request ongoing"); 737 } 738 try { 739 mRemoteDevice.waitUntilIdle(); 740 } catch (CameraRuntimeException e) { 741 throw e.asChecked(); 742 } catch (RemoteException e) { 743 // impossible 744 return; 745 } 746 } 747 } 748 749 public void flush() throws CameraAccessException { 750 synchronized(mInterfaceLock) { 751 checkIfCameraClosedOrInError(); 752 753 mDeviceHandler.post(mCallOnBusy); 754 755 // If already idle, just do a busy->idle transition immediately, don't actually 756 // flush. 757 if (mIdle) { 758 mDeviceHandler.post(mCallOnIdle); 759 return; 760 } 761 try { 762 LongParcelable lastFrameNumberRef = new LongParcelable(); 763 mRemoteDevice.flush(/*out*/lastFrameNumberRef); 764 if (mRepeatingRequestId != REQUEST_ID_NONE) { 765 long lastFrameNumber = lastFrameNumberRef.getNumber(); 766 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 767 mRepeatingRequestId = REQUEST_ID_NONE; 768 } 769 } catch (CameraRuntimeException e) { 770 throw e.asChecked(); 771 } catch (RemoteException e) { 772 // impossible 773 return; 774 } 775 } 776 } 777 778 @Override 779 public void close() { 780 synchronized (mInterfaceLock) { 781 try { 782 if (mRemoteDevice != null) { 783 mRemoteDevice.disconnect(); 784 } 785 } catch (CameraRuntimeException e) { 786 Log.e(TAG, "Exception while closing: ", e.asChecked()); 787 } catch (RemoteException e) { 788 // impossible 789 } 790 791 // Only want to fire the onClosed callback once; 792 // either a normal close where the remote device is valid 793 // or a close after a startup error (no remote device but in error state) 794 if (mRemoteDevice != null || mInError) { 795 mDeviceHandler.post(mCallOnClosed); 796 } 797 798 mRemoteDevice = null; 799 mInError = false; 800 } 801 } 802 803 @Override 804 protected void finalize() throws Throwable { 805 try { 806 close(); 807 } 808 finally { 809 super.finalize(); 810 } 811 } 812 813 /** 814 * <p>A callback for tracking the progress of a {@link CaptureRequest} 815 * submitted to the camera device.</p> 816 * 817 */ 818 public static abstract class CaptureCallback { 819 820 /** 821 * This constant is used to indicate that no images were captured for 822 * the request. 823 * 824 * @hide 825 */ 826 public static final int NO_FRAMES_CAPTURED = -1; 827 828 /** 829 * This method is called when the camera device has started capturing 830 * the output image for the request, at the beginning of image exposure. 831 * 832 * @see android.media.MediaActionSound 833 */ 834 public void onCaptureStarted(CameraDevice camera, 835 CaptureRequest request, long timestamp, long frameNumber) { 836 // default empty implementation 837 } 838 839 /** 840 * This method is called when some results from an image capture are 841 * available. 842 * 843 * @hide 844 */ 845 public void onCapturePartial(CameraDevice camera, 846 CaptureRequest request, CaptureResult result) { 847 // default empty implementation 848 } 849 850 /** 851 * This method is called when an image capture makes partial forward progress; some 852 * (but not all) results from an image capture are available. 853 * 854 */ 855 public void onCaptureProgressed(CameraDevice camera, 856 CaptureRequest request, CaptureResult partialResult) { 857 // default empty implementation 858 } 859 860 /** 861 * This method is called when an image capture has fully completed and all the 862 * result metadata is available. 863 */ 864 public void onCaptureCompleted(CameraDevice camera, 865 CaptureRequest request, TotalCaptureResult result) { 866 // default empty implementation 867 } 868 869 /** 870 * This method is called instead of {@link #onCaptureCompleted} when the 871 * camera device failed to produce a {@link CaptureResult} for the 872 * request. 873 */ 874 public void onCaptureFailed(CameraDevice camera, 875 CaptureRequest request, CaptureFailure failure) { 876 // default empty implementation 877 } 878 879 /** 880 * This method is called independently of the others in CaptureCallback, 881 * when a capture sequence finishes and all {@link CaptureResult} 882 * or {@link CaptureFailure} for it have been returned via this callback. 883 */ 884 public void onCaptureSequenceCompleted(CameraDevice camera, 885 int sequenceId, long frameNumber) { 886 // default empty implementation 887 } 888 889 /** 890 * This method is called independently of the others in CaptureCallback, 891 * when a capture sequence aborts before any {@link CaptureResult} 892 * or {@link CaptureFailure} for it have been returned via this callback. 893 */ 894 public void onCaptureSequenceAborted(CameraDevice camera, 895 int sequenceId) { 896 // default empty implementation 897 } 898 } 899 900 /** 901 * A callback for notifications about the state of a camera device, adding in the callbacks that 902 * were part of the earlier KK API design, but now only used internally. 903 */ 904 public static abstract class StateCallbackKK extends StateCallback { 905 /** 906 * The method called when a camera device has no outputs configured. 907 * 908 */ 909 public void onUnconfigured(CameraDevice camera) { 910 // Default empty implementation 911 } 912 913 /** 914 * The method called when a camera device begins processing 915 * {@link CaptureRequest capture requests}. 916 * 917 */ 918 public void onActive(CameraDevice camera) { 919 // Default empty implementation 920 } 921 922 /** 923 * The method called when a camera device is busy. 924 * 925 */ 926 public void onBusy(CameraDevice camera) { 927 // Default empty implementation 928 } 929 930 /** 931 * The method called when a camera device has finished processing all 932 * submitted capture requests and has reached an idle state. 933 * 934 */ 935 public void onIdle(CameraDevice camera) { 936 // Default empty implementation 937 } 938 } 939 940 static class CaptureCallbackHolder { 941 942 private final boolean mRepeating; 943 private final CaptureCallback mCallback; 944 private final List<CaptureRequest> mRequestList; 945 private final Handler mHandler; 946 947 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, 948 Handler handler, boolean repeating) { 949 if (callback == null || handler == null) { 950 throw new UnsupportedOperationException( 951 "Must have a valid handler and a valid callback"); 952 } 953 mRepeating = repeating; 954 mHandler = handler; 955 mRequestList = new ArrayList<CaptureRequest>(requestList); 956 mCallback = callback; 957 } 958 959 public boolean isRepeating() { 960 return mRepeating; 961 } 962 963 public CaptureCallback getCallback() { 964 return mCallback; 965 } 966 967 public CaptureRequest getRequest(int subsequenceId) { 968 if (subsequenceId >= mRequestList.size()) { 969 throw new IllegalArgumentException( 970 String.format( 971 "Requested subsequenceId %d is larger than request list size %d.", 972 subsequenceId, mRequestList.size())); 973 } else { 974 if (subsequenceId < 0) { 975 throw new IllegalArgumentException(String.format( 976 "Requested subsequenceId %d is negative", subsequenceId)); 977 } else { 978 return mRequestList.get(subsequenceId); 979 } 980 } 981 } 982 983 public CaptureRequest getRequest() { 984 return getRequest(0); 985 } 986 987 public Handler getHandler() { 988 return mHandler; 989 } 990 991 } 992 993 /** 994 * This class tracks the last frame number for submitted requests. 995 */ 996 public class FrameNumberTracker { 997 998 private long mCompletedFrameNumber = -1; 999 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>(); 1000 /** Map frame numbers to list of partial results */ 1001 private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); 1002 1003 private void update() { 1004 Iterator<Long> iter = mFutureErrorSet.iterator(); 1005 while (iter.hasNext()) { 1006 long errorFrameNumber = iter.next(); 1007 if (errorFrameNumber == mCompletedFrameNumber + 1) { 1008 mCompletedFrameNumber++; 1009 iter.remove(); 1010 } else { 1011 break; 1012 } 1013 } 1014 } 1015 1016 /** 1017 * This function is called every time when a result or an error is received. 1018 * @param frameNumber the frame number corresponding to the result or error 1019 * @param isError true if it is an error, false if it is not an error 1020 */ 1021 public void updateTracker(long frameNumber, boolean isError) { 1022 if (isError) { 1023 mFutureErrorSet.add(frameNumber); 1024 } else { 1025 /** 1026 * HAL cannot send an OnResultReceived for frame N unless it knows for 1027 * sure that all frames prior to N have either errored out or completed. 1028 * So if the current frame is not an error, then all previous frames 1029 * should have arrived. The following line checks whether this holds. 1030 */ 1031 if (frameNumber != mCompletedFrameNumber + 1) { 1032 Log.e(TAG, String.format( 1033 "result frame number %d comes out of order, should be %d + 1", 1034 frameNumber, mCompletedFrameNumber)); 1035 // Continue on to set the completed frame number to this frame anyway, 1036 // to be robust to lower-level errors and allow for clean shutdowns. 1037 } 1038 mCompletedFrameNumber = frameNumber; 1039 } 1040 update(); 1041 } 1042 1043 /** 1044 * This function is called every time a result has been completed. 1045 * 1046 * <p>It keeps a track of all the partial results already created for a particular 1047 * frame number.</p> 1048 * 1049 * @param frameNumber the frame number corresponding to the result 1050 * @param result the total or partial result 1051 * @param partial {@true} if the result is partial, {@code false} if total 1052 */ 1053 public void updateTracker(long frameNumber, CaptureResult result, boolean partial) { 1054 1055 if (!partial) { 1056 // Update the total result's frame status as being successful 1057 updateTracker(frameNumber, /*isError*/false); 1058 // Don't keep a list of total results, we don't need to track them 1059 return; 1060 } 1061 1062 if (result == null) { 1063 // Do not record blank results; this also means there will be no total result 1064 // so it doesn't matter that the partials were not recorded 1065 return; 1066 } 1067 1068 // Partial results must be aggregated in-order for that frame number 1069 List<CaptureResult> partials = mPartialResults.get(frameNumber); 1070 if (partials == null) { 1071 partials = new ArrayList<>(); 1072 mPartialResults.put(frameNumber, partials); 1073 } 1074 1075 partials.add(result); 1076 } 1077 1078 /** 1079 * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. 1080 * 1081 * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} 1082 * is called again with new partials for that frame number).</p> 1083 * 1084 * @param frameNumber the frame number corresponding to the result 1085 * @return a list of partial results for that frame with at least 1 element, 1086 * or {@code null} if there were no partials recorded for that frame 1087 */ 1088 public List<CaptureResult> popPartialResults(long frameNumber) { 1089 return mPartialResults.remove(frameNumber); 1090 } 1091 1092 public long getCompletedFrameNumber() { 1093 return mCompletedFrameNumber; 1094 } 1095 1096 } 1097 1098 private void checkAndFireSequenceComplete() { 1099 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); 1100 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator(); 1101 while (iter.hasNext()) { 1102 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next(); 1103 if (frameNumberRequestPair.getKey() <= completedFrameNumber) { 1104 1105 // remove request from mCaptureCallbackMap 1106 final int requestId = frameNumberRequestPair.getValue(); 1107 final CaptureCallbackHolder holder; 1108 synchronized(mInterfaceLock) { 1109 if (mRemoteDevice == null) { 1110 Log.w(TAG, "Camera closed while checking sequences"); 1111 return; 1112 } 1113 1114 int index = mCaptureCallbackMap.indexOfKey(requestId); 1115 holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) 1116 : null; 1117 if (holder != null) { 1118 mCaptureCallbackMap.removeAt(index); 1119 if (DEBUG) { 1120 Log.v(TAG, String.format( 1121 "remove holder for requestId %d, " 1122 + "because lastFrame %d is <= %d", 1123 requestId, frameNumberRequestPair.getKey(), 1124 completedFrameNumber)); 1125 } 1126 } 1127 } 1128 iter.remove(); 1129 1130 // Call onCaptureSequenceCompleted 1131 if (holder != null) { 1132 Runnable resultDispatch = new Runnable() { 1133 @Override 1134 public void run() { 1135 if (!CameraDeviceImpl.this.isClosed()){ 1136 if (DEBUG) { 1137 Log.d(TAG, String.format( 1138 "fire sequence complete for request %d", 1139 requestId)); 1140 } 1141 1142 long lastFrameNumber = frameNumberRequestPair.getKey(); 1143 if (lastFrameNumber < Integer.MIN_VALUE 1144 || lastFrameNumber > Integer.MAX_VALUE) { 1145 throw new AssertionError(lastFrameNumber 1146 + " cannot be cast to int"); 1147 } 1148 holder.getCallback().onCaptureSequenceCompleted( 1149 CameraDeviceImpl.this, 1150 requestId, 1151 lastFrameNumber); 1152 } 1153 } 1154 }; 1155 holder.getHandler().post(resultDispatch); 1156 } 1157 1158 } 1159 } 1160 } 1161 1162 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 1163 // 1164 // Constants below need to be kept up-to-date with 1165 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h 1166 // 1167 1168 // 1169 // Error codes for onCameraError 1170 // 1171 1172 /** 1173 * Camera has been disconnected 1174 */ 1175 public static final int ERROR_CAMERA_DISCONNECTED = 0; 1176 /** 1177 * Camera has encountered a device-level error 1178 * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE 1179 */ 1180 public static final int ERROR_CAMERA_DEVICE = 1; 1181 /** 1182 * Camera has encountered a service-level error 1183 * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE 1184 */ 1185 public static final int ERROR_CAMERA_SERVICE = 2; 1186 /** 1187 * Camera has encountered an error processing a single request. 1188 */ 1189 public static final int ERROR_CAMERA_REQUEST = 3; 1190 /** 1191 * Camera has encountered an error producing metadata for a single capture 1192 */ 1193 public static final int ERROR_CAMERA_RESULT = 4; 1194 /** 1195 * Camera has encountered an error producing an image buffer for a single capture 1196 */ 1197 public static final int ERROR_CAMERA_BUFFER = 5; 1198 1199 @Override 1200 public IBinder asBinder() { 1201 return this; 1202 } 1203 1204 @Override 1205 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { 1206 if (DEBUG) { 1207 Log.d(TAG, String.format( 1208 "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", 1209 errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), 1210 resultExtras.getSubsequenceId())); 1211 } 1212 1213 synchronized(mInterfaceLock) { 1214 if (mRemoteDevice == null) { 1215 return; // Camera already closed 1216 } 1217 1218 switch (errorCode) { 1219 case ERROR_CAMERA_DISCONNECTED: 1220 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected); 1221 break; 1222 default: 1223 Log.e(TAG, "Unknown error from camera device: " + errorCode); 1224 // no break 1225 case ERROR_CAMERA_DEVICE: 1226 case ERROR_CAMERA_SERVICE: 1227 mInError = true; 1228 Runnable r = new Runnable() { 1229 @Override 1230 public void run() { 1231 if (!CameraDeviceImpl.this.isClosed()) { 1232 mDeviceCallback.onError(CameraDeviceImpl.this, errorCode); 1233 } 1234 } 1235 }; 1236 CameraDeviceImpl.this.mDeviceHandler.post(r); 1237 break; 1238 case ERROR_CAMERA_REQUEST: 1239 case ERROR_CAMERA_RESULT: 1240 case ERROR_CAMERA_BUFFER: 1241 onCaptureErrorLocked(errorCode, resultExtras); 1242 break; 1243 } 1244 } 1245 } 1246 1247 @Override 1248 public void onDeviceIdle() { 1249 if (DEBUG) { 1250 Log.d(TAG, "Camera now idle"); 1251 } 1252 synchronized(mInterfaceLock) { 1253 if (mRemoteDevice == null) return; // Camera already closed 1254 1255 if (!CameraDeviceImpl.this.mIdle) { 1256 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle); 1257 } 1258 CameraDeviceImpl.this.mIdle = true; 1259 } 1260 } 1261 1262 @Override 1263 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 1264 int requestId = resultExtras.getRequestId(); 1265 final long frameNumber = resultExtras.getFrameNumber(); 1266 1267 if (DEBUG) { 1268 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber); 1269 } 1270 final CaptureCallbackHolder holder; 1271 1272 synchronized(mInterfaceLock) { 1273 if (mRemoteDevice == null) return; // Camera already closed 1274 1275 // Get the callback for this frame ID, if there is one 1276 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1277 1278 if (holder == null) { 1279 return; 1280 } 1281 1282 if (isClosed()) return; 1283 1284 // Dispatch capture start notice 1285 holder.getHandler().post( 1286 new Runnable() { 1287 @Override 1288 public void run() { 1289 if (!CameraDeviceImpl.this.isClosed()) { 1290 holder.getCallback().onCaptureStarted( 1291 CameraDeviceImpl.this, 1292 holder.getRequest(resultExtras.getSubsequenceId()), 1293 timestamp, frameNumber); 1294 } 1295 } 1296 }); 1297 1298 } 1299 } 1300 1301 @Override 1302 public void onResultReceived(CameraMetadataNative result, 1303 CaptureResultExtras resultExtras) throws RemoteException { 1304 1305 int requestId = resultExtras.getRequestId(); 1306 long frameNumber = resultExtras.getFrameNumber(); 1307 1308 if (DEBUG) { 1309 Log.v(TAG, "Received result frame " + frameNumber + " for id " 1310 + requestId); 1311 } 1312 1313 synchronized(mInterfaceLock) { 1314 if (mRemoteDevice == null) return; // Camera already closed 1315 1316 // TODO: Handle CameraCharacteristics access from CaptureResult correctly. 1317 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, 1318 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); 1319 1320 final CaptureCallbackHolder holder = 1321 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1322 1323 boolean isPartialResult = 1324 (resultExtras.getPartialResultCount() < mTotalPartialCount); 1325 1326 // Check if we have a callback for this 1327 if (holder == null) { 1328 if (DEBUG) { 1329 Log.d(TAG, 1330 "holder is null, early return at frame " 1331 + frameNumber); 1332 } 1333 1334 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult); 1335 1336 return; 1337 } 1338 1339 if (isClosed()) { 1340 if (DEBUG) { 1341 Log.d(TAG, 1342 "camera is closed, early return at frame " 1343 + frameNumber); 1344 } 1345 1346 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult); 1347 return; 1348 } 1349 1350 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); 1351 1352 Runnable resultDispatch = null; 1353 1354 CaptureResult finalResult; 1355 1356 // Either send a partial result or the final capture completed result 1357 if (isPartialResult) { 1358 final CaptureResult resultAsCapture = 1359 new CaptureResult(result, request, resultExtras); 1360 1361 // Partial result 1362 resultDispatch = new Runnable() { 1363 @Override 1364 public void run() { 1365 if (!CameraDeviceImpl.this.isClosed()){ 1366 holder.getCallback().onCaptureProgressed( 1367 CameraDeviceImpl.this, 1368 request, 1369 resultAsCapture); 1370 } 1371 } 1372 }; 1373 1374 finalResult = resultAsCapture; 1375 } else { 1376 List<CaptureResult> partialResults = 1377 mFrameNumberTracker.popPartialResults(frameNumber); 1378 1379 final TotalCaptureResult resultAsCapture = 1380 new TotalCaptureResult(result, request, resultExtras, partialResults); 1381 1382 // Final capture result 1383 resultDispatch = new Runnable() { 1384 @Override 1385 public void run() { 1386 if (!CameraDeviceImpl.this.isClosed()){ 1387 holder.getCallback().onCaptureCompleted( 1388 CameraDeviceImpl.this, 1389 request, 1390 resultAsCapture); 1391 } 1392 } 1393 }; 1394 1395 finalResult = resultAsCapture; 1396 } 1397 1398 holder.getHandler().post(resultDispatch); 1399 1400 // Collect the partials for a total result; or mark the frame as totally completed 1401 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult); 1402 1403 // Fire onCaptureSequenceCompleted 1404 if (!isPartialResult) { 1405 checkAndFireSequenceComplete(); 1406 } 1407 } 1408 } 1409 1410 /** 1411 * Called by onDeviceError for handling single-capture failures. 1412 */ 1413 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { 1414 1415 final int requestId = resultExtras.getRequestId(); 1416 final int subsequenceId = resultExtras.getSubsequenceId(); 1417 final long frameNumber = resultExtras.getFrameNumber(); 1418 final CaptureCallbackHolder holder = 1419 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1420 1421 final CaptureRequest request = holder.getRequest(subsequenceId); 1422 1423 // No way to report buffer errors right now 1424 if (errorCode == ERROR_CAMERA_BUFFER) { 1425 Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber)); 1426 return; 1427 } 1428 1429 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); 1430 1431 // This is only approximate - exact handling needs the camera service and HAL to 1432 // disambiguate between request failures to due abort and due to real errors. 1433 // For now, assume that if the session believes we're mid-abort, then the error 1434 // is due to abort. 1435 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? 1436 CaptureFailure.REASON_FLUSHED : 1437 CaptureFailure.REASON_ERROR; 1438 1439 final CaptureFailure failure = new CaptureFailure( 1440 request, 1441 reason, 1442 /*dropped*/ mayHaveBuffers, 1443 requestId, 1444 frameNumber); 1445 1446 Runnable failureDispatch = new Runnable() { 1447 @Override 1448 public void run() { 1449 if (!CameraDeviceImpl.this.isClosed()){ 1450 holder.getCallback().onCaptureFailed( 1451 CameraDeviceImpl.this, 1452 request, 1453 failure); 1454 } 1455 } 1456 }; 1457 holder.getHandler().post(failureDispatch); 1458 1459 // Fire onCaptureSequenceCompleted if appropriate 1460 if (DEBUG) { 1461 Log.v(TAG, String.format("got error frame %d", frameNumber)); 1462 } 1463 mFrameNumberTracker.updateTracker(frameNumber, /*error*/true); 1464 checkAndFireSequenceComplete(); 1465 } 1466 1467 } // public class CameraDeviceCallbacks 1468 1469 /** 1470 * Default handler management. 1471 * 1472 * <p> 1473 * If handler is null, get the current thread's 1474 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}. 1475 * </p> 1476 */ 1477 static Handler checkHandler(Handler handler) { 1478 if (handler == null) { 1479 Looper looper = Looper.myLooper(); 1480 if (looper == null) { 1481 throw new IllegalArgumentException( 1482 "No handler given, and current thread has no looper!"); 1483 } 1484 handler = new Handler(looper); 1485 } 1486 return handler; 1487 } 1488 1489 /** 1490 * Default handler management, conditional on there being a callback. 1491 * 1492 * <p>If the callback isn't null, check the handler, otherwise pass it through.</p> 1493 */ 1494 static <T> Handler checkHandler(Handler handler, T callback) { 1495 if (callback != null) { 1496 return checkHandler(handler); 1497 } 1498 return handler; 1499 } 1500 1501 private void checkIfCameraClosedOrInError() throws CameraAccessException { 1502 if (mInError) { 1503 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, 1504 "The camera device has encountered a serious error"); 1505 } 1506 if (mRemoteDevice == null) { 1507 throw new IllegalStateException("CameraDevice was already closed"); 1508 } 1509 } 1510 1511 /** Whether the camera device has started to close (may not yet have finished) */ 1512 private boolean isClosed() { 1513 return mClosing; 1514 } 1515 1516 private CameraCharacteristics getCharacteristics() { 1517 return mCharacteristics; 1518 } 1519} 1520