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