1 /* 2 * Copyright (C) 2019 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.Preconditions.checkNotNull; 20 21 import android.annotation.FlaggedApi; 22 import android.hardware.camera2.CameraAccessException; 23 import android.hardware.camera2.CameraCaptureSession; 24 import android.hardware.camera2.CameraCharacteristics; 25 import android.hardware.camera2.CameraDevice; 26 import android.hardware.camera2.CameraOfflineSession; 27 import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback; 28 import android.hardware.camera2.CaptureFailure; 29 import android.hardware.camera2.CaptureRequest; 30 import android.hardware.camera2.CaptureResult; 31 import android.hardware.camera2.CameraMetadataInfo; 32 import android.hardware.camera2.ICameraDeviceCallbacks; 33 import android.hardware.camera2.ICameraOfflineSession; 34 import android.hardware.camera2.TotalCaptureResult; 35 import android.hardware.camera2.params.InputConfiguration; 36 import android.hardware.camera2.params.OutputConfiguration; 37 import android.os.Binder; 38 import android.os.Handler; 39 import android.os.IBinder; 40 import android.os.RemoteException; 41 import android.util.Log; 42 import android.util.Range; 43 import android.util.SparseArray; 44 import android.view.Surface; 45 46 import com.android.internal.camera.flags.Flags; 47 48 import java.util.AbstractMap.SimpleEntry; 49 import java.util.ArrayList; 50 import java.util.Collection; 51 import java.util.Iterator; 52 import java.util.List; 53 import java.util.concurrent.Executor; 54 import java.util.concurrent.atomic.AtomicBoolean; 55 56 public class CameraOfflineSessionImpl extends CameraOfflineSession 57 implements IBinder.DeathRecipient { 58 private static final String TAG = "CameraOfflineSessionImpl"; 59 private static final int REQUEST_ID_NONE = -1; 60 private static final long NANO_PER_SECOND = 1000000000; //ns 61 private final boolean DEBUG = false; 62 63 private ICameraOfflineSession mRemoteSession; 64 private final AtomicBoolean mClosing = new AtomicBoolean(); 65 66 private SimpleEntry<Integer, InputConfiguration> mOfflineInput = 67 new SimpleEntry<>(REQUEST_ID_NONE, null); 68 private SparseArray<OutputConfiguration> mOfflineOutputs = new SparseArray<>(); 69 private SparseArray<OutputConfiguration> mConfiguredOutputs = new SparseArray<>(); 70 71 final Object mInterfaceLock = new Object(); // access from this class and Session only! 72 73 private final String mCameraId; 74 private final CameraCharacteristics mCharacteristics; 75 private final int mTotalPartialCount; 76 77 private final Executor mOfflineExecutor; 78 private final CameraOfflineSessionCallback mOfflineCallback; 79 80 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); 81 82 /** 83 * A list tracking request and its expected last regular/reprocess/zslStill frame 84 * number. 85 */ 86 private List<RequestLastFrameNumbersHolder> mOfflineRequestLastFrameNumbersList = 87 new ArrayList<>(); 88 89 /** 90 * An object tracking received frame numbers. 91 * Updated when receiving callbacks from ICameraDeviceCallbacks. 92 */ 93 private FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker(); 94 95 /** map request IDs to callback/request data */ 96 private SparseArray<CaptureCallbackHolder> mCaptureCallbackMap = 97 new SparseArray<CaptureCallbackHolder>(); 98 CameraOfflineSessionImpl(String cameraId, CameraCharacteristics characteristics, Executor offlineExecutor, CameraOfflineSessionCallback offlineCallback, SparseArray<OutputConfiguration> offlineOutputs, SimpleEntry<Integer, InputConfiguration> offlineInput, SparseArray<OutputConfiguration> configuredOutputs, FrameNumberTracker frameNumberTracker, SparseArray<CaptureCallbackHolder> callbackMap, List<RequestLastFrameNumbersHolder> frameNumberList)99 public CameraOfflineSessionImpl(String cameraId, CameraCharacteristics characteristics, 100 Executor offlineExecutor, CameraOfflineSessionCallback offlineCallback, 101 SparseArray<OutputConfiguration> offlineOutputs, 102 SimpleEntry<Integer, InputConfiguration> offlineInput, 103 SparseArray<OutputConfiguration> configuredOutputs, 104 FrameNumberTracker frameNumberTracker, SparseArray<CaptureCallbackHolder> callbackMap, 105 List<RequestLastFrameNumbersHolder> frameNumberList) { 106 if ((cameraId == null) || (characteristics == null)) { 107 throw new IllegalArgumentException("Null argument given"); 108 } 109 110 mCameraId = cameraId; 111 mCharacteristics = characteristics; 112 113 Integer partialCount = 114 mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT); 115 if (partialCount == null) { 116 // 1 means partial result is not supported. 117 mTotalPartialCount = 1; 118 } else { 119 mTotalPartialCount = partialCount; 120 } 121 122 mOfflineRequestLastFrameNumbersList.addAll(frameNumberList); 123 mFrameNumberTracker = frameNumberTracker; 124 mCaptureCallbackMap = callbackMap; 125 mConfiguredOutputs = configuredOutputs; 126 mOfflineOutputs = offlineOutputs; 127 mOfflineInput = offlineInput; 128 mOfflineExecutor = checkNotNull(offlineExecutor, "offline executor must not be null"); 129 mOfflineCallback = checkNotNull(offlineCallback, "offline callback must not be null"); 130 131 } 132 getCallbacks()133 public CameraDeviceCallbacks getCallbacks() { 134 return mCallbacks; 135 } 136 137 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { 138 @Override asBinder()139 public IBinder asBinder() { 140 return this; 141 } 142 143 @Override onDeviceError(final int errorCode, CaptureResultExtras resultExtras)144 public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { 145 synchronized(mInterfaceLock) { 146 147 switch (errorCode) { 148 case CameraDeviceCallbacks.ERROR_CAMERA_REQUEST: 149 case CameraDeviceCallbacks.ERROR_CAMERA_RESULT: 150 case CameraDeviceCallbacks.ERROR_CAMERA_BUFFER: 151 onCaptureErrorLocked(errorCode, resultExtras); 152 break; 153 default: { 154 Runnable errorDispatch = new Runnable() { 155 @Override 156 public void run() { 157 if (!isClosed()) { 158 mOfflineCallback.onError(CameraOfflineSessionImpl.this, 159 CameraOfflineSessionCallback.STATUS_INTERNAL_ERROR); 160 } 161 } 162 }; 163 164 final long ident = Binder.clearCallingIdentity(); 165 try { 166 mOfflineExecutor.execute(errorDispatch); 167 } finally { 168 Binder.restoreCallingIdentity(ident); 169 } 170 } 171 } 172 } 173 } 174 175 @Override onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId)176 public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) { 177 Log.e(TAG, "Unexpected repeating request error received. Last frame number is " + 178 lastFrameNumber); 179 } 180 181 @Override 182 @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) onClientSharedAccessPriorityChanged(boolean primaryClient)183 public void onClientSharedAccessPriorityChanged(boolean primaryClient) { 184 Log.v(TAG, "onClientSharedAccessPriorityChanged primaryClient = " + primaryClient); 185 } 186 187 @Override onDeviceIdle()188 public void onDeviceIdle() { 189 synchronized(mInterfaceLock) { 190 if (mRemoteSession == null) { 191 Log.v(TAG, "Ignoring idle state notifications during offline switches"); 192 return; 193 } 194 195 // Remove all capture callbacks now that device has gone to IDLE state. 196 removeCompletedCallbackHolderLocked( 197 Long.MAX_VALUE, /*lastCompletedRegularFrameNumber*/ 198 Long.MAX_VALUE, /*lastCompletedReprocessFrameNumber*/ 199 Long.MAX_VALUE /*lastCompletedZslStillFrameNumber*/); 200 201 Runnable idleDispatch = new Runnable() { 202 @Override 203 public void run() { 204 if (!isClosed()) { 205 mOfflineCallback.onIdle(CameraOfflineSessionImpl.this); 206 } 207 } 208 }; 209 210 final long ident = Binder.clearCallingIdentity(); 211 try { 212 mOfflineExecutor.execute(idleDispatch); 213 } finally { 214 Binder.restoreCallingIdentity(ident); 215 } 216 } 217 } 218 219 @Override onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)220 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 221 int requestId = resultExtras.getRequestId(); 222 final long frameNumber = resultExtras.getFrameNumber(); 223 final long lastCompletedRegularFrameNumber = 224 resultExtras.getLastCompletedRegularFrameNumber(); 225 final long lastCompletedReprocessFrameNumber = 226 resultExtras.getLastCompletedReprocessFrameNumber(); 227 final long lastCompletedZslFrameNumber = 228 resultExtras.getLastCompletedZslFrameNumber(); 229 230 final CaptureCallbackHolder holder; 231 232 synchronized(mInterfaceLock) { 233 // Check if it's okay to remove completed callbacks from mCaptureCallbackMap. 234 // A callback is completed if the corresponding inflight request has been removed 235 // from the inflight queue in cameraservice. 236 removeCompletedCallbackHolderLocked(lastCompletedRegularFrameNumber, 237 lastCompletedReprocessFrameNumber, lastCompletedZslFrameNumber); 238 239 // Get the callback for this frame ID, if there is one 240 holder = CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId); 241 242 if (holder == null) { 243 return; 244 } 245 246 final Executor executor = holder.getCallback().getExecutor(); 247 if (isClosed() || (executor == null)) return; 248 249 // Dispatch capture start notice 250 final long ident = Binder.clearCallingIdentity(); 251 try { 252 executor.execute( 253 new Runnable() { 254 @Override 255 public void run() { 256 final CameraCaptureSession.CaptureCallback callback = 257 holder.getCallback().getSessionCallback(); 258 if (!CameraOfflineSessionImpl.this.isClosed() && 259 (callback != null)) { 260 final int subsequenceId = resultExtras.getSubsequenceId(); 261 final CaptureRequest request = holder.getRequest(subsequenceId); 262 263 if (holder.hasBatchedOutputs()) { 264 // Send derived onCaptureStarted for requests within the 265 // batch 266 final Range<Integer> fpsRange = 267 request.get( 268 CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); 269 for (int i = 0; i < holder.getRequestCount(); i++) { 270 final CaptureRequest cbRequest = holder.getRequest(i); 271 final long cbTimestamp = 272 timestamp - (subsequenceId - i) * 273 NANO_PER_SECOND/fpsRange.getUpper(); 274 final long cbFrameNumber = 275 frameNumber - (subsequenceId - i); 276 callback.onCaptureStarted(CameraOfflineSessionImpl.this, 277 cbRequest, cbTimestamp, cbFrameNumber); 278 } 279 } else { 280 callback.onCaptureStarted(CameraOfflineSessionImpl.this, 281 holder.getRequest( 282 resultExtras.getSubsequenceId()), 283 timestamp, frameNumber); 284 } 285 } 286 } 287 }); 288 } finally { 289 Binder.restoreCallingIdentity(ident); 290 } 291 } 292 } 293 294 @Override onResultReceived(CameraMetadataInfo resultInfo, CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])295 public void onResultReceived(CameraMetadataInfo resultInfo, 296 CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[]) 297 throws RemoteException { 298 CameraMetadataNative result = resultInfo.getMetadata(); 299 int requestId = resultExtras.getRequestId(); 300 long frameNumber = resultExtras.getFrameNumber(); 301 302 synchronized(mInterfaceLock) { 303 // TODO: Handle CameraCharacteristics access from CaptureResult correctly. 304 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, 305 mCharacteristics.get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); 306 307 final CaptureCallbackHolder holder = 308 CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId); 309 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); 310 311 boolean isPartialResult = 312 (resultExtras.getPartialResultCount() < mTotalPartialCount); 313 int requestType = request.getRequestType(); 314 315 // Check if we have a callback for this 316 if (holder == null) { 317 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 318 requestType); 319 320 return; 321 } 322 323 if (isClosed()) { 324 mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, 325 requestType); 326 return; 327 } 328 329 330 Runnable resultDispatch = null; 331 332 CaptureResult finalResult; 333 // Make a copy of the native metadata before it gets moved to a CaptureResult 334 // object. 335 final CameraMetadataNative resultCopy; 336 if (holder.hasBatchedOutputs()) { 337 resultCopy = new CameraMetadataNative(result); 338 } else { 339 resultCopy = null; 340 } 341 342 final Executor executor = holder.getCallback().getExecutor(); 343 // Either send a partial result or the final capture completed result 344 if (isPartialResult) { 345 final CaptureResult resultAsCapture = 346 new CaptureResult(mCameraId, result, request, resultExtras); 347 // Partial result 348 resultDispatch = new Runnable() { 349 @Override 350 public void run() { 351 final CameraCaptureSession.CaptureCallback callback = 352 holder.getCallback().getSessionCallback(); 353 if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) { 354 if (holder.hasBatchedOutputs()) { 355 // Send derived onCaptureProgressed for requests within 356 // the batch. 357 for (int i = 0; i < holder.getRequestCount(); i++) { 358 CameraMetadataNative resultLocal = 359 new CameraMetadataNative(resultCopy); 360 final CaptureResult resultInBatch = new CaptureResult( 361 mCameraId, resultLocal, holder.getRequest(i), 362 resultExtras); 363 364 final CaptureRequest cbRequest = holder.getRequest(i); 365 callback.onCaptureProgressed(CameraOfflineSessionImpl.this, 366 cbRequest, resultInBatch); 367 } 368 } else { 369 callback.onCaptureProgressed(CameraOfflineSessionImpl.this, 370 request, resultAsCapture); 371 } 372 } 373 } 374 }; 375 finalResult = resultAsCapture; 376 } else { 377 List<CaptureResult> partialResults = 378 mFrameNumberTracker.popPartialResults(frameNumber); 379 380 final long sensorTimestamp = 381 result.get(CaptureResult.SENSOR_TIMESTAMP); 382 final Range<Integer> fpsRange = 383 request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); 384 final int subsequenceId = resultExtras.getSubsequenceId(); 385 final TotalCaptureResult resultAsCapture = new TotalCaptureResult(mCameraId, 386 result, request, resultExtras, partialResults, holder.getSessionId(), 387 physicalResults); 388 // Final capture result 389 resultDispatch = new Runnable() { 390 @Override 391 public void run() { 392 final CameraCaptureSession.CaptureCallback callback = 393 holder.getCallback().getSessionCallback(); 394 if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) { 395 if (holder.hasBatchedOutputs()) { 396 // Send derived onCaptureCompleted for requests within 397 // the batch. 398 for (int i = 0; i < holder.getRequestCount(); i++) { 399 resultCopy.set(CaptureResult.SENSOR_TIMESTAMP, 400 sensorTimestamp - (subsequenceId - i) * 401 NANO_PER_SECOND/fpsRange.getUpper()); 402 CameraMetadataNative resultLocal = 403 new CameraMetadataNative(resultCopy); 404 // No logical multi-camera support for batched output mode. 405 TotalCaptureResult resultInBatch = new TotalCaptureResult( 406 mCameraId, resultLocal, holder.getRequest(i), 407 resultExtras, partialResults, holder.getSessionId(), 408 new PhysicalCaptureResultInfo[0]); 409 410 final CaptureRequest cbRequest = holder.getRequest(i); 411 callback.onCaptureCompleted(CameraOfflineSessionImpl.this, 412 cbRequest, resultInBatch); 413 } 414 } else { 415 callback.onCaptureCompleted(CameraOfflineSessionImpl.this, 416 request, resultAsCapture); 417 } 418 } 419 } 420 }; 421 finalResult = resultAsCapture; 422 } 423 424 if (executor != null) { 425 final long ident = Binder.clearCallingIdentity(); 426 try { 427 executor.execute(resultDispatch); 428 } finally { 429 Binder.restoreCallingIdentity(ident); 430 } 431 } 432 433 // Collect the partials for a total result; or mark the frame as totally completed 434 mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, 435 requestType); 436 437 // Fire onCaptureSequenceCompleted 438 if (!isPartialResult) { 439 checkAndFireSequenceComplete(); 440 } 441 } 442 } 443 444 @Override onPrepared(int streamId)445 public void onPrepared(int streamId) { 446 Log.e(TAG, "Unexpected stream " + streamId + " is prepared"); 447 } 448 449 @Override onRequestQueueEmpty()450 public void onRequestQueueEmpty() { 451 // No-op during offline mode 452 Log.v(TAG, "onRequestQueueEmpty"); 453 } 454 455 /** 456 * Called by onDeviceError for handling single-capture failures. 457 */ onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras)458 private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { 459 final int requestId = resultExtras.getRequestId(); 460 final int subsequenceId = resultExtras.getSubsequenceId(); 461 final long frameNumber = resultExtras.getFrameNumber(); 462 final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId(); 463 final CaptureCallbackHolder holder = 464 CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId); 465 466 if (holder == null) { 467 Log.e(TAG, String.format("Receive capture error on unknown request ID %d", 468 requestId)); 469 return; 470 } 471 472 final CaptureRequest request = holder.getRequest(subsequenceId); 473 474 Runnable failureDispatch = null; 475 if (errorCode == ERROR_CAMERA_BUFFER) { 476 // Because 1 stream id could map to multiple surfaces, we need to specify both 477 // streamId and surfaceId. 478 OutputConfiguration config; 479 if ((mRemoteSession == null) && !isClosed()) { 480 config = mConfiguredOutputs.get(resultExtras.getErrorStreamId()); 481 } else { 482 config = mOfflineOutputs.get(resultExtras.getErrorStreamId()); 483 } 484 if (config == null) { 485 Log.v(TAG, String.format( 486 "Stream %d has been removed. Skipping buffer lost callback", 487 resultExtras.getErrorStreamId())); 488 return; 489 } 490 for (Surface surface : config.getSurfaces()) { 491 if (!request.containsTarget(surface)) { 492 continue; 493 } 494 final Executor executor = holder.getCallback().getExecutor(); 495 failureDispatch = new Runnable() { 496 @Override 497 public void run() { 498 final CameraCaptureSession.CaptureCallback callback = 499 holder.getCallback().getSessionCallback(); 500 if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) { 501 callback.onCaptureBufferLost( CameraOfflineSessionImpl.this, 502 request, surface, frameNumber); 503 } 504 } 505 }; 506 if (executor != null) { 507 // Dispatch the failure callback 508 final long ident = Binder.clearCallingIdentity(); 509 try { 510 executor.execute(failureDispatch); 511 } finally { 512 Binder.restoreCallingIdentity(ident); 513 } 514 } 515 } 516 } else { 517 boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); 518 int reason = CaptureFailure.REASON_ERROR; 519 520 final CaptureFailure failure = new CaptureFailure( 521 request, 522 reason, 523 /*dropped*/ mayHaveBuffers, 524 requestId, 525 frameNumber, 526 errorPhysicalCameraId); 527 528 final Executor executor = holder.getCallback().getExecutor(); 529 failureDispatch = new Runnable() { 530 @Override 531 public void run() { 532 final CameraCaptureSession.CaptureCallback callback = 533 holder.getCallback().getSessionCallback(); 534 if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) { 535 callback.onCaptureFailed(CameraOfflineSessionImpl.this, request, 536 failure); 537 } 538 } 539 }; 540 541 // Fire onCaptureSequenceCompleted if appropriate 542 mFrameNumberTracker.updateTracker(frameNumber, 543 /*error*/true, request.getRequestType()); 544 checkAndFireSequenceComplete(); 545 546 if (executor != null) { 547 // Dispatch the failure callback 548 final long ident = Binder.clearCallingIdentity(); 549 try { 550 executor.execute(failureDispatch); 551 } finally { 552 Binder.restoreCallingIdentity(ident); 553 } 554 } 555 } 556 557 } 558 559 } 560 checkAndFireSequenceComplete()561 private void checkAndFireSequenceComplete() { 562 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); 563 long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); 564 long completedZslStillFrameNumber = mFrameNumberTracker.getCompletedZslStillFrameNumber(); 565 Iterator<RequestLastFrameNumbersHolder> iter = 566 mOfflineRequestLastFrameNumbersList.iterator(); 567 while (iter.hasNext()) { 568 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); 569 boolean sequenceCompleted = false; 570 final int requestId = requestLastFrameNumbers.getRequestId(); 571 final CaptureCallbackHolder holder; 572 final Executor executor; 573 final CameraCaptureSession.CaptureCallback callback; 574 synchronized(mInterfaceLock) { 575 int index = mCaptureCallbackMap.indexOfKey(requestId); 576 holder = (index >= 0) ? 577 mCaptureCallbackMap.valueAt(index) : null; 578 if (holder != null) { 579 long lastRegularFrameNumber = 580 requestLastFrameNumbers.getLastRegularFrameNumber(); 581 long lastReprocessFrameNumber = 582 requestLastFrameNumbers.getLastReprocessFrameNumber(); 583 long lastZslStillFrameNumber = 584 requestLastFrameNumbers.getLastZslStillFrameNumber(); 585 executor = holder.getCallback().getExecutor(); 586 callback = holder.getCallback().getSessionCallback(); 587 // check if it's okay to remove request from mCaptureCallbackMap 588 if (lastRegularFrameNumber <= completedFrameNumber 589 && lastReprocessFrameNumber <= completedReprocessFrameNumber 590 && lastZslStillFrameNumber <= completedZslStillFrameNumber) { 591 sequenceCompleted = true; 592 mCaptureCallbackMap.removeAt(index); 593 } 594 } else { 595 executor = null; 596 callback = null; 597 } 598 } 599 600 // If no callback is registered for this requestId or sequence completed, remove it 601 // from the frame number->request pair because it's not needed anymore. 602 if (holder == null || sequenceCompleted) { 603 iter.remove(); 604 } 605 606 // Call onCaptureSequenceCompleted 607 if ((sequenceCompleted) && (callback != null) && (executor != null)) { 608 Runnable resultDispatch = new Runnable() { 609 @Override 610 public void run() { 611 if (!isClosed()) { 612 callback.onCaptureSequenceCompleted(CameraOfflineSessionImpl.this, 613 requestId, requestLastFrameNumbers.getLastFrameNumber()); 614 } 615 } 616 }; 617 618 final long ident = Binder.clearCallingIdentity(); 619 try { 620 executor.execute(resultDispatch); 621 } finally { 622 Binder.restoreCallingIdentity(ident); 623 } 624 625 if (mCaptureCallbackMap.size() == 0) { 626 getCallbacks().onDeviceIdle(); 627 } 628 } 629 630 } 631 } 632 removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber, long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber)633 private void removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber, 634 long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber) { 635 if (DEBUG) { 636 Log.v(TAG, String.format("remove completed callback holders for " 637 + "lastCompletedRegularFrameNumber %d, " 638 + "lastCompletedReprocessFrameNumber %d, " 639 + "lastCompletedZslStillFrameNumber %d", 640 lastCompletedRegularFrameNumber, 641 lastCompletedReprocessFrameNumber, 642 lastCompletedZslStillFrameNumber)); 643 } 644 645 boolean isReprocess = false; 646 Iterator<RequestLastFrameNumbersHolder> iter = 647 mOfflineRequestLastFrameNumbersList.iterator(); 648 while (iter.hasNext()) { 649 final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); 650 final int requestId = requestLastFrameNumbers.getRequestId(); 651 final CaptureCallbackHolder holder; 652 653 int index = mCaptureCallbackMap.indexOfKey(requestId); 654 holder = (index >= 0) ? 655 mCaptureCallbackMap.valueAt(index) : null; 656 if (holder != null) { 657 long lastRegularFrameNumber = 658 requestLastFrameNumbers.getLastRegularFrameNumber(); 659 long lastReprocessFrameNumber = 660 requestLastFrameNumbers.getLastReprocessFrameNumber(); 661 long lastZslStillFrameNumber = 662 requestLastFrameNumbers.getLastZslStillFrameNumber(); 663 if (lastRegularFrameNumber <= lastCompletedRegularFrameNumber 664 && lastReprocessFrameNumber <= lastCompletedReprocessFrameNumber 665 && lastZslStillFrameNumber <= lastCompletedZslStillFrameNumber) { 666 if (requestLastFrameNumbers.isSequenceCompleted()) { 667 mCaptureCallbackMap.removeAt(index); 668 if (DEBUG) { 669 Log.v(TAG, String.format( 670 "Remove holder for requestId %d, because lastRegularFrame %d " 671 + "is <= %d, lastReprocessFrame %d is <= %d, " 672 + "lastZslStillFrame %d is <= %d", requestId, 673 lastRegularFrameNumber, lastCompletedRegularFrameNumber, 674 lastReprocessFrameNumber, lastCompletedReprocessFrameNumber, 675 lastZslStillFrameNumber, lastCompletedZslStillFrameNumber)); 676 } 677 678 iter.remove(); 679 } else { 680 Log.e(TAG, "Sequence not yet completed for request id " + requestId); 681 continue; 682 } 683 } 684 } 685 } 686 } 687 notifyFailedSwitch()688 public void notifyFailedSwitch() { 689 synchronized(mInterfaceLock) { 690 Runnable switchFailDispatch = new Runnable() { 691 @Override 692 public void run() { 693 mOfflineCallback.onSwitchFailed(CameraOfflineSessionImpl.this); 694 } 695 }; 696 697 final long ident = Binder.clearCallingIdentity(); 698 try { 699 mOfflineExecutor.execute(switchFailDispatch); 700 } finally { 701 Binder.restoreCallingIdentity(ident); 702 } 703 } 704 } 705 706 /** 707 * Set remote session. 708 * 709 */ setRemoteSession(ICameraOfflineSession remoteSession)710 public void setRemoteSession(ICameraOfflineSession remoteSession) throws CameraAccessException { 711 synchronized(mInterfaceLock) { 712 if (remoteSession == null) { 713 notifyFailedSwitch(); 714 return; 715 } 716 717 mRemoteSession = remoteSession; 718 719 IBinder remoteSessionBinder = remoteSession.asBinder(); 720 if (remoteSessionBinder == null) { 721 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 722 "The camera offline session has encountered a serious error"); 723 } 724 725 try { 726 remoteSessionBinder.linkToDeath(this, /*flag*/ 0); 727 } catch (RemoteException e) { 728 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 729 "The camera offline session has encountered a serious error"); 730 } 731 732 Runnable readyDispatch = new Runnable() { 733 @Override 734 public void run() { 735 if (!isClosed()) { 736 mOfflineCallback.onReady(CameraOfflineSessionImpl.this); 737 } 738 } 739 }; 740 741 final long ident = Binder.clearCallingIdentity(); 742 try { 743 mOfflineExecutor.execute(readyDispatch); 744 } finally { 745 Binder.restoreCallingIdentity(ident); 746 } 747 } 748 } 749 750 /** Whether the offline session has started to close (may not yet have finished) */ isClosed()751 private boolean isClosed() { 752 return mClosing.get(); 753 } 754 disconnect()755 private void disconnect() { 756 synchronized (mInterfaceLock) { 757 if (mClosing.getAndSet(true)) { 758 return; 759 } 760 761 if (mRemoteSession != null) { 762 mRemoteSession.asBinder().unlinkToDeath(this, /*flags*/0); 763 764 try { 765 mRemoteSession.disconnect(); 766 } catch (RemoteException e) { 767 Log.e(TAG, "Exception while disconnecting from offline session: ", e); 768 } 769 } else { 770 throw new IllegalStateException("Offline session is not yet ready"); 771 } 772 773 mRemoteSession = null; 774 775 Runnable closeDispatch = new Runnable() { 776 @Override 777 public void run() { 778 mOfflineCallback.onClosed(CameraOfflineSessionImpl.this); 779 } 780 }; 781 782 final long ident = Binder.clearCallingIdentity(); 783 try { 784 mOfflineExecutor.execute(closeDispatch); 785 } finally { 786 Binder.restoreCallingIdentity(ident); 787 } 788 } 789 } 790 791 @Override finalize()792 protected void finalize() throws Throwable { 793 try { 794 disconnect(); 795 } 796 finally { 797 super.finalize(); 798 } 799 } 800 801 /** 802 * Listener for binder death. 803 * 804 * <p> Handle binder death for ICameraOfflineSession.</p> 805 */ 806 @Override binderDied()807 public void binderDied() { 808 Log.w(TAG, "CameraOfflineSession on device " + mCameraId + " died unexpectedly"); 809 disconnect(); 810 } 811 812 @Override getDevice()813 public CameraDevice getDevice() { 814 throw new UnsupportedOperationException("Operation not supported in offline mode"); 815 } 816 817 @Override prepare(Surface surface)818 public void prepare(Surface surface) throws CameraAccessException { 819 throw new UnsupportedOperationException("Operation not supported in offline mode"); 820 } 821 822 @Override prepare(int maxCount, Surface surface)823 public void prepare(int maxCount, Surface surface) throws CameraAccessException { 824 throw new UnsupportedOperationException("Operation not supported in offline mode"); 825 } 826 827 @Override tearDown(Surface surface)828 public void tearDown(Surface surface) throws CameraAccessException { 829 throw new UnsupportedOperationException("Operation not supported in offline mode"); 830 } 831 832 @Override finalizeOutputConfigurations( List<OutputConfiguration> outputConfigs)833 public void finalizeOutputConfigurations( 834 List<OutputConfiguration> outputConfigs) throws CameraAccessException { 835 throw new UnsupportedOperationException("Operation not supported in offline mode"); 836 } 837 838 @Override capture(CaptureRequest request, CaptureCallback callback, Handler handler)839 public int capture(CaptureRequest request, CaptureCallback callback, 840 Handler handler) throws CameraAccessException { 841 throw new UnsupportedOperationException("Operation not supported in offline mode"); 842 } 843 844 @Override captureSingleRequest(CaptureRequest request, Executor executor, CaptureCallback callback)845 public int captureSingleRequest(CaptureRequest request, Executor executor, 846 CaptureCallback callback) throws CameraAccessException { 847 throw new UnsupportedOperationException("Operation not supported in offline mode"); 848 } 849 850 @Override captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)851 public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, 852 Handler handler) throws CameraAccessException { 853 throw new UnsupportedOperationException("Operation not supported in offline mode"); 854 } 855 856 @Override captureBurstRequests(List<CaptureRequest> requests, Executor executor, CaptureCallback callback)857 public int captureBurstRequests(List<CaptureRequest> requests, Executor executor, 858 CaptureCallback callback) throws CameraAccessException { 859 throw new UnsupportedOperationException("Operation not supported in offline mode"); 860 } 861 862 @Override setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler)863 public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, 864 Handler handler) throws CameraAccessException { 865 throw new UnsupportedOperationException("Operation not supported in offline mode"); 866 } 867 868 @Override setSingleRepeatingRequest(CaptureRequest request, Executor executor, CaptureCallback callback)869 public int setSingleRepeatingRequest(CaptureRequest request, Executor executor, 870 CaptureCallback callback) throws CameraAccessException { 871 throw new UnsupportedOperationException("Operation not supported in offline mode"); 872 } 873 874 @Override setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler)875 public int setRepeatingBurst(List<CaptureRequest> requests, 876 CaptureCallback callback, Handler handler) throws CameraAccessException { 877 throw new UnsupportedOperationException("Operation not supported in offline mode"); 878 } 879 880 @Override setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor, CaptureCallback callback)881 public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor, 882 CaptureCallback callback) throws CameraAccessException { 883 throw new UnsupportedOperationException("Operation not supported in offline mode"); 884 } 885 886 @Override stopRepeating()887 public void stopRepeating() throws CameraAccessException { 888 throw new UnsupportedOperationException("Operation not supported in offline mode"); 889 } 890 891 @Override abortCaptures()892 public void abortCaptures() throws CameraAccessException { 893 throw new UnsupportedOperationException("Operation not supported in offline mode"); 894 } 895 896 @Override updateOutputConfiguration(OutputConfiguration config)897 public void updateOutputConfiguration(OutputConfiguration config) 898 throws CameraAccessException { 899 throw new UnsupportedOperationException("Operation not supported in offline mode"); 900 } 901 902 @Override isReprocessable()903 public boolean isReprocessable() { 904 throw new UnsupportedOperationException("Operation not supported in offline mode"); 905 } 906 907 @Override getInputSurface()908 public Surface getInputSurface() { 909 throw new UnsupportedOperationException("Operation not supported in offline mode"); 910 } 911 912 @Override switchToOffline(Collection<Surface> offlineOutputs, Executor executor, CameraOfflineSessionCallback listener)913 public CameraOfflineSession switchToOffline(Collection<Surface> offlineOutputs, 914 Executor executor, CameraOfflineSessionCallback listener) throws CameraAccessException { 915 throw new UnsupportedOperationException("Operation not supported in offline mode"); 916 } 917 918 @Override supportsOfflineProcessing(Surface surface)919 public boolean supportsOfflineProcessing(Surface surface) { 920 throw new UnsupportedOperationException("Operation not supported in offline mode"); 921 } 922 923 @Override close()924 public void close() { 925 disconnect(); 926 } 927 } 928