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