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