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