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