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