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