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