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