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 com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; 20 21 import android.annotation.NonNull; 22 import android.hardware.ICameraService; 23 import android.hardware.camera2.CameraAccessException; 24 import android.hardware.camera2.CameraCaptureSession; 25 import android.hardware.camera2.CameraCharacteristics; 26 import android.hardware.camera2.CameraDevice; 27 import android.hardware.camera2.CaptureFailure; 28 import android.hardware.camera2.CaptureRequest; 29 import android.hardware.camera2.CaptureResult; 30 import android.hardware.camera2.ICameraDeviceCallbacks; 31 import android.hardware.camera2.ICameraDeviceUser; 32 import android.hardware.camera2.TotalCaptureResult; 33 import android.hardware.camera2.params.InputConfiguration; 34 import android.hardware.camera2.params.OutputConfiguration; 35 import android.hardware.camera2.params.SessionConfiguration; 36 import android.hardware.camera2.params.StreamConfigurationMap; 37 import android.hardware.camera2.utils.SubmitInfo; 38 import android.hardware.camera2.utils.SurfaceUtils; 39 import android.os.Binder; 40 import android.os.Build; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.Looper; 44 import android.os.RemoteException; 45 import android.os.ServiceSpecificException; 46 import android.util.Log; 47 import android.util.Range; 48 import android.util.Size; 49 import android.util.SparseArray; 50 import android.view.Surface; 51 52 import com.android.internal.util.Preconditions; 53 54 import java.util.AbstractMap.SimpleEntry; 55 import java.util.ArrayList; 56 import java.util.Collection; 57 import java.util.HashMap; 58 import java.util.HashSet; 59 import java.util.Iterator; 60 import java.util.LinkedList; 61 import java.util.List; 62 import java.util.Set; 63 import java.util.TreeMap; 64 import java.util.concurrent.atomic.AtomicBoolean; 65 import java.util.concurrent.Executor; 66 67 68 /** 69 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate 70 */ 71 public class CameraDeviceImpl extends CameraDevice 72 implements IBinder.DeathRecipient { 73 private final String TAG; 74 private final boolean DEBUG = false; 75 76 private static final int REQUEST_ID_NONE = -1; 77 78 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed) 79 private ICameraDeviceUserWrapper mRemoteDevice; 80 81 // Lock to synchronize cross-thread access to device public interface 82 final Object mInterfaceLock = new Object(); // access from this class and Session only! 83 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); 84 85 private final StateCallback mDeviceCallback; 86 private volatile StateCallbackKK mSessionStateCallback; 87 private final Executor mDeviceExecutor; 88 89 private final AtomicBoolean mClosing = new AtomicBoolean(); 90 private boolean mInError = false; 91 private boolean mIdle = true; 92 93 /** map request IDs to callback/request data */ 94 private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap = 95 new SparseArray<CaptureCallbackHolder>(); 96 97 private int mRepeatingRequestId = REQUEST_ID_NONE; 98 // Latest repeating request list's types 99 private int[] mRepeatingRequestTypes; 100 // Map stream IDs to input/output configurations 101 private SimpleEntry<Integer, InputConfiguration> mConfiguredInput = 102 new SimpleEntry<>(REQUEST_ID_NONE, null); 103 private final SparseArray<OutputConfiguration> mConfiguredOutputs = 104 new SparseArray<>(); 105 106 private final String mCameraId; 107 private final CameraCharacteristics mCharacteristics; 108 private final int mTotalPartialCount; 109 110 private static final long NANO_PER_SECOND = 1000000000; //ns 111 112 /** 113 * A list tracking request and its expected last regular/reprocess/zslStill frame 114 * number. Updated when calling ICameraDeviceUser methods. 115 */ 116 private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList = 117 new ArrayList<>(); 118 119 /** 120 * An object tracking received frame numbers. 121 * Updated when receiving callbacks from ICameraDeviceCallbacks. 122 */ 123 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker(); 124 125 private CameraCaptureSessionCore mCurrentSession; 126 private int mNextSessionId = 0; 127 128 private final int mAppTargetSdkVersion; 129 130 // Runnables for all state transitions, except error, which needs the 131 // error code argument 132 133 private final Runnable mCallOnOpened = new Runnable() { 134 @Override 135 public void run() { 136 StateCallbackKK sessionCallback = null; 137 synchronized(mInterfaceLock) { 138 if (mRemoteDevice == null) return; // Camera already closed 139 140 sessionCallback = mSessionStateCallback; 141 } 142 if (sessionCallback != null) { 143 sessionCallback.onOpened(CameraDeviceImpl.this); 144 } 145 mDeviceCallback.onOpened(CameraDeviceImpl.this); 146 } 147 }; 148 149 private final Runnable mCallOnUnconfigured = new Runnable() { 150 @Override 151 public void run() { 152 StateCallbackKK sessionCallback = null; 153 synchronized(mInterfaceLock) { 154 if (mRemoteDevice == null) return; // Camera already closed 155 156 sessionCallback = mSessionStateCallback; 157 } 158 if (sessionCallback != null) { 159 sessionCallback.onUnconfigured(CameraDeviceImpl.this); 160 } 161 } 162 }; 163 164 private final Runnable mCallOnActive = new Runnable() { 165 @Override 166 public void run() { 167 StateCallbackKK sessionCallback = null; 168 synchronized(mInterfaceLock) { 169 if (mRemoteDevice == null) return; // Camera already closed 170 171 sessionCallback = mSessionStateCallback; 172 } 173 if (sessionCallback != null) { 174 sessionCallback.onActive(CameraDeviceImpl.this); 175 } 176 } 177 }; 178 179 private final Runnable mCallOnBusy = new Runnable() { 180 @Override 181 public void run() { 182 StateCallbackKK sessionCallback = null; 183 synchronized(mInterfaceLock) { 184 if (mRemoteDevice == null) return; // Camera already closed 185 186 sessionCallback = mSessionStateCallback; 187 } 188 if (sessionCallback != null) { 189 sessionCallback.onBusy(CameraDeviceImpl.this); 190 } 191 } 192 }; 193 194 private final Runnable mCallOnClosed = new Runnable() { 195 private boolean mClosedOnce = false; 196 197 @Override 198 public void run() { 199 if (mClosedOnce) { 200 throw new AssertionError("Don't post #onClosed more than once"); 201 } 202 StateCallbackKK sessionCallback = null; 203 synchronized(mInterfaceLock) { 204 sessionCallback = mSessionStateCallback; 205 } 206 if (sessionCallback != null) { 207 sessionCallback.onClosed(CameraDeviceImpl.this); 208 } 209 mDeviceCallback.onClosed(CameraDeviceImpl.this); 210 mClosedOnce = true; 211 } 212 }; 213 214 private final Runnable mCallOnIdle = new Runnable() { 215 @Override 216 public void run() { 217 StateCallbackKK sessionCallback = null; 218 synchronized(mInterfaceLock) { 219 if (mRemoteDevice == null) return; // Camera already closed 220 221 sessionCallback = mSessionStateCallback; 222 } 223 if (sessionCallback != null) { 224 sessionCallback.onIdle(CameraDeviceImpl.this); 225 } 226 } 227 }; 228 229 private final Runnable mCallOnDisconnected = new Runnable() { 230 @Override 231 public void run() { 232 StateCallbackKK sessionCallback = null; 233 synchronized(mInterfaceLock) { 234 if (mRemoteDevice == null) return; // Camera already closed 235 236 sessionCallback = mSessionStateCallback; 237 } 238 if (sessionCallback != null) { 239 sessionCallback.onDisconnected(CameraDeviceImpl.this); 240 } 241 mDeviceCallback.onDisconnected(CameraDeviceImpl.this); 242 } 243 }; 244 CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor, CameraCharacteristics characteristics, int appTargetSdkVersion)245 public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor, 246 CameraCharacteristics characteristics, int appTargetSdkVersion) { 247 if (cameraId == null || callback == null || executor == null || characteristics == null) { 248 throw new IllegalArgumentException("Null argument given"); 249 } 250 mCameraId = cameraId; 251 mDeviceCallback = callback; 252 mDeviceExecutor = executor; 253 mCharacteristics = characteristics; 254 mAppTargetSdkVersion = appTargetSdkVersion; 255 256 final int MAX_TAG_LEN = 23; 257 String tag = String.format("CameraDevice-JV-%s", mCameraId); 258 if (tag.length() > MAX_TAG_LEN) { 259 tag = tag.substring(0, MAX_TAG_LEN); 260 } 261 TAG = tag; 262 263 Integer partialCount = 264 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT); 265 if (partialCount == null) { 266 // 1 means partial result is not supported. 267 mTotalPartialCount = 1; 268 } else { 269 mTotalPartialCount = partialCount; 270 } 271 } 272 getCallbacks()273 public CameraDeviceCallbacks getCallbacks() { 274 return mCallbacks; 275 } 276 277 /** 278 * Set remote device, which triggers initial onOpened/onUnconfigured callbacks 279 * 280 * <p>This function may post onDisconnected and throw CAMERA_DISCONNECTED if remoteDevice dies 281 * during setup.</p> 282 * 283 */ setRemoteDevice(ICameraDeviceUser remoteDevice)284 public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException { 285 synchronized(mInterfaceLock) { 286 // TODO: Move from decorator to direct binder-mediated exceptions 287 // If setRemoteFailure already called, do nothing 288 if (mInError) return; 289 290 mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice); 291 292 IBinder remoteDeviceBinder = remoteDevice.asBinder(); 293 // For legacy camera device, remoteDevice is in the same process, and 294 // asBinder returns NULL. 295 if (remoteDeviceBinder != null) { 296 try { 297 remoteDeviceBinder.linkToDeath(this, /*flag*/ 0); 298 } catch (RemoteException e) { 299 CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected); 300 301 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 302 "The camera device has encountered a serious error"); 303 } 304 } 305 306 mDeviceExecutor.execute(mCallOnOpened); 307 mDeviceExecutor.execute(mCallOnUnconfigured); 308 } 309 } 310 311 /** 312 * Call to indicate failed connection to a remote camera device. 313 * 314 * <p>This places the camera device in the error state and informs the callback. 315 * Use in place of setRemoteDevice() when startup fails.</p> 316 */ setRemoteFailure(final ServiceSpecificException failure)317 public void setRemoteFailure(final ServiceSpecificException failure) { 318 int failureCode = StateCallback.ERROR_CAMERA_DEVICE; 319 boolean failureIsError = true; 320 321 switch (failure.errorCode) { 322 case ICameraService.ERROR_CAMERA_IN_USE: 323 failureCode = StateCallback.ERROR_CAMERA_IN_USE; 324 break; 325 case ICameraService.ERROR_MAX_CAMERAS_IN_USE: 326 failureCode = StateCallback.ERROR_MAX_CAMERAS_IN_USE; 327 break; 328 case ICameraService.ERROR_DISABLED: 329 failureCode = StateCallback.ERROR_CAMERA_DISABLED; 330 break; 331 case ICameraService.ERROR_DISCONNECTED: 332 failureIsError = false; 333 break; 334 case ICameraService.ERROR_INVALID_OPERATION: 335 failureCode = StateCallback.ERROR_CAMERA_DEVICE; 336 break; 337 default: 338 Log.e(TAG, "Unexpected failure in opening camera device: " + failure.errorCode + 339 failure.getMessage()); 340 break; 341 } 342 final int code = failureCode; 343 final boolean isError = failureIsError; 344 synchronized(mInterfaceLock) { 345 mInError = true; 346 mDeviceExecutor.execute(new Runnable() { 347 @Override 348 public void run() { 349 if (isError) { 350 mDeviceCallback.onError(CameraDeviceImpl.this, code); 351 } else { 352 mDeviceCallback.onDisconnected(CameraDeviceImpl.this); 353 } 354 } 355 }); 356 } 357 } 358 359 @Override getId()360 public String getId() { 361 return mCameraId; 362 } 363 configureOutputs(List<Surface> outputs)364 public void configureOutputs(List<Surface> outputs) throws CameraAccessException { 365 // Leave this here for backwards compatibility with older code using this directly 366 ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size()); 367 for (Surface s : outputs) { 368 outputConfigs.add(new OutputConfiguration(s)); 369 } 370 configureStreamsChecked(/*inputConfig*/null, outputConfigs, 371 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); 372 373 } 374 375 /** 376 * Attempt to configure the input and outputs; the device goes to idle and then configures the 377 * new input and outputs if possible. 378 * 379 * <p>The configuration may gracefully fail, if input configuration is not supported, 380 * if there are too many outputs, if the formats are not supported, or if the sizes for that 381 * format is not supported. In this case this function will return {@code false} and the 382 * unconfigured callback will be fired.</p> 383 * 384 * <p>If the configuration succeeds (with 1 or more outputs with or without an input), 385 * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p> 386 * 387 * @param inputConfig input configuration or {@code null} for no input 388 * @param outputs a list of one or more surfaces, or {@code null} to unconfigure 389 * @param operatingMode If the stream configuration is for a normal session, 390 * a constrained high speed session, or something else. 391 * @param sessionParams Session parameters. 392 * @return whether or not the configuration was successful 393 * 394 * @throws CameraAccessException if there were any unexpected problems during configuration 395 */ configureStreamsChecked(InputConfiguration inputConfig, List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams)396 public boolean configureStreamsChecked(InputConfiguration inputConfig, 397 List<OutputConfiguration> outputs, int operatingMode, CaptureRequest sessionParams) 398 throws CameraAccessException { 399 // Treat a null input the same an empty list 400 if (outputs == null) { 401 outputs = new ArrayList<OutputConfiguration>(); 402 } 403 if (outputs.size() == 0 && inputConfig != null) { 404 throw new IllegalArgumentException("cannot configure an input stream without " + 405 "any output streams"); 406 } 407 408 checkInputConfiguration(inputConfig); 409 410 boolean success = false; 411 412 synchronized(mInterfaceLock) { 413 checkIfCameraClosedOrInError(); 414 // Streams to create 415 HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs); 416 // Streams to delete 417 List<Integer> deleteList = new ArrayList<Integer>(); 418 419 // Determine which streams need to be created, which to be deleted 420 for (int i = 0; i < mConfiguredOutputs.size(); ++i) { 421 int streamId = mConfiguredOutputs.keyAt(i); 422 OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i); 423 424 if (!outputs.contains(outConfig) || outConfig.isDeferredConfiguration()) { 425 // Always delete the deferred output configuration when the session 426 // is created, as the deferred output configuration doesn't have unique surface 427 // related identifies. 428 deleteList.add(streamId); 429 } else { 430 addSet.remove(outConfig); // Don't create a stream previously created 431 } 432 } 433 434 mDeviceExecutor.execute(mCallOnBusy); 435 stopRepeating(); 436 437 try { 438 waitUntilIdle(); 439 440 mRemoteDevice.beginConfigure(); 441 442 // reconfigure the input stream if the input configuration is different. 443 InputConfiguration currentInputConfig = mConfiguredInput.getValue(); 444 if (inputConfig != currentInputConfig && 445 (inputConfig == null || !inputConfig.equals(currentInputConfig))) { 446 if (currentInputConfig != null) { 447 mRemoteDevice.deleteStream(mConfiguredInput.getKey()); 448 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>( 449 REQUEST_ID_NONE, null); 450 } 451 if (inputConfig != null) { 452 int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(), 453 inputConfig.getHeight(), inputConfig.getFormat()); 454 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>( 455 streamId, inputConfig); 456 } 457 } 458 459 // Delete all streams first (to free up HW resources) 460 for (Integer streamId : deleteList) { 461 mRemoteDevice.deleteStream(streamId); 462 mConfiguredOutputs.delete(streamId); 463 } 464 465 // Add all new streams 466 for (OutputConfiguration outConfig : outputs) { 467 if (addSet.contains(outConfig)) { 468 int streamId = mRemoteDevice.createStream(outConfig); 469 mConfiguredOutputs.put(streamId, outConfig); 470 } 471 } 472 473 if (sessionParams != null) { 474 mRemoteDevice.endConfigure(operatingMode, sessionParams.getNativeCopy()); 475 } else { 476 mRemoteDevice.endConfigure(operatingMode, null); 477 } 478 479 success = true; 480 } catch (IllegalArgumentException e) { 481 // OK. camera service can reject stream config if it's not supported by HAL 482 // This is only the result of a programmer misusing the camera2 api. 483 Log.w(TAG, "Stream configuration failed due to: " + e.getMessage()); 484 return false; 485 } catch (CameraAccessException e) { 486 if (e.getReason() == CameraAccessException.CAMERA_IN_USE) { 487 throw new IllegalStateException("The camera is currently busy." + 488 " You must wait until the previous operation completes.", e); 489 } 490 throw e; 491 } finally { 492 if (success && outputs.size() > 0) { 493 mDeviceExecutor.execute(mCallOnIdle); 494 } else { 495 // Always return to the 'unconfigured' state if we didn't hit a fatal error 496 mDeviceExecutor.execute(mCallOnUnconfigured); 497 } 498 } 499 } 500 501 return success; 502 } 503 504 @Override createCaptureSession(List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)505 public void createCaptureSession(List<Surface> outputs, 506 CameraCaptureSession.StateCallback callback, Handler handler) 507 throws CameraAccessException { 508 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 509 for (Surface surface : outputs) { 510 outConfigurations.add(new OutputConfiguration(surface)); 511 } 512 createCaptureSessionInternal(null, outConfigurations, callback, 513 checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, 514 /*sessionParams*/ null); 515 } 516 517 @Override createCaptureSessionByOutputConfigurations( List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Handler handler)518 public void createCaptureSessionByOutputConfigurations( 519 List<OutputConfiguration> outputConfigurations, 520 CameraCaptureSession.StateCallback callback, Handler handler) 521 throws CameraAccessException { 522 if (DEBUG) { 523 Log.d(TAG, "createCaptureSessionByOutputConfigurations"); 524 } 525 526 // OutputConfiguration objects are immutable, but need to have our own array 527 List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations); 528 529 createCaptureSessionInternal(null, currentOutputs, callback, checkAndWrapHandler(handler), 530 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/null); 531 } 532 533 @Override createReprocessableCaptureSession(InputConfiguration inputConfig, List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)534 public void createReprocessableCaptureSession(InputConfiguration inputConfig, 535 List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) 536 throws CameraAccessException { 537 if (DEBUG) { 538 Log.d(TAG, "createReprocessableCaptureSession"); 539 } 540 541 if (inputConfig == null) { 542 throw new IllegalArgumentException("inputConfig cannot be null when creating a " + 543 "reprocessable capture session"); 544 } 545 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 546 for (Surface surface : outputs) { 547 outConfigurations.add(new OutputConfiguration(surface)); 548 } 549 createCaptureSessionInternal(inputConfig, outConfigurations, callback, 550 checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, 551 /*sessionParams*/ null); 552 } 553 554 @Override createReprocessableCaptureSessionByConfigurations(InputConfiguration inputConfig, List<OutputConfiguration> outputs, android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)555 public void createReprocessableCaptureSessionByConfigurations(InputConfiguration inputConfig, 556 List<OutputConfiguration> outputs, 557 android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler) 558 throws CameraAccessException { 559 if (DEBUG) { 560 Log.d(TAG, "createReprocessableCaptureSessionWithConfigurations"); 561 } 562 563 if (inputConfig == null) { 564 throw new IllegalArgumentException("inputConfig cannot be null when creating a " + 565 "reprocessable capture session"); 566 } 567 568 if (outputs == null) { 569 throw new IllegalArgumentException("Output configurations cannot be null when " + 570 "creating a reprocessable capture session"); 571 } 572 573 // OutputConfiguration objects aren't immutable, make a copy before using. 574 List<OutputConfiguration> currentOutputs = new ArrayList<OutputConfiguration>(); 575 for (OutputConfiguration output : outputs) { 576 currentOutputs.add(new OutputConfiguration(output)); 577 } 578 createCaptureSessionInternal(inputConfig, currentOutputs, 579 callback, checkAndWrapHandler(handler), 580 /*operatingMode*/ICameraDeviceUser.NORMAL_MODE, /*sessionParams*/ null); 581 } 582 583 @Override createConstrainedHighSpeedCaptureSession(List<Surface> outputs, android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)584 public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs, 585 android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler) 586 throws CameraAccessException { 587 if (outputs == null || outputs.size() == 0 || outputs.size() > 2) { 588 throw new IllegalArgumentException( 589 "Output surface list must not be null and the size must be no more than 2"); 590 } 591 List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); 592 for (Surface surface : outputs) { 593 outConfigurations.add(new OutputConfiguration(surface)); 594 } 595 createCaptureSessionInternal(null, outConfigurations, callback, 596 checkAndWrapHandler(handler), 597 /*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE, 598 /*sessionParams*/ null); 599 } 600 601 @Override createCustomCaptureSession(InputConfiguration inputConfig, List<OutputConfiguration> outputs, int operatingMode, android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)602 public void createCustomCaptureSession(InputConfiguration inputConfig, 603 List<OutputConfiguration> outputs, 604 int operatingMode, 605 android.hardware.camera2.CameraCaptureSession.StateCallback callback, 606 Handler handler) throws CameraAccessException { 607 List<OutputConfiguration> currentOutputs = new ArrayList<OutputConfiguration>(); 608 for (OutputConfiguration output : outputs) { 609 currentOutputs.add(new OutputConfiguration(output)); 610 } 611 createCaptureSessionInternal(inputConfig, currentOutputs, callback, 612 checkAndWrapHandler(handler), operatingMode, /*sessionParams*/ null); 613 } 614 615 @Override createCaptureSession(SessionConfiguration config)616 public void createCaptureSession(SessionConfiguration config) 617 throws CameraAccessException { 618 if (config == null) { 619 throw new IllegalArgumentException("Invalid session configuration"); 620 } 621 622 List<OutputConfiguration> outputConfigs = config.getOutputConfigurations(); 623 if (outputConfigs == null) { 624 throw new IllegalArgumentException("Invalid output configurations"); 625 } 626 if (config.getExecutor() == null) { 627 throw new IllegalArgumentException("Invalid executor"); 628 } 629 createCaptureSessionInternal(config.getInputConfiguration(), outputConfigs, 630 config.getStateCallback(), config.getExecutor(), config.getSessionType(), 631 config.getSessionParameters()); 632 } 633 createCaptureSessionInternal(InputConfiguration inputConfig, List<OutputConfiguration> outputConfigurations, CameraCaptureSession.StateCallback callback, Executor executor, int operatingMode, CaptureRequest sessionParams)634 private void createCaptureSessionInternal(InputConfiguration inputConfig, 635 List<OutputConfiguration> outputConfigurations, 636 CameraCaptureSession.StateCallback callback, Executor executor, 637 int operatingMode, CaptureRequest sessionParams) throws CameraAccessException { 638 synchronized(mInterfaceLock) { 639 if (DEBUG) { 640 Log.d(TAG, "createCaptureSessionInternal"); 641 } 642 643 checkIfCameraClosedOrInError(); 644 645 boolean isConstrainedHighSpeed = 646 (operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE); 647 if (isConstrainedHighSpeed && inputConfig != null) { 648 throw new IllegalArgumentException("Constrained high speed session doesn't support" 649 + " input configuration yet."); 650 } 651 652 // Notify current session that it's going away, before starting camera operations 653 // After this call completes, the session is not allowed to call into CameraDeviceImpl 654 if (mCurrentSession != null) { 655 mCurrentSession.replaceSessionClose(); 656 } 657 658 // TODO: dont block for this 659 boolean configureSuccess = true; 660 CameraAccessException pendingException = null; 661 Surface input = null; 662 try { 663 // configure streams and then block until IDLE 664 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, 665 operatingMode, sessionParams); 666 if (configureSuccess == true && inputConfig != null) { 667 input = mRemoteDevice.getInputSurface(); 668 } 669 } catch (CameraAccessException e) { 670 configureSuccess = false; 671 pendingException = e; 672 input = null; 673 if (DEBUG) { 674 Log.v(TAG, "createCaptureSession - failed with exception ", e); 675 } 676 } 677 678 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. 679 CameraCaptureSessionCore newSession = null; 680 if (isConstrainedHighSpeed) { 681 ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size()); 682 for (OutputConfiguration outConfig : outputConfigurations) { 683 surfaces.add(outConfig.getSurface()); 684 } 685 StreamConfigurationMap config = 686 getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 687 SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config); 688 689 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, 690 callback, executor, this, mDeviceExecutor, configureSuccess, 691 mCharacteristics); 692 } else { 693 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, 694 callback, executor, this, mDeviceExecutor, configureSuccess); 695 } 696 697 // TODO: wait until current session closes, then create the new session 698 mCurrentSession = newSession; 699 700 if (pendingException != null) { 701 throw pendingException; 702 } 703 704 mSessionStateCallback = mCurrentSession.getDeviceStateCallback(); 705 } 706 } 707 708 @Override isSessionConfigurationSupported( @onNull SessionConfiguration sessionConfig)709 public boolean isSessionConfigurationSupported( 710 @NonNull SessionConfiguration sessionConfig) throws CameraAccessException, 711 UnsupportedOperationException, IllegalArgumentException { 712 synchronized(mInterfaceLock) { 713 checkIfCameraClosedOrInError(); 714 715 return mRemoteDevice.isSessionConfigurationSupported(sessionConfig); 716 } 717 } 718 719 /** 720 * For use by backwards-compatibility code only. 721 */ setSessionListener(StateCallbackKK sessionCallback)722 public void setSessionListener(StateCallbackKK sessionCallback) { 723 synchronized(mInterfaceLock) { 724 mSessionStateCallback = sessionCallback; 725 } 726 } 727 overrideEnableZsl(CameraMetadataNative request, boolean newValue)728 private void overrideEnableZsl(CameraMetadataNative request, boolean newValue) { 729 Boolean enableZsl = request.get(CaptureRequest.CONTROL_ENABLE_ZSL); 730 if (enableZsl == null) { 731 // If enableZsl is not available, don't override. 732 return; 733 } 734 735 request.set(CaptureRequest.CONTROL_ENABLE_ZSL, newValue); 736 } 737 738 @Override createCaptureRequest(int templateType, Set<String> physicalCameraIdSet)739 public CaptureRequest.Builder createCaptureRequest(int templateType, 740 Set<String> physicalCameraIdSet) 741 throws CameraAccessException { 742 synchronized(mInterfaceLock) { 743 checkIfCameraClosedOrInError(); 744 745 for (String physicalId : physicalCameraIdSet) { 746 if (physicalId == getId()) { 747 throw new IllegalStateException("Physical id matches the logical id!"); 748 } 749 } 750 751 CameraMetadataNative templatedRequest = null; 752 753 templatedRequest = mRemoteDevice.createDefaultRequest(templateType); 754 755 // If app target SDK is older than O, or it's not a still capture template, enableZsl 756 // must be false in the default request. 757 if (mAppTargetSdkVersion < Build.VERSION_CODES.O || 758 templateType != TEMPLATE_STILL_CAPTURE) { 759 overrideEnableZsl(templatedRequest, false); 760 } 761 762 CaptureRequest.Builder builder = new CaptureRequest.Builder( 763 templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE, 764 getId(), physicalCameraIdSet); 765 766 return builder; 767 } 768 } 769 770 @Override createCaptureRequest(int templateType)771 public CaptureRequest.Builder createCaptureRequest(int templateType) 772 throws CameraAccessException { 773 synchronized(mInterfaceLock) { 774 checkIfCameraClosedOrInError(); 775 776 CameraMetadataNative templatedRequest = null; 777 778 templatedRequest = mRemoteDevice.createDefaultRequest(templateType); 779 780 // If app target SDK is older than O, or it's not a still capture template, enableZsl 781 // must be false in the default request. 782 if (mAppTargetSdkVersion < Build.VERSION_CODES.O || 783 templateType != TEMPLATE_STILL_CAPTURE) { 784 overrideEnableZsl(templatedRequest, false); 785 } 786 787 CaptureRequest.Builder builder = new CaptureRequest.Builder( 788 templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE, 789 getId(), /*physicalCameraIdSet*/ null); 790 791 return builder; 792 } 793 } 794 795 @Override createReprocessCaptureRequest(TotalCaptureResult inputResult)796 public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult) 797 throws CameraAccessException { 798 synchronized(mInterfaceLock) { 799 checkIfCameraClosedOrInError(); 800 801 CameraMetadataNative resultMetadata = new 802 CameraMetadataNative(inputResult.getNativeCopy()); 803 804 return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true, 805 inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null); 806 } 807 } 808 prepare(Surface surface)809 public void prepare(Surface surface) throws CameraAccessException { 810 if (surface == null) throw new IllegalArgumentException("Surface is null"); 811 812 synchronized(mInterfaceLock) { 813 int streamId = -1; 814 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 815 final List<Surface> surfaces = mConfiguredOutputs.valueAt(i).getSurfaces(); 816 if (surfaces.contains(surface)) { 817 streamId = mConfiguredOutputs.keyAt(i); 818 break; 819 } 820 } 821 if (streamId == -1) { 822 throw new IllegalArgumentException("Surface is not part of this session"); 823 } 824 825 mRemoteDevice.prepare(streamId); 826 } 827 } 828 prepare(int maxCount, Surface surface)829 public void prepare(int maxCount, Surface surface) throws CameraAccessException { 830 if (surface == null) throw new IllegalArgumentException("Surface is null"); 831 if (maxCount <= 0) throw new IllegalArgumentException("Invalid maxCount given: " + 832 maxCount); 833 834 synchronized(mInterfaceLock) { 835 int streamId = -1; 836 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 837 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 838 streamId = mConfiguredOutputs.keyAt(i); 839 break; 840 } 841 } 842 if (streamId == -1) { 843 throw new IllegalArgumentException("Surface is not part of this session"); 844 } 845 846 mRemoteDevice.prepare2(maxCount, streamId); 847 } 848 } 849 updateOutputConfiguration(OutputConfiguration config)850 public void updateOutputConfiguration(OutputConfiguration config) 851 throws CameraAccessException { 852 synchronized(mInterfaceLock) { 853 int streamId = -1; 854 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 855 if (config.getSurface() == mConfiguredOutputs.valueAt(i).getSurface()) { 856 streamId = mConfiguredOutputs.keyAt(i); 857 break; 858 } 859 } 860 if (streamId == -1) { 861 throw new IllegalArgumentException("Invalid output configuration"); 862 } 863 864 mRemoteDevice.updateOutputConfiguration(streamId, config); 865 mConfiguredOutputs.put(streamId, config); 866 } 867 } 868 tearDown(Surface surface)869 public void tearDown(Surface surface) throws CameraAccessException { 870 if (surface == null) throw new IllegalArgumentException("Surface is null"); 871 872 synchronized(mInterfaceLock) { 873 int streamId = -1; 874 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 875 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 876 streamId = mConfiguredOutputs.keyAt(i); 877 break; 878 } 879 } 880 if (streamId == -1) { 881 throw new IllegalArgumentException("Surface is not part of this session"); 882 } 883 884 mRemoteDevice.tearDown(streamId); 885 } 886 } 887 finalizeOutputConfigs(List<OutputConfiguration> outputConfigs)888 public void finalizeOutputConfigs(List<OutputConfiguration> outputConfigs) 889 throws CameraAccessException { 890 if (outputConfigs == null || outputConfigs.size() == 0) { 891 throw new IllegalArgumentException("deferred config is null or empty"); 892 } 893 894 synchronized(mInterfaceLock) { 895 for (OutputConfiguration config : outputConfigs) { 896 int streamId = -1; 897 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 898 // Have to use equal here, as createCaptureSessionByOutputConfigurations() and 899 // createReprocessableCaptureSessionByConfigurations() do a copy of the configs. 900 if (config.equals(mConfiguredOutputs.valueAt(i))) { 901 streamId = mConfiguredOutputs.keyAt(i); 902 break; 903 } 904 } 905 if (streamId == -1) { 906 throw new IllegalArgumentException("Deferred config is not part of this " 907 + "session"); 908 } 909 910 if (config.getSurfaces().size() == 0) { 911 throw new IllegalArgumentException("The final config for stream " + streamId 912 + " must have at least 1 surface"); 913 } 914 mRemoteDevice.finalizeOutputConfigurations(streamId, config); 915 mConfiguredOutputs.put(streamId, config); 916 } 917 } 918 } 919 capture(CaptureRequest request, CaptureCallback callback, Executor executor)920 public int capture(CaptureRequest request, CaptureCallback callback, Executor executor) 921 throws CameraAccessException { 922 if (DEBUG) { 923 Log.d(TAG, "calling capture"); 924 } 925 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 926 requestList.add(request); 927 return submitCaptureRequest(requestList, callback, executor, /*streaming*/false); 928 } 929 captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Executor executor)930 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, 931 Executor executor) throws CameraAccessException { 932 if (requests == null || requests.isEmpty()) { 933 throw new IllegalArgumentException("At least one request must be given"); 934 } 935 return submitCaptureRequest(requests, callback, executor, /*streaming*/false); 936 } 937 938 /** 939 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for 940 * starting and stopping repeating request and flushing. 941 * 942 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never 943 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered. 944 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last 945 * regular frame number will be added to the list mRequestLastFrameNumbersList.</p> 946 * 947 * @param requestId the request ID of the current repeating request. 948 * @param lastFrameNumber last frame number returned from binder. 949 * @param repeatingRequestTypes the repeating requests' types. 950 */ checkEarlyTriggerSequenceComplete( final int requestId, final long lastFrameNumber, final int[] repeatingRequestTypes)951 private void checkEarlyTriggerSequenceComplete( 952 final int requestId, final long lastFrameNumber, 953 final int[] repeatingRequestTypes) { 954 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request 955 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately. 956 if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 957 final CaptureCallbackHolder holder; 958 int index = mCaptureCallbackMap.indexOfKey(requestId); 959 holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null; 960 if (holder != null) { 961 mCaptureCallbackMap.removeAt(index); 962 if (DEBUG) { 963 Log.v(TAG, String.format( 964 "remove holder for requestId %d, " 965 + "because lastFrame is %d.", 966 requestId, lastFrameNumber)); 967 } 968 } 969 970 if (holder != null) { 971 if (DEBUG) { 972 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because" 973 + " request did not reach HAL"); 974 } 975 976 Runnable resultDispatch = new Runnable() { 977 @Override 978 public void run() { 979 if (!CameraDeviceImpl.this.isClosed()) { 980 if (DEBUG) { 981 Log.d(TAG, String.format( 982 "early trigger sequence complete for request %d", 983 requestId)); 984 } 985 holder.getCallback().onCaptureSequenceAborted( 986 CameraDeviceImpl.this, 987 requestId); 988 } 989 } 990 }; 991 final long ident = Binder.clearCallingIdentity(); 992 try { 993 holder.getExecutor().execute(resultDispatch); 994 } finally { 995 Binder.restoreCallingIdentity(ident); 996 } 997 } else { 998 Log.w(TAG, String.format( 999 "did not register callback to request %d", 1000 requestId)); 1001 } 1002 } else { 1003 // This function is only called for regular/ZslStill request so lastFrameNumber is the 1004 // last regular/ZslStill frame number. 1005 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId, 1006 lastFrameNumber, repeatingRequestTypes)); 1007 1008 // It is possible that the last frame has already arrived, so we need to check 1009 // for sequence completion right away 1010 checkAndFireSequenceComplete(); 1011 } 1012 } 1013 getRequestTypes(final CaptureRequest[] requestArray)1014 private int[] getRequestTypes(final CaptureRequest[] requestArray) { 1015 int[] requestTypes = new int[requestArray.length]; 1016 for (int i = 0; i < requestArray.length; i++) { 1017 requestTypes[i] = requestArray[i].getRequestType(); 1018 } 1019 return requestTypes; 1020 } 1021 submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, Executor executor, boolean repeating)1022 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, 1023 Executor executor, boolean repeating) throws CameraAccessException { 1024 1025 // Need a valid executor, or current thread needs to have a looper, if 1026 // callback is valid 1027 executor = checkExecutor(executor, callback); 1028 1029 synchronized(mInterfaceLock) { 1030 checkIfCameraClosedOrInError(); 1031 1032 // Make sure that there all requests have at least 1 surface; all surfaces are non-null; 1033 // the surface isn't a physical stream surface for reprocessing request 1034 for (CaptureRequest request : requestList) { 1035 if (request.getTargets().isEmpty()) { 1036 throw new IllegalArgumentException( 1037 "Each request must have at least one Surface target"); 1038 } 1039 1040 for (Surface surface : request.getTargets()) { 1041 if (surface == null) { 1042 throw new IllegalArgumentException("Null Surface targets are not allowed"); 1043 } 1044 1045 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 1046 OutputConfiguration configuration = mConfiguredOutputs.valueAt(i); 1047 if (configuration.isForPhysicalCamera() 1048 && configuration.getSurfaces().contains(surface)) { 1049 if (request.isReprocess()) { 1050 throw new IllegalArgumentException( 1051 "Reprocess request on physical stream is not allowed"); 1052 } 1053 } 1054 } 1055 } 1056 } 1057 1058 if (repeating) { 1059 stopRepeating(); 1060 } 1061 1062 SubmitInfo requestInfo; 1063 1064 CaptureRequest[] requestArray = requestList.toArray(new CaptureRequest[requestList.size()]); 1065 // Convert Surface to streamIdx and surfaceIdx 1066 for (CaptureRequest request : requestArray) { 1067 request.convertSurfaceToStreamId(mConfiguredOutputs); 1068 } 1069 1070 requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating); 1071 if (DEBUG) { 1072 Log.v(TAG, "last frame number " + requestInfo.getLastFrameNumber()); 1073 } 1074 1075 for (CaptureRequest request : requestArray) { 1076 request.recoverStreamIdToSurface(); 1077 } 1078 1079 if (callback != null) { 1080 mCaptureCallbackMap.put(requestInfo.getRequestId(), 1081 new CaptureCallbackHolder( 1082 callback, requestList, executor, repeating, mNextSessionId - 1)); 1083 } else { 1084 if (DEBUG) { 1085 Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null"); 1086 } 1087 } 1088 1089 if (repeating) { 1090 if (mRepeatingRequestId != REQUEST_ID_NONE) { 1091 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, 1092 requestInfo.getLastFrameNumber(), 1093 mRepeatingRequestTypes); 1094 } 1095 mRepeatingRequestId = requestInfo.getRequestId(); 1096 mRepeatingRequestTypes = getRequestTypes(requestArray); 1097 } else { 1098 mRequestLastFrameNumbersList.add( 1099 new RequestLastFrameNumbersHolder(requestList, requestInfo)); 1100 } 1101 1102 if (mIdle) { 1103 mDeviceExecutor.execute(mCallOnActive); 1104 } 1105 mIdle = false; 1106 1107 return requestInfo.getRequestId(); 1108 } 1109 } 1110 setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Executor executor)1111 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, 1112 Executor executor) throws CameraAccessException { 1113 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 1114 requestList.add(request); 1115 return submitCaptureRequest(requestList, callback, executor, /*streaming*/true); 1116 } 1117 setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Executor executor)1118 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, 1119 Executor executor) throws CameraAccessException { 1120 if (requests == null || requests.isEmpty()) { 1121 throw new IllegalArgumentException("At least one request must be given"); 1122 } 1123 return submitCaptureRequest(requests, callback, executor, /*streaming*/true); 1124 } 1125 stopRepeating()1126 public void stopRepeating() throws CameraAccessException { 1127 1128 synchronized(mInterfaceLock) { 1129 checkIfCameraClosedOrInError(); 1130 if (mRepeatingRequestId != REQUEST_ID_NONE) { 1131 1132 int requestId = mRepeatingRequestId; 1133 mRepeatingRequestId = REQUEST_ID_NONE; 1134 int[] requestTypes = mRepeatingRequestTypes; 1135 mRepeatingRequestTypes = null; 1136 1137 long lastFrameNumber; 1138 try { 1139 lastFrameNumber = mRemoteDevice.cancelRequest(requestId); 1140 } catch (IllegalArgumentException e) { 1141 if (DEBUG) { 1142 Log.v(TAG, "Repeating request was already stopped for request " + requestId); 1143 } 1144 // Repeating request was already stopped. Nothing more to do. 1145 return; 1146 } 1147 1148 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber, requestTypes); 1149 } 1150 } 1151 } 1152 waitUntilIdle()1153 private void waitUntilIdle() throws CameraAccessException { 1154 1155 synchronized(mInterfaceLock) { 1156 checkIfCameraClosedOrInError(); 1157 1158 if (mRepeatingRequestId != REQUEST_ID_NONE) { 1159 throw new IllegalStateException("Active repeating request ongoing"); 1160 } 1161 1162 mRemoteDevice.waitUntilIdle(); 1163 } 1164 } 1165 flush()1166 public void flush() throws CameraAccessException { 1167 synchronized(mInterfaceLock) { 1168 checkIfCameraClosedOrInError(); 1169 1170 mDeviceExecutor.execute(mCallOnBusy); 1171 1172 // If already idle, just do a busy->idle transition immediately, don't actually 1173 // flush. 1174 if (mIdle) { 1175 mDeviceExecutor.execute(mCallOnIdle); 1176 return; 1177 } 1178 1179 long lastFrameNumber = mRemoteDevice.flush(); 1180 if (mRepeatingRequestId != REQUEST_ID_NONE) { 1181 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber, 1182 mRepeatingRequestTypes); 1183 mRepeatingRequestId = REQUEST_ID_NONE; 1184 mRepeatingRequestTypes = null; 1185 } 1186 } 1187 } 1188 1189 @Override close()1190 public void close() { 1191 synchronized (mInterfaceLock) { 1192 if (mClosing.getAndSet(true)) { 1193 return; 1194 } 1195 1196 if (mRemoteDevice != null) { 1197 mRemoteDevice.disconnect(); 1198 mRemoteDevice.unlinkToDeath(this, /*flags*/0); 1199 } 1200 1201 // Only want to fire the onClosed callback once; 1202 // either a normal close where the remote device is valid 1203 // or a close after a startup error (no remote device but in error state) 1204 if (mRemoteDevice != null || mInError) { 1205 mDeviceExecutor.execute(mCallOnClosed); 1206 } 1207 1208 mRemoteDevice = null; 1209 } 1210 } 1211 1212 @Override finalize()1213 protected void finalize() throws Throwable { 1214 try { 1215 close(); 1216 } 1217 finally { 1218 super.finalize(); 1219 } 1220 } 1221 checkInputConfiguration(InputConfiguration inputConfig)1222 private void checkInputConfiguration(InputConfiguration inputConfig) { 1223 if (inputConfig != null) { 1224 StreamConfigurationMap configMap = mCharacteristics.get( 1225 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 1226 1227 int[] inputFormats = configMap.getInputFormats(); 1228 boolean validFormat = false; 1229 for (int format : inputFormats) { 1230 if (format == inputConfig.getFormat()) { 1231 validFormat = true; 1232 } 1233 } 1234 1235 if (validFormat == false) { 1236 throw new IllegalArgumentException("input format " + inputConfig.getFormat() + 1237 " is not valid"); 1238 } 1239 1240 boolean validSize = false; 1241 Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat()); 1242 for (Size s : inputSizes) { 1243 if (inputConfig.getWidth() == s.getWidth() && 1244 inputConfig.getHeight() == s.getHeight()) { 1245 validSize = true; 1246 } 1247 } 1248 1249 if (validSize == false) { 1250 throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" + 1251 inputConfig.getHeight() + " is not valid"); 1252 } 1253 } 1254 } 1255 1256 /** 1257 * <p>A callback for tracking the progress of a {@link CaptureRequest} 1258 * submitted to the camera device.</p> 1259 * 1260 * An interface instead of an abstract class because this is internal and 1261 * we want to make sure we always implement all its callbacks until we reach 1262 * the public layer. 1263 */ 1264 public interface CaptureCallback { 1265 1266 /** 1267 * This constant is used to indicate that no images were captured for 1268 * the request. 1269 * 1270 * @hide 1271 */ 1272 public static final int NO_FRAMES_CAPTURED = -1; 1273 1274 /** 1275 * This method is called when the camera device has started capturing 1276 * the output image for the request, at the beginning of image exposure. 1277 * 1278 * @see android.media.MediaActionSound 1279 */ onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp, long frameNumber)1280 public void onCaptureStarted(CameraDevice camera, 1281 CaptureRequest request, long timestamp, long frameNumber); 1282 1283 /** 1284 * This method is called when some results from an image capture are 1285 * available. 1286 * 1287 * @hide 1288 */ onCapturePartial(CameraDevice camera, CaptureRequest request, CaptureResult result)1289 public void onCapturePartial(CameraDevice camera, 1290 CaptureRequest request, CaptureResult result); 1291 1292 /** 1293 * This method is called when an image capture makes partial forward progress; some 1294 * (but not all) results from an image capture are available. 1295 * 1296 */ onCaptureProgressed(CameraDevice camera, CaptureRequest request, CaptureResult partialResult)1297 public void onCaptureProgressed(CameraDevice camera, 1298 CaptureRequest request, CaptureResult partialResult); 1299 1300 /** 1301 * This method is called when an image capture has fully completed and all the 1302 * result metadata is available. 1303 */ onCaptureCompleted(CameraDevice camera, CaptureRequest request, TotalCaptureResult result)1304 public void onCaptureCompleted(CameraDevice camera, 1305 CaptureRequest request, TotalCaptureResult result); 1306 1307 /** 1308 * This method is called instead of {@link #onCaptureCompleted} when the 1309 * camera device failed to produce a {@link CaptureResult} for the 1310 * request. 1311 */ onCaptureFailed(CameraDevice camera, CaptureRequest request, CaptureFailure failure)1312 public void onCaptureFailed(CameraDevice camera, 1313 CaptureRequest request, CaptureFailure failure); 1314 1315 /** 1316 * This method is called independently of the others in CaptureCallback, 1317 * when a capture sequence finishes and all {@link CaptureResult} 1318 * or {@link CaptureFailure} for it have been returned via this callback. 1319 */ onCaptureSequenceCompleted(CameraDevice camera, int sequenceId, long frameNumber)1320 public void onCaptureSequenceCompleted(CameraDevice camera, 1321 int sequenceId, long frameNumber); 1322 1323 /** 1324 * This method is called independently of the others in CaptureCallback, 1325 * when a capture sequence aborts before any {@link CaptureResult} 1326 * or {@link CaptureFailure} for it have been returned via this callback. 1327 */ onCaptureSequenceAborted(CameraDevice camera, int sequenceId)1328 public void onCaptureSequenceAborted(CameraDevice camera, 1329 int sequenceId); 1330 1331 /** 1332 * This method is called independently of the others in CaptureCallback, if an output buffer 1333 * is dropped for a particular capture request. 1334 * 1335 * Loss of metadata is communicated via onCaptureFailed, independently of any buffer loss. 1336 */ onCaptureBufferLost(CameraDevice camera, CaptureRequest request, Surface target, long frameNumber)1337 public void onCaptureBufferLost(CameraDevice camera, 1338 CaptureRequest request, Surface target, long frameNumber); 1339 } 1340 1341 /** 1342 * A callback for notifications about the state of a camera device, adding in the callbacks that 1343 * were part of the earlier KK API design, but now only used internally. 1344 */ 1345 public static abstract class StateCallbackKK extends StateCallback { 1346 /** 1347 * The method called when a camera device has no outputs configured. 1348 * 1349 */ onUnconfigured(CameraDevice camera)1350 public void onUnconfigured(CameraDevice camera) { 1351 // Default empty implementation 1352 } 1353 1354 /** 1355 * The method called when a camera device begins processing 1356 * {@link CaptureRequest capture requests}. 1357 * 1358 */ onActive(CameraDevice camera)1359 public void onActive(CameraDevice camera) { 1360 // Default empty implementation 1361 } 1362 1363 /** 1364 * The method called when a camera device is busy. 1365 * 1366 */ onBusy(CameraDevice camera)1367 public void onBusy(CameraDevice camera) { 1368 // Default empty implementation 1369 } 1370 1371 /** 1372 * The method called when a camera device has finished processing all 1373 * submitted capture requests and has reached an idle state. 1374 * 1375 */ onIdle(CameraDevice camera)1376 public void onIdle(CameraDevice camera) { 1377 // Default empty implementation 1378 } 1379 1380 /** 1381 * This method is called when camera device's non-repeating request queue is empty, 1382 * and is ready to start capturing next image. 1383 */ onRequestQueueEmpty()1384 public void onRequestQueueEmpty() { 1385 // Default empty implementation 1386 } 1387 1388 /** 1389 * The method called when the camera device has finished preparing 1390 * an output Surface 1391 */ onSurfacePrepared(Surface surface)1392 public void onSurfacePrepared(Surface surface) { 1393 // Default empty implementation 1394 } 1395 } 1396 1397 static class CaptureCallbackHolder { 1398 1399 private final boolean mRepeating; 1400 private final CaptureCallback mCallback; 1401 private final List<CaptureRequest> mRequestList; 1402 private final Executor mExecutor; 1403 private final int mSessionId; 1404 /** 1405 * <p>Determine if the callback holder is for a constrained high speed request list that 1406 * expects batched capture results. Capture results will be batched if the request list 1407 * is interleaved with preview and video requests. Capture results won't be batched if the 1408 * request list only contains preview requests, or if the request doesn't belong to a 1409 * constrained high speed list. 1410 */ 1411 private final boolean mHasBatchedOutputs; 1412 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, Executor executor, boolean repeating, int sessionId)1413 CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, 1414 Executor executor, boolean repeating, int sessionId) { 1415 if (callback == null || executor == null) { 1416 throw new UnsupportedOperationException( 1417 "Must have a valid handler and a valid callback"); 1418 } 1419 mRepeating = repeating; 1420 mExecutor = executor; 1421 mRequestList = new ArrayList<CaptureRequest>(requestList); 1422 mCallback = callback; 1423 mSessionId = sessionId; 1424 1425 // Check whether this callback holder is for batched outputs. 1426 // The logic here should match createHighSpeedRequestList. 1427 boolean hasBatchedOutputs = true; 1428 for (int i = 0; i < requestList.size(); i++) { 1429 CaptureRequest request = requestList.get(i); 1430 if (!request.isPartOfCRequestList()) { 1431 hasBatchedOutputs = false; 1432 break; 1433 } 1434 if (i == 0) { 1435 Collection<Surface> targets = request.getTargets(); 1436 if (targets.size() != 2) { 1437 hasBatchedOutputs = false; 1438 break; 1439 } 1440 } 1441 } 1442 mHasBatchedOutputs = hasBatchedOutputs; 1443 } 1444 isRepeating()1445 public boolean isRepeating() { 1446 return mRepeating; 1447 } 1448 getCallback()1449 public CaptureCallback getCallback() { 1450 return mCallback; 1451 } 1452 getRequest(int subsequenceId)1453 public CaptureRequest getRequest(int subsequenceId) { 1454 if (subsequenceId >= mRequestList.size()) { 1455 throw new IllegalArgumentException( 1456 String.format( 1457 "Requested subsequenceId %d is larger than request list size %d.", 1458 subsequenceId, mRequestList.size())); 1459 } else { 1460 if (subsequenceId < 0) { 1461 throw new IllegalArgumentException(String.format( 1462 "Requested subsequenceId %d is negative", subsequenceId)); 1463 } else { 1464 return mRequestList.get(subsequenceId); 1465 } 1466 } 1467 } 1468 getRequest()1469 public CaptureRequest getRequest() { 1470 return getRequest(0); 1471 } 1472 getExecutor()1473 public Executor getExecutor() { 1474 return mExecutor; 1475 } 1476 getSessionId()1477 public int getSessionId() { 1478 return mSessionId; 1479 } 1480 getRequestCount()1481 public int getRequestCount() { 1482 return mRequestList.size(); 1483 } 1484 hasBatchedOutputs()1485 public boolean hasBatchedOutputs() { 1486 return mHasBatchedOutputs; 1487 } 1488 } 1489 1490 /** 1491 * This class holds a capture ID and its expected last regular, zslStill, and reprocess 1492 * frame number. 1493 */ 1494 static class RequestLastFrameNumbersHolder { 1495 // request ID 1496 private final int mRequestId; 1497 // The last regular frame number for this request ID. It's 1498 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request. 1499 private final long mLastRegularFrameNumber; 1500 // The last reprocess frame number for this request ID. It's 1501 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request. 1502 private final long mLastReprocessFrameNumber; 1503 // The last ZSL still capture frame number for this request ID. It's 1504 // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no zsl request. 1505 private final long mLastZslStillFrameNumber; 1506 1507 /** 1508 * Create a request-last-frame-numbers holder with a list of requests, request ID, and 1509 * the last frame number returned by camera service. 1510 */ RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, SubmitInfo requestInfo)1511 public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, SubmitInfo requestInfo) { 1512 long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1513 long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1514 long lastZslStillFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1515 long frameNumber = requestInfo.getLastFrameNumber(); 1516 1517 if (requestInfo.getLastFrameNumber() < requestList.size() - 1) { 1518 throw new IllegalArgumentException( 1519 "lastFrameNumber: " + requestInfo.getLastFrameNumber() + 1520 " should be at least " + (requestList.size() - 1) + " for the number of " + 1521 " requests in the list: " + requestList.size()); 1522 } 1523 1524 // find the last regular, zslStill, and reprocess frame number 1525 for (int i = requestList.size() - 1; i >= 0; i--) { 1526 CaptureRequest request = requestList.get(i); 1527 int requestType = request.getRequestType(); 1528 if (requestType == CaptureRequest.REQUEST_TYPE_REPROCESS 1529 && lastReprocessFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 1530 lastReprocessFrameNumber = frameNumber; 1531 } else if (requestType == CaptureRequest.REQUEST_TYPE_ZSL_STILL 1532 && lastZslStillFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 1533 lastZslStillFrameNumber = frameNumber; 1534 } else if (requestType == CaptureRequest.REQUEST_TYPE_REGULAR 1535 && lastRegularFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 1536 lastRegularFrameNumber = frameNumber; 1537 } 1538 1539 if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED 1540 && lastZslStillFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED 1541 && lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) { 1542 break; 1543 } 1544 1545 frameNumber--; 1546 } 1547 1548 mLastRegularFrameNumber = lastRegularFrameNumber; 1549 mLastReprocessFrameNumber = lastReprocessFrameNumber; 1550 mLastZslStillFrameNumber = lastZslStillFrameNumber; 1551 mRequestId = requestInfo.getRequestId(); 1552 } 1553 1554 /** 1555 * Create a request-last-frame-numbers holder with a request ID and last regular/ZslStill 1556 * frame number. 1557 */ RequestLastFrameNumbersHolder(int requestId, long lastFrameNumber, int[] repeatingRequestTypes)1558 RequestLastFrameNumbersHolder(int requestId, long lastFrameNumber, 1559 int[] repeatingRequestTypes) { 1560 long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1561 long lastZslStillFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1562 1563 if (repeatingRequestTypes == null) { 1564 throw new IllegalArgumentException( 1565 "repeatingRequest list must not be null"); 1566 } 1567 if (lastFrameNumber < repeatingRequestTypes.length - 1) { 1568 throw new IllegalArgumentException( 1569 "lastFrameNumber: " + lastFrameNumber + " should be at least " 1570 + (repeatingRequestTypes.length - 1) 1571 + " for the number of requests in the list: " 1572 + repeatingRequestTypes.length); 1573 } 1574 1575 long frameNumber = lastFrameNumber; 1576 for (int i = repeatingRequestTypes.length - 1; i >= 0; i--) { 1577 if (repeatingRequestTypes[i] == CaptureRequest.REQUEST_TYPE_ZSL_STILL 1578 && lastZslStillFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 1579 lastZslStillFrameNumber = frameNumber; 1580 } else if (repeatingRequestTypes[i] == CaptureRequest.REQUEST_TYPE_REGULAR 1581 && lastRegularFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { 1582 lastRegularFrameNumber = frameNumber; 1583 } 1584 1585 if (lastZslStillFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED 1586 && lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) { 1587 break; 1588 } 1589 1590 frameNumber--; 1591 } 1592 1593 mLastRegularFrameNumber = lastRegularFrameNumber; 1594 mLastZslStillFrameNumber = lastZslStillFrameNumber; 1595 mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; 1596 mRequestId = requestId; 1597 } 1598 1599 /** 1600 * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1601 * it contains no regular request. 1602 */ getLastRegularFrameNumber()1603 public long getLastRegularFrameNumber() { 1604 return mLastRegularFrameNumber; 1605 } 1606 1607 /** 1608 * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1609 * it contains no reprocess request. 1610 */ getLastReprocessFrameNumber()1611 public long getLastReprocessFrameNumber() { 1612 return mLastReprocessFrameNumber; 1613 } 1614 1615 /** 1616 * Return the last ZslStill frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if 1617 * it contains no Zsl request. 1618 */ getLastZslStillFrameNumber()1619 public long getLastZslStillFrameNumber() { 1620 return mLastZslStillFrameNumber; 1621 } 1622 1623 /** 1624 * Return the last frame number overall. 1625 */ getLastFrameNumber()1626 public long getLastFrameNumber() { 1627 return Math.max(mLastZslStillFrameNumber, 1628 Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber)); 1629 } 1630 1631 /** 1632 * Return the request ID. 1633 */ getRequestId()1634 public int getRequestId() { 1635 return mRequestId; 1636 } 1637 } 1638 1639 /** 1640 * This class tracks the last frame number for submitted requests. 1641 */ 1642 public class FrameNumberTracker { 1643 1644 /** the completed frame number for each type of capture results */ 1645 private long[] mCompletedFrameNumber = new long[CaptureRequest.REQUEST_TYPE_COUNT]; 1646 1647 /** the skipped frame numbers that don't belong to each type of capture results */ 1648 private final LinkedList<Long>[] mSkippedOtherFrameNumbers = 1649 new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT]; 1650 1651 /** the skipped frame numbers that belong to each type of capture results */ 1652 private final LinkedList<Long>[] mSkippedFrameNumbers = 1653 new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT]; 1654 1655 /** frame number -> request type */ 1656 private final TreeMap<Long, Integer> mFutureErrorMap = new TreeMap<Long, Integer>(); 1657 /** Map frame numbers to list of partial results */ 1658 private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); 1659 FrameNumberTracker()1660 public FrameNumberTracker() { 1661 for (int i = 0; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) { 1662 mCompletedFrameNumber[i] = CaptureCallback.NO_FRAMES_CAPTURED; 1663 mSkippedOtherFrameNumbers[i] = new LinkedList<Long>(); 1664 mSkippedFrameNumbers[i] = new LinkedList<Long>(); 1665 } 1666 } 1667 update()1668 private void update() { 1669 Iterator iter = mFutureErrorMap.entrySet().iterator(); 1670 while (iter.hasNext()) { 1671 TreeMap.Entry pair = (TreeMap.Entry)iter.next(); 1672 Long errorFrameNumber = (Long)pair.getKey(); 1673 int requestType = (int) pair.getValue(); 1674 Boolean removeError = false; 1675 if (errorFrameNumber == mCompletedFrameNumber[requestType] + 1) { 1676 mCompletedFrameNumber[requestType] = errorFrameNumber; 1677 removeError = true; 1678 } else { 1679 if (!mSkippedFrameNumbers[requestType].isEmpty()) { 1680 if (errorFrameNumber == mSkippedFrameNumbers[requestType].element()) { 1681 mCompletedFrameNumber[requestType] = errorFrameNumber; 1682 mSkippedFrameNumbers[requestType].remove(); 1683 removeError = true; 1684 } 1685 } else { 1686 for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) { 1687 int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT; 1688 if (!mSkippedOtherFrameNumbers[otherType].isEmpty() && errorFrameNumber 1689 == mSkippedOtherFrameNumbers[otherType].element()) { 1690 mCompletedFrameNumber[requestType] = errorFrameNumber; 1691 mSkippedOtherFrameNumbers[otherType].remove(); 1692 removeError = true; 1693 break; 1694 } 1695 } 1696 } 1697 } 1698 if (removeError) { 1699 iter.remove(); 1700 } 1701 } 1702 } 1703 1704 /** 1705 * This function is called every time when a result or an error is received. 1706 * @param frameNumber the frame number corresponding to the result or error 1707 * @param isError true if it is an error, false if it is not an error 1708 * @param requestType the type of capture request: Reprocess, ZslStill, or Regular. 1709 */ updateTracker(long frameNumber, boolean isError, int requestType)1710 public void updateTracker(long frameNumber, boolean isError, int requestType) { 1711 if (isError) { 1712 mFutureErrorMap.put(frameNumber, requestType); 1713 } else { 1714 try { 1715 updateCompletedFrameNumber(frameNumber, requestType); 1716 } catch (IllegalArgumentException e) { 1717 Log.e(TAG, e.getMessage()); 1718 } 1719 } 1720 update(); 1721 } 1722 1723 /** 1724 * This function is called every time a result has been completed. 1725 * 1726 * <p>It keeps a track of all the partial results already created for a particular 1727 * frame number.</p> 1728 * 1729 * @param frameNumber the frame number corresponding to the result 1730 * @param result the total or partial result 1731 * @param partial {@true} if the result is partial, {@code false} if total 1732 * @param requestType the type of capture request: Reprocess, ZslStill, or Regular. 1733 */ updateTracker(long frameNumber, CaptureResult result, boolean partial, int requestType)1734 public void updateTracker(long frameNumber, CaptureResult result, boolean partial, 1735 int requestType) { 1736 if (!partial) { 1737 // Update the total result's frame status as being successful 1738 updateTracker(frameNumber, /*isError*/false, requestType); 1739 // Don't keep a list of total results, we don't need to track them 1740 return; 1741 } 1742 1743 if (result == null) { 1744 // Do not record blank results; this also means there will be no total result 1745 // so it doesn't matter that the partials were not recorded 1746 return; 1747 } 1748 1749 // Partial results must be aggregated in-order for that frame number 1750 List<CaptureResult> partials = mPartialResults.get(frameNumber); 1751 if (partials == null) { 1752 partials = new ArrayList<>(); 1753 mPartialResults.put(frameNumber, partials); 1754 } 1755 1756 partials.add(result); 1757 } 1758 1759 /** 1760 * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. 1761 * 1762 * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} 1763 * is called again with new partials for that frame number).</p> 1764 * 1765 * @param frameNumber the frame number corresponding to the result 1766 * @return a list of partial results for that frame with at least 1 element, 1767 * or {@code null} if there were no partials recorded for that frame 1768 */ popPartialResults(long frameNumber)1769 public List<CaptureResult> popPartialResults(long frameNumber) { 1770 return mPartialResults.remove(frameNumber); 1771 } 1772 getCompletedFrameNumber()1773 public long getCompletedFrameNumber() { 1774 return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REGULAR]; 1775 } 1776 getCompletedReprocessFrameNumber()1777 public long getCompletedReprocessFrameNumber() { 1778 return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REPROCESS]; 1779 } 1780 getCompletedZslStillFrameNumber()1781 public long getCompletedZslStillFrameNumber() { 1782 return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_ZSL_STILL]; 1783 } 1784 1785 /** 1786 * Update the completed frame number for results of 3 categories 1787 * (Regular/Reprocess/ZslStill). 1788 * 1789 * It validates that all previous frames of the same category have arrived. 1790 * 1791 * If there is a gap since previous frame number of the same category, assume the frames in 1792 * the gap are other categories and store them in the skipped frame number queue to check 1793 * against when frames of those categories arrive. 1794 */ updateCompletedFrameNumber(long frameNumber, int requestType)1795 private void updateCompletedFrameNumber(long frameNumber, 1796 int requestType) throws IllegalArgumentException { 1797 if (frameNumber <= mCompletedFrameNumber[requestType]) { 1798 throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); 1799 } 1800 1801 // Assume there are only 3 different types of capture requests. 1802 int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT; 1803 int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT; 1804 long maxOtherFrameNumberSeen = 1805 Math.max(mCompletedFrameNumber[otherType1], mCompletedFrameNumber[otherType2]); 1806 if (frameNumber < maxOtherFrameNumberSeen) { 1807 // if frame number is smaller than completed frame numbers of other categories, 1808 // it must be: 1809 // - the head of mSkippedFrameNumbers for this category, or 1810 // - in one of other mSkippedOtherFrameNumbers 1811 if (!mSkippedFrameNumbers[requestType].isEmpty()) { 1812 // frame number must be head of current type of mSkippedFrameNumbers if 1813 // mSkippedFrameNumbers isn't empty. 1814 if (frameNumber < mSkippedFrameNumbers[requestType].element()) { 1815 throw new IllegalArgumentException("frame number " + frameNumber 1816 + " is a repeat"); 1817 } else if (frameNumber > mSkippedFrameNumbers[requestType].element()) { 1818 throw new IllegalArgumentException("frame number " + frameNumber 1819 + " comes out of order. Expecting " 1820 + mSkippedFrameNumbers[requestType].element()); 1821 } 1822 // frame number matches the head of the skipped frame number queue. 1823 mSkippedFrameNumbers[requestType].remove(); 1824 } else { 1825 // frame number must be in one of the other mSkippedOtherFrameNumbers. 1826 int index1 = mSkippedOtherFrameNumbers[otherType1].indexOf(frameNumber); 1827 int index2 = mSkippedOtherFrameNumbers[otherType2].indexOf(frameNumber); 1828 boolean inSkippedOther1 = index1 != -1; 1829 boolean inSkippedOther2 = index2 != -1; 1830 if (!(inSkippedOther1 ^ inSkippedOther2)) { 1831 throw new IllegalArgumentException("frame number " + frameNumber 1832 + " is a repeat or invalid"); 1833 } 1834 1835 // We know the category of frame numbers in skippedOtherFrameNumbers leading up 1836 // to the current frame number. Move them into the correct skippedFrameNumbers. 1837 LinkedList<Long> srcList, dstList; 1838 int index; 1839 if (inSkippedOther1) { 1840 srcList = mSkippedOtherFrameNumbers[otherType1]; 1841 dstList = mSkippedFrameNumbers[otherType2]; 1842 index = index1; 1843 } else { 1844 srcList = mSkippedOtherFrameNumbers[otherType2]; 1845 dstList = mSkippedFrameNumbers[otherType1]; 1846 index = index2; 1847 } 1848 for (int i = 0; i < index; i++) { 1849 dstList.add(srcList.removeFirst()); 1850 } 1851 1852 // Remove current frame number from skippedOtherFrameNumbers 1853 srcList.remove(); 1854 } 1855 } else { 1856 // there is a gap of unseen frame numbers which should belong to the other 1857 // 2 categories. Put all the skipped frame numbers in the queue. 1858 for (long i = 1859 Math.max(maxOtherFrameNumberSeen, mCompletedFrameNumber[requestType]) + 1; 1860 i < frameNumber; i++) { 1861 mSkippedOtherFrameNumbers[requestType].add(i); 1862 } 1863 } 1864 1865 mCompletedFrameNumber[requestType] = frameNumber; 1866 } 1867 } 1868 checkAndFireSequenceComplete()1869 private void checkAndFireSequenceComplete() { 1870 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); 1871 long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); 1872 long completedZslStillFrameNumber = mFrameNumberTracker.getCompletedZslStillFrameNumber(); 1873 boolean isReprocess = false; 1874 Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator(); 1875 while (iter.hasNext()) { 1876 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); 1877 boolean sequenceCompleted = false; 1878 final int requestId = requestLastFrameNumbers.getRequestId(); 1879 final CaptureCallbackHolder holder; 1880 synchronized(mInterfaceLock) { 1881 if (mRemoteDevice == null) { 1882 Log.w(TAG, "Camera closed while checking sequences"); 1883 return; 1884 } 1885 1886 int index = mCaptureCallbackMap.indexOfKey(requestId); 1887 holder = (index >= 0) ? 1888 mCaptureCallbackMap.valueAt(index) : null; 1889 if (holder != null) { 1890 long lastRegularFrameNumber = 1891 requestLastFrameNumbers.getLastRegularFrameNumber(); 1892 long lastReprocessFrameNumber = 1893 requestLastFrameNumbers.getLastReprocessFrameNumber(); 1894 long lastZslStillFrameNumber = 1895 requestLastFrameNumbers.getLastZslStillFrameNumber(); 1896 // check if it's okay to remove request from mCaptureCallbackMap 1897 if (lastRegularFrameNumber <= completedFrameNumber 1898 && lastReprocessFrameNumber <= completedReprocessFrameNumber 1899 && lastZslStillFrameNumber <= completedZslStillFrameNumber) { 1900 sequenceCompleted = true; 1901 mCaptureCallbackMap.removeAt(index); 1902 if (DEBUG) { 1903 Log.v(TAG, String.format( 1904 "Remove holder for requestId %d, because lastRegularFrame %d " 1905 + "is <= %d, lastReprocessFrame %d is <= %d, " 1906 + "lastZslStillFrame %d is <= %d", requestId, 1907 lastRegularFrameNumber, completedFrameNumber, 1908 lastReprocessFrameNumber, completedReprocessFrameNumber, 1909 lastZslStillFrameNumber, completedZslStillFrameNumber)); 1910 } 1911 } 1912 } 1913 } 1914 1915 // If no callback is registered for this requestId or sequence completed, remove it 1916 // from the frame number->request pair because it's not needed anymore. 1917 if (holder == null || sequenceCompleted) { 1918 iter.remove(); 1919 } 1920 1921 // Call onCaptureSequenceCompleted 1922 if (sequenceCompleted) { 1923 Runnable resultDispatch = new Runnable() { 1924 @Override 1925 public void run() { 1926 if (!CameraDeviceImpl.this.isClosed()){ 1927 if (DEBUG) { 1928 Log.d(TAG, String.format( 1929 "fire sequence complete for request %d", 1930 requestId)); 1931 } 1932 1933 holder.getCallback().onCaptureSequenceCompleted( 1934 CameraDeviceImpl.this, 1935 requestId, 1936 requestLastFrameNumbers.getLastFrameNumber()); 1937 } 1938 } 1939 }; 1940 final long ident = Binder.clearCallingIdentity(); 1941 try { 1942 holder.getExecutor().execute(resultDispatch); 1943 } finally { 1944 Binder.restoreCallingIdentity(ident); 1945 } 1946 } 1947 } 1948 } 1949 1950 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 1951 1952 @Override asBinder()1953 public IBinder asBinder() { 1954 return this; 1955 } 1956 1957 @Override onDeviceError(final int errorCode, CaptureResultExtras resultExtras)1958 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { 1959 if (DEBUG) { 1960 Log.d(TAG, String.format( 1961 "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", 1962 errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), 1963 resultExtras.getSubsequenceId())); 1964 } 1965 1966 synchronized(mInterfaceLock) { 1967 if (mRemoteDevice == null) { 1968 return; // Camera already closed 1969 } 1970 1971 switch (errorCode) { 1972 case ERROR_CAMERA_DISCONNECTED: 1973 final long ident = Binder.clearCallingIdentity(); 1974 try { 1975 CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected); 1976 } finally { 1977 Binder.restoreCallingIdentity(ident); 1978 } 1979 break; 1980 case ERROR_CAMERA_REQUEST: 1981 case ERROR_CAMERA_RESULT: 1982 case ERROR_CAMERA_BUFFER: 1983 onCaptureErrorLocked(errorCode, resultExtras); 1984 break; 1985 case ERROR_CAMERA_DEVICE: 1986 scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE); 1987 break; 1988 case ERROR_CAMERA_DISABLED: 1989 scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED); 1990 break; 1991 default: 1992 Log.e(TAG, "Unknown error from camera device: " + errorCode); 1993 scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE); 1994 } 1995 } 1996 } 1997 scheduleNotifyError(int code)1998 private void scheduleNotifyError(int code) { 1999 mInError = true; 2000 final long ident = Binder.clearCallingIdentity(); 2001 try { 2002 CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable( 2003 CameraDeviceCallbacks::notifyError, this, code).recycleOnUse()); 2004 } finally { 2005 Binder.restoreCallingIdentity(ident); 2006 } 2007 } 2008 notifyError(int code)2009 private void notifyError(int code) { 2010 if (!CameraDeviceImpl.this.isClosed()) { 2011 mDeviceCallback.onError(CameraDeviceImpl.this, code); 2012 } 2013 } 2014 2015 @Override onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId)2016 public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) { 2017 if (DEBUG) { 2018 Log.d(TAG, "Repeating request error received. Last frame number is " + 2019 lastFrameNumber); 2020 } 2021 2022 synchronized(mInterfaceLock) { 2023 // Camera is already closed or no repeating request is present. 2024 if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) { 2025 return; // Camera already closed 2026 } 2027 2028 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber, 2029 mRepeatingRequestTypes); 2030 // Check if there is already a new repeating request 2031 if (mRepeatingRequestId == repeatingRequestId) { 2032 mRepeatingRequestId = REQUEST_ID_NONE; 2033 mRepeatingRequestTypes = null; 2034 } 2035 } 2036 } 2037 2038 @Override onDeviceIdle()2039 public void onDeviceIdle() { 2040 if (DEBUG) { 2041 Log.d(TAG, "Camera now idle"); 2042 } 2043 synchronized(mInterfaceLock) { 2044 if (mRemoteDevice == null) return; // Camera already closed 2045 2046 if (!CameraDeviceImpl.this.mIdle) { 2047 final long ident = Binder.clearCallingIdentity(); 2048 try { 2049 CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnIdle); 2050 } finally { 2051 Binder.restoreCallingIdentity(ident); 2052 } 2053 } 2054 CameraDeviceImpl.this.mIdle = true; 2055 } 2056 } 2057 2058 @Override onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)2059 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 2060 int requestId = resultExtras.getRequestId(); 2061 final long frameNumber = resultExtras.getFrameNumber(); 2062 2063 if (DEBUG) { 2064 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber); 2065 } 2066 final CaptureCallbackHolder holder; 2067 2068 synchronized(mInterfaceLock) { 2069 if (mRemoteDevice == null) return; // Camera already closed 2070 2071 // Get the callback for this frame ID, if there is one 2072 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 2073 2074 if (holder == null) { 2075 return; 2076 } 2077 2078 if (isClosed()) return; 2079 2080 // Dispatch capture start notice 2081 final long ident = Binder.clearCallingIdentity(); 2082 try { 2083 holder.getExecutor().execute( 2084 new Runnable() { 2085 @Override 2086 public void run() { 2087 if (!CameraDeviceImpl.this.isClosed()) { 2088 final int subsequenceId = resultExtras.getSubsequenceId(); 2089 final CaptureRequest request = holder.getRequest(subsequenceId); 2090 2091 if (holder.hasBatchedOutputs()) { 2092 // Send derived onCaptureStarted for requests within the 2093 // batch 2094 final Range<Integer> fpsRange = 2095 request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); 2096 for (int i = 0; i < holder.getRequestCount(); i++) { 2097 holder.getCallback().onCaptureStarted( 2098 CameraDeviceImpl.this, 2099 holder.getRequest(i), 2100 timestamp - (subsequenceId - i) * 2101 NANO_PER_SECOND/fpsRange.getUpper(), 2102 frameNumber - (subsequenceId - i)); 2103 } 2104 } else { 2105 holder.getCallback().onCaptureStarted( 2106 CameraDeviceImpl.this, 2107 holder.getRequest(resultExtras.getSubsequenceId()), 2108 timestamp, frameNumber); 2109 } 2110 } 2111 } 2112 }); 2113 } finally { 2114 Binder.restoreCallingIdentity(ident); 2115 } 2116 } 2117 } 2118 2119 @Override onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])2120 public void onResultReceived(CameraMetadataNative result, 2121 CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[]) 2122 throws RemoteException { 2123 2124 int requestId = resultExtras.getRequestId(); 2125 long frameNumber = resultExtras.getFrameNumber(); 2126 2127 if (DEBUG) { 2128 Log.v(TAG, "Received result frame " + frameNumber + " for id " 2129 + requestId); 2130 } 2131 2132 synchronized(mInterfaceLock) { 2133 if (mRemoteDevice == null) return; // Camera already closed 2134 2135 // TODO: Handle CameraCharacteristics access from CaptureResult correctly. 2136 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, 2137 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); 2138 2139 final CaptureCallbackHolder holder = 2140 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 2141 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); 2142 2143 boolean isPartialResult = 2144 (resultExtras.getPartialResultCount() < mTotalPartialCount); 2145 int requestType = request.getRequestType(); 2146 2147 // Check if we have a callback for this 2148 if (holder == null) { 2149 if (DEBUG) { 2150 Log.d(TAG, 2151 "holder is null, early return at frame " 2152 + frameNumber); 2153 } 2154 2155 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 2156 requestType); 2157 2158 return; 2159 } 2160 2161 if (isClosed()) { 2162 if (DEBUG) { 2163 Log.d(TAG, 2164 "camera is closed, early return at frame " 2165 + frameNumber); 2166 } 2167 2168 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 2169 requestType); 2170 return; 2171 } 2172 2173 2174 Runnable resultDispatch = null; 2175 2176 CaptureResult finalResult; 2177 // Make a copy of the native metadata before it gets moved to a CaptureResult 2178 // object. 2179 final CameraMetadataNative resultCopy; 2180 if (holder.hasBatchedOutputs()) { 2181 resultCopy = new CameraMetadataNative(result); 2182 } else { 2183 resultCopy = null; 2184 } 2185 2186 // Either send a partial result or the final capture completed result 2187 if (isPartialResult) { 2188 final CaptureResult resultAsCapture = 2189 new CaptureResult(result, request, resultExtras); 2190 // Partial result 2191 resultDispatch = new Runnable() { 2192 @Override 2193 public void run() { 2194 if (!CameraDeviceImpl.this.isClosed()) { 2195 if (holder.hasBatchedOutputs()) { 2196 // Send derived onCaptureProgressed for requests within 2197 // the batch. 2198 for (int i = 0; i < holder.getRequestCount(); i++) { 2199 CameraMetadataNative resultLocal = 2200 new CameraMetadataNative(resultCopy); 2201 CaptureResult resultInBatch = new CaptureResult( 2202 resultLocal, holder.getRequest(i), resultExtras); 2203 2204 holder.getCallback().onCaptureProgressed( 2205 CameraDeviceImpl.this, 2206 holder.getRequest(i), 2207 resultInBatch); 2208 } 2209 } else { 2210 holder.getCallback().onCaptureProgressed( 2211 CameraDeviceImpl.this, 2212 request, 2213 resultAsCapture); 2214 } 2215 } 2216 } 2217 }; 2218 finalResult = resultAsCapture; 2219 } else { 2220 List<CaptureResult> partialResults = 2221 mFrameNumberTracker.popPartialResults(frameNumber); 2222 2223 final long sensorTimestamp = 2224 result.get(CaptureResult.SENSOR_TIMESTAMP); 2225 final Range<Integer> fpsRange = 2226 request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); 2227 final int subsequenceId = resultExtras.getSubsequenceId(); 2228 final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result, 2229 request, resultExtras, partialResults, holder.getSessionId(), 2230 physicalResults); 2231 // Final capture result 2232 resultDispatch = new Runnable() { 2233 @Override 2234 public void run() { 2235 if (!CameraDeviceImpl.this.isClosed()){ 2236 if (holder.hasBatchedOutputs()) { 2237 // Send derived onCaptureCompleted for requests within 2238 // the batch. 2239 for (int i = 0; i < holder.getRequestCount(); i++) { 2240 resultCopy.set(CaptureResult.SENSOR_TIMESTAMP, 2241 sensorTimestamp - (subsequenceId - i) * 2242 NANO_PER_SECOND/fpsRange.getUpper()); 2243 CameraMetadataNative resultLocal = 2244 new CameraMetadataNative(resultCopy); 2245 // No logical multi-camera support for batched output mode. 2246 TotalCaptureResult resultInBatch = new TotalCaptureResult( 2247 resultLocal, holder.getRequest(i), resultExtras, 2248 partialResults, holder.getSessionId(), 2249 new PhysicalCaptureResultInfo[0]); 2250 2251 holder.getCallback().onCaptureCompleted( 2252 CameraDeviceImpl.this, 2253 holder.getRequest(i), 2254 resultInBatch); 2255 } 2256 } else { 2257 holder.getCallback().onCaptureCompleted( 2258 CameraDeviceImpl.this, 2259 request, 2260 resultAsCapture); 2261 } 2262 } 2263 } 2264 }; 2265 finalResult = resultAsCapture; 2266 } 2267 2268 final long ident = Binder.clearCallingIdentity(); 2269 try { 2270 holder.getExecutor().execute(resultDispatch); 2271 } finally { 2272 Binder.restoreCallingIdentity(ident); 2273 } 2274 2275 // Collect the partials for a total result; or mark the frame as totally completed 2276 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, 2277 requestType); 2278 2279 // Fire onCaptureSequenceCompleted 2280 if (!isPartialResult) { 2281 checkAndFireSequenceComplete(); 2282 } 2283 } 2284 } 2285 2286 @Override onPrepared(int streamId)2287 public void onPrepared(int streamId) { 2288 final OutputConfiguration output; 2289 final StateCallbackKK sessionCallback; 2290 2291 if (DEBUG) { 2292 Log.v(TAG, "Stream " + streamId + " is prepared"); 2293 } 2294 2295 synchronized(mInterfaceLock) { 2296 output = mConfiguredOutputs.get(streamId); 2297 sessionCallback = mSessionStateCallback; 2298 } 2299 2300 if (sessionCallback == null) return; 2301 2302 if (output == null) { 2303 Log.w(TAG, "onPrepared invoked for unknown output Surface"); 2304 return; 2305 } 2306 final List<Surface> surfaces = output.getSurfaces(); 2307 for (Surface surface : surfaces) { 2308 sessionCallback.onSurfacePrepared(surface); 2309 } 2310 } 2311 2312 @Override onRequestQueueEmpty()2313 public void onRequestQueueEmpty() { 2314 final StateCallbackKK sessionCallback; 2315 2316 if (DEBUG) { 2317 Log.v(TAG, "Request queue becomes empty"); 2318 } 2319 2320 synchronized(mInterfaceLock) { 2321 sessionCallback = mSessionStateCallback; 2322 } 2323 2324 if (sessionCallback == null) return; 2325 2326 sessionCallback.onRequestQueueEmpty(); 2327 } 2328 2329 /** 2330 * Called by onDeviceError for handling single-capture failures. 2331 */ onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras)2332 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { 2333 2334 final int requestId = resultExtras.getRequestId(); 2335 final int subsequenceId = resultExtras.getSubsequenceId(); 2336 final long frameNumber = resultExtras.getFrameNumber(); 2337 final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId(); 2338 final CaptureCallbackHolder holder = 2339 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 2340 2341 if (holder == null) { 2342 Log.e(TAG, String.format("Receive capture error on unknown request ID %d", 2343 requestId)); 2344 return; 2345 } 2346 2347 final CaptureRequest request = holder.getRequest(subsequenceId); 2348 2349 Runnable failureDispatch = null; 2350 if (errorCode == ERROR_CAMERA_BUFFER) { 2351 // Because 1 stream id could map to multiple surfaces, we need to specify both 2352 // streamId and surfaceId. 2353 OutputConfiguration config = mConfiguredOutputs.get( 2354 resultExtras.getErrorStreamId()); 2355 if (config == null) { 2356 Log.v(TAG, String.format( 2357 "Stream %d has been removed. Skipping buffer lost callback", 2358 resultExtras.getErrorStreamId())); 2359 return; 2360 } 2361 for (Surface surface : config.getSurfaces()) { 2362 if (!request.containsTarget(surface)) { 2363 continue; 2364 } 2365 if (DEBUG) { 2366 Log.v(TAG, String.format( 2367 "Lost output buffer reported for frame %d, target %s", 2368 frameNumber, surface)); 2369 } 2370 failureDispatch = new Runnable() { 2371 @Override 2372 public void run() { 2373 if (!CameraDeviceImpl.this.isClosed()){ 2374 holder.getCallback().onCaptureBufferLost( 2375 CameraDeviceImpl.this, 2376 request, 2377 surface, 2378 frameNumber); 2379 } 2380 } 2381 }; 2382 // Dispatch the failure callback 2383 final long ident = Binder.clearCallingIdentity(); 2384 try { 2385 holder.getExecutor().execute(failureDispatch); 2386 } finally { 2387 Binder.restoreCallingIdentity(ident); 2388 } 2389 } 2390 } else { 2391 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); 2392 2393 // This is only approximate - exact handling needs the camera service and HAL to 2394 // disambiguate between request failures to due abort and due to real errors. For 2395 // now, assume that if the session believes we're mid-abort, then the error is due 2396 // to abort. 2397 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? 2398 CaptureFailure.REASON_FLUSHED : 2399 CaptureFailure.REASON_ERROR; 2400 2401 final CaptureFailure failure = new CaptureFailure( 2402 request, 2403 reason, 2404 /*dropped*/ mayHaveBuffers, 2405 requestId, 2406 frameNumber, 2407 errorPhysicalCameraId); 2408 2409 failureDispatch = new Runnable() { 2410 @Override 2411 public void run() { 2412 if (!CameraDeviceImpl.this.isClosed()){ 2413 holder.getCallback().onCaptureFailed( 2414 CameraDeviceImpl.this, 2415 request, 2416 failure); 2417 } 2418 } 2419 }; 2420 2421 // Fire onCaptureSequenceCompleted if appropriate 2422 if (DEBUG) { 2423 Log.v(TAG, String.format("got error frame %d", frameNumber)); 2424 } 2425 mFrameNumberTracker.updateTracker(frameNumber, 2426 /*error*/true, request.getRequestType()); 2427 checkAndFireSequenceComplete(); 2428 2429 // Dispatch the failure callback 2430 final long ident = Binder.clearCallingIdentity(); 2431 try { 2432 holder.getExecutor().execute(failureDispatch); 2433 } finally { 2434 Binder.restoreCallingIdentity(ident); 2435 } 2436 } 2437 2438 } 2439 2440 } // public class CameraDeviceCallbacks 2441 2442 /** 2443 * A camera specific adapter {@link Executor} that posts all executed tasks onto the given 2444 * {@link Handler}. 2445 * 2446 * @hide 2447 */ 2448 private static class CameraHandlerExecutor implements Executor { 2449 private final Handler mHandler; 2450 CameraHandlerExecutor(@onNull Handler handler)2451 public CameraHandlerExecutor(@NonNull Handler handler) { 2452 mHandler = Preconditions.checkNotNull(handler); 2453 } 2454 2455 @Override execute(Runnable command)2456 public void execute(Runnable command) { 2457 // Return value of 'post()' will be ignored in order to keep the 2458 // same camera behavior. For further details see b/74605221 . 2459 mHandler.post(command); 2460 } 2461 } 2462 2463 /** 2464 * Default executor management. 2465 * 2466 * <p> 2467 * If executor is null, get the current thread's 2468 * Looper to create a Executor with. If no looper exists, throw 2469 * {@code IllegalArgumentException}. 2470 * </p> 2471 */ checkExecutor(Executor executor)2472 static Executor checkExecutor(Executor executor) { 2473 return (executor == null) ? checkAndWrapHandler(null) : executor; 2474 } 2475 2476 /** 2477 * Default executor management. 2478 * 2479 * <p>If the callback isn't null, check the executor, otherwise pass it through.</p> 2480 */ checkExecutor(Executor executor, T callback)2481 public static <T> Executor checkExecutor(Executor executor, T callback) { 2482 return (callback != null) ? checkExecutor(executor) : executor; 2483 } 2484 2485 /** 2486 * Wrap Handler in Executor. 2487 * 2488 * <p> 2489 * If handler is null, get the current thread's 2490 * Looper to create a Executor with. If no looper exists, throw 2491 * {@code IllegalArgumentException}. 2492 * </p> 2493 */ checkAndWrapHandler(Handler handler)2494 public static Executor checkAndWrapHandler(Handler handler) { 2495 return new CameraHandlerExecutor(checkHandler(handler)); 2496 } 2497 2498 /** 2499 * Default handler management. 2500 * 2501 * <p> 2502 * If handler is null, get the current thread's 2503 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}. 2504 * </p> 2505 */ checkHandler(Handler handler)2506 static Handler checkHandler(Handler handler) { 2507 if (handler == null) { 2508 Looper looper = Looper.myLooper(); 2509 if (looper == null) { 2510 throw new IllegalArgumentException( 2511 "No handler given, and current thread has no looper!"); 2512 } 2513 handler = new Handler(looper); 2514 } 2515 return handler; 2516 } 2517 2518 /** 2519 * Default handler management, conditional on there being a callback. 2520 * 2521 * <p>If the callback isn't null, check the handler, otherwise pass it through.</p> 2522 */ checkHandler(Handler handler, T callback)2523 static <T> Handler checkHandler(Handler handler, T callback) { 2524 if (callback != null) { 2525 return checkHandler(handler); 2526 } 2527 return handler; 2528 } 2529 checkIfCameraClosedOrInError()2530 private void checkIfCameraClosedOrInError() throws CameraAccessException { 2531 if (mRemoteDevice == null) { 2532 throw new IllegalStateException("CameraDevice was already closed"); 2533 } 2534 if (mInError) { 2535 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, 2536 "The camera device has encountered a serious error"); 2537 } 2538 } 2539 2540 /** Whether the camera device has started to close (may not yet have finished) */ isClosed()2541 private boolean isClosed() { 2542 return mClosing.get(); 2543 } 2544 getCharacteristics()2545 private CameraCharacteristics getCharacteristics() { 2546 return mCharacteristics; 2547 } 2548 2549 /** 2550 * Listener for binder death. 2551 * 2552 * <p> Handle binder death for ICameraDeviceUser. Trigger onError.</p> 2553 */ 2554 @Override binderDied()2555 public void binderDied() { 2556 Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly"); 2557 2558 if (mRemoteDevice == null) { 2559 return; // Camera already closed 2560 } 2561 2562 mInError = true; 2563 Runnable r = new Runnable() { 2564 @Override 2565 public void run() { 2566 if (!isClosed()) { 2567 mDeviceCallback.onError(CameraDeviceImpl.this, 2568 StateCallback.ERROR_CAMERA_SERVICE); 2569 } 2570 } 2571 }; 2572 final long ident = Binder.clearCallingIdentity(); 2573 try { 2574 CameraDeviceImpl.this.mDeviceExecutor.execute(r); 2575 } finally { 2576 Binder.restoreCallingIdentity(ident); 2577 } 2578 } 2579 } 2580