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 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 // Notify current session that it's going away, before starting camera operations 705 // After this call completes, the session is not allowed to call into CameraDeviceImpl 706 if (mCurrentSession != null) { 707 mCurrentSession.replaceSessionClose(); 708 } 709 710 if (mCurrentExtensionSession != null) { 711 mCurrentExtensionSession.release(false /*skipCloseNotification*/); 712 mCurrentExtensionSession = null; 713 } 714 715 if (mCurrentAdvancedExtensionSession != null) { 716 mCurrentAdvancedExtensionSession.release(false /*skipCloseNotification*/); 717 mCurrentAdvancedExtensionSession = null; 718 } 719 720 // TODO: dont block for this 721 boolean configureSuccess = true; 722 CameraAccessException pendingException = null; 723 Surface input = null; 724 try { 725 // configure streams and then block until IDLE 726 configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations, 727 operatingMode, sessionParams, createSessionStartTime); 728 if (configureSuccess == true && inputConfig != null) { 729 input = mRemoteDevice.getInputSurface(); 730 } 731 } catch (CameraAccessException e) { 732 configureSuccess = false; 733 pendingException = e; 734 input = null; 735 if (DEBUG) { 736 Log.v(TAG, "createCaptureSession - failed with exception ", e); 737 } 738 } 739 740 // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise. 741 CameraCaptureSessionCore newSession = null; 742 if (isConstrainedHighSpeed) { 743 ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size()); 744 for (OutputConfiguration outConfig : outputConfigurations) { 745 surfaces.add(outConfig.getSurface()); 746 } 747 StreamConfigurationMap config = 748 getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 749 SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config); 750 751 newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++, 752 callback, executor, this, mDeviceExecutor, configureSuccess, 753 mCharacteristics); 754 } else { 755 newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, 756 callback, executor, this, mDeviceExecutor, configureSuccess); 757 } 758 759 // TODO: wait until current session closes, then create the new session 760 mCurrentSession = newSession; 761 762 if (pendingException != null) { 763 throw pendingException; 764 } 765 766 mSessionStateCallback = mCurrentSession.getDeviceStateCallback(); 767 } 768 } 769 770 @Override isSessionConfigurationSupported( @onNull SessionConfiguration sessionConfig)771 public boolean isSessionConfigurationSupported( 772 @NonNull SessionConfiguration sessionConfig) throws CameraAccessException, 773 UnsupportedOperationException, IllegalArgumentException { 774 synchronized(mInterfaceLock) { 775 checkIfCameraClosedOrInError(); 776 777 return mRemoteDevice.isSessionConfigurationSupported(sessionConfig); 778 } 779 } 780 781 /** 782 * For use by backwards-compatibility code only. 783 */ setSessionListener(StateCallbackKK sessionCallback)784 public void setSessionListener(StateCallbackKK sessionCallback) { 785 synchronized(mInterfaceLock) { 786 mSessionStateCallback = sessionCallback; 787 } 788 } 789 overrideEnableZsl(CameraMetadataNative request, boolean newValue)790 private void overrideEnableZsl(CameraMetadataNative request, boolean newValue) { 791 Boolean enableZsl = request.get(CaptureRequest.CONTROL_ENABLE_ZSL); 792 if (enableZsl == null) { 793 // If enableZsl is not available, don't override. 794 return; 795 } 796 797 request.set(CaptureRequest.CONTROL_ENABLE_ZSL, newValue); 798 } 799 800 @Override createCaptureRequest(int templateType, Set<String> physicalCameraIdSet)801 public CaptureRequest.Builder createCaptureRequest(int templateType, 802 Set<String> physicalCameraIdSet) 803 throws CameraAccessException { 804 synchronized(mInterfaceLock) { 805 checkIfCameraClosedOrInError(); 806 807 for (String physicalId : physicalCameraIdSet) { 808 if (physicalId == getId()) { 809 throw new IllegalStateException("Physical id matches the logical id!"); 810 } 811 } 812 813 CameraMetadataNative templatedRequest = null; 814 815 templatedRequest = mRemoteDevice.createDefaultRequest(templateType); 816 817 // If app target SDK is older than O, or it's not a still capture template, enableZsl 818 // must be false in the default request. 819 if (mAppTargetSdkVersion < Build.VERSION_CODES.O || 820 templateType != TEMPLATE_STILL_CAPTURE) { 821 overrideEnableZsl(templatedRequest, false); 822 } 823 824 CaptureRequest.Builder builder = new CaptureRequest.Builder( 825 templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE, 826 getId(), physicalCameraIdSet); 827 828 return builder; 829 } 830 } 831 832 @Override createCaptureRequest(int templateType)833 public CaptureRequest.Builder createCaptureRequest(int templateType) 834 throws CameraAccessException { 835 synchronized(mInterfaceLock) { 836 checkIfCameraClosedOrInError(); 837 838 CameraMetadataNative templatedRequest = null; 839 840 templatedRequest = mRemoteDevice.createDefaultRequest(templateType); 841 842 // If app target SDK is older than O, or it's not a still capture template, enableZsl 843 // must be false in the default request. 844 if (mAppTargetSdkVersion < Build.VERSION_CODES.O || 845 templateType != TEMPLATE_STILL_CAPTURE) { 846 overrideEnableZsl(templatedRequest, false); 847 } 848 849 CaptureRequest.Builder builder = new CaptureRequest.Builder( 850 templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE, 851 getId(), /*physicalCameraIdSet*/ null); 852 853 return builder; 854 } 855 } 856 857 @Override createReprocessCaptureRequest(TotalCaptureResult inputResult)858 public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult) 859 throws CameraAccessException { 860 synchronized(mInterfaceLock) { 861 checkIfCameraClosedOrInError(); 862 863 CameraMetadataNative resultMetadata = new 864 CameraMetadataNative(inputResult.getNativeCopy()); 865 866 return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true, 867 inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null); 868 } 869 } 870 prepare(Surface surface)871 public void prepare(Surface surface) throws CameraAccessException { 872 if (surface == null) throw new IllegalArgumentException("Surface is null"); 873 874 synchronized(mInterfaceLock) { 875 checkIfCameraClosedOrInError(); 876 int streamId = -1; 877 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 878 final List<Surface> surfaces = mConfiguredOutputs.valueAt(i).getSurfaces(); 879 if (surfaces.contains(surface)) { 880 streamId = mConfiguredOutputs.keyAt(i); 881 break; 882 } 883 } 884 if (streamId == -1) { 885 throw new IllegalArgumentException("Surface is not part of this session"); 886 } 887 888 mRemoteDevice.prepare(streamId); 889 } 890 } 891 prepare(int maxCount, Surface surface)892 public void prepare(int maxCount, Surface surface) throws CameraAccessException { 893 if (surface == null) throw new IllegalArgumentException("Surface is null"); 894 if (maxCount <= 0) throw new IllegalArgumentException("Invalid maxCount given: " + 895 maxCount); 896 897 synchronized(mInterfaceLock) { 898 checkIfCameraClosedOrInError(); 899 int streamId = -1; 900 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 901 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 902 streamId = mConfiguredOutputs.keyAt(i); 903 break; 904 } 905 } 906 if (streamId == -1) { 907 throw new IllegalArgumentException("Surface is not part of this session"); 908 } 909 910 mRemoteDevice.prepare2(maxCount, streamId); 911 } 912 } 913 updateOutputConfiguration(OutputConfiguration config)914 public void updateOutputConfiguration(OutputConfiguration config) 915 throws CameraAccessException { 916 synchronized(mInterfaceLock) { 917 checkIfCameraClosedOrInError(); 918 int streamId = -1; 919 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 920 if (config.getSurface() == mConfiguredOutputs.valueAt(i).getSurface()) { 921 streamId = mConfiguredOutputs.keyAt(i); 922 break; 923 } 924 } 925 if (streamId == -1) { 926 throw new IllegalArgumentException("Invalid output configuration"); 927 } 928 929 mRemoteDevice.updateOutputConfiguration(streamId, config); 930 mConfiguredOutputs.put(streamId, config); 931 } 932 } 933 switchToOffline( @onNull Collection<Surface> offlineOutputs, @NonNull Executor executor, @NonNull CameraOfflineSession.CameraOfflineSessionCallback listener)934 public CameraOfflineSession switchToOffline( 935 @NonNull Collection<Surface> offlineOutputs, @NonNull Executor executor, 936 @NonNull CameraOfflineSession.CameraOfflineSessionCallback listener) 937 throws CameraAccessException { 938 if (offlineOutputs.isEmpty()) { 939 throw new IllegalArgumentException("Invalid offline surfaces!"); 940 } 941 942 HashSet<Integer> offlineStreamIds = new HashSet<Integer>(); 943 SparseArray<OutputConfiguration> offlineConfiguredOutputs = 944 new SparseArray<OutputConfiguration>(); 945 CameraOfflineSession ret; 946 947 synchronized(mInterfaceLock) { 948 checkIfCameraClosedOrInError(); 949 if (mOfflineSessionImpl != null) { 950 throw new IllegalStateException("Switch to offline mode already in progress"); 951 } 952 953 for (Surface surface : offlineOutputs) { 954 int streamId = -1; 955 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 956 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 957 streamId = mConfiguredOutputs.keyAt(i); 958 offlineConfiguredOutputs.append(streamId, mConfiguredOutputs.valueAt(i)); 959 break; 960 } 961 } 962 if (streamId == -1) { 963 throw new IllegalArgumentException("Offline surface is not part of this" + 964 " session"); 965 } 966 967 if (!mOfflineSupport.contains(streamId)) { 968 throw new IllegalArgumentException("Surface: " + surface + " does not " + 969 " support offline mode"); 970 } 971 972 offlineStreamIds.add(streamId); 973 } 974 stopRepeating(); 975 976 mOfflineSessionImpl = new CameraOfflineSessionImpl(mCameraId, 977 mCharacteristics, executor, listener, offlineConfiguredOutputs, 978 mConfiguredInput, mConfiguredOutputs, mFrameNumberTracker, mCaptureCallbackMap, 979 mRequestLastFrameNumbersList); 980 ret = mOfflineSessionImpl; 981 982 mOfflineSwitchService = Executors.newSingleThreadExecutor(); 983 mConfiguredOutputs.clear(); 984 mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(REQUEST_ID_NONE, null); 985 mIdle = true; 986 mCaptureCallbackMap = new SparseArray<CaptureCallbackHolder>(); 987 mBatchOutputMap = new HashMap<>(); 988 mFrameNumberTracker = new FrameNumberTracker(); 989 990 mCurrentSession.closeWithoutDraining(); 991 mCurrentSession = null; 992 } 993 994 mOfflineSwitchService.execute(new Runnable() { 995 @Override 996 public void run() { 997 // We cannot hold 'mInterfaceLock' during the remote IPC in 'switchToOffline'. 998 // The call will block until all non-offline requests are completed and/or flushed. 999 // The results/errors need to be handled by 'CameraDeviceCallbacks' which also sync 1000 // on 'mInterfaceLock'. 1001 try { 1002 ICameraOfflineSession remoteOfflineSession = mRemoteDevice.switchToOffline( 1003 mOfflineSessionImpl.getCallbacks(), 1004 Arrays.stream(offlineStreamIds.toArray( 1005 new Integer[offlineStreamIds.size()])).mapToInt( 1006 Integer::intValue).toArray()); 1007 mOfflineSessionImpl.setRemoteSession(remoteOfflineSession); 1008 } catch (CameraAccessException e) { 1009 mOfflineSessionImpl.notifyFailedSwitch(); 1010 } finally { 1011 mOfflineSessionImpl = null; 1012 } 1013 } 1014 }); 1015 1016 return ret; 1017 } 1018 supportsOfflineProcessing(Surface surface)1019 public boolean supportsOfflineProcessing(Surface surface) { 1020 if (surface == null) throw new IllegalArgumentException("Surface is null"); 1021 1022 synchronized(mInterfaceLock) { 1023 int streamId = -1; 1024 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 1025 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 1026 streamId = mConfiguredOutputs.keyAt(i); 1027 break; 1028 } 1029 } 1030 if (streamId == -1) { 1031 throw new IllegalArgumentException("Surface is not part of this session"); 1032 } 1033 1034 return mOfflineSupport.contains(streamId); 1035 } 1036 } 1037 tearDown(Surface surface)1038 public void tearDown(Surface surface) throws CameraAccessException { 1039 if (surface == null) throw new IllegalArgumentException("Surface is null"); 1040 1041 synchronized(mInterfaceLock) { 1042 checkIfCameraClosedOrInError(); 1043 int streamId = -1; 1044 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 1045 if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { 1046 streamId = mConfiguredOutputs.keyAt(i); 1047 break; 1048 } 1049 } 1050 if (streamId == -1) { 1051 throw new IllegalArgumentException("Surface is not part of this session"); 1052 } 1053 1054 mRemoteDevice.tearDown(streamId); 1055 } 1056 } 1057 finalizeOutputConfigs(List<OutputConfiguration> outputConfigs)1058 public void finalizeOutputConfigs(List<OutputConfiguration> outputConfigs) 1059 throws CameraAccessException { 1060 if (outputConfigs == null || outputConfigs.size() == 0) { 1061 throw new IllegalArgumentException("deferred config is null or empty"); 1062 } 1063 1064 synchronized(mInterfaceLock) { 1065 checkIfCameraClosedOrInError(); 1066 1067 for (OutputConfiguration config : outputConfigs) { 1068 int streamId = -1; 1069 for (int i = 0; i < mConfiguredOutputs.size(); i++) { 1070 // Have to use equal here, as createCaptureSessionByOutputConfigurations() and 1071 // createReprocessableCaptureSessionByConfigurations() do a copy of the configs. 1072 if (config.equals(mConfiguredOutputs.valueAt(i))) { 1073 streamId = mConfiguredOutputs.keyAt(i); 1074 break; 1075 } 1076 } 1077 if (streamId == -1) { 1078 throw new IllegalArgumentException("Deferred config is not part of this " 1079 + "session"); 1080 } 1081 1082 if (config.getSurfaces().size() == 0) { 1083 throw new IllegalArgumentException("The final config for stream " + streamId 1084 + " must have at least 1 surface"); 1085 } 1086 mRemoteDevice.finalizeOutputConfigurations(streamId, config); 1087 mConfiguredOutputs.put(streamId, config); 1088 } 1089 } 1090 } 1091 capture(CaptureRequest request, CaptureCallback callback, Executor executor)1092 public int capture(CaptureRequest request, CaptureCallback callback, Executor executor) 1093 throws CameraAccessException { 1094 if (DEBUG) { 1095 Log.d(TAG, "calling capture"); 1096 } 1097 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 1098 requestList.add(request); 1099 return submitCaptureRequest(requestList, callback, executor, /*streaming*/false); 1100 } 1101 captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Executor executor)1102 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, 1103 Executor executor) throws CameraAccessException { 1104 if (requests == null || requests.isEmpty()) { 1105 throw new IllegalArgumentException("At least one request must be given"); 1106 } 1107 return submitCaptureRequest(requests, callback, executor, /*streaming*/false); 1108 } 1109 1110 /** 1111 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for 1112 * starting and stopping repeating request and flushing. 1113 * 1114 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never 1115 * sent to HAL. Then onCaptureSequenceAborted is immediately triggered. 1116 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last 1117 * regular frame number will be added to the list mRequestLastFrameNumbersList.</p> 1118 * 1119 * @param requestId the request ID of the current repeating request. 1120 * @param lastFrameNumber last frame number returned from binder. 1121 * @param repeatingRequestTypes the repeating requests' types. 1122 */ checkEarlyTriggerSequenceCompleteLocked( final int requestId, final long lastFrameNumber, final int[] repeatingRequestTypes)1123 private void checkEarlyTriggerSequenceCompleteLocked( 1124 final int requestId, final long lastFrameNumber, 1125 final int[] repeatingRequestTypes) { 1126 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request 1127 // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately. 1128 if (lastFrameNumber == CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { 1129 final CaptureCallbackHolder holder; 1130 int index = mCaptureCallbackMap.indexOfKey(requestId); 1131 holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null; 1132 if (holder != null) { 1133 mCaptureCallbackMap.removeAt(index); 1134 if (DEBUG) { 1135 Log.v(TAG, String.format( 1136 "remove holder for requestId %d, " 1137 + "because lastFrame is %d.", 1138 requestId, lastFrameNumber)); 1139 } 1140 } 1141 1142 if (holder != null) { 1143 if (DEBUG) { 1144 Log.v(TAG, "immediately trigger onCaptureSequenceAborted because" 1145 + " request did not reach HAL"); 1146 } 1147 1148 Runnable resultDispatch = new Runnable() { 1149 @Override 1150 public void run() { 1151 if (!CameraDeviceImpl.this.isClosed()) { 1152 if (DEBUG) { 1153 Log.d(TAG, String.format( 1154 "early trigger sequence complete for request %d", 1155 requestId)); 1156 } 1157 holder.getCallback().onCaptureSequenceAborted( 1158 CameraDeviceImpl.this, 1159 requestId); 1160 } 1161 } 1162 }; 1163 final long ident = Binder.clearCallingIdentity(); 1164 try { 1165 holder.getExecutor().execute(resultDispatch); 1166 } finally { 1167 Binder.restoreCallingIdentity(ident); 1168 } 1169 } else { 1170 Log.w(TAG, String.format( 1171 "did not register callback to request %d", 1172 requestId)); 1173 } 1174 } else { 1175 // This function is only called for regular/ZslStill request so lastFrameNumber is the 1176 // last regular/ZslStill frame number. 1177 mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId, 1178 lastFrameNumber, repeatingRequestTypes)); 1179 1180 // It is possible that the last frame has already arrived, so we need to check 1181 // for sequence completion right away 1182 checkAndFireSequenceComplete(); 1183 } 1184 } 1185 getRequestTypes(final CaptureRequest[] requestArray)1186 private int[] getRequestTypes(final CaptureRequest[] requestArray) { 1187 int[] requestTypes = new int[requestArray.length]; 1188 for (int i = 0; i < requestArray.length; i++) { 1189 requestTypes[i] = requestArray[i].getRequestType(); 1190 } 1191 return requestTypes; 1192 } 1193 hasBatchedOutputs(List<CaptureRequest> requestList)1194 private boolean hasBatchedOutputs(List<CaptureRequest> requestList) { 1195 boolean hasBatchedOutputs = true; 1196 for (int i = 0; i < requestList.size(); i++) { 1197 CaptureRequest request = requestList.get(i); 1198 if (!request.isPartOfCRequestList()) { 1199 hasBatchedOutputs = false; 1200 break; 1201 } 1202 if (i == 0) { 1203 Collection<Surface> targets = request.getTargets(); 1204 if (targets.size() != 2) { 1205 hasBatchedOutputs = false; 1206 break; 1207 } 1208 } 1209 } 1210 return hasBatchedOutputs; 1211 } 1212 updateTracker(int requestId, long frameNumber, int requestType, CaptureResult result, boolean isPartialResult)1213 private void updateTracker(int requestId, long frameNumber, 1214 int requestType, CaptureResult result, boolean isPartialResult) { 1215 int requestCount = 1; 1216 // If the request has batchedOutputs update each frame within the batch. 1217 if (mBatchOutputMap.containsKey(requestId)) { 1218 requestCount = mBatchOutputMap.get(requestId); 1219 for (int i = 0; i < requestCount; i++) { 1220 mFrameNumberTracker.updateTracker(frameNumber - (requestCount - 1 - i), 1221 result, isPartialResult, requestType); 1222 } 1223 } else { 1224 mFrameNumberTracker.updateTracker(frameNumber, result, 1225 isPartialResult, requestType); 1226 } 1227 } 1228 submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, Executor executor, boolean repeating)1229 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, 1230 Executor executor, boolean repeating) throws CameraAccessException { 1231 1232 // Need a valid executor, or current thread needs to have a looper, if 1233 // callback is valid 1234 executor = checkExecutor(executor, callback); 1235 1236 synchronized(mInterfaceLock) { 1237 checkIfCameraClosedOrInError(); 1238 1239 // Make sure that there all requests have at least 1 surface; all surfaces are non-null; 1240 for (CaptureRequest request : requestList) { 1241 if (request.getTargets().isEmpty()) { 1242 throw new IllegalArgumentException( 1243 "Each request must have at least one Surface target"); 1244 } 1245 1246 for (Surface surface : request.getTargets()) { 1247 if (surface == null) { 1248 throw new IllegalArgumentException("Null Surface targets are not allowed"); 1249 } 1250 } 1251 } 1252 1253 if (repeating) { 1254 stopRepeating(); 1255 } 1256 1257 SubmitInfo requestInfo; 1258 1259 CaptureRequest[] requestArray = requestList.toArray(new CaptureRequest[requestList.size()]); 1260 // Convert Surface to streamIdx and surfaceIdx 1261 for (CaptureRequest request : requestArray) { 1262 request.convertSurfaceToStreamId(mConfiguredOutputs); 1263 } 1264 1265 requestInfo = mRemoteDevice.submitRequestList(requestArray, repeating); 1266 if (DEBUG) { 1267 Log.v(TAG, "last frame number " + requestInfo.getLastFrameNumber()); 1268 } 1269 1270 for (CaptureRequest request : requestArray) { 1271 request.recoverStreamIdToSurface(); 1272 } 1273 1274 // If the request has batched outputs, then store the 1275 // requestCount and requestId in the map. 1276 boolean hasBatchedOutputs = hasBatchedOutputs(requestList); 1277 if (hasBatchedOutputs) { 1278 int requestCount = requestList.size(); 1279 mBatchOutputMap.put(requestInfo.getRequestId(), requestCount); 1280 } 1281 1282 if (callback != null) { 1283 mCaptureCallbackMap.put(requestInfo.getRequestId(), 1284 new CaptureCallbackHolder( 1285 callback, requestList, executor, repeating, mNextSessionId - 1)); 1286 } else { 1287 if (DEBUG) { 1288 Log.d(TAG, "Listen for request " + requestInfo.getRequestId() + " is null"); 1289 } 1290 } 1291 1292 if (repeating) { 1293 if (mRepeatingRequestId != REQUEST_ID_NONE) { 1294 checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId, 1295 requestInfo.getLastFrameNumber(), 1296 mRepeatingRequestTypes); 1297 } 1298 mRepeatingRequestId = requestInfo.getRequestId(); 1299 mRepeatingRequestTypes = getRequestTypes(requestArray); 1300 } else { 1301 mRequestLastFrameNumbersList.add( 1302 new RequestLastFrameNumbersHolder(requestList, requestInfo)); 1303 } 1304 1305 if (mIdle) { 1306 mDeviceExecutor.execute(mCallOnActive); 1307 } 1308 mIdle = false; 1309 1310 return requestInfo.getRequestId(); 1311 } 1312 } 1313 setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Executor executor)1314 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, 1315 Executor executor) throws CameraAccessException { 1316 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); 1317 requestList.add(request); 1318 return submitCaptureRequest(requestList, callback, executor, /*streaming*/true); 1319 } 1320 setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Executor executor)1321 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, 1322 Executor executor) throws CameraAccessException { 1323 if (requests == null || requests.isEmpty()) { 1324 throw new IllegalArgumentException("At least one request must be given"); 1325 } 1326 return submitCaptureRequest(requests, callback, executor, /*streaming*/true); 1327 } 1328 stopRepeating()1329 public void stopRepeating() throws CameraAccessException { 1330 1331 synchronized(mInterfaceLock) { 1332 checkIfCameraClosedOrInError(); 1333 if (mRepeatingRequestId != REQUEST_ID_NONE) { 1334 1335 int requestId = mRepeatingRequestId; 1336 mRepeatingRequestId = REQUEST_ID_NONE; 1337 mFailedRepeatingRequestId = REQUEST_ID_NONE; 1338 int[] requestTypes = mRepeatingRequestTypes; 1339 mRepeatingRequestTypes = null; 1340 mFailedRepeatingRequestTypes = null; 1341 1342 long lastFrameNumber; 1343 try { 1344 lastFrameNumber = mRemoteDevice.cancelRequest(requestId); 1345 } catch (IllegalArgumentException e) { 1346 if (DEBUG) { 1347 Log.v(TAG, "Repeating request was already stopped for request " + 1348 requestId); 1349 } 1350 // Cache request id and request types in case of a race with 1351 // "onRepeatingRequestError" which may no yet be scheduled on another thread 1352 // or blocked by us. 1353 mFailedRepeatingRequestId = requestId; 1354 mFailedRepeatingRequestTypes = requestTypes; 1355 1356 // Repeating request was already stopped. Nothing more to do. 1357 return; 1358 } 1359 1360 checkEarlyTriggerSequenceCompleteLocked(requestId, lastFrameNumber, requestTypes); 1361 } 1362 } 1363 } 1364 waitUntilIdle()1365 private void waitUntilIdle() throws CameraAccessException { 1366 1367 synchronized(mInterfaceLock) { 1368 checkIfCameraClosedOrInError(); 1369 1370 if (mRepeatingRequestId != REQUEST_ID_NONE) { 1371 throw new IllegalStateException("Active repeating request ongoing"); 1372 } 1373 1374 mRemoteDevice.waitUntilIdle(); 1375 } 1376 } 1377 flush()1378 public void flush() throws CameraAccessException { 1379 synchronized(mInterfaceLock) { 1380 checkIfCameraClosedOrInError(); 1381 1382 mDeviceExecutor.execute(mCallOnBusy); 1383 1384 // If already idle, just do a busy->idle transition immediately, don't actually 1385 // flush. 1386 if (mIdle) { 1387 mDeviceExecutor.execute(mCallOnIdle); 1388 return; 1389 } 1390 1391 long lastFrameNumber = mRemoteDevice.flush(); 1392 if (mRepeatingRequestId != REQUEST_ID_NONE) { 1393 checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId, lastFrameNumber, 1394 mRepeatingRequestTypes); 1395 mRepeatingRequestId = REQUEST_ID_NONE; 1396 mRepeatingRequestTypes = null; 1397 } 1398 } 1399 } 1400 1401 @Override close()1402 public void close() { 1403 synchronized (mInterfaceLock) { 1404 if (mClosing.getAndSet(true)) { 1405 return; 1406 } 1407 1408 if (mOfflineSwitchService != null) { 1409 mOfflineSwitchService.shutdownNow(); 1410 mOfflineSwitchService = null; 1411 } 1412 1413 if (mRemoteDevice != null) { 1414 mRemoteDevice.disconnect(); 1415 mRemoteDevice.unlinkToDeath(this, /*flags*/0); 1416 } 1417 1418 if (mCurrentExtensionSession != null) { 1419 mCurrentExtensionSession.release(true /*skipCloseNotification*/); 1420 mCurrentExtensionSession = null; 1421 } 1422 1423 if (mCurrentAdvancedExtensionSession != null) { 1424 mCurrentAdvancedExtensionSession.release(true /*skipCloseNotification*/); 1425 mCurrentAdvancedExtensionSession = null; 1426 } 1427 1428 // Only want to fire the onClosed callback once; 1429 // either a normal close where the remote device is valid 1430 // or a close after a startup error (no remote device but in error state) 1431 if (mRemoteDevice != null || mInError) { 1432 mDeviceExecutor.execute(mCallOnClosed); 1433 } 1434 1435 mRemoteDevice = null; 1436 } 1437 } 1438 1439 @Override finalize()1440 protected void finalize() throws Throwable { 1441 try { 1442 close(); 1443 } 1444 finally { 1445 super.finalize(); 1446 } 1447 } 1448 checkInputConfigurationWithStreamConfigurationsAs( InputConfiguration inputConfig, StreamConfigurationMap configMap)1449 private boolean checkInputConfigurationWithStreamConfigurationsAs( 1450 InputConfiguration inputConfig, StreamConfigurationMap configMap) { 1451 int[] inputFormats = configMap.getInputFormats(); 1452 boolean validFormat = false; 1453 int inputFormat = inputConfig.getFormat(); 1454 for (int format : inputFormats) { 1455 if (format == inputFormat) { 1456 validFormat = true; 1457 } 1458 } 1459 1460 if (validFormat == false) { 1461 return false; 1462 } 1463 1464 boolean validSize = false; 1465 Size[] inputSizes = configMap.getInputSizes(inputFormat); 1466 for (Size s : inputSizes) { 1467 if (inputConfig.getWidth() == s.getWidth() && 1468 inputConfig.getHeight() == s.getHeight()) { 1469 validSize = true; 1470 } 1471 } 1472 1473 if (validSize == false) { 1474 return false; 1475 } 1476 return true; 1477 } 1478 checkInputConfigurationWithStreamConfigurations( InputConfiguration inputConfig, boolean maxResolution)1479 private boolean checkInputConfigurationWithStreamConfigurations( 1480 InputConfiguration inputConfig, boolean maxResolution) { 1481 // Check if either this logical camera or any of its physical cameras support the 1482 // input config. If they do, the input config is valid. 1483 CameraCharacteristics.Key<StreamConfigurationMap> ck = 1484 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP; 1485 1486 if (maxResolution) { 1487 ck = CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION; 1488 } 1489 1490 StreamConfigurationMap configMap = mCharacteristics.get(ck); 1491 1492 if (configMap != null && 1493 checkInputConfigurationWithStreamConfigurationsAs(inputConfig, configMap)) { 1494 return true; 1495 } 1496 1497 for (Map.Entry<String, CameraCharacteristics> entry : mPhysicalIdsToChars.entrySet()) { 1498 configMap = entry.getValue().get(ck); 1499 1500 if (configMap != null && 1501 checkInputConfigurationWithStreamConfigurationsAs(inputConfig, configMap)) { 1502 // Input config supported. 1503 return true; 1504 } 1505 } 1506 return false; 1507 } 1508 checkInputConfiguration(InputConfiguration inputConfig)1509 private void checkInputConfiguration(InputConfiguration inputConfig) { 1510 if (inputConfig == null) { 1511 return; 1512 } 1513 int inputFormat = inputConfig.getFormat(); 1514 if (inputConfig.isMultiResolution()) { 1515 MultiResolutionStreamConfigurationMap configMap = mCharacteristics.get( 1516 CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP); 1517 1518 int[] inputFormats = configMap.getInputFormats(); 1519 boolean validFormat = false; 1520 for (int format : inputFormats) { 1521 if (format == inputFormat) { 1522 validFormat = true; 1523 } 1524 } 1525 1526 if (validFormat == false) { 1527 throw new IllegalArgumentException("multi-resolution input format " + 1528 inputFormat + " is not valid"); 1529 } 1530 1531 boolean validSize = false; 1532 Collection<MultiResolutionStreamInfo> inputStreamInfo = 1533 configMap.getInputInfo(inputFormat); 1534 for (MultiResolutionStreamInfo info : inputStreamInfo) { 1535 if (inputConfig.getWidth() == info.getWidth() && 1536 inputConfig.getHeight() == info.getHeight()) { 1537 validSize = true; 1538 } 1539 } 1540 1541 if (validSize == false) { 1542 throw new IllegalArgumentException("Multi-resolution input size " + 1543 inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid"); 1544 } 1545 } else { 1546 if (!checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/false) && 1547 !checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/true)) { 1548 throw new IllegalArgumentException("Input config with format " + 1549 inputFormat + " and size " + inputConfig.getWidth() + "x" + 1550 inputConfig.getHeight() + " not supported by camera id " + mCameraId); 1551 } 1552 } 1553 } 1554 1555 /** 1556 * A callback for notifications about the state of a camera device, adding in the callbacks that 1557 * were part of the earlier KK API design, but now only used internally. 1558 */ 1559 public static abstract class StateCallbackKK extends StateCallback { 1560 /** 1561 * The method called when a camera device has no outputs configured. 1562 * 1563 */ onUnconfigured(CameraDevice camera)1564 public void onUnconfigured(CameraDevice camera) { 1565 // Default empty implementation 1566 } 1567 1568 /** 1569 * The method called when a camera device begins processing 1570 * {@link CaptureRequest capture requests}. 1571 * 1572 */ onActive(CameraDevice camera)1573 public void onActive(CameraDevice camera) { 1574 // Default empty implementation 1575 } 1576 1577 /** 1578 * The method called when a camera device is busy. 1579 * 1580 */ onBusy(CameraDevice camera)1581 public void onBusy(CameraDevice camera) { 1582 // Default empty implementation 1583 } 1584 1585 /** 1586 * The method called when a camera device has finished processing all 1587 * submitted capture requests and has reached an idle state. 1588 * 1589 */ onIdle(CameraDevice camera)1590 public void onIdle(CameraDevice camera) { 1591 // Default empty implementation 1592 } 1593 1594 /** 1595 * This method is called when camera device's non-repeating request queue is empty, 1596 * and is ready to start capturing next image. 1597 */ onRequestQueueEmpty()1598 public void onRequestQueueEmpty() { 1599 // Default empty implementation 1600 } 1601 1602 /** 1603 * The method called when the camera device has finished preparing 1604 * an output Surface 1605 */ onSurfacePrepared(Surface surface)1606 public void onSurfacePrepared(Surface surface) { 1607 // Default empty implementation 1608 } 1609 } 1610 checkAndFireSequenceComplete()1611 private void checkAndFireSequenceComplete() { 1612 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); 1613 long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); 1614 long completedZslStillFrameNumber = mFrameNumberTracker.getCompletedZslStillFrameNumber(); 1615 1616 Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator(); 1617 while (iter.hasNext()) { 1618 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); 1619 final int requestId = requestLastFrameNumbers.getRequestId(); 1620 final CaptureCallbackHolder holder; 1621 if (mRemoteDevice == null) { 1622 Log.w(TAG, "Camera closed while checking sequences"); 1623 return; 1624 } 1625 if (!requestLastFrameNumbers.isSequenceCompleted()) { 1626 long lastRegularFrameNumber = 1627 requestLastFrameNumbers.getLastRegularFrameNumber(); 1628 long lastReprocessFrameNumber = 1629 requestLastFrameNumbers.getLastReprocessFrameNumber(); 1630 long lastZslStillFrameNumber = 1631 requestLastFrameNumbers.getLastZslStillFrameNumber(); 1632 if (lastRegularFrameNumber <= completedFrameNumber 1633 && lastReprocessFrameNumber <= completedReprocessFrameNumber 1634 && lastZslStillFrameNumber <= completedZslStillFrameNumber) { 1635 if (DEBUG) { 1636 Log.v(TAG, String.format( 1637 "Mark requestId %d as completed, because lastRegularFrame %d " 1638 + "is <= %d, lastReprocessFrame %d is <= %d, " 1639 + "lastZslStillFrame %d is <= %d", requestId, 1640 lastRegularFrameNumber, completedFrameNumber, 1641 lastReprocessFrameNumber, completedReprocessFrameNumber, 1642 lastZslStillFrameNumber, completedZslStillFrameNumber)); 1643 } 1644 requestLastFrameNumbers.markSequenceCompleted(); 1645 } 1646 1647 // Call onCaptureSequenceCompleted 1648 int index = mCaptureCallbackMap.indexOfKey(requestId); 1649 holder = (index >= 0) ? 1650 mCaptureCallbackMap.valueAt(index) : null; 1651 if (holder != null && requestLastFrameNumbers.isSequenceCompleted()) { 1652 Runnable resultDispatch = new Runnable() { 1653 @Override 1654 public void run() { 1655 if (!CameraDeviceImpl.this.isClosed()){ 1656 if (DEBUG) { 1657 Log.d(TAG, String.format( 1658 "fire sequence complete for request %d", 1659 requestId)); 1660 } 1661 1662 holder.getCallback().onCaptureSequenceCompleted( 1663 CameraDeviceImpl.this, 1664 requestId, 1665 requestLastFrameNumbers.getLastFrameNumber()); 1666 } 1667 } 1668 }; 1669 final long ident = Binder.clearCallingIdentity(); 1670 try { 1671 holder.getExecutor().execute(resultDispatch); 1672 } finally { 1673 Binder.restoreCallingIdentity(ident); 1674 } 1675 } 1676 } 1677 1678 if (requestLastFrameNumbers.isSequenceCompleted() && 1679 requestLastFrameNumbers.isInflightCompleted()) { 1680 int index = mCaptureCallbackMap.indexOfKey(requestId); 1681 if (index >= 0) { 1682 mCaptureCallbackMap.removeAt(index); 1683 } 1684 if (DEBUG) { 1685 Log.v(TAG, String.format( 1686 "Remove holder for requestId %d", requestId)); 1687 } 1688 iter.remove(); 1689 } 1690 } 1691 } 1692 removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber, long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber)1693 private void removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber, 1694 long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber) { 1695 if (DEBUG) { 1696 Log.v(TAG, String.format("remove completed callback holders for " 1697 + "lastCompletedRegularFrameNumber %d, " 1698 + "lastCompletedReprocessFrameNumber %d, " 1699 + "lastCompletedZslStillFrameNumber %d", 1700 lastCompletedRegularFrameNumber, 1701 lastCompletedReprocessFrameNumber, 1702 lastCompletedZslStillFrameNumber)); 1703 } 1704 1705 Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator(); 1706 while (iter.hasNext()) { 1707 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); 1708 final int requestId = requestLastFrameNumbers.getRequestId(); 1709 final CaptureCallbackHolder holder; 1710 if (mRemoteDevice == null) { 1711 Log.w(TAG, "Camera closed while removing completed callback holders"); 1712 return; 1713 } 1714 1715 long lastRegularFrameNumber = 1716 requestLastFrameNumbers.getLastRegularFrameNumber(); 1717 long lastReprocessFrameNumber = 1718 requestLastFrameNumbers.getLastReprocessFrameNumber(); 1719 long lastZslStillFrameNumber = 1720 requestLastFrameNumbers.getLastZslStillFrameNumber(); 1721 1722 if (lastRegularFrameNumber <= lastCompletedRegularFrameNumber 1723 && lastReprocessFrameNumber <= lastCompletedReprocessFrameNumber 1724 && lastZslStillFrameNumber <= lastCompletedZslStillFrameNumber) { 1725 1726 if (requestLastFrameNumbers.isSequenceCompleted()) { 1727 int index = mCaptureCallbackMap.indexOfKey(requestId); 1728 if (index >= 0) { 1729 mCaptureCallbackMap.removeAt(index); 1730 } 1731 if (DEBUG) { 1732 Log.v(TAG, String.format( 1733 "Remove holder for requestId %d, because lastRegularFrame %d " 1734 + "is <= %d, lastReprocessFrame %d is <= %d, " 1735 + "lastZslStillFrame %d is <= %d", requestId, 1736 lastRegularFrameNumber, lastCompletedRegularFrameNumber, 1737 lastReprocessFrameNumber, lastCompletedReprocessFrameNumber, 1738 lastZslStillFrameNumber, lastCompletedZslStillFrameNumber)); 1739 } 1740 iter.remove(); 1741 } else { 1742 if (DEBUG) { 1743 Log.v(TAG, "Sequence not yet completed for request id " + requestId); 1744 } 1745 requestLastFrameNumbers.markInflightCompleted(); 1746 } 1747 } 1748 } 1749 } 1750 onDeviceError(final int errorCode, CaptureResultExtras resultExtras)1751 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { 1752 if (DEBUG) { 1753 Log.d(TAG, String.format( 1754 "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", 1755 errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), 1756 resultExtras.getSubsequenceId())); 1757 } 1758 1759 synchronized(mInterfaceLock) { 1760 if (mRemoteDevice == null && mRemoteDeviceInit) { 1761 return; // Camera already closed, user is not interested in errors anymore. 1762 } 1763 1764 // Redirect device callback to the offline session in case we are in the middle 1765 // of an offline switch 1766 if (mOfflineSessionImpl != null) { 1767 mOfflineSessionImpl.getCallbacks().onDeviceError(errorCode, resultExtras); 1768 return; 1769 } 1770 1771 switch (errorCode) { 1772 case CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED: { 1773 final long ident = Binder.clearCallingIdentity(); 1774 try { 1775 mDeviceExecutor.execute(mCallOnDisconnected); 1776 } finally { 1777 Binder.restoreCallingIdentity(ident); 1778 } 1779 break; 1780 } 1781 case CameraDeviceCallbacks.ERROR_CAMERA_REQUEST: 1782 case CameraDeviceCallbacks.ERROR_CAMERA_RESULT: 1783 case CameraDeviceCallbacks.ERROR_CAMERA_BUFFER: 1784 onCaptureErrorLocked(errorCode, resultExtras); 1785 break; 1786 case CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: 1787 scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE); 1788 break; 1789 case CameraDeviceCallbacks.ERROR_CAMERA_DISABLED: 1790 scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED); 1791 break; 1792 default: 1793 Log.e(TAG, "Unknown error from camera device: " + errorCode); 1794 scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE); 1795 } 1796 } 1797 } 1798 scheduleNotifyError(int code)1799 private void scheduleNotifyError(int code) { 1800 mInError = true; 1801 final long ident = Binder.clearCallingIdentity(); 1802 try { 1803 mDeviceExecutor.execute(obtainRunnable( 1804 CameraDeviceImpl::notifyError, this, code).recycleOnUse()); 1805 } finally { 1806 Binder.restoreCallingIdentity(ident); 1807 } 1808 } 1809 notifyError(int code)1810 private void notifyError(int code) { 1811 if (!CameraDeviceImpl.this.isClosed()) { 1812 mDeviceCallback.onError(CameraDeviceImpl.this, code); 1813 } 1814 } 1815 1816 /** 1817 * Called by onDeviceError for handling single-capture failures. 1818 */ onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras)1819 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { 1820 1821 final int requestId = resultExtras.getRequestId(); 1822 final int subsequenceId = resultExtras.getSubsequenceId(); 1823 final long frameNumber = resultExtras.getFrameNumber(); 1824 final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId(); 1825 final CaptureCallbackHolder holder = mCaptureCallbackMap.get(requestId); 1826 1827 if (holder == null) { 1828 Log.e(TAG, String.format("Receive capture error on unknown request ID %d", 1829 requestId)); 1830 return; 1831 } 1832 1833 final CaptureRequest request = holder.getRequest(subsequenceId); 1834 1835 Runnable failureDispatch = null; 1836 if (errorCode == CameraDeviceCallbacks.ERROR_CAMERA_BUFFER) { 1837 // Because 1 stream id could map to multiple surfaces, we need to specify both 1838 // streamId and surfaceId. 1839 OutputConfiguration config = mConfiguredOutputs.get( 1840 resultExtras.getErrorStreamId()); 1841 if (config == null) { 1842 Log.v(TAG, String.format( 1843 "Stream %d has been removed. Skipping buffer lost callback", 1844 resultExtras.getErrorStreamId())); 1845 return; 1846 } 1847 for (Surface surface : config.getSurfaces()) { 1848 if (!request.containsTarget(surface)) { 1849 continue; 1850 } 1851 if (DEBUG) { 1852 Log.v(TAG, String.format( 1853 "Lost output buffer reported for frame %d, target %s", 1854 frameNumber, surface)); 1855 } 1856 failureDispatch = new Runnable() { 1857 @Override 1858 public void run() { 1859 if (!isClosed()){ 1860 holder.getCallback().onCaptureBufferLost(CameraDeviceImpl.this, request, 1861 surface, frameNumber); 1862 } 1863 } 1864 }; 1865 // Dispatch the failure callback 1866 final long ident = Binder.clearCallingIdentity(); 1867 try { 1868 holder.getExecutor().execute(failureDispatch); 1869 } finally { 1870 Binder.restoreCallingIdentity(ident); 1871 } 1872 } 1873 } else { 1874 boolean mayHaveBuffers = (errorCode == CameraDeviceCallbacks.ERROR_CAMERA_RESULT); 1875 1876 // This is only approximate - exact handling needs the camera service and HAL to 1877 // disambiguate between request failures to due abort and due to real errors. For 1878 // now, assume that if the session believes we're mid-abort, then the error is due 1879 // to abort. 1880 int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? 1881 CaptureFailure.REASON_FLUSHED : 1882 CaptureFailure.REASON_ERROR; 1883 1884 final CaptureFailure failure = new CaptureFailure( 1885 request, 1886 reason, 1887 mayHaveBuffers, 1888 requestId, 1889 frameNumber, 1890 errorPhysicalCameraId); 1891 1892 failureDispatch = new Runnable() { 1893 @Override 1894 public void run() { 1895 if (!isClosed()){ 1896 holder.getCallback().onCaptureFailed(CameraDeviceImpl.this, request, 1897 failure); 1898 } 1899 } 1900 }; 1901 1902 // Fire onCaptureSequenceCompleted if appropriate 1903 if (DEBUG) { 1904 Log.v(TAG, String.format("got error frame %d", frameNumber)); 1905 } 1906 1907 // Update FrameNumberTracker for every frame during HFR mode. 1908 if (mBatchOutputMap.containsKey(requestId)) { 1909 for (int i = 0; i < mBatchOutputMap.get(requestId); i++) { 1910 mFrameNumberTracker.updateTracker(frameNumber - (subsequenceId - i), 1911 /*error*/true, request.getRequestType()); 1912 } 1913 } else { 1914 mFrameNumberTracker.updateTracker(frameNumber, 1915 /*error*/true, request.getRequestType()); 1916 } 1917 1918 checkAndFireSequenceComplete(); 1919 1920 // Dispatch the failure callback 1921 final long ident = Binder.clearCallingIdentity(); 1922 try { 1923 holder.getExecutor().execute(failureDispatch); 1924 } finally { 1925 Binder.restoreCallingIdentity(ident); 1926 } 1927 } 1928 1929 } 1930 onDeviceIdle()1931 public void onDeviceIdle() { 1932 if (DEBUG) { 1933 Log.d(TAG, "Camera now idle"); 1934 } 1935 synchronized(mInterfaceLock) { 1936 if (mRemoteDevice == null) return; // Camera already closed 1937 1938 // Redirect device callback to the offline session in case we are in the middle 1939 // of an offline switch 1940 if (mOfflineSessionImpl != null) { 1941 mOfflineSessionImpl.getCallbacks().onDeviceIdle(); 1942 return; 1943 } 1944 1945 // Remove all capture callbacks now that device has gone to IDLE state. 1946 removeCompletedCallbackHolderLocked( 1947 Long.MAX_VALUE, /*lastCompletedRegularFrameNumber*/ 1948 Long.MAX_VALUE, /*lastCompletedReprocessFrameNumber*/ 1949 Long.MAX_VALUE /*lastCompletedZslStillFrameNumber*/); 1950 1951 if (!CameraDeviceImpl.this.mIdle) { 1952 final long ident = Binder.clearCallingIdentity(); 1953 try { 1954 mDeviceExecutor.execute(mCallOnIdle); 1955 } finally { 1956 Binder.restoreCallingIdentity(ident); 1957 } 1958 } 1959 mIdle = true; 1960 } 1961 } 1962 1963 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 1964 1965 @Override asBinder()1966 public IBinder asBinder() { 1967 return this; 1968 } 1969 1970 @Override onDeviceError(final int errorCode, CaptureResultExtras resultExtras)1971 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { 1972 CameraDeviceImpl.this.onDeviceError(errorCode, resultExtras); 1973 } 1974 1975 @Override onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId)1976 public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) { 1977 if (DEBUG) { 1978 Log.d(TAG, "Repeating request error received. Last frame number is " + 1979 lastFrameNumber); 1980 } 1981 1982 synchronized(mInterfaceLock) { 1983 // Camera is already closed or no repeating request is present. 1984 if (mRemoteDevice == null || mRepeatingRequestId == REQUEST_ID_NONE) { 1985 if ((mFailedRepeatingRequestId == repeatingRequestId) && 1986 (mFailedRepeatingRequestTypes != null) && (mRemoteDevice != null)) { 1987 Log.v(TAG, "Resuming stop of failed repeating request with id: " + 1988 mFailedRepeatingRequestId); 1989 1990 checkEarlyTriggerSequenceCompleteLocked(mFailedRepeatingRequestId, 1991 lastFrameNumber, mFailedRepeatingRequestTypes); 1992 mFailedRepeatingRequestId = REQUEST_ID_NONE; 1993 mFailedRepeatingRequestTypes = null; 1994 } 1995 return; 1996 } 1997 1998 // Redirect device callback to the offline session in case we are in the middle 1999 // of an offline switch 2000 if (mOfflineSessionImpl != null) { 2001 mOfflineSessionImpl.getCallbacks().onRepeatingRequestError( 2002 lastFrameNumber, repeatingRequestId); 2003 return; 2004 } 2005 2006 checkEarlyTriggerSequenceCompleteLocked(mRepeatingRequestId, lastFrameNumber, 2007 mRepeatingRequestTypes); 2008 // Check if there is already a new repeating request 2009 if (mRepeatingRequestId == repeatingRequestId) { 2010 mRepeatingRequestId = REQUEST_ID_NONE; 2011 mRepeatingRequestTypes = null; 2012 } 2013 } 2014 } 2015 2016 @Override onDeviceIdle()2017 public void onDeviceIdle() { 2018 CameraDeviceImpl.this.onDeviceIdle(); 2019 } 2020 2021 @Override onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)2022 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 2023 int requestId = resultExtras.getRequestId(); 2024 final long frameNumber = resultExtras.getFrameNumber(); 2025 final long lastCompletedRegularFrameNumber = 2026 resultExtras.getLastCompletedRegularFrameNumber(); 2027 final long lastCompletedReprocessFrameNumber = 2028 resultExtras.getLastCompletedReprocessFrameNumber(); 2029 final long lastCompletedZslFrameNumber = 2030 resultExtras.getLastCompletedZslFrameNumber(); 2031 final boolean hasReadoutTimestamp = resultExtras.hasReadoutTimestamp(); 2032 final long readoutTimestamp = resultExtras.getReadoutTimestamp(); 2033 2034 if (DEBUG) { 2035 Log.d(TAG, "Capture started for id " + requestId + " frame number " + frameNumber 2036 + ": completedRegularFrameNumber " + lastCompletedRegularFrameNumber 2037 + ", completedReprocessFrameNUmber " + lastCompletedReprocessFrameNumber 2038 + ", completedZslFrameNumber " + lastCompletedZslFrameNumber 2039 + ", hasReadoutTimestamp " + hasReadoutTimestamp 2040 + (hasReadoutTimestamp ? ", readoutTimestamp " + readoutTimestamp : "")) ; 2041 } 2042 final CaptureCallbackHolder holder; 2043 2044 synchronized(mInterfaceLock) { 2045 if (mRemoteDevice == null) return; // Camera already closed 2046 2047 2048 // Redirect device callback to the offline session in case we are in the middle 2049 // of an offline switch 2050 if (mOfflineSessionImpl != null) { 2051 mOfflineSessionImpl.getCallbacks().onCaptureStarted(resultExtras, 2052 timestamp); 2053 return; 2054 } 2055 2056 // Check if it's okay to remove completed callbacks from mCaptureCallbackMap. 2057 // A callback is completed if the corresponding inflight request has been removed 2058 // from the inflight queue in cameraservice. 2059 removeCompletedCallbackHolderLocked(lastCompletedRegularFrameNumber, 2060 lastCompletedReprocessFrameNumber, lastCompletedZslFrameNumber); 2061 2062 // Get the callback for this frame ID, if there is one 2063 holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 2064 2065 if (holder == null) { 2066 return; 2067 } 2068 2069 if (isClosed()) return; 2070 2071 // Dispatch capture start notice 2072 final long ident = Binder.clearCallingIdentity(); 2073 try { 2074 holder.getExecutor().execute( 2075 new Runnable() { 2076 @Override 2077 public void run() { 2078 if (!CameraDeviceImpl.this.isClosed()) { 2079 final int subsequenceId = resultExtras.getSubsequenceId(); 2080 final CaptureRequest request = holder.getRequest(subsequenceId); 2081 2082 if (holder.hasBatchedOutputs()) { 2083 // Send derived onCaptureStarted for requests within the 2084 // batch 2085 final Range<Integer> fpsRange = 2086 request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); 2087 for (int i = 0; i < holder.getRequestCount(); i++) { 2088 holder.getCallback().onCaptureStarted( 2089 CameraDeviceImpl.this, 2090 holder.getRequest(i), 2091 timestamp - (subsequenceId - i) * 2092 NANO_PER_SECOND / fpsRange.getUpper(), 2093 frameNumber - (subsequenceId - i)); 2094 if (hasReadoutTimestamp) { 2095 holder.getCallback().onReadoutStarted( 2096 CameraDeviceImpl.this, 2097 holder.getRequest(i), 2098 readoutTimestamp - (subsequenceId - i) * 2099 NANO_PER_SECOND / fpsRange.getUpper(), 2100 frameNumber - (subsequenceId - i)); 2101 } 2102 } 2103 } else { 2104 holder.getCallback().onCaptureStarted( 2105 CameraDeviceImpl.this, request, 2106 timestamp, frameNumber); 2107 if (hasReadoutTimestamp) { 2108 holder.getCallback().onReadoutStarted( 2109 CameraDeviceImpl.this, request, 2110 readoutTimestamp, frameNumber); 2111 } 2112 } 2113 } 2114 } 2115 }); 2116 } finally { 2117 Binder.restoreCallingIdentity(ident); 2118 } 2119 } 2120 } 2121 2122 @Override onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])2123 public void onResultReceived(CameraMetadataNative result, 2124 CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[]) 2125 throws RemoteException { 2126 int requestId = resultExtras.getRequestId(); 2127 long frameNumber = resultExtras.getFrameNumber(); 2128 2129 if (DEBUG) { 2130 Log.v(TAG, "Received result frame " + frameNumber + " for id " 2131 + requestId); 2132 } 2133 2134 synchronized(mInterfaceLock) { 2135 if (mRemoteDevice == null) return; // Camera already closed 2136 2137 2138 // Redirect device callback to the offline session in case we are in the middle 2139 // of an offline switch 2140 if (mOfflineSessionImpl != null) { 2141 mOfflineSessionImpl.getCallbacks().onResultReceived(result, resultExtras, 2142 physicalResults); 2143 return; 2144 } 2145 2146 // TODO: Handle CameraCharacteristics access from CaptureResult correctly. 2147 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, 2148 getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); 2149 2150 final CaptureCallbackHolder holder = 2151 CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); 2152 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); 2153 2154 boolean isPartialResult = 2155 (resultExtras.getPartialResultCount() < mTotalPartialCount); 2156 int requestType = request.getRequestType(); 2157 2158 // Check if we have a callback for this 2159 if (holder == null) { 2160 if (DEBUG) { 2161 Log.d(TAG, 2162 "holder is null, early return at frame " 2163 + frameNumber); 2164 } 2165 2166 updateTracker(requestId, frameNumber, requestType, /*result*/null, 2167 isPartialResult); 2168 2169 return; 2170 } 2171 2172 if (isClosed()) { 2173 if (DEBUG) { 2174 Log.d(TAG, 2175 "camera is closed, early return at frame " 2176 + frameNumber); 2177 } 2178 2179 updateTracker(requestId, frameNumber, requestType, /*result*/null, 2180 isPartialResult); 2181 2182 return; 2183 } 2184 2185 2186 Runnable resultDispatch = null; 2187 2188 CaptureResult finalResult; 2189 // Make a copy of the native metadata before it gets moved to a CaptureResult 2190 // object. 2191 final CameraMetadataNative resultCopy; 2192 if (holder.hasBatchedOutputs()) { 2193 resultCopy = new CameraMetadataNative(result); 2194 } else { 2195 resultCopy = null; 2196 } 2197 2198 // Either send a partial result or the final capture completed result 2199 if (isPartialResult) { 2200 final CaptureResult resultAsCapture = 2201 new CaptureResult(getId(), result, request, resultExtras); 2202 // Partial result 2203 resultDispatch = new Runnable() { 2204 @Override 2205 public void run() { 2206 if (!CameraDeviceImpl.this.isClosed()) { 2207 if (holder.hasBatchedOutputs()) { 2208 // Send derived onCaptureProgressed for requests within 2209 // the batch. 2210 for (int i = 0; i < holder.getRequestCount(); i++) { 2211 CameraMetadataNative resultLocal = 2212 new CameraMetadataNative(resultCopy); 2213 CaptureResult resultInBatch = new CaptureResult(getId(), 2214 resultLocal, holder.getRequest(i), resultExtras); 2215 2216 holder.getCallback().onCaptureProgressed( 2217 CameraDeviceImpl.this, 2218 holder.getRequest(i), 2219 resultInBatch); 2220 } 2221 } else { 2222 holder.getCallback().onCaptureProgressed( 2223 CameraDeviceImpl.this, 2224 request, 2225 resultAsCapture); 2226 } 2227 } 2228 } 2229 }; 2230 finalResult = resultAsCapture; 2231 } else { 2232 List<CaptureResult> partialResults = 2233 mFrameNumberTracker.popPartialResults(frameNumber); 2234 2235 final long sensorTimestamp = 2236 result.get(CaptureResult.SENSOR_TIMESTAMP); 2237 final Range<Integer> fpsRange = 2238 request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); 2239 final int subsequenceId = resultExtras.getSubsequenceId(); 2240 final TotalCaptureResult resultAsCapture = new TotalCaptureResult(getId(), 2241 result, request, resultExtras, partialResults, holder.getSessionId(), 2242 physicalResults); 2243 // Final capture result 2244 resultDispatch = new Runnable() { 2245 @Override 2246 public void run() { 2247 if (!CameraDeviceImpl.this.isClosed()){ 2248 if (holder.hasBatchedOutputs()) { 2249 // Send derived onCaptureCompleted for requests within 2250 // the batch. 2251 for (int i = 0; i < holder.getRequestCount(); i++) { 2252 resultCopy.set(CaptureResult.SENSOR_TIMESTAMP, 2253 sensorTimestamp - (subsequenceId - i) * 2254 NANO_PER_SECOND/fpsRange.getUpper()); 2255 CameraMetadataNative resultLocal = 2256 new CameraMetadataNative(resultCopy); 2257 // No logical multi-camera support for batched output mode. 2258 TotalCaptureResult resultInBatch = new TotalCaptureResult( 2259 getId(), resultLocal, holder.getRequest(i), 2260 resultExtras, partialResults, holder.getSessionId(), 2261 new PhysicalCaptureResultInfo[0]); 2262 2263 holder.getCallback().onCaptureCompleted( 2264 CameraDeviceImpl.this, 2265 holder.getRequest(i), 2266 resultInBatch); 2267 } 2268 } else { 2269 holder.getCallback().onCaptureCompleted( 2270 CameraDeviceImpl.this, 2271 request, 2272 resultAsCapture); 2273 } 2274 } 2275 } 2276 }; 2277 finalResult = resultAsCapture; 2278 } 2279 2280 final long ident = Binder.clearCallingIdentity(); 2281 try { 2282 holder.getExecutor().execute(resultDispatch); 2283 } finally { 2284 Binder.restoreCallingIdentity(ident); 2285 } 2286 2287 updateTracker(requestId, frameNumber, requestType, finalResult, isPartialResult); 2288 2289 // Fire onCaptureSequenceCompleted 2290 if (!isPartialResult) { 2291 checkAndFireSequenceComplete(); 2292 } 2293 } 2294 } 2295 2296 @Override onPrepared(int streamId)2297 public void onPrepared(int streamId) { 2298 final OutputConfiguration output; 2299 final StateCallbackKK sessionCallback; 2300 2301 if (DEBUG) { 2302 Log.v(TAG, "Stream " + streamId + " is prepared"); 2303 } 2304 2305 synchronized(mInterfaceLock) { 2306 // Redirect device callback to the offline session in case we are in the middle 2307 // of an offline switch 2308 if (mOfflineSessionImpl != null) { 2309 mOfflineSessionImpl.getCallbacks().onPrepared(streamId); 2310 return; 2311 } 2312 2313 output = mConfiguredOutputs.get(streamId); 2314 sessionCallback = mSessionStateCallback; 2315 } 2316 2317 if (sessionCallback == null) return; 2318 2319 if (output == null) { 2320 Log.w(TAG, "onPrepared invoked for unknown output Surface"); 2321 return; 2322 } 2323 final List<Surface> surfaces = output.getSurfaces(); 2324 for (Surface surface : surfaces) { 2325 sessionCallback.onSurfacePrepared(surface); 2326 } 2327 } 2328 2329 @Override onRequestQueueEmpty()2330 public void onRequestQueueEmpty() { 2331 final StateCallbackKK sessionCallback; 2332 2333 if (DEBUG) { 2334 Log.v(TAG, "Request queue becomes empty"); 2335 } 2336 2337 synchronized(mInterfaceLock) { 2338 // Redirect device callback to the offline session in case we are in the middle 2339 // of an offline switch 2340 if (mOfflineSessionImpl != null) { 2341 mOfflineSessionImpl.getCallbacks().onRequestQueueEmpty(); 2342 return; 2343 } 2344 2345 sessionCallback = mSessionStateCallback; 2346 } 2347 2348 if (sessionCallback == null) return; 2349 2350 sessionCallback.onRequestQueueEmpty(); 2351 } 2352 2353 } // public class CameraDeviceCallbacks 2354 2355 /** 2356 * A camera specific adapter {@link Executor} that posts all executed tasks onto the given 2357 * {@link Handler}. 2358 * 2359 * @hide 2360 */ 2361 private static class CameraHandlerExecutor implements Executor { 2362 private final Handler mHandler; 2363 CameraHandlerExecutor(@onNull Handler handler)2364 public CameraHandlerExecutor(@NonNull Handler handler) { 2365 mHandler = Objects.requireNonNull(handler); 2366 } 2367 2368 @Override execute(Runnable command)2369 public void execute(Runnable command) { 2370 // Return value of 'post()' will be ignored in order to keep the 2371 // same camera behavior. For further details see b/74605221 . 2372 mHandler.post(command); 2373 } 2374 } 2375 2376 /** 2377 * Default executor management. 2378 * 2379 * <p> 2380 * If executor is null, get the current thread's 2381 * Looper to create a Executor with. If no looper exists, throw 2382 * {@code IllegalArgumentException}. 2383 * </p> 2384 */ checkExecutor(Executor executor)2385 static Executor checkExecutor(Executor executor) { 2386 return (executor == null) ? checkAndWrapHandler(null) : executor; 2387 } 2388 2389 /** 2390 * Default executor management. 2391 * 2392 * <p>If the callback isn't null, check the executor, otherwise pass it through.</p> 2393 */ checkExecutor(Executor executor, T callback)2394 public static <T> Executor checkExecutor(Executor executor, T callback) { 2395 return (callback != null) ? checkExecutor(executor) : executor; 2396 } 2397 2398 /** 2399 * Wrap Handler in Executor. 2400 * 2401 * <p> 2402 * If handler is null, get the current thread's 2403 * Looper to create a Executor with. If no looper exists, throw 2404 * {@code IllegalArgumentException}. 2405 * </p> 2406 */ checkAndWrapHandler(Handler handler)2407 public static Executor checkAndWrapHandler(Handler handler) { 2408 return new CameraHandlerExecutor(checkHandler(handler)); 2409 } 2410 2411 /** 2412 * Default handler management. 2413 * 2414 * <p> 2415 * If handler is null, get the current thread's 2416 * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}. 2417 * </p> 2418 */ checkHandler(Handler handler)2419 static Handler checkHandler(Handler handler) { 2420 if (handler == null) { 2421 Looper looper = Looper.myLooper(); 2422 if (looper == null) { 2423 throw new IllegalArgumentException( 2424 "No handler given, and current thread has no looper!"); 2425 } 2426 handler = new Handler(looper); 2427 } 2428 return handler; 2429 } 2430 2431 /** 2432 * Default handler management, conditional on there being a callback. 2433 * 2434 * <p>If the callback isn't null, check the handler, otherwise pass it through.</p> 2435 */ checkHandler(Handler handler, T callback)2436 static <T> Handler checkHandler(Handler handler, T callback) { 2437 if (callback != null) { 2438 return checkHandler(handler); 2439 } 2440 return handler; 2441 } 2442 checkIfCameraClosedOrInError()2443 private void checkIfCameraClosedOrInError() throws CameraAccessException { 2444 if (mRemoteDevice == null) { 2445 throw new IllegalStateException("CameraDevice was already closed"); 2446 } 2447 if (mInError) { 2448 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, 2449 "The camera device has encountered a serious error"); 2450 } 2451 } 2452 2453 /** Whether the camera device has started to close (may not yet have finished) */ isClosed()2454 private boolean isClosed() { 2455 return mClosing.get(); 2456 } 2457 getCharacteristics()2458 private CameraCharacteristics getCharacteristics() { 2459 return mCharacteristics; 2460 } 2461 2462 /** 2463 * Listener for binder death. 2464 * 2465 * <p> Handle binder death for ICameraDeviceUser. Trigger onError.</p> 2466 */ 2467 @Override binderDied()2468 public void binderDied() { 2469 Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly"); 2470 2471 if (mRemoteDevice == null) { 2472 return; // Camera already closed 2473 } 2474 2475 mInError = true; 2476 Runnable r = new Runnable() { 2477 @Override 2478 public void run() { 2479 if (!isClosed()) { 2480 mDeviceCallback.onError(CameraDeviceImpl.this, 2481 StateCallback.ERROR_CAMERA_SERVICE); 2482 } 2483 } 2484 }; 2485 final long ident = Binder.clearCallingIdentity(); 2486 try { 2487 CameraDeviceImpl.this.mDeviceExecutor.execute(r); 2488 } finally { 2489 Binder.restoreCallingIdentity(ident); 2490 } 2491 } 2492 2493 @Override setCameraAudioRestriction( @AMERA_AUDIO_RESTRICTION int mode)2494 public void setCameraAudioRestriction( 2495 @CAMERA_AUDIO_RESTRICTION int mode) throws CameraAccessException { 2496 synchronized(mInterfaceLock) { 2497 checkIfCameraClosedOrInError(); 2498 mRemoteDevice.setCameraAudioRestriction(mode); 2499 } 2500 } 2501 2502 @Override getCameraAudioRestriction()2503 public @CAMERA_AUDIO_RESTRICTION int getCameraAudioRestriction() throws CameraAccessException { 2504 synchronized(mInterfaceLock) { 2505 checkIfCameraClosedOrInError(); 2506 return mRemoteDevice.getGlobalAudioRestriction(); 2507 } 2508 } 2509 2510 @Override createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)2511 public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration) 2512 throws CameraAccessException { 2513 try { 2514 if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) { 2515 mCurrentAdvancedExtensionSession = 2516 CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession( 2517 this, mContext, extensionConfiguration, mNextSessionId++); 2518 } else { 2519 mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession( 2520 this, mContext, extensionConfiguration, mNextSessionId++); 2521 } 2522 } catch (RemoteException e) { 2523 throw new CameraAccessException(CameraAccessException.CAMERA_ERROR); 2524 } 2525 } 2526 } 2527