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 prepare(int maxCount, Surface surface)682 public void prepare(int maxCount, Surface surface) throws CameraAccessException { 683 if (surface == null) throw new IllegalArgumentException("Surface is null"); 684 if (maxCount <= 0) throw new IllegalArgumentException("Invalid maxCount given: " + 685 maxCount); 686 687 synchronized(mInterfaceLock) { 688 int streamId = -1; 689 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 690 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 691 streamId = mConfiguredOutputs.keyAt(i); 692 break; 693 } 694 } 695 if (streamId == -1) { 696 throw new IllegalArgumentException("Surface is not part of this session"); 697 } 698 try { 699 mRemoteDevice.prepare2(maxCount, streamId); 700 } catch (CameraRuntimeException e) { 701 throw e.asChecked(); 702 } catch (RemoteException e) { 703 // impossible 704 return; 705 } 706 } 707 } 708 tearDown(Surface surface)709 public void tearDown(Surface surface) throws CameraAccessException { 710 if (surface == null) throw new IllegalArgumentException("Surface is null"); 711 712 synchronized(mInterfaceLock) { 713 int streamId = -1; 714 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 715 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 716 streamId = mConfiguredOutputs.keyAt(i); 717 break; 718 } 719 } 720 if (streamId == -1) { 721 throw new IllegalArgumentException("Surface is not part of this session"); 722 } 723 try { 724 mRemoteDevice.tearDown(streamId); 725 } catch (CameraRuntimeException e) { 726 throw e.asChecked(); 727 } catch (RemoteException e) { 728 // impossible 729 return; 730 } 731 } 732 } 733 capture(CaptureRequest request, CaptureCallback callback, Handler handler)734 public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) 735 throws CameraAccessException { 736 if (DEBUG) { 737 Log.d(TAG, "calling capture"); 738 } 739 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 740 requestList.add(request); 741 return submitCaptureRequest(requestList, callback, handler, /*streaming*/false); 742 } 743 captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)744 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, 745 Handler handler) throws CameraAccessException { 746 if (requests == null || requests.isEmpty()) { 747 throw new IllegalArgumentException("At least one request must be given"); 748 } 749 return submitCaptureRequest(requests, callback, handler, /*streaming*/false); 750 } 751 752 /** 753 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for 754 * starting and stopping repeating request and flushing. 755 * 756 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never 757 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered. 758 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last 759 * regular frame number will be added to the list mRequestLastFrameNumbersList.</p> 760 * 761 * @param requestId the request ID of the current repeating request. 762 * 763 * @param lastFrameNumber last frame number returned from binder. 764 */ checkEarlyTriggerSequenceComplete( final int requestId, final long lastFrameNumber)765 private void checkEarlyTriggerSequenceComplete( 766 final int requestId, final long lastFrameNumber) { 767 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request 768 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately. 769 if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 770 final CaptureCallbackHolder holder; 771 int index = mCaptureCallbackMap.indexOfKey(requestId); 772 holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null; 773 if (holder != null) { 774 mCaptureCallbackMap.removeAt(index); 775 if (DEBUG) { 776 Log.v(TAG, String.format( 777 "remove holder for requestId %d, " 778 + "because lastFrame is %d.", 779 requestId, lastFrameNumber)); 780 } 781 } 782 783 if (holder != null) { 784 if (DEBUG) { 785 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because" 786 + " request did not reach HAL"); 787 } 788 789 Runnable resultDispatch = new Runnable() { 790 @Override 791 public void run() { 792 if (!CameraDeviceImpl.this.isClosed()) { 793 if (DEBUG) { 794 Log.d(TAG, String.format( 795 "early trigger sequence complete for request %d", 796 requestId)); 797 } 798 holder.getCallback().onCaptureSequenceAborted( 799 CameraDeviceImpl.this, 800 requestId); 801 } 802 } 803 }; 804 holder.getHandler().post(resultDispatch); 805 } else { 806 Log.w(TAG, String.format( 807 "did not register callback to request %d", 808 requestId)); 809 } 810 } else { 811 // This function is only called for regular request so lastFrameNumber is the last 812 // regular frame number. 813 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId, 814 lastFrameNumber)); 815 816 // It is possible that the last frame has already arrived, so we need to check 817 // for sequence completion right away 818 checkAndFireSequenceComplete(); 819 } 820 } 821 submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, Handler handler, boolean repeating)822 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, 823 Handler handler, boolean repeating) throws CameraAccessException { 824 825 // Need a valid handler, or current thread needs to have a looper, if 826 // callback is valid 827 handler = checkHandler(handler, callback); 828 829 // Make sure that there all requests have at least 1 surface; all surfaces are non-null 830 for (CaptureRequest request : requestList) { 831 if (request.getTargets().isEmpty()) { 832 throw new IllegalArgumentException( 833 "Each request must have at least one Surface target"); 834 } 835 836 for (Surface surface : request.getTargets()) { 837 if (surface == null) { 838 throw new IllegalArgumentException("Null Surface targets are not allowed"); 839 } 840 } 841 } 842 843 synchronized(mInterfaceLock) { 844 checkIfCameraClosedOrInError(); 845 int requestId; 846 847 if (repeating) { 848 stopRepeating(); 849 } 850 851 LongParcelable lastFrameNumberRef = new LongParcelable(); 852 try { 853 requestId = mRemoteDevice.submitRequestList(requestList, repeating, 854 /*out*/lastFrameNumberRef); 855 if (DEBUG) { 856 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber()); 857 } 858 } catch (CameraRuntimeException e) { 859 throw e.asChecked(); 860 } catch (RemoteException e) { 861 // impossible 862 return -1; 863 } 864 865 if (callback != null) { 866 mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback, 867 requestList, handler, repeating, mNextSessionId - 1)); 868 } else { 869 if (DEBUG) { 870 Log.d(TAG, "Listen for request " + requestId + " is null"); 871 } 872 } 873 874 long lastFrameNumber = lastFrameNumberRef.getNumber(); 875 876 if (repeating) { 877 if (mRepeatingRequestId != REQUEST_ID_NONE) { 878 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 879 } 880 mRepeatingRequestId = requestId; 881 } else { 882 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestList, 883 requestId, lastFrameNumber)); 884 } 885 886 if (mIdle) { 887 mDeviceHandler.post(mCallOnActive); 888 } 889 mIdle = false; 890 891 return requestId; 892 } 893 } 894 setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler)895 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, 896 Handler handler) throws CameraAccessException { 897 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 898 requestList.add(request); 899 return submitCaptureRequest(requestList, callback, handler, /*streaming*/true); 900 } 901 setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)902 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, 903 Handler handler) throws CameraAccessException { 904 if (requests == null || requests.isEmpty()) { 905 throw new IllegalArgumentException("At least one request must be given"); 906 } 907 return submitCaptureRequest(requests, callback, handler, /*streaming*/true); 908 } 909 stopRepeating()910 public void stopRepeating() throws CameraAccessException { 911 912 synchronized(mInterfaceLock) { 913 checkIfCameraClosedOrInError(); 914 if (mRepeatingRequestId != REQUEST_ID_NONE) { 915 916 int requestId = mRepeatingRequestId; 917 mRepeatingRequestId = REQUEST_ID_NONE; 918 919 // Queue for deletion after in-flight requests finish 920 if (mCaptureCallbackMap.get(requestId) != null) { 921 mRepeatingRequestIdDeletedList.add(requestId); 922 } 923 924 try { 925 LongParcelable lastFrameNumberRef = new LongParcelable(); 926 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef); 927 long lastFrameNumber = lastFrameNumberRef.getNumber(); 928 929 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber); 930 931 } catch (CameraRuntimeException e) { 932 throw e.asChecked(); 933 } catch (RemoteException e) { 934 // impossible 935 return; 936 } 937 } 938 } 939 } 940 waitUntilIdle()941 private void waitUntilIdle() throws CameraAccessException { 942 943 synchronized(mInterfaceLock) { 944 checkIfCameraClosedOrInError(); 945 946 if (mRepeatingRequestId != REQUEST_ID_NONE) { 947 throw new IllegalStateException("Active repeating request ongoing"); 948 } 949 try { 950 mRemoteDevice.waitUntilIdle(); 951 } catch (CameraRuntimeException e) { 952 throw e.asChecked(); 953 } catch (RemoteException e) { 954 // impossible 955 return; 956 } 957 } 958 } 959 flush()960 public void flush() throws CameraAccessException { 961 synchronized(mInterfaceLock) { 962 checkIfCameraClosedOrInError(); 963 964 mDeviceHandler.post(mCallOnBusy); 965 966 // If already idle, just do a busy->idle transition immediately, don't actually 967 // flush. 968 if (mIdle) { 969 mDeviceHandler.post(mCallOnIdle); 970 return; 971 } 972 try { 973 LongParcelable lastFrameNumberRef = new LongParcelable(); 974 mRemoteDevice.flush(/*out*/lastFrameNumberRef); 975 if (mRepeatingRequestId != REQUEST_ID_NONE) { 976 long lastFrameNumber = lastFrameNumberRef.getNumber(); 977 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber); 978 mRepeatingRequestId = REQUEST_ID_NONE; 979 } 980 } catch (CameraRuntimeException e) { 981 throw e.asChecked(); 982 } catch (RemoteException e) { 983 // impossible 984 return; 985 } 986 } 987 } 988 989 @Override close()990 public void close() { 991 synchronized (mInterfaceLock) { 992 if (mClosing.getAndSet(true)) { 993 return; 994 } 995 996 try { 997 if (mRemoteDevice != null) { 998 mRemoteDevice.disconnect(); 999 } 1000 } catch (CameraRuntimeException e) { 1001 Log.e(TAG, "Exception while closing: ", e.asChecked()); 1002 } catch (RemoteException e) { 1003 // impossible 1004 } 1005 1006 // Only want to fire the onClosed callback once; 1007 // either a normal close where the remote device is valid 1008 // or a close after a startup error (no remote device but in error state) 1009 if (mRemoteDevice != null || mInError) { 1010 mDeviceHandler.post(mCallOnClosed); 1011 } 1012 1013 mRemoteDevice = null; 1014 } 1015 } 1016 1017 @Override finalize()1018 protected void finalize() throws Throwable { 1019 try { 1020 close(); 1021 } 1022 finally { 1023 super.finalize(); 1024 } 1025 } 1026 checkInputConfiguration(InputConfiguration inputConfig)1027 private void checkInputConfiguration(InputConfiguration inputConfig) { 1028 if (inputConfig != null) { 1029 StreamConfigurationMap configMap = mCharacteristics.get( 1030 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1031 1032 int[] inputFormats = configMap.getInputFormats(); 1033 boolean validFormat = false; 1034 for (int format : inputFormats) { 1035 if (format == inputConfig.getFormat()) { 1036 validFormat = true; 1037 } 1038 } 1039 1040 if (validFormat == false) { 1041 throw new IllegalArgumentException("input format " + inputConfig.getFormat() + 1042 " is not valid"); 1043 } 1044 1045 boolean validSize = false; 1046 Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat()); 1047 for (Size s : inputSizes) { 1048 if (inputConfig.getWidth() == s.getWidth() && 1049 inputConfig.getHeight() == s.getHeight()) { 1050 validSize = true; 1051 } 1052 } 1053 1054 if (validSize == false) { 1055 throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" + 1056 inputConfig.getHeight() + " is not valid"); 1057 } 1058 } 1059 } 1060 1061 /** 1062 * <p>A callback for tracking the progress of a {@link CaptureRequest} 1063 * submitted to the camera device.</p> 1064 * 1065 */ 1066 public static abstract class CaptureCallback { 1067 1068 /** 1069 * This constant is used to indicate that no images were captured for 1070 * the request. 1071 * 1072 * @hide 1073 */ 1074 public static final int NO_FRAMES_CAPTURED = -1; 1075 1076 /** 1077 * This method is called when the camera device has started capturing 1078 * the output image for the request, at the beginning of image exposure. 1079 * 1080 * @see android.media.MediaActionSound 1081 */ onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp, long frameNumber)1082 public void onCaptureStarted(CameraDevice camera, 1083 CaptureRequest request, long timestamp, long frameNumber) { 1084 // default empty implementation 1085 } 1086 1087 /** 1088 * This method is called when some results from an image capture are 1089 * available. 1090 * 1091 * @hide 1092 */ onCapturePartial(CameraDevice camera, CaptureRequest request, CaptureResult result)1093 public void onCapturePartial(CameraDevice camera, 1094 CaptureRequest request, CaptureResult result) { 1095 // default empty implementation 1096 } 1097 1098 /** 1099 * This method is called when an image capture makes partial forward progress; some 1100 * (but not all) results from an image capture are available. 1101 * 1102 */ onCaptureProgressed(CameraDevice camera, CaptureRequest request, CaptureResult partialResult)1103 public void onCaptureProgressed(CameraDevice camera, 1104 CaptureRequest request, CaptureResult partialResult) { 1105 // default empty implementation 1106 } 1107 1108 /** 1109 * This method is called when an image capture has fully completed and all the 1110 * result metadata is available. 1111 */ onCaptureCompleted(CameraDevice camera, CaptureRequest request, TotalCaptureResult result)1112 public void onCaptureCompleted(CameraDevice camera, 1113 CaptureRequest request, TotalCaptureResult result) { 1114 // default empty implementation 1115 } 1116 1117 /** 1118 * This method is called instead of {@link #onCaptureCompleted} when the 1119 * camera device failed to produce a {@link CaptureResult} for the 1120 * request. 1121 */ onCaptureFailed(CameraDevice camera, CaptureRequest request, CaptureFailure failure)1122 public void onCaptureFailed(CameraDevice camera, 1123 CaptureRequest request, CaptureFailure failure) { 1124 // default empty implementation 1125 } 1126 1127 /** 1128 * This method is called independently of the others in CaptureCallback, 1129 * when a capture sequence finishes and all {@link CaptureResult} 1130 * or {@link CaptureFailure} for it have been returned via this callback. 1131 */ onCaptureSequenceCompleted(CameraDevice camera, int sequenceId, long frameNumber)1132 public void onCaptureSequenceCompleted(CameraDevice camera, 1133 int sequenceId, long frameNumber) { 1134 // default empty implementation 1135 } 1136 1137 /** 1138 * This method is called independently of the others in CaptureCallback, 1139 * when a capture sequence aborts before any {@link CaptureResult} 1140 * or {@link CaptureFailure} for it have been returned via this callback. 1141 */ onCaptureSequenceAborted(CameraDevice camera, int sequenceId)1142 public void onCaptureSequenceAborted(CameraDevice camera, 1143 int sequenceId) { 1144 // default empty implementation 1145 } 1146 } 1147 1148 /** 1149 * A callback for notifications about the state of a camera device, adding in the callbacks that 1150 * were part of the earlier KK API design, but now only used internally. 1151 */ 1152 public static abstract class StateCallbackKK extends StateCallback { 1153 /** 1154 * The method called when a camera device has no outputs configured. 1155 * 1156 */ onUnconfigured(CameraDevice camera)1157 public void onUnconfigured(CameraDevice camera) { 1158 // Default empty implementation 1159 } 1160 1161 /** 1162 * The method called when a camera device begins processing 1163 * {@link CaptureRequest capture requests}. 1164 * 1165 */ onActive(CameraDevice camera)1166 public void onActive(CameraDevice camera) { 1167 // Default empty implementation 1168 } 1169 1170 /** 1171 * The method called when a camera device is busy. 1172 * 1173 */ onBusy(CameraDevice camera)1174 public void onBusy(CameraDevice camera) { 1175 // Default empty implementation 1176 } 1177 1178 /** 1179 * The method called when a camera device has finished processing all 1180 * submitted capture requests and has reached an idle state. 1181 * 1182 */ onIdle(CameraDevice camera)1183 public void onIdle(CameraDevice camera) { 1184 // Default empty implementation 1185 } 1186 1187 /** 1188 * The method called when the camera device has finished preparing 1189 * an output Surface 1190 */ onSurfacePrepared(Surface surface)1191 public void onSurfacePrepared(Surface surface) { 1192 // Default empty implementation 1193 } 1194 } 1195 1196 static class CaptureCallbackHolder { 1197 1198 private final boolean mRepeating; 1199 private final CaptureCallback mCallback; 1200 private final List<CaptureRequest> mRequestList; 1201 private final Handler mHandler; 1202 private final int mSessionId; 1203 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, Handler handler, boolean repeating, int sessionId)1204 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, 1205 Handler handler, boolean repeating, int sessionId) { 1206 if (callback == null || handler == null) { 1207 throw new UnsupportedOperationException( 1208 "Must have a valid handler and a valid callback"); 1209 } 1210 mRepeating = repeating; 1211 mHandler = handler; 1212 mRequestList = new ArrayList<CaptureRequest>(requestList); 1213 mCallback = callback; 1214 mSessionId = sessionId; 1215 } 1216 isRepeating()1217 public boolean isRepeating() { 1218 return mRepeating; 1219 } 1220 getCallback()1221 public CaptureCallback getCallback() { 1222 return mCallback; 1223 } 1224 getRequest(int subsequenceId)1225 public CaptureRequest getRequest(int subsequenceId) { 1226 if (subsequenceId >= mRequestList.size()) { 1227 throw new IllegalArgumentException( 1228 String.format( 1229 "Requested subsequenceId %d is larger than request list size %d.", 1230 subsequenceId, mRequestList.size())); 1231 } else { 1232 if (subsequenceId < 0) { 1233 throw new IllegalArgumentException(String.format( 1234 "Requested subsequenceId %d is negative", subsequenceId)); 1235 } else { 1236 return mRequestList.get(subsequenceId); 1237 } 1238 } 1239 } 1240 getRequest()1241 public CaptureRequest getRequest() { 1242 return getRequest(0); 1243 } 1244 getHandler()1245 public Handler getHandler() { 1246 return mHandler; 1247 } 1248 getSessionId()1249 public int getSessionId() { 1250 return mSessionId; 1251 } 1252 } 1253 1254 /** 1255 * This class holds a capture ID and its expected last regular frame number and last reprocess 1256 * frame number. 1257 */ 1258 static class RequestLastFrameNumbersHolder { 1259 // request ID 1260 private final int mRequestId; 1261 // The last regular frame number for this request ID. It's 1262 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request. 1263 private final long mLastRegularFrameNumber; 1264 // The last reprocess frame number for this request ID. It's 1265 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request. 1266 private final long mLastReprocessFrameNumber; 1267 1268 /** 1269 * Create a request-last-frame-numbers holder with a list of requests, request ID, and 1270 * the last frame number returned by camera service. 1271 */ RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId, long lastFrameNumber)1272 public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId, 1273 long lastFrameNumber) { 1274 long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1275 long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1276 long frameNumber = lastFrameNumber; 1277 1278 if (lastFrameNumber < requestList.size() - 1) { 1279 throw new IllegalArgumentException("lastFrameNumber: " + lastFrameNumber + 1280 " should be at least " + (requestList.size() - 1) + " for the number of " + 1281 " requests in the list: " + requestList.size()); 1282 } 1283 1284 // find the last regular frame number and the last reprocess frame number 1285 for (int i = requestList.size() - 1; i >= 0; i--) { 1286 CaptureRequest request = requestList.get(i); 1287 if (request.isReprocess() && lastReprocessFrameNumber == 1288 CaptureCallback.NO_FRAMES_CAPTURED) { 1289 lastReprocessFrameNumber = frameNumber; 1290 } else if (!request.isReprocess() && lastRegularFrameNumber == 1291 CaptureCallback.NO_FRAMES_CAPTURED) { 1292 lastRegularFrameNumber = frameNumber; 1293 } 1294 1295 if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED && 1296 lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) { 1297 break; 1298 } 1299 1300 frameNumber--; 1301 } 1302 1303 mLastRegularFrameNumber = lastRegularFrameNumber; 1304 mLastReprocessFrameNumber = lastReprocessFrameNumber; 1305 mRequestId = requestId; 1306 } 1307 1308 /** 1309 * Create a request-last-frame-numbers holder with a request ID and last regular frame 1310 * number. 1311 */ RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber)1312 public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) { 1313 mLastRegularFrameNumber = lastRegularFrameNumber; 1314 mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1315 mRequestId = requestId; 1316 } 1317 1318 /** 1319 * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1320 * it contains no regular request. 1321 */ getLastRegularFrameNumber()1322 public long getLastRegularFrameNumber() { 1323 return mLastRegularFrameNumber; 1324 } 1325 1326 /** 1327 * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1328 * it contains no reprocess request. 1329 */ getLastReprocessFrameNumber()1330 public long getLastReprocessFrameNumber() { 1331 return mLastReprocessFrameNumber; 1332 } 1333 1334 /** 1335 * Return the last frame number overall. 1336 */ getLastFrameNumber()1337 public long getLastFrameNumber() { 1338 return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber); 1339 } 1340 1341 /** 1342 * Return the request ID. 1343 */ getRequestId()1344 public int getRequestId() { 1345 return mRequestId; 1346 } 1347 } 1348 1349 /** 1350 * This class tracks the last frame number for submitted requests. 1351 */ 1352 public class FrameNumberTracker { 1353 1354 private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1355 private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1356 /** the skipped frame numbers that belong to regular results */ 1357 private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>(); 1358 /** the skipped frame numbers that belong to reprocess results */ 1359 private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>(); 1360 /** frame number -> is reprocess */ 1361 private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>(); 1362 /** Map frame numbers to list of partial results */ 1363 private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); 1364 update()1365 private void update() { 1366 Iterator iter = mFutureErrorMap.entrySet().iterator(); 1367 while (iter.hasNext()) { 1368 TreeMap.Entry pair = (TreeMap.Entry)iter.next(); 1369 Long errorFrameNumber = (Long)pair.getKey(); 1370 Boolean reprocess = (Boolean)pair.getValue(); 1371 Boolean removeError = true; 1372 if (reprocess) { 1373 if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) { 1374 mCompletedReprocessFrameNumber = errorFrameNumber; 1375 } else if (mSkippedReprocessFrameNumbers.isEmpty() != true && 1376 errorFrameNumber == mSkippedReprocessFrameNumbers.element()) { 1377 mCompletedReprocessFrameNumber = errorFrameNumber; 1378 mSkippedReprocessFrameNumbers.remove(); 1379 } else { 1380 removeError = false; 1381 } 1382 } else { 1383 if (errorFrameNumber == mCompletedFrameNumber + 1) { 1384 mCompletedFrameNumber = errorFrameNumber; 1385 } else if (mSkippedRegularFrameNumbers.isEmpty() != true && 1386 errorFrameNumber == mSkippedRegularFrameNumbers.element()) { 1387 mCompletedFrameNumber = errorFrameNumber; 1388 mSkippedRegularFrameNumbers.remove(); 1389 } else { 1390 removeError = false; 1391 } 1392 } 1393 if (removeError) { 1394 iter.remove(); 1395 } 1396 } 1397 } 1398 1399 /** 1400 * This function is called every time when a result or an error is received. 1401 * @param frameNumber the frame number corresponding to the result or error 1402 * @param isError true if it is an error, false if it is not an error 1403 * @param isReprocess true if it is a reprocess result, false if it is a regular result. 1404 */ updateTracker(long frameNumber, boolean isError, boolean isReprocess)1405 public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) { 1406 if (isError) { 1407 mFutureErrorMap.put(frameNumber, isReprocess); 1408 } else { 1409 try { 1410 if (isReprocess) { 1411 updateCompletedReprocessFrameNumber(frameNumber); 1412 } else { 1413 updateCompletedFrameNumber(frameNumber); 1414 } 1415 } catch (IllegalArgumentException e) { 1416 Log.e(TAG, e.getMessage()); 1417 } 1418 } 1419 update(); 1420 } 1421 1422 /** 1423 * This function is called every time a result has been completed. 1424 * 1425 * <p>It keeps a track of all the partial results already created for a particular 1426 * frame number.</p> 1427 * 1428 * @param frameNumber the frame number corresponding to the result 1429 * @param result the total or partial result 1430 * @param partial {@true} if the result is partial, {@code false} if total 1431 * @param isReprocess true if it is a reprocess result, false if it is a regular result. 1432 */ updateTracker(long frameNumber, CaptureResult result, boolean partial, boolean isReprocess)1433 public void updateTracker(long frameNumber, CaptureResult result, boolean partial, 1434 boolean isReprocess) { 1435 if (!partial) { 1436 // Update the total result's frame status as being successful 1437 updateTracker(frameNumber, /*isError*/false, isReprocess); 1438 // Don't keep a list of total results, we don't need to track them 1439 return; 1440 } 1441 1442 if (result == null) { 1443 // Do not record blank results; this also means there will be no total result 1444 // so it doesn't matter that the partials were not recorded 1445 return; 1446 } 1447 1448 // Partial results must be aggregated in-order for that frame number 1449 List<CaptureResult> partials = mPartialResults.get(frameNumber); 1450 if (partials == null) { 1451 partials = new ArrayList<>(); 1452 mPartialResults.put(frameNumber, partials); 1453 } 1454 1455 partials.add(result); 1456 } 1457 1458 /** 1459 * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. 1460 * 1461 * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} 1462 * is called again with new partials for that frame number).</p> 1463 * 1464 * @param frameNumber the frame number corresponding to the result 1465 * @return a list of partial results for that frame with at least 1 element, 1466 * or {@code null} if there were no partials recorded for that frame 1467 */ popPartialResults(long frameNumber)1468 public List<CaptureResult> popPartialResults(long frameNumber) { 1469 return mPartialResults.remove(frameNumber); 1470 } 1471 getCompletedFrameNumber()1472 public long getCompletedFrameNumber() { 1473 return mCompletedFrameNumber; 1474 } 1475 getCompletedReprocessFrameNumber()1476 public long getCompletedReprocessFrameNumber() { 1477 return mCompletedReprocessFrameNumber; 1478 } 1479 1480 /** 1481 * Update the completed frame number for regular results. 1482 * 1483 * It validates that all previous frames have arrived except for reprocess frames. 1484 * 1485 * If there is a gap since previous regular frame number, assume the frames in the gap are 1486 * reprocess frames and store them in the skipped reprocess frame number queue to check 1487 * against when reprocess frames arrive. 1488 */ updateCompletedFrameNumber(long frameNumber)1489 private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException { 1490 if (frameNumber <= mCompletedFrameNumber) { 1491 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); 1492 } else if (frameNumber <= mCompletedReprocessFrameNumber) { 1493 // if frame number is smaller than completed reprocess frame number, 1494 // it must be the head of mSkippedRegularFrameNumbers 1495 if (mSkippedRegularFrameNumbers.isEmpty() == true || 1496 frameNumber < mSkippedRegularFrameNumbers.element()) { 1497 throw new IllegalArgumentException("frame number " + frameNumber + 1498 " is a repeat"); 1499 } else if (frameNumber > mSkippedRegularFrameNumbers.element()) { 1500 throw new IllegalArgumentException("frame number " + frameNumber + 1501 " comes out of order. Expecting " + 1502 mSkippedRegularFrameNumbers.element()); 1503 } 1504 // frame number matches the head of the skipped frame number queue. 1505 mSkippedRegularFrameNumbers.remove(); 1506 } else { 1507 // there is a gap of unseen frame numbers which should belong to reprocess result 1508 // put all the skipped frame numbers in the queue 1509 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1; 1510 i < frameNumber; i++) { 1511 mSkippedReprocessFrameNumbers.add(i); 1512 } 1513 } 1514 1515 mCompletedFrameNumber = frameNumber; 1516 } 1517 1518 /** 1519 * Update the completed frame number for reprocess results. 1520 * 1521 * It validates that all previous frames have arrived except for regular frames. 1522 * 1523 * If there is a gap since previous reprocess frame number, assume the frames in the gap are 1524 * regular frames and store them in the skipped regular frame number queue to check 1525 * against when regular frames arrive. 1526 */ updateCompletedReprocessFrameNumber(long frameNumber)1527 private void updateCompletedReprocessFrameNumber(long frameNumber) 1528 throws IllegalArgumentException { 1529 if (frameNumber < mCompletedReprocessFrameNumber) { 1530 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); 1531 } else if (frameNumber < mCompletedFrameNumber) { 1532 // if reprocess frame number is smaller than completed regular frame number, 1533 // it must be the head of the skipped reprocess frame number queue. 1534 if (mSkippedReprocessFrameNumbers.isEmpty() == true || 1535 frameNumber < mSkippedReprocessFrameNumbers.element()) { 1536 throw new IllegalArgumentException("frame number " + frameNumber + 1537 " is a repeat"); 1538 } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) { 1539 throw new IllegalArgumentException("frame number " + frameNumber + 1540 " comes out of order. Expecting " + 1541 mSkippedReprocessFrameNumbers.element()); 1542 } 1543 // frame number matches the head of the skipped frame number queue. 1544 mSkippedReprocessFrameNumbers.remove(); 1545 } else { 1546 // put all the skipped frame numbers in the queue 1547 for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1; 1548 i < frameNumber; i++) { 1549 mSkippedRegularFrameNumbers.add(i); 1550 } 1551 } 1552 mCompletedReprocessFrameNumber = frameNumber; 1553 } 1554 } 1555 checkAndFireSequenceComplete()1556 private void checkAndFireSequenceComplete() { 1557 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); 1558 long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); 1559 boolean isReprocess = false; 1560 Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator(); 1561 while (iter.hasNext()) { 1562 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); 1563 boolean sequenceCompleted = false; 1564 final int requestId = requestLastFrameNumbers.getRequestId(); 1565 final CaptureCallbackHolder holder; 1566 synchronized(mInterfaceLock) { 1567 if (mRemoteDevice == null) { 1568 Log.w(TAG, "Camera closed while checking sequences"); 1569 return; 1570 } 1571 1572 int index = mCaptureCallbackMap.indexOfKey(requestId); 1573 holder = (index >= 0) ? 1574 mCaptureCallbackMap.valueAt(index) : null; 1575 if (holder != null) { 1576 long lastRegularFrameNumber = 1577 requestLastFrameNumbers.getLastRegularFrameNumber(); 1578 long lastReprocessFrameNumber = 1579 requestLastFrameNumbers.getLastReprocessFrameNumber(); 1580 1581 // check if it's okay to remove request from mCaptureCallbackMap 1582 if (lastRegularFrameNumber <= completedFrameNumber && 1583 lastReprocessFrameNumber <= completedReprocessFrameNumber) { 1584 sequenceCompleted = true; 1585 mCaptureCallbackMap.removeAt(index); 1586 if (DEBUG) { 1587 Log.v(TAG, String.format( 1588 "Remove holder for requestId %d, because lastRegularFrame %d " + 1589 "is <= %d and lastReprocessFrame %d is <= %d", requestId, 1590 lastRegularFrameNumber, completedFrameNumber, 1591 lastReprocessFrameNumber, completedReprocessFrameNumber)); 1592 } 1593 } 1594 } 1595 } 1596 1597 // If no callback is registered for this requestId or sequence completed, remove it 1598 // from the frame number->request pair because it's not needed anymore. 1599 if (holder == null || sequenceCompleted) { 1600 iter.remove(); 1601 } 1602 1603 // Call onCaptureSequenceCompleted 1604 if (sequenceCompleted) { 1605 Runnable resultDispatch = new Runnable() { 1606 @Override 1607 public void run() { 1608 if (!CameraDeviceImpl.this.isClosed()){ 1609 if (DEBUG) { 1610 Log.d(TAG, String.format( 1611 "fire sequence complete for request %d", 1612 requestId)); 1613 } 1614 1615 holder.getCallback().onCaptureSequenceCompleted( 1616 CameraDeviceImpl.this, 1617 requestId, 1618 requestLastFrameNumbers.getLastFrameNumber()); 1619 } 1620 } 1621 }; 1622 holder.getHandler().post(resultDispatch); 1623 } 1624 } 1625 } 1626 1627 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 1628 // 1629 // Constants below need to be kept up-to-date with 1630 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h 1631 // 1632 1633 // 1634 // Error codes for onCameraError 1635 // 1636 1637 /** 1638 * Camera has been disconnected 1639 */ 1640 public static final int ERROR_CAMERA_DISCONNECTED = 0; 1641 /** 1642 * Camera has encountered a device-level error 1643 * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE 1644 */ 1645 public static final int ERROR_CAMERA_DEVICE = 1; 1646 /** 1647 * Camera has encountered a service-level error 1648 * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE 1649 */ 1650 public static final int ERROR_CAMERA_SERVICE = 2; 1651 /** 1652 * Camera has encountered an error processing a single request. 1653 */ 1654 public static final int ERROR_CAMERA_REQUEST = 3; 1655 /** 1656 * Camera has encountered an error producing metadata for a single capture 1657 */ 1658 public static final int ERROR_CAMERA_RESULT = 4; 1659 /** 1660 * Camera has encountered an error producing an image buffer for a single capture 1661 */ 1662 public static final int ERROR_CAMERA_BUFFER = 5; 1663 1664 @Override asBinder()1665 public IBinder asBinder() { 1666 return this; 1667 } 1668 1669 @Override onDeviceError(final int errorCode, CaptureResultExtras resultExtras)1670 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { 1671 if (DEBUG) { 1672 Log.d(TAG, String.format( 1673 "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", 1674 errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), 1675 resultExtras.getSubsequenceId())); 1676 } 1677 1678 synchronized(mInterfaceLock) { 1679 if (mRemoteDevice == null) { 1680 return; // Camera already closed 1681 } 1682 1683 switch (errorCode) { 1684 case ERROR_CAMERA_DISCONNECTED: 1685 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected); 1686 break; 1687 default: 1688 Log.e(TAG, "Unknown error from camera device: " + errorCode); 1689 // no break 1690 case ERROR_CAMERA_DEVICE: 1691 case ERROR_CAMERA_SERVICE: 1692 mInError = true; 1693 Runnable r = new Runnable() { 1694 @Override 1695 public void run() { 1696 if (!CameraDeviceImpl.this.isClosed()) { 1697 mDeviceCallback.onError(CameraDeviceImpl.this, errorCode); 1698 } 1699 } 1700 }; 1701 CameraDeviceImpl.this.mDeviceHandler.post(r); 1702 break; 1703 case ERROR_CAMERA_REQUEST: 1704 case ERROR_CAMERA_RESULT: 1705 case ERROR_CAMERA_BUFFER: 1706 onCaptureErrorLocked(errorCode, resultExtras); 1707 break; 1708 } 1709 } 1710 } 1711 1712 @Override onDeviceIdle()1713 public void onDeviceIdle() { 1714 if (DEBUG) { 1715 Log.d(TAG, "Camera now idle"); 1716 } 1717 synchronized(mInterfaceLock) { 1718 if (mRemoteDevice == null) return; // Camera already closed 1719 1720 if (!CameraDeviceImpl.this.mIdle) { 1721 CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle); 1722 } 1723 CameraDeviceImpl.this.mIdle = true; 1724 } 1725 } 1726 1727 @Override onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)1728 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 1729 int requestId = resultExtras.getRequestId(); 1730 final long frameNumber = resultExtras.getFrameNumber(); 1731 1732 if (DEBUG) { 1733 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber); 1734 } 1735 final CaptureCallbackHolder holder; 1736 1737 synchronized(mInterfaceLock) { 1738 if (mRemoteDevice == null) return; // Camera already closed 1739 1740 // Get the callback for this frame ID, if there is one 1741 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1742 1743 if (holder == null) { 1744 return; 1745 } 1746 1747 if (isClosed()) return; 1748 1749 // Dispatch capture start notice 1750 holder.getHandler().post( 1751 new Runnable() { 1752 @Override 1753 public void run() { 1754 if (!CameraDeviceImpl.this.isClosed()) { 1755 holder.getCallback().onCaptureStarted( 1756 CameraDeviceImpl.this, 1757 holder.getRequest(resultExtras.getSubsequenceId()), 1758 timestamp, frameNumber); 1759 } 1760 } 1761 }); 1762 1763 } 1764 } 1765 1766 @Override onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras)1767 public void onResultReceived(CameraMetadataNative result, 1768 CaptureResultExtras resultExtras) throws RemoteException { 1769 1770 int requestId = resultExtras.getRequestId(); 1771 long frameNumber = resultExtras.getFrameNumber(); 1772 1773 if (DEBUG) { 1774 Log.v(TAG, "Received result frame " + frameNumber + " for id " 1775 + requestId); 1776 } 1777 1778 synchronized(mInterfaceLock) { 1779 if (mRemoteDevice == null) return; // Camera already closed 1780 1781 // TODO: Handle CameraCharacteristics access from CaptureResult correctly. 1782 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, 1783 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); 1784 1785 final CaptureCallbackHolder holder = 1786 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1787 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); 1788 1789 boolean isPartialResult = 1790 (resultExtras.getPartialResultCount() < mTotalPartialCount); 1791 boolean isReprocess = request.isReprocess(); 1792 1793 // Check if we have a callback for this 1794 if (holder == null) { 1795 if (DEBUG) { 1796 Log.d(TAG, 1797 "holder is null, early return at frame " 1798 + frameNumber); 1799 } 1800 1801 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 1802 isReprocess); 1803 1804 return; 1805 } 1806 1807 if (isClosed()) { 1808 if (DEBUG) { 1809 Log.d(TAG, 1810 "camera is closed, early return at frame " 1811 + frameNumber); 1812 } 1813 1814 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 1815 isReprocess); 1816 return; 1817 } 1818 1819 1820 Runnable resultDispatch = null; 1821 1822 CaptureResult finalResult; 1823 1824 // Either send a partial result or the final capture completed result 1825 if (isPartialResult) { 1826 final CaptureResult resultAsCapture = 1827 new CaptureResult(result, request, resultExtras); 1828 1829 // Partial result 1830 resultDispatch = new Runnable() { 1831 @Override 1832 public void run() { 1833 if (!CameraDeviceImpl.this.isClosed()){ 1834 holder.getCallback().onCaptureProgressed( 1835 CameraDeviceImpl.this, 1836 request, 1837 resultAsCapture); 1838 } 1839 } 1840 }; 1841 1842 finalResult = resultAsCapture; 1843 } else { 1844 List<CaptureResult> partialResults = 1845 mFrameNumberTracker.popPartialResults(frameNumber); 1846 1847 final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result, 1848 request, resultExtras, partialResults, holder.getSessionId()); 1849 1850 // Final capture result 1851 resultDispatch = new Runnable() { 1852 @Override 1853 public void run() { 1854 if (!CameraDeviceImpl.this.isClosed()){ 1855 holder.getCallback().onCaptureCompleted( 1856 CameraDeviceImpl.this, 1857 request, 1858 resultAsCapture); 1859 } 1860 } 1861 }; 1862 1863 finalResult = resultAsCapture; 1864 } 1865 1866 holder.getHandler().post(resultDispatch); 1867 1868 // Collect the partials for a total result; or mark the frame as totally completed 1869 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, 1870 isReprocess); 1871 1872 // Fire onCaptureSequenceCompleted 1873 if (!isPartialResult) { 1874 checkAndFireSequenceComplete(); 1875 } 1876 } 1877 } 1878 1879 @Override onPrepared(int streamId)1880 public void onPrepared(int streamId) { 1881 final OutputConfiguration output; 1882 final StateCallbackKK sessionCallback; 1883 1884 if (DEBUG) { 1885 Log.v(TAG, "Stream " + streamId + " is prepared"); 1886 } 1887 1888 synchronized(mInterfaceLock) { 1889 output = mConfiguredOutputs.get(streamId); 1890 sessionCallback = mSessionStateCallback; 1891 } 1892 1893 if (sessionCallback == null) return; 1894 1895 if (output == null) { 1896 Log.w(TAG, "onPrepared invoked for unknown output Surface"); 1897 return; 1898 } 1899 final Surface surface = output.getSurface(); 1900 1901 sessionCallback.onSurfacePrepared(surface); 1902 } 1903 1904 /** 1905 * Called by onDeviceError for handling single-capture failures. 1906 */ onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras)1907 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { 1908 1909 final int requestId = resultExtras.getRequestId(); 1910 final int subsequenceId = resultExtras.getSubsequenceId(); 1911 final long frameNumber = resultExtras.getFrameNumber(); 1912 final CaptureCallbackHolder holder = 1913 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 1914 1915 final CaptureRequest request = holder.getRequest(subsequenceId); 1916 1917 // No way to report buffer errors right now 1918 if (errorCode == ERROR_CAMERA_BUFFER) { 1919 Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber)); 1920 return; 1921 } 1922 1923 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); 1924 1925 // This is only approximate - exact handling needs the camera service and HAL to 1926 // disambiguate between request failures to due abort and due to real errors. 1927 // For now, assume that if the session believes we're mid-abort, then the error 1928 // is due to abort. 1929 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? 1930 CaptureFailure.REASON_FLUSHED : 1931 CaptureFailure.REASON_ERROR; 1932 1933 final CaptureFailure failure = new CaptureFailure( 1934 request, 1935 reason, 1936 /*dropped*/ mayHaveBuffers, 1937 requestId, 1938 frameNumber); 1939 1940 Runnable failureDispatch = new Runnable() { 1941 @Override 1942 public void run() { 1943 if (!CameraDeviceImpl.this.isClosed()){ 1944 holder.getCallback().onCaptureFailed( 1945 CameraDeviceImpl.this, 1946 request, 1947 failure); 1948 } 1949 } 1950 }; 1951 holder.getHandler().post(failureDispatch); 1952 1953 // Fire onCaptureSequenceCompleted if appropriate 1954 if (DEBUG) { 1955 Log.v(TAG, String.format("got error frame %d", frameNumber)); 1956 } 1957 mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess()); 1958 checkAndFireSequenceComplete(); 1959 } 1960 1961 } // public class CameraDeviceCallbacks 1962 1963 /** 1964 * Default handler management. 1965 * 1966 * <p> 1967 * If handler is null, get the current thread's 1968 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}. 1969 * </p> 1970 */ checkHandler(Handler handler)1971 static Handler checkHandler(Handler handler) { 1972 if (handler == null) { 1973 Looper looper = Looper.myLooper(); 1974 if (looper == null) { 1975 throw new IllegalArgumentException( 1976 "No handler given, and current thread has no looper!"); 1977 } 1978 handler = new Handler(looper); 1979 } 1980 return handler; 1981 } 1982 1983 /** 1984 * Default handler management, conditional on there being a callback. 1985 * 1986 * <p>If the callback isn't null, check the handler, otherwise pass it through.</p> 1987 */ checkHandler(Handler handler, T callback)1988 static <T> Handler checkHandler(Handler handler, T callback) { 1989 if (callback != null) { 1990 return checkHandler(handler); 1991 } 1992 return handler; 1993 } 1994 checkIfCameraClosedOrInError()1995 private void checkIfCameraClosedOrInError() throws CameraAccessException { 1996 if (mRemoteDevice == null) { 1997 throw new IllegalStateException("CameraDevice was already closed"); 1998 } 1999 if (mInError) { 2000 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, 2001 "The camera device has encountered a serious error"); 2002 } 2003 } 2004 2005 /** Whether the camera device has started to close (may not yet have finished) */ isClosed()2006 private boolean isClosed() { 2007 return mClosing.get(); 2008 } 2009 getCharacteristics()2010 private CameraCharacteristics getCharacteristics() { 2011 return mCharacteristics; 2012 } 2013 2014 } 2015