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