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