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