AndroidCamera2AgentImpl.java revision de48004068f8c16f9a56c60b0ed2485a67687b4b
1/*
2 * Copyright (C) 2014 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 com.android.ex.camera2.portability;
18
19import android.annotation.TargetApi;
20import android.content.Context;
21import android.graphics.ImageFormat;
22import android.graphics.Rect;
23import android.graphics.SurfaceTexture;
24import android.hardware.camera2.CameraAccessException;
25import android.hardware.camera2.CameraCaptureSession;
26import android.hardware.camera2.CameraCharacteristics;
27import android.hardware.camera2.CameraDevice;
28import android.hardware.camera2.CameraManager;
29import android.hardware.camera2.CaptureFailure;
30import android.hardware.camera2.CaptureRequest;
31import android.hardware.camera2.CaptureResult;
32import android.hardware.camera2.TotalCaptureResult;
33import android.hardware.camera2.params.MeteringRectangle;
34import android.media.Image;
35import android.media.ImageReader;
36import android.os.Build;
37import android.os.Handler;
38import android.os.HandlerThread;
39import android.os.Looper;
40import android.os.Message;
41import android.view.Surface;
42
43import com.android.ex.camera2.portability.debug.Log;
44import com.android.ex.camera2.utils.Camera2RequestSettingsSet;
45
46import java.nio.ByteBuffer;
47import java.util.ArrayList;
48import java.util.Arrays;
49import java.util.HashSet;
50import java.util.List;
51import java.util.Set;
52
53/**
54 * A class to implement {@link CameraAgent} of the Android camera2 framework.
55 */
56class AndroidCamera2AgentImpl extends CameraAgent {
57    private static final Log.Tag TAG = new Log.Tag("AndCam2AgntImp");
58
59    private final Camera2Handler mCameraHandler;
60    private final HandlerThread mCameraHandlerThread;
61    private final CameraStateHolder mCameraState;
62    private final DispatchThread mDispatchThread;
63    private final CameraManager mCameraManager;
64
65    /**
66     * Number of camera devices.  The length of {@code mCameraDevices} does not reveal this
67     * information because that list may contain since-invalidated indices.
68     */
69    private int mNumCameraDevices;
70
71    /**
72     * Transformation between integral camera indices and the {@link java.lang.String} indices used
73     * by the underlying API.  Note that devices may disappear because they've been disconnected or
74     * have otherwise gone offline.  Because we need to keep the meanings of whatever indices we
75     * expose stable, we cannot simply remove them in such a case; instead, we insert {@code null}s
76     * to invalidate any such indices.  Whenever new devices appear, they are appended to the end of
77     * the list, and thereby assigned the lowest index that has never yet been used.
78     */
79    private final List<String> mCameraDevices;
80
81    AndroidCamera2AgentImpl(Context context) {
82        mCameraHandlerThread = new HandlerThread("Camera2 Handler Thread");
83        mCameraHandlerThread.start();
84        mCameraHandler = new Camera2Handler(mCameraHandlerThread.getLooper());
85        mCameraState = new AndroidCamera2StateHolder();
86        mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread);
87        mDispatchThread.start();
88        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
89
90        mNumCameraDevices = 0;
91        mCameraDevices = new ArrayList<String>();
92        updateCameraDevices();
93    }
94
95    /**
96     * Updates the camera device index assignments stored in {@link mCameraDevices}, without
97     * reappropriating any currently-assigned index.
98     * @return Whether the operation was successful
99     */
100    private boolean updateCameraDevices() {
101        try {
102            String[] currentCameraDevices = mCameraManager.getCameraIdList();
103            Set<String> currentSet = new HashSet<String>(Arrays.asList(currentCameraDevices));
104
105            // Invalidate the indices assigned to any camera devices that are no longer present
106            for (int index = 0; index < mCameraDevices.size(); ++index) {
107                if (!currentSet.contains(mCameraDevices.get(index))) {
108                    mCameraDevices.set(index, null);
109                    --mNumCameraDevices;
110                }
111            }
112
113            // Assign fresh indices to any new camera devices
114            currentSet.removeAll(mCameraDevices); // The devices we didn't know about
115            for (String device : currentCameraDevices) {
116                if (currentSet.contains(device)) {
117                    mCameraDevices.add(device);
118                    ++mNumCameraDevices;
119                }
120            }
121
122            return true;
123        } catch (CameraAccessException ex) {
124            Log.e(TAG, "Could not get device listing from camera subsystem", ex);
125            return false;
126        }
127    }
128
129    // TODO: Implement
130    @Override
131    public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
132            Handler handler) {}
133
134    // TODO: Implement
135    @Override
136    public void recycle() {}
137
138    // TODO: Some indices may now be invalid; ensure everyone can handle that and update the docs
139    @Override
140    public CameraDeviceInfo getCameraDeviceInfo() {
141        updateCameraDevices();
142        return new AndroidCamera2DeviceInfo(mCameraManager, mCameraDevices.toArray(new String[0]),
143                mNumCameraDevices);
144    }
145
146    @Override
147    protected Handler getCameraHandler() {
148        return mCameraHandler;
149    }
150
151    @Override
152    protected DispatchThread getDispatchThread() {
153        return mDispatchThread;
154    }
155
156    private static abstract class CaptureAvailableListener
157            extends CameraCaptureSession.CaptureListener
158            implements ImageReader.OnImageAvailableListener {};
159
160    private class Camera2Handler extends HistoryHandler {
161        // Caller-provided when leaving CAMERA_UNOPENED state:
162        private CameraOpenCallback mOpenCallback;
163        private int mCameraIndex;
164        private String mCameraId;
165
166        // Available in CAMERA_UNCONFIGURED state and above:
167        private CameraDevice mCamera;
168        private AndroidCamera2ProxyImpl mCameraProxy;
169        private Camera2RequestSettingsSet mPersistentSettings;
170        private Rect mActiveArray;
171        private boolean mLegacyDevice;
172
173        // Available in CAMERA_CONFIGURED state and above:
174        private Size mPreviewSize;
175        private Size mPhotoSize;
176
177        // Available in PREVIEW_READY state and above:
178        private SurfaceTexture mPreviewTexture;
179        private Surface mPreviewSurface;
180        private CameraCaptureSession mSession;
181        private ImageReader mCaptureReader;
182
183        // Available from the beginning of PREVIEW_ACTIVE until the first preview frame arrives:
184        private CameraStartPreviewCallback mOneshotPreviewingCallback;
185
186        // Available in FOCUS_LOCKED between AF trigger receipt and whenever the lens stops moving:
187        private CameraAFCallback mOneshotAfCallback;
188
189        // Available when taking picture between AE trigger receipt and autoexposure convergence
190        private CaptureAvailableListener mOneshotCaptureCallback;
191
192        // Available whenever setAutoFocusMoveCallback() was last invoked with a non-null argument:
193        private CameraAFMoveCallback mPassiveAfCallback;
194
195        Camera2Handler(Looper looper) {
196            super(looper);
197        }
198
199        @Override
200        public void handleMessage(final Message msg) {
201            super.handleMessage(msg);
202            try {
203                switch(msg.what) {
204                    case CameraActions.OPEN_CAMERA:
205                    case CameraActions.RECONNECT: {
206                        CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
207                        int cameraIndex = msg.arg1;
208
209                        if (mCameraState.getState() != AndroidCamera2StateHolder.CAMERA_UNOPENED) {
210                            openCallback.onDeviceOpenedAlready(cameraIndex,
211                                    generateHistoryString(cameraIndex));
212                            break;
213                        }
214
215                        mOpenCallback = openCallback;
216                        mCameraIndex = cameraIndex;
217                        mCameraId = mCameraDevices.get(mCameraIndex);
218
219                        if (mCameraId == null) {
220                            mOpenCallback.onCameraDisabled(msg.arg1);
221                            break;
222                        }
223                        mCameraManager.openCamera(mCameraId, mCameraDeviceStateListener, this);
224
225                        break;
226                    }
227
228                    case CameraActions.RELEASE: {
229                        if (mCameraState.getState() == AndroidCamera2StateHolder.CAMERA_UNOPENED) {
230                            Log.w(TAG, "Ignoring release at inappropriate time");
231                            break;
232                        }
233
234                        if (mSession != null) {
235                            closePreviewSession();
236                            mSession = null;
237                        }
238                        if (mCamera != null) {
239                            mCamera.close();
240                            mCamera = null;
241                        }
242                        mCameraProxy = null;
243                        mPersistentSettings = null;
244                        mActiveArray = null;
245                        if (mPreviewSurface != null) {
246                            mPreviewSurface.release();
247                            mPreviewSurface = null;
248                        }
249                        mPreviewTexture = null;
250                        if (mCaptureReader != null) {
251                            mCaptureReader.close();
252                            mCaptureReader = null;
253                        }
254                        mPreviewSize = null;
255                        mPhotoSize = null;
256                        mCameraIndex = 0;
257                        mCameraId = null;
258                        mCameraState.setState(AndroidCamera2StateHolder.CAMERA_UNOPENED);
259                        break;
260                    }
261
262                    /*case CameraActions.UNLOCK: {
263                        break;
264                    }
265
266                    case CameraActions.LOCK: {
267                        break;
268                    }*/
269
270                    case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: {
271                        setPreviewTexture((SurfaceTexture) msg.obj);
272                        break;
273                    }
274
275                    case CameraActions.START_PREVIEW_ASYNC: {
276                        if (mCameraState.getState() !=
277                                        AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) {
278                            // TODO: Provide better feedback here?
279                            Log.w(TAG, "Refusing to start preview at inappropriate time");
280                            break;
281                        }
282
283                        mOneshotPreviewingCallback = (CameraStartPreviewCallback) msg.obj;
284                        mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
285                        try {
286                            mSession.setRepeatingRequest(
287                                    mPersistentSettings.createRequest(mCamera,
288                                            CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface),
289                                    /*listener*/mCameraFocusStateListener, /*handler*/this);
290                        } catch(CameraAccessException ex) {
291                            Log.w(TAG, "Unable to start preview", ex);
292                            mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
293                        }
294                        break;
295                    }
296
297                    case CameraActions.STOP_PREVIEW: {
298                        if (mCameraState.getState() <
299                                        AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
300                            Log.w(TAG, "Refusing to stop preview at inappropriate time");
301                            break;
302                        }
303
304                        mSession.stopRepeating();
305                        mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
306                        break;
307                    }
308
309                    /*case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: {
310                        break;
311                    }
312
313                    case CameraActions.ADD_CALLBACK_BUFFER: {
314                        break;
315                    }
316
317                    case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: {
318                        break;
319                    }
320
321                    case CameraActions.SET_PREVIEW_CALLBACK: {
322                        break;
323                    }
324
325                    case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: {
326                        break;
327                    }
328
329                    case CameraActions.SET_PARAMETERS: {
330                        break;
331                    }
332
333                    case CameraActions.GET_PARAMETERS: {
334                        break;
335                    }
336
337                    case CameraActions.REFRESH_PARAMETERS: {
338                        break;
339                    }*/
340
341                    case CameraActions.APPLY_SETTINGS: {
342                        AndroidCamera2Settings settings = (AndroidCamera2Settings) msg.obj;
343                        applyToRequest(settings);
344                        break;
345                    }
346
347                    case CameraActions.AUTO_FOCUS: {
348                        // We only support locking the focus while a preview is being displayed.
349                        // However, it can be requested multiple times in succession; the effect of
350                        // the subsequent invocations is determined by the focus mode defined in the
351                        // provided CameraSettings object. In passive (CONTINUOUS_*) mode, the
352                        // duplicate requests are no-ops and leave the lens locked at its current
353                        // position, but in active (AUTO) mode, they perform another scan and lock
354                        // once that is finished. In any manual focus mode, this call is a no-op,
355                        // and most notably, this is the only case where the callback isn't invoked.
356                        if (mCameraState.getState() <
357                                        AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
358                            Log.w(TAG, "Ignoring attempt to autofocus without preview");
359                            break;
360                        }
361
362                        // The earliest we can reliably tell whether the autofocus has locked in
363                        // response to our latest request is when our one-time capture completes.
364                        // However, it will probably take longer than that, so once that happens,
365                        // just start checking the repeating preview requests as they complete.
366                        final CameraAFCallback callback = (CameraAFCallback) msg.obj;
367                        CameraCaptureSession.CaptureListener deferredCallbackSetter =
368                                new CameraCaptureSession.CaptureListener() {
369                            @Override
370                            public void onCaptureCompleted(CameraCaptureSession session,
371                                                           CaptureRequest request,
372                                                           TotalCaptureResult result) {
373                                // Now our mCameraFocusStateListener will invoke the callback the
374                                // first time it finds the focus motor to be locked.
375                                mOneshotAfCallback = callback;
376                            }
377
378                            @Override
379                            public void onCaptureFailed(CameraCaptureSession session,
380                                                        CaptureRequest request,
381                                                        CaptureFailure failure) {
382                                Log.e(TAG, "Focusing failed with reason " + failure.getReason());
383                                callback.onAutoFocus(false, mCameraProxy);
384                            }};
385
386                        // Send a one-time capture to trigger the camera driver to lock focus.
387                        mCameraState.setState(AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
388                        Camera2RequestSettingsSet trigger =
389                                new Camera2RequestSettingsSet(mPersistentSettings);
390                        trigger.set(CaptureRequest.CONTROL_AF_TRIGGER,
391                                CaptureRequest.CONTROL_AF_TRIGGER_START);
392                        try {
393                            mSession.capture(
394                                    trigger.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
395                                            mPreviewSurface),
396                                    /*listener*/deferredCallbackSetter, /*handler*/ this);
397                        } catch(CameraAccessException ex) {
398                            Log.e(TAG, "Unable to lock autofocus", ex);
399                            mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
400                        }
401                        break;
402                    }
403
404                    case CameraActions.CANCEL_AUTO_FOCUS: {
405                        // Why would you want to unlock the lens if it isn't already locked?
406                        if (mCameraState.getState() <
407                                AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
408                            Log.w(TAG, "Ignoring attempt to release focus lock without preview");
409                            break;
410                        }
411
412                        // Send a one-time capture to trigger the camera driver to resume scanning.
413                        mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE);
414                        Camera2RequestSettingsSet cancel =
415                                new Camera2RequestSettingsSet(mPersistentSettings);
416                        cancel.set(CaptureRequest.CONTROL_AF_TRIGGER,
417                                CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
418                        try {
419                            mSession.capture(
420                                    cancel.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
421                                            mPreviewSurface),
422                                    /*listener*/null, /*handler*/this);
423                        } catch(CameraAccessException ex) {
424                            Log.e(TAG, "Unable to cancel autofocus", ex);
425                            mCameraState.setState(
426                                    AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
427                        }
428                        break;
429                    }
430
431                    case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: {
432                        mPassiveAfCallback = (CameraAFMoveCallback) msg.obj;
433                        break;
434                    }
435
436                    /*case CameraActions.SET_ZOOM_CHANGE_LISTENER: {
437                        break;
438                    }
439
440                    case CameraActions.SET_FACE_DETECTION_LISTENER: {
441                        break;
442                    }
443
444                    case CameraActions.START_FACE_DETECTION: {
445                        break;
446                    }
447
448                    case CameraActions.STOP_FACE_DETECTION: {
449                        break;
450                    }
451
452                    case CameraActions.SET_ERROR_CALLBACK: {
453                        break;
454                    }
455
456                    case CameraActions.ENABLE_SHUTTER_SOUND: {
457                        break;
458                    }*/
459
460                    case CameraActions.SET_DISPLAY_ORIENTATION: {
461                        // TODO: Need to handle preview in addition to capture
462                        // Only set the JPEG capture orientation if requested to do so; otherwise,
463                        // capture in the sensor's physical orientation
464                        mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg2 > 0 ?
465                                mCameraProxy.getCharacteristics().getJpegOrientation(msg.arg1) : 0);
466                        break;
467                    }
468
469                    case CameraActions.CAPTURE_PHOTO: {
470                        if (mCameraState.getState() <
471                                        AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
472                            Log.e(TAG, "Photos may only be taken when a preview is active");
473                            break;
474                        }
475                        if (mCameraState.getState() !=
476                                AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED) {
477                            Log.w(TAG, "Taking a (likely blurry) photo without the lens locked");
478                        }
479
480                        final CaptureAvailableListener listener =
481                                (CaptureAvailableListener) msg.obj;
482                        if (mLegacyDevice) {
483                            // Just snap the shot
484                            mCaptureReader.setOnImageAvailableListener(listener, /*handler*/this);
485                            try {
486                                mSession.capture(
487                                        mPersistentSettings.createRequest(mCamera,
488                                                CameraDevice.TEMPLATE_STILL_CAPTURE,
489                                                mCaptureReader.getSurface()),
490                                        listener, /*handler*/this);
491                            } catch (CameraAccessException ex) {
492                                Log.e(TAG, "Unable to initiate legacy capture", ex);
493                            }
494                        } else {
495                            // Not a legacy device, so we need to let AE converge before capturing
496                            CameraCaptureSession.CaptureListener deferredCallbackSetter =
497                                    new CameraCaptureSession.CaptureListener() {
498                                @Override
499                                public void onCaptureCompleted(CameraCaptureSession session,
500                                                               CaptureRequest request,
501                                                               TotalCaptureResult result) {
502                                    mOneshotCaptureCallback = listener;
503                                }
504
505                                @Override
506                                public void onCaptureFailed(CameraCaptureSession session,
507                                                            CaptureRequest request,
508                                                            CaptureFailure failure) {
509                                    Log.e(TAG, "Autoexposure and capture failed with reason " +
510                                            failure.getReason());
511                                    // TODO: Make an error callback?
512                                }};
513
514                            // Set a one-time capture to trigger the camera driver's autoexposure:
515                            Camera2RequestSettingsSet expose =
516                                    new Camera2RequestSettingsSet(mPersistentSettings);
517                            expose.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
518                                    CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
519                            try {
520                                mSession.capture(
521                                        expose.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW,
522                                                mPreviewSurface),
523                                        /*listener*/deferredCallbackSetter, /*handler*/this);
524                            } catch (CameraAccessException ex) {
525                                Log.e(TAG, "Unable to run autoexposure and perform capture", ex);
526                            }
527                        }
528                        break;
529                    }
530
531                    default: {
532                        // TODO: Rephrase once everything has been implemented
533                        throw new RuntimeException("Unimplemented CameraProxy message=" + msg.what);
534                    }
535                }
536            } catch (final Exception ex) {
537                if (msg.what != CameraActions.RELEASE && mCamera != null) {
538                    // TODO: Handle this better
539                    mCamera.close();
540                    mCamera = null;
541                } else if (mCamera == null) {
542                    if (msg.what == CameraActions.OPEN_CAMERA) {
543                        if (mOpenCallback != null) {
544                            mOpenCallback.onDeviceOpenFailure(mCameraIndex,
545                                    generateHistoryString(mCameraIndex));
546                        }
547                    } else {
548                        Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null");
549                    }
550                    return;
551                }
552
553                if (ex instanceof RuntimeException) {
554                    post(new Runnable() {
555                        @Override
556                        public void run() {
557                            sCameraExceptionCallback.onCameraException((RuntimeException) ex);
558                        }});
559                }
560            }
561        }
562
563        public CameraSettings buildSettings(AndroidCamera2Capabilities caps) {
564            try {
565                return new AndroidCamera2Settings(mCamera, CameraDevice.TEMPLATE_PREVIEW,
566                        mActiveArray, mPreviewSize, mPhotoSize);
567            } catch (CameraAccessException ex) {
568                Log.e(TAG, "Unable to query camera device to build settings representation");
569                return null;
570            }
571        }
572
573        /**
574         * Simply propagates settings from provided {@link CameraSettings}
575         * object to our {@link CaptureRequest.Builder} for use in captures.
576         * <p>Most conversions to match the API 2 formats are performed by
577         * {@link AndroidCamera2Capabilities.IntegralStringifier}; otherwise
578         * any final adjustments are done here before updating the builder.</p>
579         *
580         * @param settings The new/updated settings
581         */
582        private void applyToRequest(AndroidCamera2Settings settings) {
583            // TODO: If invoked when in PREVIEW_READY state, a new preview size will not take effect
584
585            mPersistentSettings.union(settings.getRequestSettings());
586            mPreviewSize = settings.getCurrentPreviewSize();
587            mPhotoSize = settings.getCurrentPhotoSize();
588
589            if (mCameraState.getState() >= AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE) {
590                // If we're already previewing, reflect most settings immediately
591                try {
592                    mSession.setRepeatingRequest(
593                            mPersistentSettings.createRequest(mCamera,
594                                    CameraDevice.TEMPLATE_PREVIEW, mPreviewSurface),
595                            /*listener*/mCameraFocusStateListener, /*handler*/this);
596                } catch (CameraAccessException ex) {
597                    Log.e(TAG, "Failed to apply updated request settings", ex);
598                }
599            } else if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) {
600                // If we're already ready to preview, this doesn't regress our state
601                mCameraState.setState(AndroidCamera2StateHolder.CAMERA_CONFIGURED);
602            }
603        }
604
605        private void setPreviewTexture(SurfaceTexture surfaceTexture) {
606            // TODO: Must be called after providing a .*Settings populated with sizes
607            // TODO: We don't technically offer a selection of sizes tailored to SurfaceTextures!
608
609            // TODO: Handle this error condition with a callback or exception
610            if (mCameraState.getState() < AndroidCamera2StateHolder.CAMERA_CONFIGURED) {
611                Log.w(TAG, "Ignoring texture setting at inappropriate time");
612                return;
613            }
614
615            // Avoid initializing another capture session unless we absolutely have to
616            if (surfaceTexture == mPreviewTexture) {
617                Log.i(TAG, "Optimizing out redundant preview texture setting");
618                return;
619            }
620
621            if (mSession != null) {
622                closePreviewSession();
623            }
624
625            mPreviewTexture = surfaceTexture;
626            surfaceTexture.setDefaultBufferSize(mPreviewSize.width(), mPreviewSize.height());
627
628            if (mPreviewSurface != null) {
629                mPreviewSurface.release();
630            }
631            mPreviewSurface = new Surface(surfaceTexture);
632
633            if (mCaptureReader != null) {
634                mCaptureReader.close();
635            }
636            mCaptureReader = ImageReader.newInstance(
637                    mPhotoSize.width(), mPhotoSize.height(), ImageFormat.JPEG, 1);
638
639            try {
640                mCamera.createCaptureSession(
641                        Arrays.asList(mPreviewSurface, mCaptureReader.getSurface()),
642                        mCameraPreviewStateListener, this);
643            } catch (CameraAccessException ex) {
644                Log.e(TAG, "Failed to create camera capture session", ex);
645            }
646        }
647
648        private void closePreviewSession() {
649            try {
650                mSession.abortCaptures();
651                mSession = null;
652            } catch (CameraAccessException ex) {
653                Log.e(TAG, "Failed to close existing camera capture session", ex);
654            }
655            mCameraState.setState(AndroidCamera2StateHolder.CAMERA_CONFIGURED);
656        }
657
658        // This listener monitors our connection to and disconnection from camera devices.
659        private CameraDevice.StateListener mCameraDeviceStateListener =
660                new CameraDevice.StateListener() {
661            @Override
662            public void onOpened(CameraDevice camera) {
663                mCamera = camera;
664                if (mOpenCallback != null) {
665                    try {
666                        CameraCharacteristics props =
667                                mCameraManager.getCameraCharacteristics(mCameraId);
668                        mCameraProxy = new AndroidCamera2ProxyImpl(mCameraIndex, mCamera,
669                                    getCameraDeviceInfo().getCharacteristics(mCameraIndex), props);
670                        mPersistentSettings = new Camera2RequestSettingsSet();
671                        mActiveArray =
672                                props.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
673                        mLegacyDevice =
674                                props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
675                                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
676                        mCameraState.setState(AndroidCamera2StateHolder.CAMERA_UNCONFIGURED);
677                        mOpenCallback.onCameraOpened(mCameraProxy);
678                    } catch (CameraAccessException ex) {
679                        mOpenCallback.onDeviceOpenFailure(mCameraIndex,
680                                generateHistoryString(mCameraIndex));
681                    }
682                }
683            }
684
685            @Override
686            public void onDisconnected(CameraDevice camera) {
687                Log.w(TAG, "Camera device '" + mCameraIndex + "' was disconnected");
688            }
689
690            @Override
691            public void onError(CameraDevice camera, int error) {
692                Log.e(TAG, "Camera device '" + mCameraIndex + "' encountered error code '" +
693                        error + '\'');
694                if (mOpenCallback != null) {
695                    mOpenCallback.onDeviceOpenFailure(mCameraIndex,
696                            generateHistoryString(mCameraIndex));
697                }
698            }};
699
700        // This listener monitors our camera session (i.e. our transition into and out of preview).
701        private CameraCaptureSession.StateListener mCameraPreviewStateListener =
702                new CameraCaptureSession.StateListener() {
703            @Override
704            public void onConfigured(CameraCaptureSession session) {
705                mSession = session;
706                mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
707            }
708
709            @Override
710            public void onConfigureFailed(CameraCaptureSession session) {
711                // TODO: Invoke a callback
712                Log.e(TAG, "Failed to configure the camera for capture");
713            }
714
715            @Override
716            public void onActive(CameraCaptureSession session) {
717                if (mOneshotPreviewingCallback != null) {
718                    // The session is up and processing preview requests. Inform the caller.
719                    mOneshotPreviewingCallback.onPreviewStarted();
720                    mOneshotPreviewingCallback = null;
721                }
722            }};
723
724        // This listener monitors requested captures and notifies any relevant callbacks.
725        private CameraCaptureSession.CaptureListener mCameraFocusStateListener =
726                new CameraCaptureSession.CaptureListener() {
727            private int mLastAfState = -1;
728
729            @Override
730            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
731                                           TotalCaptureResult result) {
732                Integer afStateMaybe = result.get(CaptureResult.CONTROL_AF_STATE);
733                if (afStateMaybe != null) {
734                    int afState = afStateMaybe;
735                    boolean afStateChanged = false;
736                    if (afState != mLastAfState) {
737                        mLastAfState = afState;
738                        afStateChanged = true;
739                    }
740
741                    switch (afState) {
742                        case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN:
743                        case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED:
744                        case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED: {
745                            if (afStateChanged && mPassiveAfCallback != null) {
746                                // A CameraAFMoveCallback is attached. If we just started to scan,
747                                // the motor is moving; otherwise, it has settled.
748                                mPassiveAfCallback.onAutoFocusMoving(
749                                        afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN,
750                                        mCameraProxy);
751                            }
752                            break;
753                        }
754
755                        case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED:
756                        case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: {
757                            if (mOneshotAfCallback != null) {
758                                // A call to autoFocus() was just made to request a focus lock.
759                                // Notify the caller that the lens is now indefinitely fixed, and
760                                // report whether the image we're now stuck with is in focus.
761                                mOneshotAfCallback.onAutoFocus(
762                                        afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED,
763                                        mCameraProxy);
764                                mOneshotAfCallback = null;
765                            }
766                            break;
767                        }
768                    }
769                }
770
771                Integer aeStateMaybe = result.get(CaptureResult.CONTROL_AE_STATE);
772                if (aeStateMaybe != null) {
773                    int aeState = aeStateMaybe;
774
775                    switch (aeState) {
776                        case CaptureResult.CONTROL_AE_STATE_CONVERGED:
777                        case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED:
778                        case CaptureResult.CONTROL_AE_STATE_LOCKED: {
779                            if (mOneshotCaptureCallback != null) {
780                                // A call to takePicture() was just made, and autoexposure converged
781                                // so it's time to initiate the capture!
782                                mCaptureReader.setOnImageAvailableListener(mOneshotCaptureCallback,
783                                        /*handler*/Camera2Handler.this);
784                                try {
785                                    mSession.capture(
786                                            mPersistentSettings.createRequest(mCamera,
787                                                    CameraDevice.TEMPLATE_STILL_CAPTURE,
788                                                    mCaptureReader.getSurface()),
789                                            /*listener*/mOneshotCaptureCallback,
790                                            /*handler*/Camera2Handler.this);
791                                } catch (CameraAccessException ex) {
792                                    Log.e(TAG, "Unable to initiate capture", ex);
793                                } finally {
794                                    mOneshotCaptureCallback = null;
795                                }
796                            }
797                            break;
798                        }
799                    }
800                }
801            }
802
803            @Override
804            public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
805                                        CaptureFailure failure) {
806                Log.e(TAG, "Capture attempt failed with reason " + failure.getReason());
807            }};
808    }
809
810    private class AndroidCamera2ProxyImpl extends CameraAgent.CameraProxy {
811        private final int mCameraIndex;
812        private final CameraDevice mCamera;
813        private final CameraDeviceInfo.Characteristics mCharacteristics;
814        private final AndroidCamera2Capabilities mCapabilities;
815
816        public AndroidCamera2ProxyImpl(int cameraIndex, CameraDevice camera,
817                CameraDeviceInfo.Characteristics characteristics,
818                CameraCharacteristics properties) {
819            mCameraIndex = cameraIndex;
820            mCamera = camera;
821            mCharacteristics = characteristics;
822            mCapabilities = new AndroidCamera2Capabilities(properties);
823        }
824
825        // TODO: Implement
826        @Override
827        public android.hardware.Camera getCamera() { return null; }
828
829        @Override
830        public int getCameraId() {
831            return mCameraIndex;
832        }
833
834        @Override
835        public CameraDeviceInfo.Characteristics getCharacteristics() {
836            return mCharacteristics;
837        }
838
839        @Override
840        public CameraCapabilities getCapabilities() {
841            return mCapabilities;
842        }
843
844        private AndroidCamera2Capabilities getSpecializedCapabilities() {
845            return mCapabilities;
846        }
847
848        // TODO: Implement
849        @Override
850        public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {}
851
852        // TODO: Implement
853        @Override
854        public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {}
855
856        // TODO: Implement
857        @Override
858        public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb)
859                {}
860
861        @Override
862        public void autoFocus(final Handler handler, final CameraAFCallback cb) {
863            mDispatchThread.runJob(new Runnable() {
864                @Override
865                public void run() {
866                    CameraAFCallback cbForward = null;
867                    if (cb != null) {
868                        cbForward = new CameraAFCallback() {
869                            @Override
870                            public void onAutoFocus(final boolean focused,
871                                                    final CameraProxy camera) {
872                                handler.post(new Runnable() {
873                                    @Override
874                                    public void run() {
875                                        cb.onAutoFocus(focused, camera);
876                                    }});
877                            }};
878                    }
879
880                    mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE |
881                            AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
882                    mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward)
883                            .sendToTarget();
884                }});
885        }
886
887        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
888        @Override
889        public void setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb) {
890            mDispatchThread.runJob(new Runnable() {
891                @Override
892                public void run() {
893                    CameraAFMoveCallback cbForward = null;
894                    if (cb != null) {
895                        cbForward = new CameraAFMoveCallback() {
896                            @Override
897                            public void onAutoFocusMoving(final boolean moving,
898                                                          final CameraProxy camera) {
899                                handler.post(new Runnable() {
900                                    @Override
901                                    public void run() {
902                                        cb.onAutoFocusMoving(moving, camera);
903                                    }});
904                                }};
905                    }
906
907                    mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
908                            cbForward).sendToTarget();
909                }});
910        }
911
912        // TODO: Implement
913        @Override
914        public void takePicture(final Handler handler,
915                                final CameraShutterCallback shutter,
916                                CameraPictureCallback raw,
917                                CameraPictureCallback postview,
918                                final CameraPictureCallback jpeg) {
919            // TODO: We never call raw or postview
920            final CaptureAvailableListener picListener =
921                    new CaptureAvailableListener() {
922                @Override
923                public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
924                                             long timestamp) {
925                    if (shutter != null) {
926                        handler.post(new Runnable() {
927                            @Override
928                            public void run() {
929                                shutter.onShutter(AndroidCamera2ProxyImpl.this);
930                            }});
931                    }
932                }
933
934                @Override
935                public void onImageAvailable(ImageReader reader) {
936                    try (Image image = reader.acquireNextImage()) {
937                        if (jpeg != null) {
938                            ByteBuffer buffer = image.getPlanes()[0].getBuffer();
939                            final byte[] pixels = new byte[buffer.remaining()];
940                            buffer.get(pixels);
941                            handler.post(new Runnable() {
942                                @Override
943                                public void run() {
944                                    jpeg.onPictureTaken(pixels, AndroidCamera2ProxyImpl.this);
945                                }});
946                        }
947                    }
948                }};
949            mDispatchThread.runJob(new Runnable() {
950                @Override
951                public void run() {
952                    mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE |
953                            AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
954                    mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener)
955                            .sendToTarget();
956                }});
957        }
958
959        // TODO: Implement
960        @Override
961        public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {}
962
963        // TODO: Implement
964        @Override
965        public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback)
966                {}
967
968        // TODO: Remove this method override once we handle this message
969        @Override
970        public void startFaceDetection() {}
971
972        // TODO: Remove this method override once we handle this message
973        @Override
974        public void stopFaceDetection() {}
975
976        // TODO: Implement
977        @Override
978        public void setErrorCallback(Handler handler, CameraErrorCallback cb) {}
979
980        // TODO: Implement
981        @Override
982        public void setParameters(android.hardware.Camera.Parameters params) {}
983
984        // TODO: Implement
985        @Override
986        public android.hardware.Camera.Parameters getParameters() { return null; }
987
988        @Override
989        public CameraSettings getSettings() {
990            return mCameraHandler.buildSettings(mCapabilities);
991        }
992
993        @Override
994        public boolean applySettings(CameraSettings settings) {
995            if (settings == null) {
996                Log.w(TAG, "null parameters in applySettings()");
997                return false;
998            }
999            if (!(settings instanceof AndroidCamera2Settings)) {
1000                Log.e(TAG, "Provided settings not compatible with the backing framework API");
1001                return false;
1002            }
1003
1004            return applySettingsHelper(settings, AndroidCamera2StateHolder.CAMERA_UNCONFIGURED |
1005                    AndroidCamera2StateHolder.CAMERA_CONFIGURED |
1006                    AndroidCamera2StateHolder.CAMERA_PREVIEW_READY);
1007        }
1008
1009        // TODO: Implement
1010        @Override
1011        public String dumpDeviceSettings() { return null; }
1012
1013        @Override
1014        public Handler getCameraHandler() {
1015            return AndroidCamera2AgentImpl.this.getCameraHandler();
1016        }
1017
1018        @Override
1019        public DispatchThread getDispatchThread() {
1020            return AndroidCamera2AgentImpl.this.getDispatchThread();
1021        }
1022
1023        @Override
1024        public CameraStateHolder getCameraState() {
1025            return mCameraState;
1026        }
1027    }
1028
1029    /** A linear state machine: each state entails all the states below it. */
1030    private static class AndroidCamera2StateHolder extends CameraStateHolder {
1031        // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() ->
1032        //             autoFocus() -> takePicture()
1033        /* Camera states */
1034        /** No camera device is opened. */
1035        public static final int CAMERA_UNOPENED = 1;
1036        /** A camera is opened, but no settings have been provided. */
1037        public static final int CAMERA_UNCONFIGURED = 2;
1038        /** The open camera has been configured by providing it with settings. */
1039        public static final int CAMERA_CONFIGURED = 3;
1040        /** A capture session is ready to stream a preview, but still has no repeating request. */
1041        public static final int CAMERA_PREVIEW_READY = 4;
1042        /** A preview is currently being streamed. */
1043        public static final int CAMERA_PREVIEW_ACTIVE = 5;
1044        /** The lens is locked on a particular region. */
1045        public static final int CAMERA_FOCUS_LOCKED = 6;
1046
1047        public AndroidCamera2StateHolder() {
1048            this(CAMERA_UNOPENED);
1049        }
1050
1051        public AndroidCamera2StateHolder(int state) {
1052            super(state);
1053        }
1054    }
1055
1056    private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo {
1057        private final CameraManager mCameraManager;
1058        private final String[] mCameraIds;
1059        private final int mNumberOfCameras;
1060        private final int mFirstBackCameraId;
1061        private final int mFirstFrontCameraId;
1062
1063        public AndroidCamera2DeviceInfo(CameraManager cameraManager,
1064                                        String[] cameraIds, int numberOfCameras) {
1065            mCameraManager = cameraManager;
1066            mCameraIds = cameraIds;
1067            mNumberOfCameras = numberOfCameras;
1068
1069            int firstBackId = NO_DEVICE;
1070            int firstFrontId = NO_DEVICE;
1071            for (int id = 0; id < cameraIds.length; ++id) {
1072                try {
1073                    int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id])
1074                            .get(CameraCharacteristics.LENS_FACING);
1075                    if (firstBackId == NO_DEVICE &&
1076                            lensDirection == CameraCharacteristics.LENS_FACING_BACK) {
1077                        firstBackId = id;
1078                    }
1079                    if (firstFrontId == NO_DEVICE &&
1080                            lensDirection == CameraCharacteristics.LENS_FACING_FRONT) {
1081                        firstFrontId = id;
1082                    }
1083                } catch (CameraAccessException ex) {
1084                    Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex);
1085                }
1086            }
1087            mFirstBackCameraId = firstBackId;
1088            mFirstFrontCameraId = firstFrontId;
1089        }
1090
1091        @Override
1092        public Characteristics getCharacteristics(int cameraId) {
1093            String actualId = mCameraIds[cameraId];
1094            try {
1095                CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId);
1096                return new AndroidCharacteristics2(info);
1097            } catch (CameraAccessException ex) {
1098                return null;
1099            }
1100        }
1101
1102        @Override
1103        public int getNumberOfCameras() {
1104            return mNumberOfCameras;
1105        }
1106
1107        @Override
1108        public int getFirstBackCameraId() {
1109            return mFirstBackCameraId;
1110        }
1111
1112        @Override
1113        public int getFirstFrontCameraId() {
1114            return mFirstFrontCameraId;
1115        }
1116
1117        private static class AndroidCharacteristics2 extends Characteristics {
1118            private CameraCharacteristics mCameraInfo;
1119
1120            AndroidCharacteristics2(CameraCharacteristics cameraInfo) {
1121                mCameraInfo = cameraInfo;
1122            }
1123
1124            @Override
1125            public boolean isFacingBack() {
1126                return mCameraInfo.get(CameraCharacteristics.LENS_FACING)
1127                        .equals(CameraCharacteristics.LENS_FACING_BACK);
1128            }
1129
1130            @Override
1131            public boolean isFacingFront() {
1132                return mCameraInfo.get(CameraCharacteristics.LENS_FACING)
1133                        .equals(CameraCharacteristics.LENS_FACING_FRONT);
1134            }
1135
1136            @Override
1137            public int getSensorOrientation() {
1138                return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION);
1139            }
1140
1141            @Override
1142            public boolean canDisableShutterSound() {
1143                // The new API doesn't support this operation, so don't encourage people to try it.
1144                // TODO: What kind of assumptions have callers made about this result's meaning?
1145                return false;
1146            }
1147        }
1148    }
1149
1150    private static final CameraExceptionCallback sCameraExceptionCallback =
1151            new CameraExceptionCallback() {
1152                @Override
1153                public synchronized void onCameraException(RuntimeException e) {
1154                    throw e;
1155                }
1156            };
1157}
1158