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