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