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