CameraDeviceImpl.java revision 83159151f618c79040a2e800de8cc78f22bf760b
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.CaptureRequest; 25import android.hardware.camera2.CaptureResult; 26import android.hardware.camera2.ICameraDeviceCallbacks; 27import android.hardware.camera2.ICameraDeviceUser; 28import android.hardware.camera2.TotalCaptureResult; 29import android.hardware.camera2.utils.CameraBinderDecorator; 30import android.hardware.camera2.utils.CameraRuntimeException; 31import android.hardware.camera2.utils.CloseableLock; 32import android.hardware.camera2.utils.CloseableLock.ScopedLock; 33import android.hardware.camera2.utils.LongParcelable; 34import android.os.Handler; 35import android.os.IBinder; 36import android.os.Looper; 37import android.os.RemoteException; 38import android.util.Log; 39import android.util.SparseArray; 40import android.view.Surface; 41 42import java.util.AbstractMap.SimpleEntry; 43import java.util.ArrayList; 44import java.util.HashSet; 45import java.util.Iterator; 46import java.util.List; 47import java.util.TreeSet; 48 49/** 50 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate 51 */ 52public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { 53 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 private final CloseableLock mCloseLock; 63 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); 64 65 private final StateListener mDeviceListener; 66 private volatile StateListener mSessionStateListener; 67 private final Handler mDeviceHandler; 68 69 private volatile boolean mClosing = false; 70 private boolean mInError = false; 71 private boolean mIdle = true; 72 73 /** map request IDs to listener/request data */ 74 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap = 75 new SparseArray<CaptureListenerHolder>(); 76 77 private int mRepeatingRequestId = REQUEST_ID_NONE; 78 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>(); 79 // Map stream IDs to Surfaces 80 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>(); 81 82 private final String mCameraId; 83 private final CameraCharacteristics mCharacteristics; 84 private final int mTotalPartialCount; 85 86 /** 87 * A list tracking request and its expected last frame. 88 * Updated when calling ICameraDeviceUser methods. 89 */ 90 private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>> 91 mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>(); 92 93 /** 94 * An object tracking received frame numbers. 95 * Updated when receiving callbacks from ICameraDeviceCallbacks. 96 */ 97 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker(); 98 99 private CameraCaptureSessionImpl mCurrentSession; 100 101 // Runnables for all state transitions, except error, which needs the 102 // error code argument 103 104 private final Runnable mCallOnOpened = new Runnable() { 105 @Override 106 public void run() { 107 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 108 if (scopedLock == null) return; // Camera already closed 109 110 StateListener sessionListener = mSessionStateListener; 111 if (sessionListener != null) { 112 sessionListener.onOpened(CameraDeviceImpl.this); 113 } 114 mDeviceListener.onOpened(CameraDeviceImpl.this); 115 } 116 } 117 }; 118 119 private final Runnable mCallOnUnconfigured = new Runnable() { 120 @Override 121 public void run() { 122 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 123 if (scopedLock == null) return; // Camera already closed 124 125 StateListener sessionListener = mSessionStateListener; 126 if (sessionListener != null) { 127 sessionListener.onUnconfigured(CameraDeviceImpl.this); 128 } 129 mDeviceListener.onUnconfigured(CameraDeviceImpl.this); 130 } 131 } 132 }; 133 134 private final Runnable mCallOnActive = new Runnable() { 135 @Override 136 public void run() { 137 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 138 if (scopedLock == null) return; // Camera already closed 139 140 StateListener sessionListener = mSessionStateListener; 141 if (sessionListener != null) { 142 sessionListener.onActive(CameraDeviceImpl.this); 143 } 144 mDeviceListener.onActive(CameraDeviceImpl.this); 145 } 146 } 147 }; 148 149 private final Runnable mCallOnBusy = new Runnable() { 150 @Override 151 public void run() { 152 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 153 if (scopedLock == null) return; // Camera already closed 154 155 StateListener sessionListener = mSessionStateListener; 156 if (sessionListener != null) { 157 sessionListener.onBusy(CameraDeviceImpl.this); 158 } 159 mDeviceListener.onBusy(CameraDeviceImpl.this); 160 } 161 } 162 }; 163 164 private final Runnable mCallOnClosed = new Runnable() { 165 private boolean mClosedOnce = false; 166 167 @Override 168 public void run() { 169 if (mClosedOnce) { 170 throw new AssertionError("Don't post #onClosed more than once"); 171 } 172 173 StateListener sessionListener = mSessionStateListener; 174 if (sessionListener != null) { 175 sessionListener.onClosed(CameraDeviceImpl.this); 176 } 177 mDeviceListener.onClosed(CameraDeviceImpl.this); 178 mClosedOnce = true; 179 } 180 }; 181 182 private final Runnable mCallOnIdle = new Runnable() { 183 @Override 184 public void run() { 185 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 186 if (scopedLock == null) return; // Camera already closed 187 188 StateListener sessionListener = mSessionStateListener; 189 if (sessionListener != null) { 190 sessionListener.onIdle(CameraDeviceImpl.this); 191 } 192 mDeviceListener.onIdle(CameraDeviceImpl.this); 193 } 194 } 195 }; 196 197 private final Runnable mCallOnDisconnected = new Runnable() { 198 @Override 199 public void run() { 200 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 201 if (scopedLock == null) return; // Camera already closed 202 203 StateListener sessionListener = mSessionStateListener; 204 if (sessionListener != null) { 205 sessionListener.onDisconnected(CameraDeviceImpl.this); 206 } 207 mDeviceListener.onDisconnected(CameraDeviceImpl.this); 208 } 209 } 210 }; 211 212 public CameraDeviceImpl(String cameraId, StateListener listener, Handler handler, 213 CameraCharacteristics characteristics) { 214 if (cameraId == null || listener == null || handler == null || characteristics == null) { 215 throw new IllegalArgumentException("Null argument given"); 216 } 217 mCameraId = cameraId; 218 mDeviceListener = listener; 219 mDeviceHandler = handler; 220 mCharacteristics = characteristics; 221 mCloseLock = new CloseableLock(/*name*/"CD-" + mCameraId); 222 223 final int MAX_TAG_LEN = 23; 224 String tag = String.format("CameraDevice-JV-%s", mCameraId); 225 if (tag.length() > MAX_TAG_LEN) { 226 tag = tag.substring(0, MAX_TAG_LEN); 227 } 228 TAG = tag; 229 DEBUG = Log.isLoggable(TAG, Log.DEBUG); 230 231 Integer partialCount = 232 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT); 233 if (partialCount == null) { 234 // 1 means partial result is not supported. 235 mTotalPartialCount = 1; 236 } else { 237 mTotalPartialCount = partialCount; 238 } 239 } 240 241 public CameraDeviceCallbacks getCallbacks() { 242 return mCallbacks; 243 } 244 245 public void setRemoteDevice(ICameraDeviceUser remoteDevice) { 246 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 247 // TODO: Move from decorator to direct binder-mediated exceptions 248 // If setRemoteFailure already called, do nothing 249 if (mInError) return; 250 251 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice); 252 253 mDeviceHandler.post(mCallOnOpened); 254 mDeviceHandler.post(mCallOnUnconfigured); 255 } 256 } 257 258 /** 259 * Call to indicate failed connection to a remote camera device. 260 * 261 * <p>This places the camera device in the error state and informs the listener. 262 * Use in place of setRemoteDevice() when startup fails.</p> 263 */ 264 public void setRemoteFailure(final CameraRuntimeException failure) { 265 int failureCode = StateListener.ERROR_CAMERA_DEVICE; 266 boolean failureIsError = true; 267 268 switch (failure.getReason()) { 269 case CameraAccessException.CAMERA_IN_USE: 270 failureCode = StateListener.ERROR_CAMERA_IN_USE; 271 break; 272 case CameraAccessException.MAX_CAMERAS_IN_USE: 273 failureCode = StateListener.ERROR_MAX_CAMERAS_IN_USE; 274 break; 275 case CameraAccessException.CAMERA_DISABLED: 276 failureCode = StateListener.ERROR_CAMERA_DISABLED; 277 break; 278 case CameraAccessException.CAMERA_DISCONNECTED: 279 failureIsError = false; 280 break; 281 case CameraAccessException.CAMERA_ERROR: 282 failureCode = StateListener.ERROR_CAMERA_DEVICE; 283 break; 284 default: 285 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason()); 286 break; 287 } 288 final int code = failureCode; 289 final boolean isError = failureIsError; 290 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 291 if (scopedLock == null) return; // Camera already closed, can't go to error state 292 293 mInError = true; 294 mDeviceHandler.post(new Runnable() { 295 @Override 296 public void run() { 297 if (isError) { 298 mDeviceListener.onError(CameraDeviceImpl.this, code); 299 } else { 300 mDeviceListener.onDisconnected(CameraDeviceImpl.this); 301 } 302 } 303 }); 304 } 305 } 306 307 @Override 308 public String getId() { 309 return mCameraId; 310 } 311 312 public void configureOutputs(List<Surface> outputs) throws CameraAccessException { 313 // Treat a null input the same an empty list 314 if (outputs == null) { 315 outputs = new ArrayList<Surface>(); 316 } 317 try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) { 318 checkIfCameraClosedOrInError(); 319 320 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create 321 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete 322 323 // Determine which streams need to be created, which to be deleted 324 for (int i = 0; i < mConfiguredOutputs.size(); ++i) { 325 int streamId = mConfiguredOutputs.keyAt(i); 326 Surface s = mConfiguredOutputs.valueAt(i); 327 328 if (!outputs.contains(s)) { 329 deleteList.add(streamId); 330 } else { 331 addSet.remove(s); // Don't create a stream previously created 332 } 333 } 334 335 mDeviceHandler.post(mCallOnBusy); 336 stopRepeating(); 337 338 try { 339 waitUntilIdle(); 340 341 mRemoteDevice.beginConfigure(); 342 // Delete all streams first (to free up HW resources) 343 for (Integer streamId : deleteList) { 344 mRemoteDevice.deleteStream(streamId); 345 mConfiguredOutputs.delete(streamId); 346 } 347 348 // Add all new streams 349 for (Surface s : addSet) { 350 // TODO: remove width,height,format since we are ignoring 351 // it. 352 int streamId = mRemoteDevice.createStream(0, 0, 0, s); 353 mConfiguredOutputs.put(streamId, s); 354 } 355 356 mRemoteDevice.endConfigure(); 357 } catch (CameraRuntimeException e) { 358 if (e.getReason() == CAMERA_IN_USE) { 359 throw new IllegalStateException("The camera is currently busy." + 360 " You must wait until the previous operation completes."); 361 } 362 363 throw e.asChecked(); 364 } catch (RemoteException e) { 365 // impossible 366 return; 367 } 368 369 if (outputs.size() > 0) { 370 mDeviceHandler.post(mCallOnIdle); 371 } else { 372 mDeviceHandler.post(mCallOnUnconfigured); 373 } 374 } 375 } 376 377 @Override 378 public void createCaptureSession(List<Surface> outputs, 379 CameraCaptureSession.StateListener listener, Handler handler) 380 throws CameraAccessException { 381 try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) { 382 if (DEBUG) { 383 Log.d(TAG, "createCaptureSession"); 384 } 385 386 checkIfCameraClosedOrInError(); 387 388 // TODO: we must be in UNCONFIGURED mode to begin with, or using another session 389 390 // TODO: dont block for this 391 boolean configureSuccess = true; 392 CameraAccessException pendingException = null; 393 try { 394 configureOutputs(outputs); // and then block until IDLE 395 } catch (CameraAccessException e) { 396 configureSuccess = false; 397 pendingException = e; 398 if (DEBUG) { 399 Log.v(TAG, "createCaptureSession - failed with exception ", e); 400 } 401 } 402 403 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. 404 CameraCaptureSessionImpl newSession = 405 new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler, 406 configureSuccess); 407 408 if (mCurrentSession != null) { 409 mCurrentSession.replaceSessionClose(newSession); 410 } 411 412 // TODO: wait until current session closes, then create the new session 413 mCurrentSession = newSession; 414 415 if (pendingException != null) { 416 throw pendingException; 417 } 418 419 mSessionStateListener = mCurrentSession.getDeviceStateListener(); 420 } 421 } 422 423 @Override 424 public CaptureRequest.Builder createCaptureRequest(int templateType) 425 throws CameraAccessException { 426 try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) { 427 checkIfCameraClosedOrInError(); 428 429 CameraMetadataNative templatedRequest = new CameraMetadataNative(); 430 431 try { 432 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest); 433 } catch (CameraRuntimeException e) { 434 throw e.asChecked(); 435 } catch (RemoteException e) { 436 // impossible 437 return null; 438 } 439 440 CaptureRequest.Builder builder = 441 new CaptureRequest.Builder(templatedRequest); 442 443 return builder; 444 } 445 } 446 447 public int capture(CaptureRequest request, CaptureListener listener, Handler handler) 448 throws CameraAccessException { 449 if (DEBUG) { 450 Log.d(TAG, "calling capture"); 451 } 452 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 453 requestList.add(request); 454 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false); 455 } 456 457 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener, 458 Handler handler) throws CameraAccessException { 459 if (requests == null || requests.isEmpty()) { 460 throw new IllegalArgumentException("At least one request must be given"); 461 } 462 return submitCaptureRequest(requests, listener, handler, /*streaming*/false); 463 } 464 465 /** 466 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for 467 * starting and stopping repeating request and flushing. 468 * 469 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never 470 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered. 471 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair 472 * is added to the list mFrameNumberRequestPairs.</p> 473 * 474 * @param requestId the request ID of the current repeating request. 475 * 476 * @param lastFrameNumber last frame number returned from binder. 477 */ 478 private void checkEarlyTriggerSequenceComplete( 479 final int requestId, final long lastFrameNumber) { 480 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request 481 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately. 482 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) { 483 final CaptureListenerHolder holder; 484 int index = mCaptureListenerMap.indexOfKey(requestId); 485 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null; 486 if (holder != null) { 487 mCaptureListenerMap.removeAt(index); 488 if (DEBUG) { 489 Log.v(TAG, String.format( 490 "remove holder for requestId %d, " 491 + "because lastFrame is %d.", 492 requestId, lastFrameNumber)); 493 } 494 } 495 496 if (holder != null) { 497 if (DEBUG) { 498 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because" 499 + " request did not reach HAL"); 500 } 501 502 Runnable resultDispatch = new Runnable() { 503 @Override 504 public void run() { 505 if (!CameraDeviceImpl.this.isClosed()) { 506 if (DEBUG) { 507 Log.d(TAG, String.format( 508 "early trigger sequence complete for request %d", 509 requestId)); 510 } 511 if (lastFrameNumber < Integer.MIN_VALUE 512 || lastFrameNumber > Integer.MAX_VALUE) { 513 throw new AssertionError(lastFrameNumber + " cannot be cast to int"); 514 } 515 holder.getListener().onCaptureSequenceAborted( 516 CameraDeviceImpl.this, 517 requestId); 518 } 519 } 520 }; 521 holder.getHandler().post(resultDispatch); 522 } else { 523 Log.w(TAG, String.format( 524 "did not register listener to request %d", 525 requestId)); 526 } 527 } else { 528 mFrameNumberRequestPairs.add( 529 new SimpleEntry<Long, Integer>(lastFrameNumber, 530 requestId)); 531 } 532 } 533 534 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener, 535 Handler handler, boolean repeating) throws CameraAccessException { 536 537 // Need a valid handler, or current thread needs to have a looper, if 538 // listener is valid 539 if (listener != null) { 540 handler = checkHandler(handler); 541 } 542 543 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 544 for (CaptureRequest request : requestList) { 545 if (request.getTargets().isEmpty()) { 546 throw new IllegalArgumentException( 547 "Each request must have at least one Surface target"); 548 } 549 550 for (Surface surface : request.getTargets()) { 551 if (surface == null) { 552 throw new IllegalArgumentException("Null Surface targets are not allowed"); 553 } 554 } 555 } 556 557 try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) { 558 checkIfCameraClosedOrInError(); 559 int requestId; 560 561 if (repeating) { 562 stopRepeating(); 563 } 564 565 LongParcelable lastFrameNumberRef = new LongParcelable(); 566 try { 567 requestId = mRemoteDevice.submitRequestList(requestList, repeating, 568 /*out*/lastFrameNumberRef); 569 if (DEBUG) { 570 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber()); 571 } 572 } catch (CameraRuntimeException e) { 573 throw e.asChecked(); 574 } catch (RemoteException e) { 575 // impossible 576 return -1; 577 } 578 579 if (listener != null) { 580 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, 581 requestList, handler, repeating)); 582 } else { 583 if (DEBUG) { 584 Log.d(TAG, "Listen for request " + requestId + " is null"); 585 } 586 } 587 588 long lastFrameNumber = lastFrameNumberRef.getNumber(); 589 590 if (repeating) { 591 if (mRepeatingRequestId != REQUEST_ID_NONE) { 592 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 593 } 594 mRepeatingRequestId = requestId; 595 } else { 596 mFrameNumberRequestPairs.add( 597 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId)); 598 } 599 600 if (mIdle) { 601 mDeviceHandler.post(mCallOnActive); 602 } 603 mIdle = false; 604 605 return requestId; 606 } 607 } 608 609 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener, 610 Handler handler) throws CameraAccessException { 611 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 612 requestList.add(request); 613 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true); 614 } 615 616 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, 617 Handler handler) throws CameraAccessException { 618 if (requests == null || requests.isEmpty()) { 619 throw new IllegalArgumentException("At least one request must be given"); 620 } 621 return submitCaptureRequest(requests, listener, handler, /*streaming*/true); 622 } 623 624 public void stopRepeating() throws CameraAccessException { 625 626 try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) { 627 checkIfCameraClosedOrInError(); 628 if (mRepeatingRequestId != REQUEST_ID_NONE) { 629 630 int requestId = mRepeatingRequestId; 631 mRepeatingRequestId = REQUEST_ID_NONE; 632 633 // Queue for deletion after in-flight requests finish 634 if (mCaptureListenerMap.get(requestId) != null) { 635 mRepeatingRequestIdDeletedList.add(requestId); 636 } 637 638 try { 639 LongParcelable lastFrameNumberRef = new LongParcelable(); 640 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef); 641 long lastFrameNumber = lastFrameNumberRef.getNumber(); 642 643 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber); 644 645 } catch (CameraRuntimeException e) { 646 throw e.asChecked(); 647 } catch (RemoteException e) { 648 // impossible 649 return; 650 } 651 } 652 } 653 } 654 655 private void waitUntilIdle() throws CameraAccessException { 656 657 try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) { 658 checkIfCameraClosedOrInError(); 659 if (mRepeatingRequestId != REQUEST_ID_NONE) { 660 throw new IllegalStateException("Active repeating request ongoing"); 661 } 662 663 try { 664 mRemoteDevice.waitUntilIdle(); 665 } catch (CameraRuntimeException e) { 666 throw e.asChecked(); 667 } catch (RemoteException e) { 668 // impossible 669 return; 670 } 671 672 mRepeatingRequestId = REQUEST_ID_NONE; 673 } 674 } 675 676 public void flush() throws CameraAccessException { 677 try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) { 678 checkIfCameraClosedOrInError(); 679 680 mDeviceHandler.post(mCallOnBusy); 681 try { 682 LongParcelable lastFrameNumberRef = new LongParcelable(); 683 mRemoteDevice.flush(/*out*/lastFrameNumberRef); 684 if (mRepeatingRequestId != REQUEST_ID_NONE) { 685 long lastFrameNumber = lastFrameNumberRef.getNumber(); 686 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 687 mRepeatingRequestId = REQUEST_ID_NONE; 688 } 689 } catch (CameraRuntimeException e) { 690 throw e.asChecked(); 691 } catch (RemoteException e) { 692 // impossible 693 return; 694 } 695 } 696 } 697 698 @Override 699 public void close() { 700 mClosing = true; 701 // Acquire exclusive lock, close, release (idempotent) 702 mCloseLock.close(); 703 704 /* 705 * The rest of this is safe, since no other methods will be able to execute 706 * (they will throw ISE instead; the callbacks will get dropped) 707 */ 708 { 709 try { 710 if (mRemoteDevice != null) { 711 mRemoteDevice.disconnect(); 712 } 713 } catch (CameraRuntimeException e) { 714 Log.e(TAG, "Exception while closing: ", e.asChecked()); 715 } catch (RemoteException e) { 716 // impossible 717 } 718 719 // Only want to fire the onClosed callback once; 720 // either a normal close where the remote device is valid 721 // or a close after a startup error (no remote device but in error state) 722 if (mRemoteDevice != null || mInError) { 723 mDeviceHandler.post(mCallOnClosed); 724 } 725 726 mRemoteDevice = null; 727 mInError = false; 728 } 729 } 730 731 @Override 732 protected void finalize() throws Throwable { 733 try { 734 close(); 735 } 736 finally { 737 super.finalize(); 738 } 739 } 740 741 static class CaptureListenerHolder { 742 743 private final boolean mRepeating; 744 private final CaptureListener mListener; 745 private final List<CaptureRequest> mRequestList; 746 private final Handler mHandler; 747 748 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList, 749 Handler handler, boolean repeating) { 750 if (listener == null || handler == null) { 751 throw new UnsupportedOperationException( 752 "Must have a valid handler and a valid listener"); 753 } 754 mRepeating = repeating; 755 mHandler = handler; 756 mRequestList = new ArrayList<CaptureRequest>(requestList); 757 mListener = listener; 758 } 759 760 public boolean isRepeating() { 761 return mRepeating; 762 } 763 764 public CaptureListener getListener() { 765 return mListener; 766 } 767 768 public CaptureRequest getRequest(int subsequenceId) { 769 if (subsequenceId >= mRequestList.size()) { 770 throw new IllegalArgumentException( 771 String.format( 772 "Requested subsequenceId %d is larger than request list size %d.", 773 subsequenceId, mRequestList.size())); 774 } else { 775 if (subsequenceId < 0) { 776 throw new IllegalArgumentException(String.format( 777 "Requested subsequenceId %d is negative", subsequenceId)); 778 } else { 779 return mRequestList.get(subsequenceId); 780 } 781 } 782 } 783 784 public CaptureRequest getRequest() { 785 return getRequest(0); 786 } 787 788 public Handler getHandler() { 789 return mHandler; 790 } 791 792 } 793 794 /** 795 * This class tracks the last frame number for submitted requests. 796 */ 797 public class FrameNumberTracker { 798 799 private long mCompletedFrameNumber = -1; 800 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>(); 801 802 private void update() { 803 Iterator<Long> iter = mFutureErrorSet.iterator(); 804 while (iter.hasNext()) { 805 long errorFrameNumber = iter.next(); 806 if (errorFrameNumber == mCompletedFrameNumber + 1) { 807 mCompletedFrameNumber++; 808 iter.remove(); 809 } else { 810 break; 811 } 812 } 813 } 814 815 /** 816 * This function is called every time when a result or an error is received. 817 * @param frameNumber: the frame number corresponding to the result or error 818 * @param isError: true if it is an error, false if it is not an error 819 */ 820 public void updateTracker(long frameNumber, boolean isError) { 821 if (isError) { 822 mFutureErrorSet.add(frameNumber); 823 } else { 824 /** 825 * HAL cannot send an OnResultReceived for frame N unless it knows for 826 * sure that all frames prior to N have either errored out or completed. 827 * So if the current frame is not an error, then all previous frames 828 * should have arrived. The following line checks whether this holds. 829 */ 830 if (frameNumber != mCompletedFrameNumber + 1) { 831 Log.e(TAG, String.format( 832 "result frame number %d comes out of order, should be %d + 1", 833 frameNumber, mCompletedFrameNumber)); 834 } 835 mCompletedFrameNumber++; 836 } 837 update(); 838 } 839 840 public long getCompletedFrameNumber() { 841 return mCompletedFrameNumber; 842 } 843 844 } 845 846 private void checkAndFireSequenceComplete() { 847 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); 848 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator(); 849 while (iter.hasNext()) { 850 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next(); 851 if (frameNumberRequestPair.getKey() <= completedFrameNumber) { 852 853 // remove request from mCaptureListenerMap 854 final int requestId = frameNumberRequestPair.getValue(); 855 final CaptureListenerHolder holder; 856 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 857 if (scopedLock == null) { 858 Log.w(TAG, "Camera closed while checking sequences"); 859 return; 860 } 861 862 int index = mCaptureListenerMap.indexOfKey(requestId); 863 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) 864 : null; 865 if (holder != null) { 866 mCaptureListenerMap.removeAt(index); 867 if (DEBUG) { 868 Log.v(TAG, String.format( 869 "remove holder for requestId %d, " 870 + "because lastFrame %d is <= %d", 871 requestId, frameNumberRequestPair.getKey(), 872 completedFrameNumber)); 873 } 874 } 875 } 876 iter.remove(); 877 878 // Call onCaptureSequenceCompleted 879 if (holder != null) { 880 Runnable resultDispatch = new Runnable() { 881 @Override 882 public void run() { 883 if (!CameraDeviceImpl.this.isClosed()){ 884 if (DEBUG) { 885 Log.d(TAG, String.format( 886 "fire sequence complete for request %d", 887 requestId)); 888 } 889 890 long lastFrameNumber = frameNumberRequestPair.getKey(); 891 if (lastFrameNumber < Integer.MIN_VALUE 892 || lastFrameNumber > Integer.MAX_VALUE) { 893 throw new AssertionError(lastFrameNumber 894 + " cannot be cast to int"); 895 } 896 holder.getListener().onCaptureSequenceCompleted( 897 CameraDeviceImpl.this, 898 requestId, 899 lastFrameNumber); 900 } 901 } 902 }; 903 holder.getHandler().post(resultDispatch); 904 } 905 906 } 907 } 908 } 909 910 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 911 912 // 913 // Constants below need to be kept up-to-date with 914 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h 915 // 916 917 // 918 // Error codes for onCameraError 919 // 920 921 /** 922 * Camera has been disconnected 923 */ 924 static final int ERROR_CAMERA_DISCONNECTED = 0; 925 926 /** 927 * Camera has encountered a device-level error 928 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE 929 */ 930 static final int ERROR_CAMERA_DEVICE = 1; 931 932 /** 933 * Camera has encountered a service-level error 934 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE 935 */ 936 static final int ERROR_CAMERA_SERVICE = 2; 937 938 @Override 939 public IBinder asBinder() { 940 return this; 941 } 942 943 @Override 944 public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) { 945 Runnable r = null; 946 947 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 948 if (scopedLock == null) { 949 return; // Camera already closed 950 } 951 952 mInError = true; 953 switch (errorCode) { 954 case ERROR_CAMERA_DISCONNECTED: 955 r = mCallOnDisconnected; 956 break; 957 default: 958 Log.e(TAG, "Unknown error from camera device: " + errorCode); 959 // no break 960 case ERROR_CAMERA_DEVICE: 961 case ERROR_CAMERA_SERVICE: 962 r = new Runnable() { 963 @Override 964 public void run() { 965 if (!CameraDeviceImpl.this.isClosed()) { 966 mDeviceListener.onError(CameraDeviceImpl.this, errorCode); 967 } 968 } 969 }; 970 break; 971 } 972 CameraDeviceImpl.this.mDeviceHandler.post(r); 973 974 // Fire onCaptureSequenceCompleted 975 if (DEBUG) { 976 Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber())); 977 } 978 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true); 979 checkAndFireSequenceComplete(); 980 } 981 } 982 983 @Override 984 public void onCameraIdle() { 985 if (DEBUG) { 986 Log.d(TAG, "Camera now idle"); 987 } 988 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 989 if (scopedLock == null) return; // Camera already closed 990 991 if (!CameraDeviceImpl.this.mIdle) { 992 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle); 993 } 994 CameraDeviceImpl.this.mIdle = true; 995 } 996 } 997 998 @Override 999 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 1000 int requestId = resultExtras.getRequestId(); 1001 if (DEBUG) { 1002 Log.d(TAG, "Capture started for id " + requestId); 1003 } 1004 final CaptureListenerHolder holder; 1005 1006 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 1007 if (scopedLock == null) return; // Camera already closed 1008 1009 // Get the listener for this frame ID, if there is one 1010 holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId); 1011 1012 if (holder == null) { 1013 return; 1014 } 1015 1016 if (isClosed()) return; 1017 1018 // Dispatch capture start notice 1019 holder.getHandler().post( 1020 new Runnable() { 1021 @Override 1022 public void run() { 1023 if (!CameraDeviceImpl.this.isClosed()) { 1024 holder.getListener().onCaptureStarted( 1025 CameraDeviceImpl.this, 1026 holder.getRequest(resultExtras.getSubsequenceId()), 1027 timestamp); 1028 } 1029 } 1030 }); 1031 1032 } 1033 } 1034 1035 @Override 1036 public void onResultReceived(CameraMetadataNative result, 1037 CaptureResultExtras resultExtras) throws RemoteException { 1038 1039 int requestId = resultExtras.getRequestId(); 1040 if (DEBUG) { 1041 Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id " 1042 + requestId); 1043 } 1044 1045 try (ScopedLock scopedLock = mCloseLock.acquireLock()) { 1046 if (scopedLock == null) return; // Camera already closed 1047 1048 // TODO: Handle CameraCharacteristics access from CaptureResult correctly. 1049 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, 1050 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); 1051 1052 final CaptureListenerHolder holder = 1053 CameraDeviceImpl.this.mCaptureListenerMap.get(requestId); 1054 1055 boolean isPartialResult = 1056 (resultExtras.getPartialResultCount() < mTotalPartialCount); 1057 1058 // Update tracker (increment counter) when it's not a partial result. 1059 if (!isPartialResult) { 1060 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), 1061 /*error*/false); 1062 } 1063 1064 // Check if we have a listener for this 1065 if (holder == null) { 1066 if (DEBUG) { 1067 Log.d(TAG, 1068 "holder is null, early return at frame " 1069 + resultExtras.getFrameNumber()); 1070 } 1071 return; 1072 } 1073 1074 if (isClosed()) { 1075 if (DEBUG) { 1076 Log.d(TAG, 1077 "camera is closed, early return at frame " 1078 + resultExtras.getFrameNumber()); 1079 } 1080 return; 1081 } 1082 1083 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); 1084 1085 1086 Runnable resultDispatch = null; 1087 1088 // Either send a partial result or the final capture completed result 1089 if (isPartialResult) { 1090 final CaptureResult resultAsCapture = 1091 new CaptureResult(result, request, requestId); 1092 1093 // Partial result 1094 resultDispatch = new Runnable() { 1095 @Override 1096 public void run() { 1097 if (!CameraDeviceImpl.this.isClosed()){ 1098 holder.getListener().onCapturePartial( 1099 CameraDeviceImpl.this, 1100 request, 1101 resultAsCapture); 1102 } 1103 } 1104 }; 1105 } else { 1106 final TotalCaptureResult resultAsCapture = 1107 new TotalCaptureResult(result, request, requestId); 1108 1109 // Final capture result 1110 resultDispatch = new Runnable() { 1111 @Override 1112 public void run() { 1113 if (!CameraDeviceImpl.this.isClosed()){ 1114 holder.getListener().onCaptureCompleted( 1115 CameraDeviceImpl.this, 1116 request, 1117 resultAsCapture); 1118 } 1119 } 1120 }; 1121 } 1122 1123 holder.getHandler().post(resultDispatch); 1124 1125 // Fire onCaptureSequenceCompleted 1126 if (!isPartialResult) { 1127 checkAndFireSequenceComplete(); 1128 } 1129 1130 } 1131 } 1132 1133 } 1134 1135 /** 1136 * Default handler management. 1137 * 1138 * <p> 1139 * If handler is null, get the current thread's 1140 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}. 1141 * </p> 1142 */ 1143 static Handler checkHandler(Handler handler) { 1144 if (handler == null) { 1145 Looper looper = Looper.myLooper(); 1146 if (looper == null) { 1147 throw new IllegalArgumentException( 1148 "No handler given, and current thread has no looper!"); 1149 } 1150 handler = new Handler(looper); 1151 } 1152 return handler; 1153 } 1154 1155 private void checkIfCameraClosedOrInError() throws CameraAccessException { 1156 if (mInError) { 1157 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, 1158 "The camera device has encountered a serious error"); 1159 } 1160 if (mRemoteDevice == null) { 1161 throw new IllegalStateException("CameraDevice was already closed"); 1162 } 1163 } 1164 1165 /** Whether the camera device has started to close (may not yet have finished) */ 1166 private boolean isClosed() { 1167 return mClosing; 1168 } 1169 1170 private CameraCharacteristics getCharacteristics() { 1171 return mCharacteristics; 1172 } 1173} 1174