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