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