1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.camera2.impl; 18 19 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; 20 21 import android.graphics.ImageFormat; 22 import android.hardware.camera2.CameraAccessException; 23 import android.hardware.camera2.CameraCaptureSession; 24 import android.hardware.camera2.CameraCharacteristics; 25 import android.hardware.camera2.CameraDevice; 26 import android.hardware.camera2.CaptureRequest; 27 import android.hardware.camera2.CaptureResult; 28 import android.hardware.camera2.CaptureFailure; 29 import android.hardware.camera2.ICameraDeviceCallbacks; 30 import android.hardware.camera2.ICameraDeviceUser; 31 import android.hardware.camera2.TotalCaptureResult; 32 import android.hardware.camera2.params.InputConfiguration; 33 import android.hardware.camera2.params.OutputConfiguration; 34 import android.hardware.camera2.params.ReprocessFormatsMap; 35 import android.hardware.camera2.params.StreamConfigurationMap; 36 import android.hardware.camera2.utils.CameraBinderDecorator; 37 import android.hardware.camera2.utils.CameraRuntimeException; 38 import android.hardware.camera2.utils.LongParcelable; 39 import android.hardware.camera2.utils.SurfaceUtils; 40 import android.os.Handler; 41 import android.os.IBinder; 42 import android.os.Looper; 43 import android.os.RemoteException; 44 import android.util.Log; 45 import android.util.Range; 46 import android.util.Size; 47 import android.util.SparseArray; 48 import android.view.Surface; 49 50 import java.util.AbstractMap.SimpleEntry; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 import java.util.Collection; 54 import java.util.Collections; 55 import java.util.concurrent.atomic.AtomicBoolean; 56 import java.util.HashMap; 57 import java.util.HashSet; 58 import java.util.Iterator; 59 import java.util.List; 60 import java.util.LinkedList; 61 import java.util.TreeMap; 62 63 /** 64 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate 65 */ 66 public class CameraDeviceImpl extends CameraDevice { 67 private final String TAG; 68 private final boolean DEBUG = false; 69 70 private static final int REQUEST_ID_NONE = -1; 71 72 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) 73 private ICameraDeviceUser mRemoteDevice; 74 75 // Lock to synchronize cross-thread access to device public interface 76 final Object mInterfaceLock = new Object(); // access from this class and Session only! 77 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); 78 79 private final StateCallback mDeviceCallback; 80 private volatile StateCallbackKK mSessionStateCallback; 81 private final Handler mDeviceHandler; 82 83 private final AtomicBoolean mClosing = new AtomicBoolean(); 84 private boolean mInError = false; 85 private boolean mIdle = true; 86 87 /** map request IDs to callback/request data */ 88 private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap = 89 new SparseArray<CaptureCallbackHolder>(); 90 91 private int mRepeatingRequestId = REQUEST_ID_NONE; 92 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>(); 93 // Map stream IDs to input/output configurations 94 private SimpleEntry<Integer, InputConfiguration> mConfiguredInput = 95 new SimpleEntry<>(REQUEST_ID_NONE, null); 96 private final SparseArray<OutputConfiguration> mConfiguredOutputs = 97 new SparseArray<>(); 98 99 private final String mCameraId; 100 private final CameraCharacteristics mCharacteristics; 101 private final int mTotalPartialCount; 102 103 /** 104 * A list tracking request and its expected last regular frame number and last reprocess frame 105 * number. Updated when calling ICameraDeviceUser methods. 106 */ 107 private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList = 108 new ArrayList<>(); 109 110 /** 111 * An object tracking received frame numbers. 112 * Updated when receiving callbacks from ICameraDeviceCallbacks. 113 */ 114 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker(); 115 116 private CameraCaptureSessionCore mCurrentSession; 117 private int mNextSessionId = 0; 118 119 // Runnables for all state transitions, except error, which needs the 120 // error code argument 121 122 private final Runnable mCallOnOpened = new Runnable() { 123 @Override 124 public void run() { 125 StateCallbackKK sessionCallback = null; 126 synchronized(mInterfaceLock) { 127 if (mRemoteDevice == null) return; // Camera already closed 128 129 sessionCallback = mSessionStateCallback; 130 } 131 if (sessionCallback != null) { 132 sessionCallback.onOpened(CameraDeviceImpl.this); 133 } 134 mDeviceCallback.onOpened(CameraDeviceImpl.this); 135 } 136 }; 137 138 private final Runnable mCallOnUnconfigured = new Runnable() { 139 @Override 140 public void run() { 141 StateCallbackKK sessionCallback = null; 142 synchronized(mInterfaceLock) { 143 if (mRemoteDevice == null) return; // Camera already closed 144 145 sessionCallback = mSessionStateCallback; 146 } 147 if (sessionCallback != null) { 148 sessionCallback.onUnconfigured(CameraDeviceImpl.this); 149 } 150 } 151 }; 152 153 private final Runnable mCallOnActive = new Runnable() { 154 @Override 155 public void run() { 156 StateCallbackKK sessionCallback = null; 157 synchronized(mInterfaceLock) { 158 if (mRemoteDevice == null) return; // Camera already closed 159 160 sessionCallback = mSessionStateCallback; 161 } 162 if (sessionCallback != null) { 163 sessionCallback.onActive(CameraDeviceImpl.this); 164 } 165 } 166 }; 167 168 private final Runnable mCallOnBusy = new Runnable() { 169 @Override 170 public void run() { 171 StateCallbackKK sessionCallback = null; 172 synchronized(mInterfaceLock) { 173 if (mRemoteDevice == null) return; // Camera already closed 174 175 sessionCallback = mSessionStateCallback; 176 } 177 if (sessionCallback != null) { 178 sessionCallback.onBusy(CameraDeviceImpl.this); 179 } 180 } 181 }; 182 183 private final Runnable mCallOnClosed = new Runnable() { 184 private boolean mClosedOnce = false; 185 186 @Override 187 public void run() { 188 if (mClosedOnce) { 189 throw new AssertionError("Don't post #onClosed more than once"); 190 } 191 StateCallbackKK sessionCallback = null; 192 synchronized(mInterfaceLock) { 193 sessionCallback = mSessionStateCallback; 194 } 195 if (sessionCallback != null) { 196 sessionCallback.onClosed(CameraDeviceImpl.this); 197 } 198 mDeviceCallback.onClosed(CameraDeviceImpl.this); 199 mClosedOnce = true; 200 } 201 }; 202 203 private final Runnable mCallOnIdle = new Runnable() { 204 @Override 205 public void run() { 206 StateCallbackKK sessionCallback = null; 207 synchronized(mInterfaceLock) { 208 if (mRemoteDevice == null) return; // Camera already closed 209 210 sessionCallback = mSessionStateCallback; 211 } 212 if (sessionCallback != null) { 213 sessionCallback.onIdle(CameraDeviceImpl.this); 214 } 215 } 216 }; 217 218 private final Runnable mCallOnDisconnected = new Runnable() { 219 @Override 220 public void run() { 221 StateCallbackKK sessionCallback = null; 222 synchronized(mInterfaceLock) { 223 if (mRemoteDevice == null) return; // Camera already closed 224 225 sessionCallback = mSessionStateCallback; 226 } 227 if (sessionCallback != null) { 228 sessionCallback.onDisconnected(CameraDeviceImpl.this); 229 } 230 mDeviceCallback.onDisconnected(CameraDeviceImpl.this); 231 } 232 }; 233 CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler, CameraCharacteristics characteristics)234 public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler, 235 CameraCharacteristics characteristics) { 236 if (cameraId == null || callback == null || handler == null || characteristics == null) { 237 throw new IllegalArgumentException("Null argument given"); 238 } 239 mCameraId = cameraId; 240 mDeviceCallback = callback; 241 mDeviceHandler = handler; 242 mCharacteristics = characteristics; 243 244 final int MAX_TAG_LEN = 23; 245 String tag = String.format("CameraDevice-JV-%s", mCameraId); 246 if (tag.length() > MAX_TAG_LEN) { 247 tag = tag.substring(0, MAX_TAG_LEN); 248 } 249 TAG = tag; 250 251 Integer partialCount = 252 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT); 253 if (partialCount == null) { 254 // 1 means partial result is not supported. 255 mTotalPartialCount = 1; 256 } else { 257 mTotalPartialCount = partialCount; 258 } 259 } 260 getCallbacks()261 public CameraDeviceCallbacks getCallbacks() { 262 return mCallbacks; 263 } 264 setRemoteDevice(ICameraDeviceUser remoteDevice)265 public void setRemoteDevice(ICameraDeviceUser remoteDevice) { 266 synchronized(mInterfaceLock) { 267 // TODO: Move from decorator to direct binder-mediated exceptions 268 // If setRemoteFailure already called, do nothing 269 if (mInError) return; 270 271 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice); 272 273 mDeviceHandler.post(mCallOnOpened); 274 mDeviceHandler.post(mCallOnUnconfigured); 275 } 276 } 277 278 /** 279 * Call to indicate failed connection to a remote camera device. 280 * 281 * <p>This places the camera device in the error state and informs the callback. 282 * Use in place of setRemoteDevice() when startup fails.</p> 283 */ setRemoteFailure(final CameraRuntimeException failure)284 public void setRemoteFailure(final CameraRuntimeException failure) { 285 int failureCode = StateCallback.ERROR_CAMERA_DEVICE; 286 boolean failureIsError = true; 287 288 switch (failure.getReason()) { 289 case CameraAccessException.CAMERA_IN_USE: 290 failureCode = StateCallback.ERROR_CAMERA_IN_USE; 291 break; 292 case CameraAccessException.MAX_CAMERAS_IN_USE: 293 failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE; 294 break; 295 case CameraAccessException.CAMERA_DISABLED: 296 failureCode = StateCallback.ERROR_CAMERA_DISABLED; 297 break; 298 case CameraAccessException.CAMERA_DISCONNECTED: 299 failureIsError = false; 300 break; 301 case CameraAccessException.CAMERA_ERROR: 302 failureCode = StateCallback.ERROR_CAMERA_DEVICE; 303 break; 304 default: 305 Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason()); 306 break; 307 } 308 final int code = failureCode; 309 final boolean isError = failureIsError; 310 synchronized(mInterfaceLock) { 311 mInError = true; 312 mDeviceHandler.post(new Runnable() { 313 @Override 314 public void run() { 315 if (isError) { 316 mDeviceCallback.onError(CameraDeviceImpl.this, code); 317 } else { 318 mDeviceCallback.onDisconnected(CameraDeviceImpl.this); 319 } 320 } 321 }); 322 } 323 } 324 325 @Override getId()326 public String getId() { 327 return mCameraId; 328 } 329 configureOutputs(List<Surface> outputs)330 public void configureOutputs(List<Surface> outputs) throws CameraAccessException { 331 // Leave this here for backwards compatibility with older code using this directly 332 ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size()); 333 for (Surface s : outputs) { 334 outputConfigs.add(new OutputConfiguration(s)); 335 } 336 configureStreamsChecked(/*inputConfig*/null, outputConfigs, 337 /*isConstrainedHighSpeed*/false); 338 339 } 340 341 /** 342 * Attempt to configure the input and outputs; the device goes to idle and then configures the 343 * new input and outputs if possible. 344 * 345 * <p>The configuration may gracefully fail, if input configuration is not supported, 346 * if there are too many outputs, if the formats are not supported, or if the sizes for that 347 * format is not supported. In this case this function will return {@code false} and the 348 * unconfigured callback will be fired.</p> 349 * 350 * <p>If the configuration succeeds (with 1 or more outputs with or without an input), 351 * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p> 352 * 353 * @param inputConfig input configuration or {@code null} for no input 354 * @param outputs a list of one or more surfaces, or {@code null} to unconfigure 355 * @param isConstrainedHighSpeed If the streams configuration is for constrained high speed output. 356 * @return whether or not the configuration was successful 357 * 358 * @throws CameraAccessException if there were any unexpected problems during configuration 359 */ configureStreamsChecked(InputConfiguration inputConfig, List<OutputConfiguration> outputs, boolean isConstrainedHighSpeed)360 public boolean configureStreamsChecked(InputConfiguration inputConfig, 361 List<OutputConfiguration> outputs, boolean isConstrainedHighSpeed) 362 throws CameraAccessException { 363 // Treat a null input the same an empty list 364 if (outputs == null) { 365 outputs = new ArrayList<OutputConfiguration>(); 366 } 367 if (outputs.size() == 0 && inputConfig != null) { 368 throw new IllegalArgumentException("cannot configure an input stream without " + 369 "any output streams"); 370 } 371 372 checkInputConfiguration(inputConfig); 373 374 boolean success = false; 375 376 synchronized(mInterfaceLock) { 377 checkIfCameraClosedOrInError(); 378 // Streams to create 379 HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs); 380 // Streams to delete 381 List<Integer> deleteList = new ArrayList<Integer>(); 382 383 // Determine which streams need to be created, which to be deleted 384 for (int i = 0; i < mConfiguredOutputs.size(); ++i) { 385 int streamId = mConfiguredOutputs.keyAt(i); 386 OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i); 387 388 if (!outputs.contains(outConfig)) { 389 deleteList.add(streamId); 390 } else { 391 addSet.remove(outConfig); // Don't create a stream previously created 392 } 393 } 394 395 mDeviceHandler.post(mCallOnBusy); 396 stopRepeating(); 397 398 try { 399 waitUntilIdle(); 400 401 mRemoteDevice.beginConfigure(); 402 403 // reconfigure the input stream if the input configuration is different. 404 InputConfiguration currentInputConfig = mConfiguredInput.getValue(); 405 if (inputConfig != currentInputConfig && 406 (inputConfig == null || !inputConfig.equals(currentInputConfig))) { 407 if (currentInputConfig != null) { 408 mRemoteDevice.deleteStream(mConfiguredInput.getKey()); 409 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>( 410 REQUEST_ID_NONE, null); 411 } 412 if (inputConfig != null) { 413 int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(), 414 inputConfig.getHeight(), inputConfig.getFormat()); 415 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>( 416 streamId, inputConfig); 417 } 418 } 419 420 // Delete all streams first (to free up HW resources) 421 for (Integer streamId : deleteList) { 422 mRemoteDevice.deleteStream(streamId); 423 mConfiguredOutputs.delete(streamId); 424 } 425 426 // Add all new streams 427 for (OutputConfiguration outConfig : outputs) { 428 if (addSet.contains(outConfig)) { 429 int streamId = mRemoteDevice.createStream(outConfig); 430 mConfiguredOutputs.put(streamId, outConfig); 431 } 432 } 433 434 try { 435 mRemoteDevice.endConfigure(isConstrainedHighSpeed); 436 } 437 catch (IllegalArgumentException e) { 438 // OK. camera service can reject stream config if it's not supported by HAL 439 // This is only the result of a programmer misusing the camera2 api. 440 Log.w(TAG, "Stream configuration failed"); 441 return false; 442 } 443 444 success = true; 445 } catch (CameraRuntimeException e) { 446 if (e.getReason() == CAMERA_IN_USE) { 447 throw new IllegalStateException("The camera is currently busy." + 448 " You must wait until the previous operation completes."); 449 } 450 451 throw e.asChecked(); 452 } catch (RemoteException e) { 453 // impossible 454 return false; 455 } finally { 456 if (success && outputs.size() > 0) { 457 mDeviceHandler.post(mCallOnIdle); 458 } else { 459 // Always return to the 'unconfigured' state if we didn't hit a fatal error 460 mDeviceHandler.post(mCallOnUnconfigured); 461 } 462 } 463 } 464 465 return success; 466 } 467 468 @Override createCaptureSession(List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)469 public void createCaptureSession(List<Surface> outputs, 470 CameraCaptureSession.StateCallback callback, Handler handler) 471 throws CameraAccessException { 472 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 473 for (Surface surface : outputs) { 474 outConfigurations.add(new OutputConfiguration(surface)); 475 } 476 createCaptureSessionInternal(null, outConfigurations, callback, handler, 477 /*isConstrainedHighSpeed*/false); 478 } 479 480 @Override createCaptureSessionByOutputConfiguration( List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Handler handler)481 public void createCaptureSessionByOutputConfiguration( 482 List<OutputConfiguration> outputConfigurations, 483 CameraCaptureSession.StateCallback callback, Handler handler) 484 throws CameraAccessException { 485 if (DEBUG) { 486 Log.d(TAG, "createCaptureSessionByOutputConfiguration"); 487 } 488 489 createCaptureSessionInternal(null, outputConfigurations, callback, handler, 490 /*isConstrainedHighSpeed*/false); 491 } 492 493 @Override createReprocessableCaptureSession(InputConfiguration inputConfig, List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)494 public void createReprocessableCaptureSession(InputConfiguration inputConfig, 495 List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) 496 throws CameraAccessException { 497 if (DEBUG) { 498 Log.d(TAG, "createReprocessableCaptureSession"); 499 } 500 501 if (inputConfig == null) { 502 throw new IllegalArgumentException("inputConfig cannot be null when creating a " + 503 "reprocessable capture session"); 504 } 505 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 506 for (Surface surface : outputs) { 507 outConfigurations.add(new OutputConfiguration(surface)); 508 } 509 createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler, 510 /*isConstrainedHighSpeed*/false); 511 } 512 513 @Override createConstrainedHighSpeedCaptureSession(List<Surface> outputs, android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)514 public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs, 515 android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler) 516 throws CameraAccessException { 517 if (outputs == null || outputs.size() == 0 || outputs.size() > 2) { 518 throw new IllegalArgumentException( 519 "Output surface list must not be null and the size must be no more than 2"); 520 } 521 StreamConfigurationMap config = 522 getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 523 SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputs, /*fpsRange*/null, config); 524 525 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 526 for (Surface surface : outputs) { 527 outConfigurations.add(new OutputConfiguration(surface)); 528 } 529 createCaptureSessionInternal(null, outConfigurations, callback, handler, 530 /*isConstrainedHighSpeed*/true); 531 } 532 createCaptureSessionInternal(InputConfiguration inputConfig, List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Handler handler, boolean isConstrainedHighSpeed)533 private void createCaptureSessionInternal(InputConfiguration inputConfig, 534 List<OutputConfiguration> outputConfigurations, 535 CameraCaptureSession.StateCallback callback, Handler handler, 536 boolean isConstrainedHighSpeed) throws CameraAccessException { 537 synchronized(mInterfaceLock) { 538 if (DEBUG) { 539 Log.d(TAG, "createCaptureSessionInternal"); 540 } 541 542 checkIfCameraClosedOrInError(); 543 544 if (isConstrainedHighSpeed && inputConfig != null) { 545 throw new IllegalArgumentException("Constrained high speed session doesn't support" 546 + " input configuration yet."); 547 } 548 549 // Notify current session that it's going away, before starting camera operations 550 // After this call completes, the session is not allowed to call into CameraDeviceImpl 551 if (mCurrentSession != null) { 552 mCurrentSession.replaceSessionClose(); 553 } 554 555 // TODO: dont block for this 556 boolean configureSuccess = true; 557 CameraAccessException pendingException = null; 558 Surface input = null; 559 try { 560 // configure streams and then block until IDLE 561 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, 562 isConstrainedHighSpeed); 563 if (configureSuccess == true && inputConfig != null) { 564 input = new Surface(); 565 try { 566 mRemoteDevice.getInputSurface(/*out*/input); 567 } catch (CameraRuntimeException e) { 568 e.asChecked(); 569 } 570 } 571 } catch (CameraAccessException e) { 572 configureSuccess = false; 573 pendingException = e; 574 input = null; 575 if (DEBUG) { 576 Log.v(TAG, "createCaptureSession - failed with exception ", e); 577 } 578 } catch (RemoteException e) { 579 // impossible 580 return; 581 } 582 583 List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size()); 584 for (OutputConfiguration config : outputConfigurations) { 585 outSurfaces.add(config.getSurface()); 586 } 587 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. 588 CameraCaptureSessionCore newSession = null; 589 if (isConstrainedHighSpeed) { 590 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, 591 outSurfaces, callback, handler, this, mDeviceHandler, configureSuccess, 592 mCharacteristics); 593 } else { 594 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, 595 outSurfaces, callback, handler, this, mDeviceHandler, 596 configureSuccess); 597 } 598 599 // TODO: wait until current session closes, then create the new session 600 mCurrentSession = newSession; 601 602 if (pendingException != null) { 603 throw pendingException; 604 } 605 606 mSessionStateCallback = mCurrentSession.getDeviceStateCallback(); 607 } 608 } 609 610 /** 611 * For use by backwards-compatibility code only. 612 */ setSessionListener(StateCallbackKK sessionCallback)613 public void setSessionListener(StateCallbackKK sessionCallback) { 614 synchronized(mInterfaceLock) { 615 mSessionStateCallback = sessionCallback; 616 } 617 } 618 619 @Override createCaptureRequest(int templateType)620 public CaptureRequest.Builder createCaptureRequest(int templateType) 621 throws CameraAccessException { 622 synchronized(mInterfaceLock) { 623 checkIfCameraClosedOrInError(); 624 625 CameraMetadataNative templatedRequest = new CameraMetadataNative(); 626 627 try { 628 mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest); 629 } catch (CameraRuntimeException e) { 630 throw e.asChecked(); 631 } catch (RemoteException e) { 632 // impossible 633 return null; 634 } 635 636 CaptureRequest.Builder builder = new CaptureRequest.Builder( 637 templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); 638 639 return builder; 640 } 641 } 642 643 @Override createReprocessCaptureRequest(TotalCaptureResult inputResult)644 public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult) 645 throws CameraAccessException { 646 synchronized(mInterfaceLock) { 647 checkIfCameraClosedOrInError(); 648 649 CameraMetadataNative resultMetadata = new 650 CameraMetadataNative(inputResult.getNativeCopy()); 651 652 return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true, 653 inputResult.getSessionId()); 654 } 655 } 656 prepare(Surface surface)657 public void prepare(Surface surface) throws CameraAccessException { 658 if (surface == null) throw new IllegalArgumentException("Surface is null"); 659 660 synchronized(mInterfaceLock) { 661 int streamId = -1; 662 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 663 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 664 streamId = mConfiguredOutputs.keyAt(i); 665 break; 666 } 667 } 668 if (streamId == -1) { 669 throw new IllegalArgumentException("Surface is not part of this session"); 670 } 671 try { 672 mRemoteDevice.prepare(streamId); 673 } catch (CameraRuntimeException e) { 674 throw e.asChecked(); 675 } catch (RemoteException e) { 676 // impossible 677 return; 678 } 679 } 680 } 681 tearDown(Surface surface)682 public void tearDown(Surface surface) throws CameraAccessException { 683 if (surface == null) throw new IllegalArgumentException("Surface is null"); 684 685 synchronized(mInterfaceLock) { 686 int streamId = -1; 687 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 688 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 689 streamId = mConfiguredOutputs.keyAt(i); 690 break; 691 } 692 } 693 if (streamId == -1) { 694 throw new IllegalArgumentException("Surface is not part of this session"); 695 } 696 try { 697 mRemoteDevice.tearDown(streamId); 698 } catch (CameraRuntimeException e) { 699 throw e.asChecked(); 700 } catch (RemoteException e) { 701 // impossible 702 return; 703 } 704 } 705 } 706 capture(CaptureRequest request, CaptureCallback callback, Handler handler)707 public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) 708 throws CameraAccessException { 709 if (DEBUG) { 710 Log.d(TAG, "calling capture"); 711 } 712 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 713 requestList.add(request); 714 return submitCaptureRequest(requestList, callback, handler, /*streaming*/false); 715 } 716 captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)717 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, 718 Handler handler) throws CameraAccessException { 719 if (requests == null || requests.isEmpty()) { 720 throw new IllegalArgumentException("At least one request must be given"); 721 } 722 return submitCaptureRequest(requests, callback, handler, /*streaming*/false); 723 } 724 725 /** 726 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for 727 * starting and stopping repeating request and flushing. 728 * 729 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never 730 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered. 731 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last 732 * regular frame number will be added to the list mRequestLastFrameNumbersList.</p> 733 * 734 * @param requestId the request ID of the current repeating request. 735 * 736 * @param lastFrameNumber last frame number returned from binder. 737 */ checkEarlyTriggerSequenceComplete( final int requestId, final long lastFrameNumber)738 private void checkEarlyTriggerSequenceComplete( 739 final int requestId, final long lastFrameNumber) { 740 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request 741 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately. 742 if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 743 final CaptureCallbackHolder holder; 744 int index = mCaptureCallbackMap.indexOfKey(requestId); 745 holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null; 746 if (holder != null) { 747 mCaptureCallbackMap.removeAt(index); 748 if (DEBUG) { 749 Log.v(TAG, String.format( 750 "remove holder for requestId %d, " 751 + "because lastFrame is %d.", 752 requestId, lastFrameNumber)); 753 } 754 } 755 756 if (holder != null) { 757 if (DEBUG) { 758 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because" 759 + " request did not reach HAL"); 760 } 761 762 Runnable resultDispatch = new Runnable() { 763 @Override 764 public void run() { 765 if (!CameraDeviceImpl.this.isClosed()) { 766 if (DEBUG) { 767 Log.d(TAG, String.format( 768 "early trigger sequence complete for request %d", 769 requestId)); 770 } 771 holder.getCallback().onCaptureSequenceAborted( 772 CameraDeviceImpl.this, 773 requestId); 774 } 775 } 776 }; 777 holder.getHandler().post(resultDispatch); 778 } else { 779 Log.w(TAG, String.format( 780 "did not register callback to request %d", 781 requestId)); 782 } 783 } else { 784 // This function is only called for regular request so lastFrameNumber is the last 785 // regular frame number. 786 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId, 787 lastFrameNumber)); 788 789 // It is possible that the last frame has already arrived, so we need to check 790 // for sequence completion right away 791 checkAndFireSequenceComplete(); 792 } 793 } 794 submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, Handler handler, boolean repeating)795 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, 796 Handler handler, boolean repeating) throws CameraAccessException { 797 798 // Need a valid handler, or current thread needs to have a looper, if 799 // callback is valid 800 handler = checkHandler(handler, callback); 801 802 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 803 for (CaptureRequest request : requestList) { 804 if (request.getTargets().isEmpty()) { 805 throw new IllegalArgumentException( 806 "Each request must have at least one Surface target"); 807 } 808 809 for (Surface surface : request.getTargets()) { 810 if (surface == null) { 811 throw new IllegalArgumentException("Null Surface targets are not allowed"); 812 } 813 } 814 } 815 816 synchronized(mInterfaceLock) { 817 checkIfCameraClosedOrInError(); 818 int requestId; 819 820 if (repeating) { 821 stopRepeating(); 822 } 823 824 LongParcelable lastFrameNumberRef = new LongParcelable(); 825 try { 826 requestId = mRemoteDevice.submitRequestList(requestList, repeating, 827 /*out*/lastFrameNumberRef); 828 if (DEBUG) { 829 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber()); 830 } 831 } catch (CameraRuntimeException e) { 832 throw e.asChecked(); 833 } catch (RemoteException e) { 834 // impossible 835 return -1; 836 } 837 838 if (callback != null) { 839 mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback, 840 requestList, handler, repeating, mNextSessionId - 1)); 841 } else { 842 if (DEBUG) { 843 Log.d(TAG, "Listen for request " + requestId + " is null"); 844 } 845 } 846 847 long lastFrameNumber = lastFrameNumberRef.getNumber(); 848 849 if (repeating) { 850 if (mRepeatingRequestId != REQUEST_ID_NONE) { 851 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 852 } 853 mRepeatingRequestId = requestId; 854 } else { 855 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestList, 856 requestId, lastFrameNumber)); 857 } 858 859 if (mIdle) { 860 mDeviceHandler.post(mCallOnActive); 861 } 862 mIdle = false; 863 864 return requestId; 865 } 866 } 867 setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler)868 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, 869 Handler handler) throws CameraAccessException { 870 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 871 requestList.add(request); 872 return submitCaptureRequest(requestList, callback, handler, /*streaming*/true); 873 } 874 setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)875 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, 876 Handler handler) throws CameraAccessException { 877 if (requests == null || requests.isEmpty()) { 878 throw new IllegalArgumentException("At least one request must be given"); 879 } 880 return submitCaptureRequest(requests, callback, handler, /*streaming*/true); 881 } 882 stopRepeating()883 public void stopRepeating() throws CameraAccessException { 884 885 synchronized(mInterfaceLock) { 886 checkIfCameraClosedOrInError(); 887 if (mRepeatingRequestId != REQUEST_ID_NONE) { 888 889 int requestId = mRepeatingRequestId; 890 mRepeatingRequestId = REQUEST_ID_NONE; 891 892 // Queue for deletion after in-flight requests finish 893 if (mCaptureCallbackMap.get(requestId) != null) { 894 mRepeatingRequestIdDeletedList.add(requestId); 895 } 896 897 try { 898 LongParcelable lastFrameNumberRef = new LongParcelable(); 899 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef); 900 long lastFrameNumber = lastFrameNumberRef.getNumber(); 901 902 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber); 903 904 } catch (CameraRuntimeException e) { 905 throw e.asChecked(); 906 } catch (RemoteException e) { 907 // impossible 908 return; 909 } 910 } 911 } 912 } 913 waitUntilIdle()914 private void waitUntilIdle() throws CameraAccessException { 915 916 synchronized(mInterfaceLock) { 917 checkIfCameraClosedOrInError(); 918 919 if (mRepeatingRequestId != REQUEST_ID_NONE) { 920 throw new IllegalStateException("Active repeating request ongoing"); 921 } 922 try { 923 mRemoteDevice.waitUntilIdle(); 924 } catch (CameraRuntimeException e) { 925 throw e.asChecked(); 926 } catch (RemoteException e) { 927 // impossible 928 return; 929 } 930 } 931 } 932 flush()933 public void flush() throws CameraAccessException { 934 synchronized(mInterfaceLock) { 935 checkIfCameraClosedOrInError(); 936 937 mDeviceHandler.post(mCallOnBusy); 938 939 // If already idle, just do a busy->idle transition immediately, don't actually 940 // flush. 941 if (mIdle) { 942 mDeviceHandler.post(mCallOnIdle); 943 return; 944 } 945 try { 946 LongParcelable lastFrameNumberRef = new LongParcelable(); 947 mRemoteDevice.flush(/*out*/lastFrameNumberRef); 948 if (mRepeatingRequestId != REQUEST_ID_NONE) { 949 long lastFrameNumber = lastFrameNumberRef.getNumber(); 950 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 951 mRepeatingRequestId = REQUEST_ID_NONE; 952 } 953 } catch (CameraRuntimeException e) { 954 throw e.asChecked(); 955 } catch (RemoteException e) { 956 // impossible 957 return; 958 } 959 } 960 } 961 962 @Override close()963 public void close() { 964 synchronized (mInterfaceLock) { 965 if (mClosing.getAndSet(true)) { 966 return; 967 } 968 969 try { 970 if (mRemoteDevice != null) { 971 mRemoteDevice.disconnect(); 972 } 973 } catch (CameraRuntimeException e) { 974 Log.e(TAG, "Exception while closing: ", e.asChecked()); 975 } catch (RemoteException e) { 976 // impossible 977 } 978 979 // Only want to fire the onClosed callback once; 980 // either a normal close where the remote device is valid 981 // or a close after a startup error (no remote device but in error state) 982 if (mRemoteDevice != null || mInError) { 983 mDeviceHandler.post(mCallOnClosed); 984 } 985 986 mRemoteDevice = null; 987 } 988 } 989 990 @Override finalize()991 protected void finalize() throws Throwable { 992 try { 993 close(); 994 } 995 finally { 996 super.finalize(); 997 } 998 } 999 checkInputConfiguration(InputConfiguration inputConfig)1000 private void checkInputConfiguration(InputConfiguration inputConfig) { 1001 if (inputConfig != null) { 1002 StreamConfigurationMap configMap = mCharacteristics.get( 1003 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1004 1005 int[] inputFormats = configMap.getInputFormats(); 1006 boolean validFormat = false; 1007 for (int format : inputFormats) { 1008 if (format == inputConfig.getFormat()) { 1009 validFormat = true; 1010 } 1011 } 1012 1013 if (validFormat == false) { 1014 throw new IllegalArgumentException("input format " + inputConfig.getFormat() + 1015 " is not valid"); 1016 } 1017 1018 boolean validSize = false; 1019 Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat()); 1020 for (Size s : inputSizes) { 1021 if (inputConfig.getWidth() == s.getWidth() && 1022 inputConfig.getHeight() == s.getHeight()) { 1023 validSize = true; 1024 } 1025 } 1026 1027 if (validSize == false) { 1028 throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" + 1029 inputConfig.getHeight() + " is not valid"); 1030 } 1031 } 1032 } 1033 1034 /** 1035 * <p>A callback for tracking the progress of a {@link CaptureRequest} 1036 * submitted to the camera device.</p> 1037 * 1038 */ 1039 public static abstract class CaptureCallback { 1040 1041 /** 1042 * This constant is used to indicate that no images were captured for 1043 * the request. 1044 * 1045 * @hide 1046 */ 1047 public static final int NO_FRAMES_CAPTURED = -1; 1048 1049 /** 1050 * This method is called when the camera device has started capturing 1051 * the output image for the request, at the beginning of image exposure. 1052 * 1053 * @see android.media.MediaActionSound 1054 */ onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp, long frameNumber)1055 public void onCaptureStarted(CameraDevice camera, 1056 CaptureRequest request, long timestamp, long frameNumber) { 1057 // default empty implementation 1058 } 1059 1060 /** 1061 * This method is called when some results from an image capture are 1062 * available. 1063 * 1064 * @hide 1065 */ onCapturePartial(CameraDevice camera, CaptureRequest request, CaptureResult result)1066 public void onCapturePartial(CameraDevice camera, 1067 CaptureRequest request, CaptureResult result) { 1068 // default empty implementation 1069 } 1070 1071 /** 1072 * This method is called when an image capture makes partial forward progress; some 1073 * (but not all) results from an image capture are available. 1074 * 1075 */ onCaptureProgressed(CameraDevice camera, CaptureRequest request, CaptureResult partialResult)1076 public void onCaptureProgressed(CameraDevice camera, 1077 CaptureRequest request, CaptureResult partialResult) { 1078 // default empty implementation 1079 } 1080 1081 /** 1082 * This method is called when an image capture has fully completed and all the 1083 * result metadata is available. 1084 */ onCaptureCompleted(CameraDevice camera, CaptureRequest request, TotalCaptureResult result)1085 public void onCaptureCompleted(CameraDevice camera, 1086 CaptureRequest request, TotalCaptureResult result) { 1087 // default empty implementation 1088 } 1089 1090 /** 1091 * This method is called instead of {@link #onCaptureCompleted} when the 1092 * camera device failed to produce a {@link CaptureResult} for the 1093 * request. 1094 */ onCaptureFailed(CameraDevice camera, CaptureRequest request, CaptureFailure failure)1095 public void onCaptureFailed(CameraDevice camera, 1096 CaptureRequest request, CaptureFailure failure) { 1097 // default empty implementation 1098 } 1099 1100 /** 1101 * This method is called independently of the others in CaptureCallback, 1102 * when a capture sequence finishes and all {@link CaptureResult} 1103 * or {@link CaptureFailure} for it have been returned via this callback. 1104 */ onCaptureSequenceCompleted(CameraDevice camera, int sequenceId, long frameNumber)1105 public void onCaptureSequenceCompleted(CameraDevice camera, 1106 int sequenceId, long frameNumber) { 1107 // default empty implementation 1108 } 1109 1110 /** 1111 * This method is called independently of the others in CaptureCallback, 1112 * when a capture sequence aborts before any {@link CaptureResult} 1113 * or {@link CaptureFailure} for it have been returned via this callback. 1114 */ onCaptureSequenceAborted(CameraDevice camera, int sequenceId)1115 public void onCaptureSequenceAborted(CameraDevice camera, 1116 int sequenceId) { 1117 // default empty implementation 1118 } 1119 } 1120 1121 /** 1122 * A callback for notifications about the state of a camera device, adding in the callbacks that 1123 * were part of the earlier KK API design, but now only used internally. 1124 */ 1125 public static abstract class StateCallbackKK extends StateCallback { 1126 /** 1127 * The method called when a camera device has no outputs configured. 1128 * 1129 */ onUnconfigured(CameraDevice camera)1130 public void onUnconfigured(CameraDevice camera) { 1131 // Default empty implementation 1132 } 1133 1134 /** 1135 * The method called when a camera device begins processing 1136 * {@link CaptureRequest capture requests}. 1137 * 1138 */ onActive(CameraDevice camera)1139 public void onActive(CameraDevice camera) { 1140 // Default empty implementation 1141 } 1142 1143 /** 1144 * The method called when a camera device is busy. 1145 * 1146 */ onBusy(CameraDevice camera)1147 public void onBusy(CameraDevice camera) { 1148 // Default empty implementation 1149 } 1150 1151 /** 1152 * The method called when a camera device has finished processing all 1153 * submitted capture requests and has reached an idle state. 1154 * 1155 */ onIdle(CameraDevice camera)1156 public void onIdle(CameraDevice camera) { 1157 // Default empty implementation 1158 } 1159 1160 /** 1161 * The method called when the camera device has finished preparing 1162 * an output Surface 1163 */ onSurfacePrepared(Surface surface)1164 public void onSurfacePrepared(Surface surface) { 1165 // Default empty implementation 1166 } 1167 } 1168 1169 static class CaptureCallbackHolder { 1170 1171 private final boolean mRepeating; 1172 private final CaptureCallback mCallback; 1173 private final List<CaptureRequest> mRequestList; 1174 private final Handler mHandler; 1175 private final int mSessionId; 1176 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, Handler handler, boolean repeating, int sessionId)1177 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, 1178 Handler handler, boolean repeating, int sessionId) { 1179 if (callback == null || handler == null) { 1180 throw new UnsupportedOperationException( 1181 "Must have a valid handler and a valid callback"); 1182 } 1183 mRepeating = repeating; 1184 mHandler = handler; 1185 mRequestList = new ArrayList<CaptureRequest>(requestList); 1186 mCallback = callback; 1187 mSessionId = sessionId; 1188 } 1189 isRepeating()1190 public boolean isRepeating() { 1191 return mRepeating; 1192 } 1193 getCallback()1194 public CaptureCallback getCallback() { 1195 return mCallback; 1196 } 1197 getRequest(int subsequenceId)1198 public CaptureRequest getRequest(int subsequenceId) { 1199 if (subsequenceId >= mRequestList.size()) { 1200 throw new IllegalArgumentException( 1201 String.format( 1202 "Requested subsequenceId %d is larger than request list size %d.", 1203 subsequenceId, mRequestList.size())); 1204 } else { 1205 if (subsequenceId < 0) { 1206 throw new IllegalArgumentException(String.format( 1207 "Requested subsequenceId %d is negative", subsequenceId)); 1208 } else { 1209 return mRequestList.get(subsequenceId); 1210 } 1211 } 1212 } 1213 getRequest()1214 public CaptureRequest getRequest() { 1215 return getRequest(0); 1216 } 1217 getHandler()1218 public Handler getHandler() { 1219 return mHandler; 1220 } 1221 getSessionId()1222 public int getSessionId() { 1223 return mSessionId; 1224 } 1225 } 1226 1227 /** 1228 * This class holds a capture ID and its expected last regular frame number and last reprocess 1229 * frame number. 1230 */ 1231 static class RequestLastFrameNumbersHolder { 1232 // request ID 1233 private final int mRequestId; 1234 // The last regular frame number for this request ID. It's 1235 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request. 1236 private final long mLastRegularFrameNumber; 1237 // The last reprocess frame number for this request ID. It's 1238 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request. 1239 private final long mLastReprocessFrameNumber; 1240 1241 /** 1242 * Create a request-last-frame-numbers holder with a list of requests, request ID, and 1243 * the last frame number returned by camera service. 1244 */ RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId, long lastFrameNumber)1245 public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId, 1246 long lastFrameNumber) { 1247 long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1248 long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1249 long frameNumber = lastFrameNumber; 1250 1251 if (lastFrameNumber < requestList.size() - 1) { 1252 throw new IllegalArgumentException("lastFrameNumber: " + lastFrameNumber + 1253 " should be at least " + (requestList.size() - 1) + " for the number of " + 1254 " requests in the list: " + requestList.size()); 1255 } 1256 1257 // find the last regular frame number and the last reprocess frame number 1258 for (int i = requestList.size() - 1; i >= 0; i--) { 1259 CaptureRequest request = requestList.get(i); 1260 if (request.isReprocess() && lastReprocessFrameNumber == 1261 CaptureCallback.NO_FRAMES_CAPTURED) { 1262 lastReprocessFrameNumber = frameNumber; 1263 } else if (!request.isReprocess() && lastRegularFrameNumber == 1264 CaptureCallback.NO_FRAMES_CAPTURED) { 1265 lastRegularFrameNumber = frameNumber; 1266 } 1267 1268 if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED && 1269 lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) { 1270 break; 1271 } 1272 1273 frameNumber--; 1274 } 1275 1276 mLastRegularFrameNumber = lastRegularFrameNumber; 1277 mLastReprocessFrameNumber = lastReprocessFrameNumber; 1278 mRequestId = requestId; 1279 } 1280 1281 /** 1282 * Create a request-last-frame-numbers holder with a request ID and last regular frame 1283 * number. 1284 */ RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber)1285 public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) { 1286 mLastRegularFrameNumber = lastRegularFrameNumber; 1287 mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1288 mRequestId = requestId; 1289 } 1290 1291 /** 1292 * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1293 * it contains no regular request. 1294 */ getLastRegularFrameNumber()1295 public long getLastRegularFrameNumber() { 1296 return mLastRegularFrameNumber; 1297 } 1298 1299 /** 1300 * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1301 * it contains no reprocess request. 1302 */ getLastReprocessFrameNumber()1303 public long getLastReprocessFrameNumber() { 1304 return mLastReprocessFrameNumber; 1305 } 1306 1307 /** 1308 * Return the last frame number overall. 1309 */ getLastFrameNumber()1310 public long getLastFrameNumber() { 1311 return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber); 1312 } 1313 1314 /** 1315 * Return the request ID. 1316 */ getRequestId()1317 public int getRequestId() { 1318 return mRequestId; 1319 } 1320 } 1321 1322 /** 1323 * This class tracks the last frame number for submitted requests. 1324 */ 1325 public class FrameNumberTracker { 1326 1327 private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1328 private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1329 /** the skipped frame numbers that belong to regular results */ 1330 private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>(); 1331 /** the skipped frame numbers that belong to reprocess results */ 1332 private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>(); 1333 /** frame number -> is reprocess */ 1334 private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>(); 1335 /** Map frame numbers to list of partial results */ 1336 private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); 1337 update()1338 private void update() { 1339 Iterator iter = mFutureErrorMap.entrySet().iterator(); 1340 while (iter.hasNext()) { 1341 TreeMap.Entry pair = (TreeMap.Entry)iter.next(); 1342 Long errorFrameNumber = (Long)pair.getKey(); 1343 Boolean reprocess = (Boolean)pair.getValue(); 1344 Boolean removeError = true; 1345 if (reprocess) { 1346 if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) { 1347 mCompletedReprocessFrameNumber = errorFrameNumber; 1348 } else if (mSkippedReprocessFrameNumbers.isEmpty() != true && 1349 errorFrameNumber == mSkippedReprocessFrameNumbers.element()) { 1350 mCompletedReprocessFrameNumber = errorFrameNumber; 1351 mSkippedReprocessFrameNumbers.remove(); 1352 } else { 1353 removeError = false; 1354 } 1355 } else { 1356 if (errorFrameNumber == mCompletedFrameNumber + 1) { 1357 mCompletedFrameNumber = errorFrameNumber; 1358 } else if (mSkippedRegularFrameNumbers.isEmpty() != true && 1359 errorFrameNumber == mSkippedRegularFrameNumbers.element()) { 1360 mCompletedFrameNumber = errorFrameNumber; 1361 mSkippedRegularFrameNumbers.remove(); 1362 } else { 1363 removeError = false; 1364 } 1365 } 1366 if (removeError) { 1367 iter.remove(); 1368 } 1369 } 1370 } 1371 1372 /** 1373 * This function is called every time when a result or an error is received. 1374 * @param frameNumber the frame number corresponding to the result or error 1375 * @param isError true if it is an error, false if it is not an error 1376 * @param isReprocess true if it is a reprocess result, false if it is a regular result. 1377 */ updateTracker(long frameNumber, boolean isError, boolean isReprocess)1378 public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) { 1379 if (isError) { 1380 mFutureErrorMap.put(frameNumber, isReprocess); 1381 } else { 1382 try { 1383 if (isReprocess) { 1384 updateCompletedReprocessFrameNumber(frameNumber); 1385 } else { 1386 updateCompletedFrameNumber(frameNumber); 1387 } 1388 } catch (IllegalArgumentException e) { 1389 Log.e(TAG, e.getMessage()); 1390 } 1391 } 1392 update(); 1393 } 1394 1395 /** 1396 * This function is called every time a result has been completed. 1397 * 1398 * <p>It keeps a track of all the partial results already created for a particular 1399 * frame number.</p> 1400 * 1401 * @param frameNumber the frame number corresponding to the result 1402 * @param result the total or partial result 1403 * @param partial {@true} if the result is partial, {@code false} if total 1404 * @param isReprocess true if it is a reprocess result, false if it is a regular result. 1405 */ updateTracker(long frameNumber, CaptureResult result, boolean partial, boolean isReprocess)1406 public void updateTracker(long frameNumber, CaptureResult result, boolean partial, 1407 boolean isReprocess) { 1408 if (!partial) { 1409 // Update the total result's frame status as being successful 1410 updateTracker(frameNumber, /*isError*/false, isReprocess); 1411 // Don't keep a list of total results, we don't need to track them 1412 return; 1413 } 1414 1415 if (result == null) { 1416 // Do not record blank results; this also means there will be no total result 1417 // so it doesn't matter that the partials were not recorded 1418 return; 1419 } 1420 1421 // Partial results must be aggregated in-order for that frame number 1422 List<CaptureResult> partials = mPartialResults.get(frameNumber); 1423 if (partials == null) { 1424 partials = new ArrayList<>(); 1425 mPartialResults.put(frameNumber, partials); 1426 } 1427 1428 partials.add(result); 1429 } 1430 1431 /** 1432 * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. 1433 * 1434 * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} 1435 * is called again with new partials for that frame number).</p> 1436 * 1437 * @param frameNumber the frame number corresponding to the result 1438 * @return a list of partial results for that frame with at least 1 element, 1439 * or {@code null} if there were no partials recorded for that frame 1440 */ popPartialResults(long frameNumber)1441 public List<CaptureResult> popPartialResults(long frameNumber) { 1442 return mPartialResults.remove(frameNumber); 1443 } 1444 getCompletedFrameNumber()1445 public long getCompletedFrameNumber() { 1446 return mCompletedFrameNumber; 1447 } 1448 getCompletedReprocessFrameNumber()1449 public long getCompletedReprocessFrameNumber() { 1450 return mCompletedReprocessFrameNumber; 1451 } 1452 1453 /** 1454 * Update the completed frame number for regular results. 1455 * 1456 * It validates that all previous frames have arrived except for reprocess frames. 1457 * 1458 * If there is a gap since previous regular frame number, assume the frames in the gap are 1459 * reprocess frames and store them in the skipped reprocess frame number queue to check 1460 * against when reprocess frames arrive. 1461 */ updateCompletedFrameNumber(long frameNumber)1462 private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException { 1463 if (frameNumber <= mCompletedFrameNumber) { 1464 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); 1465 } else if (frameNumber <= mCompletedReprocessFrameNumber) { 1466 // if frame number is smaller than completed reprocess frame number, 1467 // it must be the head of mSkippedRegularFrameNumbers 1468 if (mSkippedRegularFrameNumbers.isEmpty() == true || 1469 frameNumber < mSkippedRegularFrameNumbers.element()) { 1470 throw new IllegalArgumentException("frame number " + frameNumber + 1471 " is a repeat"); 1472 } else if (frameNumber > mSkippedRegularFrameNumbers.element()) { 1473 throw new IllegalArgumentException("frame number " + frameNumber + 1474 " comes out of order. Expecting " + 1475 mSkippedRegularFrameNumbers.element()); 1476 } 1477 // frame number matches the head of the skipped frame number queue. 1478 mSkippedRegularFrameNumbers.remove(); 1479 } else { 1480 // there is a gap of unseen frame numbers which should belong to reprocess result 1481 // put all the skipped frame numbers in the queue 1482 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1; 1483 i < frameNumber; i++) { 1484 mSkippedReprocessFrameNumbers.add(i); 1485 } 1486 } 1487 1488 mCompletedFrameNumber = frameNumber; 1489 } 1490 1491 /** 1492 * Update the completed frame number for reprocess results. 1493 * 1494 * It validates that all previous frames have arrived except for regular frames. 1495 * 1496 * If there is a gap since previous reprocess frame number, assume the frames in the gap are 1497 * regular frames and store them in the skipped regular frame number queue to check 1498 * against when regular frames arrive. 1499 */ updateCompletedReprocessFrameNumber(long frameNumber)1500 private void updateCompletedReprocessFrameNumber(long frameNumber) 1501 throws IllegalArgumentException { 1502 if (frameNumber < mCompletedReprocessFrameNumber) { 1503 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); 1504 } else if (frameNumber < mCompletedFrameNumber) { 1505 // if reprocess frame number is smaller than completed regular frame number, 1506 // it must be the head of the skipped reprocess frame number queue. 1507 if (mSkippedReprocessFrameNumbers.isEmpty() == true || 1508 frameNumber < mSkippedReprocessFrameNumbers.element()) { 1509 throw new IllegalArgumentException("frame number " + frameNumber + 1510 " is a repeat"); 1511 } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) { 1512 throw new IllegalArgumentException("frame number " + frameNumber + 1513 " comes out of order. Expecting " + 1514 mSkippedReprocessFrameNumbers.element()); 1515 } 1516 // frame number matches the head of the skipped frame number queue. 1517 mSkippedReprocessFrameNumbers.remove(); 1518 } else { 1519 // put all the skipped frame numbers in the queue 1520 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1; 1521 i < frameNumber; i++) { 1522 mSkippedRegularFrameNumbers.add(i); 1523 } 1524 } 1525 mCompletedReprocessFrameNumber = frameNumber; 1526 } 1527 } 1528 checkAndFireSequenceComplete()1529 private void checkAndFireSequenceComplete() { 1530 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); 1531 long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); 1532 boolean isReprocess = false; 1533 Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator(); 1534 while (iter.hasNext()) { 1535 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); 1536 boolean sequenceCompleted = false; 1537 final int requestId = requestLastFrameNumbers.getRequestId(); 1538 final CaptureCallbackHolder holder; 1539 synchronized(mInterfaceLock) { 1540 if (mRemoteDevice == null) { 1541 Log.w(TAG, "Camera closed while checking sequences"); 1542 return; 1543 } 1544 1545 int index = mCaptureCallbackMap.indexOfKey(requestId); 1546 holder = (index >= 0) ? 1547 mCaptureCallbackMap.valueAt(index) : null; 1548 if (holder != null) { 1549 long lastRegularFrameNumber = 1550 requestLastFrameNumbers.getLastRegularFrameNumber(); 1551 long lastReprocessFrameNumber = 1552 requestLastFrameNumbers.getLastReprocessFrameNumber(); 1553 1554 // check if it's okay to remove request from mCaptureCallbackMap 1555 if (lastRegularFrameNumber <= completedFrameNumber && 1556 lastReprocessFrameNumber <= completedReprocessFrameNumber) { 1557 sequenceCompleted = true; 1558 mCaptureCallbackMap.removeAt(index); 1559 if (DEBUG) { 1560 Log.v(TAG, String.format( 1561 "Remove holder for requestId %d, because lastRegularFrame %d " + 1562 "is <= %d and lastReprocessFrame %d is <= %d", requestId, 1563 lastRegularFrameNumber, completedFrameNumber, 1564 lastReprocessFrameNumber, completedReprocessFrameNumber)); 1565 } 1566 } 1567 } 1568 } 1569 1570 // If no callback is registered for this requestId or sequence completed, remove it 1571 // from the frame number->request pair because it's not needed anymore. 1572 if (holder == null || sequenceCompleted) { 1573 iter.remove(); 1574 } 1575 1576 // Call onCaptureSequenceCompleted 1577 if (sequenceCompleted) { 1578 Runnable resultDispatch = new Runnable() { 1579 @Override 1580 public void run() { 1581 if (!CameraDeviceImpl.this.isClosed()){ 1582 if (DEBUG) { 1583 Log.d(TAG, String.format( 1584 "fire sequence complete for request %d", 1585 requestId)); 1586 } 1587 1588 holder.getCallback().onCaptureSequenceCompleted( 1589 CameraDeviceImpl.this, 1590 requestId, 1591 requestLastFrameNumbers.getLastFrameNumber()); 1592 } 1593 } 1594 }; 1595 holder.getHandler().post(resultDispatch); 1596 } 1597 } 1598 } 1599 1600 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 1601 // 1602 // Constants below need to be kept up-to-date with 1603 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h 1604 // 1605 1606 // 1607 // Error codes for onCameraError 1608 // 1609 1610 /** 1611 * Camera has been disconnected 1612 */ 1613 public static final int ERROR_CAMERA_DISCONNECTED = 0; 1614 /** 1615 * Camera has encountered a device-level error 1616 * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE 1617 */ 1618 public static final int ERROR_CAMERA_DEVICE = 1; 1619 /** 1620 * Camera has encountered a service-level error 1621 * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE 1622 */ 1623 public static final int ERROR_CAMERA_SERVICE = 2; 1624 /** 1625 * Camera has encountered an error processing a single request. 1626 */ 1627 public static final int ERROR_CAMERA_REQUEST = 3; 1628 /** 1629 * Camera has encountered an error producing metadata for a single capture 1630 */ 1631 public static final int ERROR_CAMERA_RESULT = 4; 1632 /** 1633 * Camera has encountered an error producing an image buffer for a single capture 1634 */ 1635 public static final int ERROR_CAMERA_BUFFER = 5; 1636 1637 @Override asBinder()1638 public IBinder asBinder() { 1639 return this; 1640 } 1641 1642 @Override onDeviceError(final int errorCode, CaptureResultExtras resultExtras)1643 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { 1644 if (DEBUG) { 1645 Log.d(TAG, String.format( 1646 "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", 1647 errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), 1648 resultExtras.getSubsequenceId())); 1649 } 1650 1651 synchronized(mInterfaceLock) { 1652 if (mRemoteDevice == null) { 1653 return; // Camera already closed 1654 } 1655 1656 switch (errorCode) { 1657 case ERROR_CAMERA_DISCONNECTED: 1658 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected); 1659 break; 1660 default: 1661 Log.e(TAG, "Unknown error from camera device: " + errorCode); 1662 // no break 1663 case ERROR_CAMERA_DEVICE: 1664 case ERROR_CAMERA_SERVICE: 1665 mInError = true; 1666 Runnable r = new Runnable() { 1667 @Override 1668 public void run() { 1669 if (!CameraDeviceImpl.this.isClosed()) { 1670 mDeviceCallback.onError(CameraDeviceImpl.this, errorCode); 1671 } 1672 } 1673 }; 1674 CameraDeviceImpl.this.mDeviceHandler.post(r); 1675 break; 1676 case ERROR_CAMERA_REQUEST: 1677 case ERROR_CAMERA_RESULT: 1678 case ERROR_CAMERA_BUFFER: 1679 onCaptureErrorLocked(errorCode, resultExtras); 1680 break; 1681 } 1682 } 1683 } 1684 1685 @Override onDeviceIdle()1686 public void onDeviceIdle() { 1687 if (DEBUG) { 1688 Log.d(TAG, "Camera now idle"); 1689 } 1690 synchronized(mInterfaceLock) { 1691 if (mRemoteDevice == null) return; // Camera already closed 1692 1693 if (!CameraDeviceImpl.this.mIdle) { 1694 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle); 1695 } 1696 CameraDeviceImpl.this.mIdle = true; 1697 } 1698 } 1699 1700 @Override onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)1701 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 1702 int requestId = resultExtras.getRequestId(); 1703 final long frameNumber = resultExtras.getFrameNumber(); 1704 1705 if (DEBUG) { 1706 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber); 1707 } 1708 final CaptureCallbackHolder holder; 1709 1710 synchronized(mInterfaceLock) { 1711 if (mRemoteDevice == null) return; // Camera already closed 1712 1713 // Get the callback for this frame ID, if there is one 1714 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1715 1716 if (holder == null) { 1717 return; 1718 } 1719 1720 if (isClosed()) return; 1721 1722 // Dispatch capture start notice 1723 holder.getHandler().post( 1724 new Runnable() { 1725 @Override 1726 public void run() { 1727 if (!CameraDeviceImpl.this.isClosed()) { 1728 holder.getCallback().onCaptureStarted( 1729 CameraDeviceImpl.this, 1730 holder.getRequest(resultExtras.getSubsequenceId()), 1731 timestamp, frameNumber); 1732 } 1733 } 1734 }); 1735 1736 } 1737 } 1738 1739 @Override onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras)1740 public void onResultReceived(CameraMetadataNative result, 1741 CaptureResultExtras resultExtras) throws RemoteException { 1742 1743 int requestId = resultExtras.getRequestId(); 1744 long frameNumber = resultExtras.getFrameNumber(); 1745 1746 if (DEBUG) { 1747 Log.v(TAG, "Received result frame " + frameNumber + " for id " 1748 + requestId); 1749 } 1750 1751 synchronized(mInterfaceLock) { 1752 if (mRemoteDevice == null) return; // Camera already closed 1753 1754 // TODO: Handle CameraCharacteristics access from CaptureResult correctly. 1755 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, 1756 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); 1757 1758 final CaptureCallbackHolder holder = 1759 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1760 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); 1761 1762 boolean isPartialResult = 1763 (resultExtras.getPartialResultCount() < mTotalPartialCount); 1764 boolean isReprocess = request.isReprocess(); 1765 1766 // Check if we have a callback for this 1767 if (holder == null) { 1768 if (DEBUG) { 1769 Log.d(TAG, 1770 "holder is null, early return at frame " 1771 + frameNumber); 1772 } 1773 1774 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 1775 isReprocess); 1776 1777 return; 1778 } 1779 1780 if (isClosed()) { 1781 if (DEBUG) { 1782 Log.d(TAG, 1783 "camera is closed, early return at frame " 1784 + frameNumber); 1785 } 1786 1787 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 1788 isReprocess); 1789 return; 1790 } 1791 1792 1793 Runnable resultDispatch = null; 1794 1795 CaptureResult finalResult; 1796 1797 // Either send a partial result or the final capture completed result 1798 if (isPartialResult) { 1799 final CaptureResult resultAsCapture = 1800 new CaptureResult(result, request, resultExtras); 1801 1802 // Partial result 1803 resultDispatch = new Runnable() { 1804 @Override 1805 public void run() { 1806 if (!CameraDeviceImpl.this.isClosed()){ 1807 holder.getCallback().onCaptureProgressed( 1808 CameraDeviceImpl.this, 1809 request, 1810 resultAsCapture); 1811 } 1812 } 1813 }; 1814 1815 finalResult = resultAsCapture; 1816 } else { 1817 List<CaptureResult> partialResults = 1818 mFrameNumberTracker.popPartialResults(frameNumber); 1819 1820 final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result, 1821 request, resultExtras, partialResults, holder.getSessionId()); 1822 1823 // Final capture result 1824 resultDispatch = new Runnable() { 1825 @Override 1826 public void run() { 1827 if (!CameraDeviceImpl.this.isClosed()){ 1828 holder.getCallback().onCaptureCompleted( 1829 CameraDeviceImpl.this, 1830 request, 1831 resultAsCapture); 1832 } 1833 } 1834 }; 1835 1836 finalResult = resultAsCapture; 1837 } 1838 1839 holder.getHandler().post(resultDispatch); 1840 1841 // Collect the partials for a total result; or mark the frame as totally completed 1842 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, 1843 isReprocess); 1844 1845 // Fire onCaptureSequenceCompleted 1846 if (!isPartialResult) { 1847 checkAndFireSequenceComplete(); 1848 } 1849 } 1850 } 1851 1852 @Override onPrepared(int streamId)1853 public void onPrepared(int streamId) { 1854 final OutputConfiguration output; 1855 final StateCallbackKK sessionCallback; 1856 1857 if (DEBUG) { 1858 Log.v(TAG, "Stream " + streamId + " is prepared"); 1859 } 1860 1861 synchronized(mInterfaceLock) { 1862 output = mConfiguredOutputs.get(streamId); 1863 sessionCallback = mSessionStateCallback; 1864 } 1865 1866 if (sessionCallback == null) return; 1867 1868 if (output == null) { 1869 Log.w(TAG, "onPrepared invoked for unknown output Surface"); 1870 return; 1871 } 1872 final Surface surface = output.getSurface(); 1873 1874 sessionCallback.onSurfacePrepared(surface); 1875 } 1876 1877 /** 1878 * Called by onDeviceError for handling single-capture failures. 1879 */ onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras)1880 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { 1881 1882 final int requestId = resultExtras.getRequestId(); 1883 final int subsequenceId = resultExtras.getSubsequenceId(); 1884 final long frameNumber = resultExtras.getFrameNumber(); 1885 final CaptureCallbackHolder holder = 1886 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1887 1888 final CaptureRequest request = holder.getRequest(subsequenceId); 1889 1890 // No way to report buffer errors right now 1891 if (errorCode == ERROR_CAMERA_BUFFER) { 1892 Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber)); 1893 return; 1894 } 1895 1896 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); 1897 1898 // This is only approximate - exact handling needs the camera service and HAL to 1899 // disambiguate between request failures to due abort and due to real errors. 1900 // For now, assume that if the session believes we're mid-abort, then the error 1901 // is due to abort. 1902 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? 1903 CaptureFailure.REASON_FLUSHED : 1904 CaptureFailure.REASON_ERROR; 1905 1906 final CaptureFailure failure = new CaptureFailure( 1907 request, 1908 reason, 1909 /*dropped*/ mayHaveBuffers, 1910 requestId, 1911 frameNumber); 1912 1913 Runnable failureDispatch = new Runnable() { 1914 @Override 1915 public void run() { 1916 if (!CameraDeviceImpl.this.isClosed()){ 1917 holder.getCallback().onCaptureFailed( 1918 CameraDeviceImpl.this, 1919 request, 1920 failure); 1921 } 1922 } 1923 }; 1924 holder.getHandler().post(failureDispatch); 1925 1926 // Fire onCaptureSequenceCompleted if appropriate 1927 if (DEBUG) { 1928 Log.v(TAG, String.format("got error frame %d", frameNumber)); 1929 } 1930 mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess()); 1931 checkAndFireSequenceComplete(); 1932 } 1933 1934 } // public class CameraDeviceCallbacks 1935 1936 /** 1937 * Default handler management. 1938 * 1939 * <p> 1940 * If handler is null, get the current thread's 1941 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}. 1942 * </p> 1943 */ checkHandler(Handler handler)1944 static Handler checkHandler(Handler handler) { 1945 if (handler == null) { 1946 Looper looper = Looper.myLooper(); 1947 if (looper == null) { 1948 throw new IllegalArgumentException( 1949 "No handler given, and current thread has no looper!"); 1950 } 1951 handler = new Handler(looper); 1952 } 1953 return handler; 1954 } 1955 1956 /** 1957 * Default handler management, conditional on there being a callback. 1958 * 1959 * <p>If the callback isn't null, check the handler, otherwise pass it through.</p> 1960 */ checkHandler(Handler handler, T callback)1961 static <T> Handler checkHandler(Handler handler, T callback) { 1962 if (callback != null) { 1963 return checkHandler(handler); 1964 } 1965 return handler; 1966 } 1967 checkIfCameraClosedOrInError()1968 private void checkIfCameraClosedOrInError() throws CameraAccessException { 1969 if (mRemoteDevice == null) { 1970 throw new IllegalStateException("CameraDevice was already closed"); 1971 } 1972 if (mInError) { 1973 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, 1974 "The camera device has encountered a serious error"); 1975 } 1976 } 1977 1978 /** Whether the camera device has started to close (may not yet have finished) */ isClosed()1979 private boolean isClosed() { 1980 return mClosing.get(); 1981 } 1982 getCharacteristics()1983 private CameraCharacteristics getCharacteristics() { 1984 return mCharacteristics; 1985 } 1986 1987 } 1988