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