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