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