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