AndroidCameraAgentImpl.java revision bad9708bd92213e65bfc9761ce3e69f8a59e1483
1/*
2 * Copyright (C) 2013 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.graphics.SurfaceTexture;
21import android.hardware.Camera;
22import android.hardware.Camera.AutoFocusCallback;
23import android.hardware.Camera.AutoFocusMoveCallback;
24import android.hardware.Camera.ErrorCallback;
25import android.hardware.Camera.FaceDetectionListener;
26import android.hardware.Camera.OnZoomChangeListener;
27import android.hardware.Camera.Parameters;
28import android.hardware.Camera.PictureCallback;
29import android.hardware.Camera.PreviewCallback;
30import android.hardware.Camera.ShutterCallback;
31import android.os.Build;
32import android.os.Handler;
33import android.os.HandlerThread;
34import android.os.Looper;
35import android.os.Message;
36import android.view.SurfaceHolder;
37
38import com.android.ex.camera2.portability.debug.Log;
39
40import java.io.IOException;
41import java.util.Collections;
42import java.util.List;
43import java.util.StringTokenizer;
44
45/**
46 * A class to implement {@link CameraAgent} of the Android camera framework.
47 */
48class AndroidCameraAgentImpl extends CameraAgent {
49    private static final Log.Tag TAG = new Log.Tag("AndCamAgntImp");
50
51    private CameraDeviceInfo.Characteristics mCharacteristics;
52    private AndroidCameraCapabilities mCapabilities;
53
54    private final CameraHandler mCameraHandler;
55    private final HandlerThread mCameraHandlerThread;
56    private final CameraStateHolder mCameraState;
57    private final DispatchThread mDispatchThread;
58
59    private Handler mCameraExceptionCallbackHandler;
60    private CameraExceptionCallback mCameraExceptionCallback =
61        new CameraExceptionCallback() {
62            @Override
63            public void onCameraException(RuntimeException e) {
64                throw e;
65            }
66        };
67
68    AndroidCameraAgentImpl() {
69        mCameraHandlerThread = new HandlerThread("Camera Handler Thread");
70        mCameraHandlerThread.start();
71        mCameraHandler = new CameraHandler(mCameraHandlerThread.getLooper());
72        mCameraExceptionCallbackHandler = mCameraHandler;
73        mCameraState = new AndroidCameraStateHolder();
74        mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread);
75        mDispatchThread.start();
76    }
77
78    @Override
79    public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
80            Handler handler) {
81        synchronized (mCameraExceptionCallback) {
82            mCameraExceptionCallback = callback;
83            mCameraExceptionCallbackHandler = handler;
84        }
85    }
86
87    @Override
88    public void recycle() {
89        closeCamera(null, true);
90        mDispatchThread.end();
91    }
92
93    @Override
94    public CameraDeviceInfo getCameraDeviceInfo() {
95        return AndroidCameraDeviceInfo.create();
96    }
97
98    @Override
99    protected Handler getCameraHandler() {
100        return mCameraHandler;
101    }
102
103    @Override
104    protected DispatchThread getDispatchThread() {
105        return mDispatchThread;
106    }
107
108    private static class AndroidCameraDeviceInfo implements CameraDeviceInfo {
109        private final Camera.CameraInfo[] mCameraInfos;
110        private final int mNumberOfCameras;
111        private final int mFirstBackCameraId;
112        private final int mFirstFrontCameraId;
113
114        private AndroidCameraDeviceInfo(Camera.CameraInfo[] info, int numberOfCameras,
115                int firstBackCameraId, int firstFrontCameraId) {
116
117            mCameraInfos = info;
118            mNumberOfCameras = numberOfCameras;
119            mFirstBackCameraId = firstBackCameraId;
120            mFirstFrontCameraId = firstFrontCameraId;
121        }
122
123        public static AndroidCameraDeviceInfo create() {
124            int numberOfCameras;
125            Camera.CameraInfo[] cameraInfos;
126            try {
127                numberOfCameras = Camera.getNumberOfCameras();
128                cameraInfos = new Camera.CameraInfo[numberOfCameras];
129                for (int i = 0; i < numberOfCameras; i++) {
130                    cameraInfos[i] = new Camera.CameraInfo();
131                    Camera.getCameraInfo(i, cameraInfos[i]);
132                }
133            } catch (RuntimeException ex) {
134                Log.e(TAG, "Exception while creating CameraDeviceInfo", ex);
135                return null;
136            }
137
138            int firstFront = NO_DEVICE;
139            int firstBack = NO_DEVICE;
140            // Get the first (smallest) back and first front camera id.
141            for (int i = numberOfCameras - 1; i >= 0; i--) {
142                if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
143                    firstBack = i;
144                } else {
145                    if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
146                        firstFront = i;
147                    }
148                }
149            }
150
151            return new AndroidCameraDeviceInfo(cameraInfos, numberOfCameras, firstBack, firstFront);
152        }
153
154        @Override
155        public Characteristics getCharacteristics(int cameraId) {
156            Camera.CameraInfo info = mCameraInfos[cameraId];
157            if (info != null) {
158                return new AndroidCharacteristics(info);
159            } else {
160                return null;
161            }
162        }
163
164        @Override
165        public int getNumberOfCameras() {
166            return mNumberOfCameras;
167        }
168
169        @Override
170        public int getFirstBackCameraId() {
171            return mFirstBackCameraId;
172        }
173
174        @Override
175        public int getFirstFrontCameraId() {
176            return mFirstFrontCameraId;
177        }
178
179        private static class AndroidCharacteristics extends Characteristics {
180            private Camera.CameraInfo mCameraInfo;
181
182            AndroidCharacteristics(Camera.CameraInfo cameraInfo) {
183                mCameraInfo = cameraInfo;
184            }
185
186            @Override
187            public boolean isFacingBack() {
188                return mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK;
189            }
190
191            @Override
192            public boolean isFacingFront() {
193                return mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT;
194            }
195
196            @Override
197            public int getSensorOrientation() {
198                return mCameraInfo.orientation;
199            }
200
201            @Override
202            public boolean canDisableShutterSound() {
203                return mCameraInfo.canDisableShutterSound;
204            }
205        }
206    }
207
208    private static class ParametersCache {
209        private Parameters mParameters;
210        private Camera mCamera;
211
212        public ParametersCache(Camera camera) {
213            mCamera = camera;
214        }
215
216        public synchronized void invalidate() {
217            mParameters = null;
218        }
219
220        /**
221         * Access parameters from the cache. If cache is empty, block by
222         * retrieving parameters directly from Camera, but if cache is present,
223         * returns immediately.
224         */
225        public synchronized Parameters getBlocking() {
226            if (mParameters == null) {
227                mParameters = mCamera.getParameters();
228                if (mParameters == null) {
229                    Log.e(TAG, "Camera object returned null parameters!");
230                    throw new IllegalStateException("camera.getParameters returned null");
231                }
232            }
233            return mParameters;
234        }
235    }
236
237    /**
238     * The handler on which the actual camera operations happen.
239     */
240    private class CameraHandler extends HistoryHandler {
241
242        private Camera mCamera;
243        private int mCameraId;
244        private ParametersCache mParameterCache;
245        private int mCancelAfPending = 0;
246
247        private class CaptureCallbacks {
248            public final ShutterCallback mShutter;
249            public final PictureCallback mRaw;
250            public final PictureCallback mPostView;
251            public final PictureCallback mJpeg;
252
253            CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView,
254                    PictureCallback jpeg) {
255                mShutter = shutter;
256                mRaw = raw;
257                mPostView = postView;
258                mJpeg = jpeg;
259            }
260        }
261
262        CameraHandler(Looper looper) {
263            super(looper);
264        }
265
266        private void startFaceDetection() {
267            mCamera.startFaceDetection();
268        }
269
270        private void stopFaceDetection() {
271            mCamera.stopFaceDetection();
272        }
273
274        private void setFaceDetectionListener(FaceDetectionListener listener) {
275            mCamera.setFaceDetectionListener(listener);
276        }
277
278        private void setPreviewTexture(Object surfaceTexture) {
279            try {
280                mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
281            } catch (IOException e) {
282                Log.e(TAG, "Could not set preview texture", e);
283            }
284        }
285
286        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
287        private void enableShutterSound(boolean enable) {
288            mCamera.enableShutterSound(enable);
289        }
290
291        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
292        private void setAutoFocusMoveCallback(
293                android.hardware.Camera camera, Object cb) {
294            try {
295                camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
296            } catch (RuntimeException ex) {
297                Log.w(TAG, ex.getMessage());
298            }
299        }
300
301        private void capture(final CaptureCallbacks cb) {
302            try {
303                mCamera.takePicture(cb.mShutter, cb.mRaw, cb.mPostView, cb.mJpeg);
304            } catch (RuntimeException e) {
305                // TODO: output camera state and focus state for debugging.
306                Log.e(TAG, "take picture failed.");
307                throw e;
308            }
309        }
310
311        public void requestTakePicture(
312                final ShutterCallback shutter,
313                final PictureCallback raw,
314                final PictureCallback postView,
315                final PictureCallback jpeg) {
316            final CaptureCallbacks callbacks = new CaptureCallbacks(shutter, raw, postView, jpeg);
317            obtainMessage(CameraActions.CAPTURE_PHOTO, callbacks).sendToTarget();
318        }
319
320        /**
321         * This method does not deal with the API level check.  Everyone should
322         * check first for supported operations before sending message to this handler.
323         */
324        @Override
325        public void handleMessage(final Message msg) {
326            super.handleMessage(msg);
327            Log.v(TAG, "handleMessage - action = '" + CameraActions.stringify(msg.what) + "'");
328            try {
329                switch (msg.what) {
330                    case CameraActions.OPEN_CAMERA: {
331                        final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
332                        final int cameraId = msg.arg1;
333                        if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_UNOPENED) {
334                            openCallback.onDeviceOpenedAlready(cameraId, generateHistoryString(cameraId));
335                            break;
336                        }
337
338                        Log.i(TAG, "Opening camera " + cameraId + " with camera1 API");
339                        mCamera = android.hardware.Camera.open(cameraId);
340                        if (mCamera != null) {
341                            mCameraId = cameraId;
342                            mParameterCache = new ParametersCache(mCamera);
343
344                            mCharacteristics =
345                                    AndroidCameraDeviceInfo.create().getCharacteristics(cameraId);
346                            mCapabilities = new AndroidCameraCapabilities(
347                                    mParameterCache.getBlocking());
348
349                            mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
350                            if (openCallback != null) {
351                                openCallback.onCameraOpened(
352                                        new AndroidCameraProxyImpl(cameraId, mCamera,
353                                                mCharacteristics, mCapabilities));
354                            }
355                        } else {
356                            if (openCallback != null) {
357                                openCallback.onDeviceOpenFailure(cameraId, generateHistoryString(cameraId));
358                            }
359                        }
360                        break;
361                    }
362
363                    case CameraActions.RELEASE: {
364                        if (mCamera != null) {
365                            mCamera.release();
366                            mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED);
367                            mCamera = null;
368                        } else {
369                            Log.w(TAG, "Releasing camera without any camera opened.");
370                        }
371                        break;
372                    }
373
374                    case CameraActions.RECONNECT: {
375                        final CameraOpenCallbackForward cbForward =
376                                (CameraOpenCallbackForward) msg.obj;
377                        final int cameraId = msg.arg1;
378                        try {
379                            mCamera.reconnect();
380                        } catch (IOException ex) {
381                            if (cbForward != null) {
382                                cbForward.onReconnectionFailure(AndroidCameraAgentImpl.this,
383                                        generateHistoryString(mCameraId));
384                            }
385                            break;
386                        }
387
388                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
389                        if (cbForward != null) {
390                            cbForward.onCameraOpened(
391                                    new AndroidCameraProxyImpl(cameraId, mCamera, mCharacteristics,
392                                            mCapabilities));
393                        }
394                        break;
395                    }
396
397                    case CameraActions.UNLOCK: {
398                        mCamera.unlock();
399                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNLOCKED);
400                        break;
401                    }
402
403                    case CameraActions.LOCK: {
404                        mCamera.lock();
405                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
406                        break;
407                    }
408
409                    // TODO: Lock the CameraSettings object's sizes
410                    case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: {
411                        setPreviewTexture(msg.obj);
412                        break;
413                    }
414
415                    case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: {
416                        try {
417                            mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
418                        } catch (IOException e) {
419                            throw new RuntimeException(e);
420                        }
421                        break;
422                    }
423
424                    case CameraActions.START_PREVIEW_ASYNC: {
425                        final CameraStartPreviewCallbackForward cbForward =
426                            (CameraStartPreviewCallbackForward) msg.obj;
427                        mCamera.startPreview();
428                        if (cbForward != null) {
429                            cbForward.onPreviewStarted();
430                        }
431                        break;
432                    }
433
434                    // TODO: Unlock the CameraSettings object's sizes
435                    case CameraActions.STOP_PREVIEW: {
436                        mCamera.stopPreview();
437                        break;
438                    }
439
440                    case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: {
441                        mCamera.setPreviewCallbackWithBuffer((PreviewCallback) msg.obj);
442                        break;
443                    }
444
445                    case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: {
446                        mCamera.setOneShotPreviewCallback((PreviewCallback) msg.obj);
447                        break;
448                    }
449
450                    case CameraActions.ADD_CALLBACK_BUFFER: {
451                        mCamera.addCallbackBuffer((byte[]) msg.obj);
452                        break;
453                    }
454
455                    case CameraActions.AUTO_FOCUS: {
456                        if (mCancelAfPending > 0) {
457                            Log.v(TAG, "handleMessage - Ignored AUTO_FOCUS because there was "
458                                    + mCancelAfPending + " pending CANCEL_AUTO_FOCUS messages");
459                            break; // ignore AF because a CANCEL_AF is queued after this
460                        }
461                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_FOCUSING);
462                        mCamera.autoFocus((AutoFocusCallback) msg.obj);
463                        break;
464                    }
465
466                    case CameraActions.CANCEL_AUTO_FOCUS: {
467                        // Ignore all AFs that were already queued until we see
468                        // a CANCEL_AUTO_FOCUS_FINISH
469                        mCancelAfPending++;
470                        mCamera.cancelAutoFocus();
471                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
472                        break;
473                    }
474
475                    case CameraActions.CANCEL_AUTO_FOCUS_FINISH: {
476                        // Stop ignoring AUTO_FOCUS messages unless there are additional
477                        // CANCEL_AUTO_FOCUSes that were added
478                        mCancelAfPending--;
479                        break;
480                    }
481
482                    case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: {
483                        setAutoFocusMoveCallback(mCamera, msg.obj);
484                        break;
485                    }
486
487                    case CameraActions.SET_DISPLAY_ORIENTATION: {
488                        // Update preview orientation
489                        mCamera.setDisplayOrientation(
490                                mCharacteristics.getPreviewOrientation(msg.arg1));
491                        // Only set the JPEG capture orientation if requested to do so; otherwise,
492                        // capture in the sensor's physical orientation. (e.g., JPEG rotation is
493                        // necessary in auto-rotate mode.
494                        Parameters parameters = mParameterCache.getBlocking();
495                        parameters.setRotation(
496                                msg.arg2 > 0 ? mCharacteristics.getJpegOrientation(msg.arg1) : 0);
497                        mCamera.setParameters(parameters);
498                        mParameterCache.invalidate();
499                        break;
500                    }
501
502                    case CameraActions.SET_JPEG_ORIENTATION: {
503                        Parameters parameters = mParameterCache.getBlocking();
504                        parameters.setRotation(msg.arg1);
505                        mCamera.setParameters(parameters);
506                        mParameterCache.invalidate();
507                        break;
508                    }
509
510                    case CameraActions.SET_ZOOM_CHANGE_LISTENER: {
511                        mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj);
512                        break;
513                    }
514
515                    case CameraActions.SET_FACE_DETECTION_LISTENER: {
516                        setFaceDetectionListener((FaceDetectionListener) msg.obj);
517                        break;
518                    }
519
520                    case CameraActions.START_FACE_DETECTION: {
521                        startFaceDetection();
522                        break;
523                    }
524
525                    case CameraActions.STOP_FACE_DETECTION: {
526                        stopFaceDetection();
527                        break;
528                    }
529
530                    case CameraActions.SET_ERROR_CALLBACK: {
531                        mCamera.setErrorCallback((ErrorCallback) msg.obj);
532                        break;
533                    }
534
535                    case CameraActions.APPLY_SETTINGS: {
536                        Parameters parameters = mParameterCache.getBlocking();
537                        CameraSettings settings = (CameraSettings) msg.obj;
538                        applySettingsToParameters(settings, parameters);
539                        mCamera.setParameters(parameters);
540                        mParameterCache.invalidate();
541                        break;
542                    }
543
544                    case CameraActions.SET_PARAMETERS: {
545                        Parameters parameters = mParameterCache.getBlocking();
546                        parameters.unflatten((String) msg.obj);
547                        mCamera.setParameters(parameters);
548                        mParameterCache.invalidate();
549                        break;
550                    }
551
552                    case CameraActions.GET_PARAMETERS: {
553                        Parameters[] parametersHolder = (Parameters[]) msg.obj;
554                        Parameters parameters = mParameterCache.getBlocking();
555                        parametersHolder[0] = parameters;
556                        break;
557                    }
558
559                    case CameraActions.SET_PREVIEW_CALLBACK: {
560                        mCamera.setPreviewCallback((PreviewCallback) msg.obj);
561                        break;
562                    }
563
564                    case CameraActions.ENABLE_SHUTTER_SOUND: {
565                        enableShutterSound((msg.arg1 == 1) ? true : false);
566                        break;
567                    }
568
569                    case CameraActions.REFRESH_PARAMETERS: {
570                        mParameterCache.invalidate();;
571                        break;
572                    }
573
574                    case CameraActions.CAPTURE_PHOTO: {
575                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_CAPTURING);
576                        capture((CaptureCallbacks) msg.obj);
577                        break;
578                    }
579
580                    default: {
581                        throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
582                    }
583                }
584            } catch (final RuntimeException e) {
585                Log.e(TAG, "Exception during camera operation " + msg.what, e);
586                if (msg.what != CameraActions.RELEASE && mCamera != null) {
587                    try {
588                        mCamera.release();
589                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED);
590                    } catch (Exception ex) {
591                        Log.e(TAG, "Fail to release the camera.");
592                    }
593                    mCamera = null;
594                } else {
595                    if (mCamera == null) {
596                        if (msg.what == CameraActions.OPEN_CAMERA) {
597                            final int cameraId = msg.arg1;
598                            if (msg.obj != null) {
599                                ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(
600                                        msg.arg1, generateHistoryString(cameraId));
601                            }
602                        } else {
603                            Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null.");
604                        }
605                        return;
606                    }
607                }
608                synchronized (mCameraExceptionCallback) {
609                    mCameraExceptionCallbackHandler.post(new Runnable() {
610                        @Override
611                        public void run() {
612                                mCameraExceptionCallback.onCameraException(e);
613                            }
614                        });
615                }
616            } finally {
617                WaitDoneBundle.unblockSyncWaiters(msg);
618            }
619        }
620
621        private void applySettingsToParameters(final CameraSettings settings,
622                final Parameters parameters) {
623            final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier();
624            Size photoSize = settings.getCurrentPhotoSize();
625            parameters.setPictureSize(photoSize.width(), photoSize.height());
626            Size previewSize = settings.getCurrentPreviewSize();
627            parameters.setPreviewSize(previewSize.width(), previewSize.height());
628            if (settings.getPreviewFrameRate() == -1) {
629                parameters.setPreviewFpsRange(settings.getPreviewFpsRangeMin(),
630                        settings.getPreviewFpsRangeMax());
631            } else {
632                parameters.setPreviewFrameRate(settings.getPreviewFrameRate());
633            }
634            parameters.setPreviewFormat(settings.getCurrentPreviewFormat());
635            parameters.setJpegQuality(settings.getPhotoJpegCompressionQuality());
636            if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
637                parameters.setZoom(zoomRatioToIndex(settings.getCurrentZoomRatio(),
638                        parameters.getZoomRatios()));
639            }
640            parameters.setExposureCompensation(settings.getExposureCompensationIndex());
641            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) {
642                parameters.setAutoExposureLock(settings.isAutoExposureLocked());
643            }
644            parameters.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode()));
645            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) {
646                parameters.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked());
647            }
648            if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) {
649                if (settings.getFocusAreas().size() != 0) {
650                    parameters.setFocusAreas(settings.getFocusAreas());
651                } else {
652                    parameters.setFocusAreas(null);
653                }
654            }
655            if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) {
656                if (settings.getMeteringAreas().size() != 0) {
657                    parameters.setMeteringAreas(settings.getMeteringAreas());
658                } else {
659                    parameters.setMeteringAreas(null);
660                }
661            }
662            if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) {
663                parameters.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode()));
664            }
665            if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) {
666                if (settings.getCurrentSceneMode() != null) {
667                    parameters
668                            .setSceneMode(stringifier.stringify(settings.getCurrentSceneMode()));
669                }
670            }
671            parameters.setRecordingHint(settings.isRecordingHintEnabled());
672            Size jpegThumbSize = settings.getExifThumbnailSize();
673            parameters.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
674            parameters.setPictureFormat(settings.getCurrentPhotoFormat());
675
676            CameraSettings.GpsData gpsData = settings.getGpsData();
677            if (gpsData == null) {
678                parameters.removeGpsData();
679            } else {
680                parameters.setGpsTimestamp(gpsData.timeStamp);
681                if (gpsData.processingMethod != null) {
682                    // It's a hack since we always use GPS time stamp but does
683                    // not use other fields sometimes. Setting processing
684                    // method to null means the other fields should not be used.
685                    parameters.setGpsAltitude(gpsData.altitude);
686                    parameters.setGpsLatitude(gpsData.latitude);
687                    parameters.setGpsLongitude(gpsData.longitude);
688                    parameters.setGpsProcessingMethod(gpsData.processingMethod);
689                }
690            }
691
692        }
693
694        /**
695         * @param ratio Desired zoom ratio, in [1.0f,+Inf).
696         * @param percentages Available zoom ratios, as percentages.
697         * @return Index of the closest corresponding ratio, rounded up toward
698         *         that of the maximum available ratio.
699         */
700        private int zoomRatioToIndex(float ratio, List<Integer> percentages) {
701            int percent = (int) (ratio * AndroidCameraCapabilities.ZOOM_MULTIPLIER);
702            int index = Collections.binarySearch(percentages, percent);
703            if (index >= 0) {
704                // Found the desired ratio in the supported list
705                return index;
706            } else {
707                // Didn't find an exact match. Where would it have been?
708                index = -(index + 1);
709                if (index == percentages.size()) {
710                    // Put it back in bounds by setting to the maximum allowable zoom
711                    --index;
712                }
713                return index;
714            }
715        }
716    }
717
718    /**
719     * A class which implements {@link CameraAgent.CameraProxy} and
720     * camera handler thread.
721     */
722    private class AndroidCameraProxyImpl extends CameraAgent.CameraProxy {
723        private final int mCameraId;
724        /* TODO: remove this Camera instance. */
725        private final Camera mCamera;
726        private final CameraDeviceInfo.Characteristics mCharacteristics;
727        private final AndroidCameraCapabilities mCapabilities;
728
729        private AndroidCameraProxyImpl(int cameraId, Camera camera,
730                CameraDeviceInfo.Characteristics characteristics,
731                AndroidCameraCapabilities capabilities) {
732            mCamera = camera;
733            mCameraId = cameraId;
734            mCharacteristics = characteristics;
735            mCapabilities = capabilities;
736        }
737
738        @Deprecated
739        @Override
740        public android.hardware.Camera getCamera() {
741            return mCamera;
742        }
743
744        @Override
745        public int getCameraId() {
746            return mCameraId;
747        }
748
749        @Override
750        public CameraDeviceInfo.Characteristics getCharacteristics() {
751            return mCharacteristics;
752        }
753
754        @Override
755        public CameraCapabilities getCapabilities() {
756            return new AndroidCameraCapabilities(mCapabilities);
757        }
758
759        @Override
760        public void setPreviewDataCallback(
761                final Handler handler, final CameraPreviewDataCallback cb) {
762            mDispatchThread.runJob(new Runnable() {
763                @Override
764                public void run() {
765                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK,
766                            PreviewCallbackForward.getNewInstance(
767                                    handler, AndroidCameraProxyImpl.this, cb))
768                            .sendToTarget();
769                }
770            });
771        }
772
773        @Override
774        public void setOneShotPreviewCallback(final Handler handler,
775                final CameraPreviewDataCallback cb) {
776            mDispatchThread.runJob(new Runnable() {
777                @Override
778                public void run() {
779                    mCameraHandler.obtainMessage(CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK,
780                            PreviewCallbackForward
781                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
782                            .sendToTarget();
783                }
784            });
785        }
786
787        @Override
788        public void setPreviewDataCallbackWithBuffer(
789                final Handler handler, final CameraPreviewDataCallback cb) {
790            mDispatchThread.runJob(new Runnable() {
791                @Override
792                public void run() {
793                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER,
794                            PreviewCallbackForward
795                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
796                            .sendToTarget();
797                }
798            });
799        }
800
801        @Override
802        public void autoFocus(final Handler handler, final CameraAFCallback cb) {
803            final AutoFocusCallback afCallback = new AutoFocusCallback() {
804                @Override
805                public void onAutoFocus(final boolean b, Camera camera) {
806                    if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_FOCUSING) {
807                        Log.w(TAG, "onAutoFocus callback returning when not focusing");
808                    } else {
809                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
810                    }
811                    handler.post(new Runnable() {
812                        @Override
813                        public void run() {
814                            cb.onAutoFocus(b, AndroidCameraProxyImpl.this);
815                        }
816                    });
817                }
818            };
819            mDispatchThread.runJob(new Runnable() {
820                @Override
821                public void run() {
822                    mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE);
823                    mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback)
824                            .sendToTarget();
825                }
826            });
827        }
828
829        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
830        @Override
831        public void setAutoFocusMoveCallback(
832                final Handler handler, final CameraAFMoveCallback cb) {
833            mDispatchThread.runJob(new Runnable() {
834                @Override
835                public void run() {
836                    mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
837                            AFMoveCallbackForward.getNewInstance(
838                                    handler, AndroidCameraProxyImpl.this, cb))
839                            .sendToTarget();
840                }
841            });
842        }
843
844        @Override
845        public void takePicture(
846                final Handler handler, final CameraShutterCallback shutter,
847                final CameraPictureCallback raw, final CameraPictureCallback post,
848                final CameraPictureCallback jpeg) {
849            final PictureCallback jpegCallback = new PictureCallback() {
850                @Override
851                public void onPictureTaken(final byte[] data, Camera camera) {
852                    if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_CAPTURING) {
853                        Log.w(TAG, "picture callback returning when not capturing");
854                    } else {
855                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
856                    }
857                    handler.post(new Runnable() {
858                        @Override
859                        public void run() {
860                            jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this);
861                        }
862                    });
863                }
864            };
865
866            mDispatchThread.runJob(new Runnable() {
867                @Override
868                public void run() {
869                    mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
870                            AndroidCameraStateHolder.CAMERA_UNLOCKED);
871                    mCameraHandler.requestTakePicture(ShutterCallbackForward
872                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
873                            PictureCallbackForward
874                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
875                            PictureCallbackForward
876                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
877                            jpegCallback
878                    );
879                }
880            });
881        }
882
883        @Override
884        public void setZoomChangeListener(final OnZoomChangeListener listener) {
885            mDispatchThread.runJob(new Runnable() {
886                @Override
887                public void run() {
888                    mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener)
889                            .sendToTarget();
890                }
891            });
892        }
893
894        @Override
895        public void setFaceDetectionCallback(final Handler handler,
896                final CameraFaceDetectionCallback cb) {
897            mDispatchThread.runJob(new Runnable() {
898                @Override
899                public void run() {
900                    mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER,
901                            FaceDetectionCallbackForward
902                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
903                            .sendToTarget();
904                }
905            });
906        }
907
908        @Override
909        public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) {
910            mDispatchThread.runJob(new Runnable() {
911                @Override
912                public void run() {
913                    mCameraHandler.obtainMessage(CameraActions.SET_ERROR_CALLBACK,
914                            ErrorCallbackForward.getNewInstance(
915                                    handler, AndroidCameraProxyImpl.this, cb))
916                            .sendToTarget();
917                }
918            });
919        }
920
921        @Deprecated
922        @Override
923        public void setParameters(final Parameters params) {
924            if (params == null) {
925                Log.v(TAG, "null parameters in setParameters()");
926                return;
927            }
928            final String flattenedParameters = params.flatten();
929            mDispatchThread.runJob(new Runnable() {
930                @Override
931                public void run() {
932                    mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
933                            AndroidCameraStateHolder.CAMERA_UNLOCKED);
934                    mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters)
935                            .sendToTarget();
936                }
937            });
938        }
939
940        @Deprecated
941        @Override
942        public Parameters getParameters() {
943            final WaitDoneBundle bundle = new WaitDoneBundle();
944            final Parameters[] parametersHolder = new Parameters[1];
945            mDispatchThread.runJobSync(new Runnable() {
946                @Override
947                public void run() {
948                    mCameraHandler.obtainMessage(
949                            CameraActions.GET_PARAMETERS, parametersHolder).sendToTarget();
950                    mCameraHandler.post(bundle.mUnlockRunnable);
951                }
952            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
953            return parametersHolder[0];
954        }
955
956        @Override
957        public CameraSettings getSettings() {
958            return new AndroidCameraSettings(mCapabilities, getParameters());
959        }
960
961        @Override
962        public boolean applySettings(CameraSettings settings) {
963            return applySettingsHelper(settings, AndroidCameraStateHolder.CAMERA_IDLE |
964                    AndroidCameraStateHolder.CAMERA_UNLOCKED);
965        }
966
967        @Override
968        public String dumpDeviceSettings() {
969            Parameters parameters = getParameters();
970            if (parameters != null) {
971                String flattened = getParameters().flatten();
972                StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
973                String dumpedSettings = new String();
974                while (tokenizer.hasMoreElements()) {
975                    dumpedSettings += tokenizer.nextToken() + '\n';
976                }
977
978                return dumpedSettings;
979            } else {
980                return "[no parameters retrieved]";
981            }
982        }
983
984        @Override
985        public Handler getCameraHandler() {
986            return AndroidCameraAgentImpl.this.getCameraHandler();
987        }
988
989        @Override
990        public DispatchThread getDispatchThread() {
991            return AndroidCameraAgentImpl.this.getDispatchThread();
992        }
993
994        @Override
995        public CameraStateHolder getCameraState() {
996            return mCameraState;
997        }
998    }
999
1000    private static class AndroidCameraStateHolder extends CameraStateHolder {
1001        /* Camera states */
1002        // These states are defined bitwise so we can easily to specify a set of
1003        // states together.
1004        public static final int CAMERA_UNOPENED = 1;
1005        public static final int CAMERA_IDLE = 1 << 1;
1006        public static final int CAMERA_UNLOCKED = 1 << 2;
1007        public static final int CAMERA_CAPTURING = 1 << 3;
1008        public static final int CAMERA_FOCUSING = 1 << 4;
1009
1010        public AndroidCameraStateHolder() {
1011            this(CAMERA_UNOPENED);
1012        }
1013
1014        public AndroidCameraStateHolder(int state) {
1015            super(state);
1016        }
1017    }
1018
1019    /**
1020     * A helper class to forward AutoFocusCallback to another thread.
1021     */
1022    private static class AFCallbackForward implements AutoFocusCallback {
1023        private final Handler mHandler;
1024        private final CameraProxy mCamera;
1025        private final CameraAFCallback mCallback;
1026
1027        /**
1028         * Returns a new instance of {@link AFCallbackForward}.
1029         *
1030         * @param handler The handler in which the callback will be invoked in.
1031         * @param camera  The {@link CameraProxy} which the callback is from.
1032         * @param cb      The callback to be invoked.
1033         * @return        The instance of the {@link AFCallbackForward},
1034         *                or null if any parameter is null.
1035         */
1036        public static AFCallbackForward getNewInstance(
1037                Handler handler, CameraProxy camera, CameraAFCallback cb) {
1038            if (handler == null || camera == null || cb == null) {
1039                return null;
1040            }
1041            return new AFCallbackForward(handler, camera, cb);
1042        }
1043
1044        private AFCallbackForward(
1045                Handler h, CameraProxy camera, CameraAFCallback cb) {
1046            mHandler = h;
1047            mCamera = camera;
1048            mCallback = cb;
1049        }
1050
1051        @Override
1052        public void onAutoFocus(final boolean b, Camera camera) {
1053            mHandler.post(new Runnable() {
1054                @Override
1055                public void run() {
1056                    mCallback.onAutoFocus(b, mCamera);
1057                }
1058            });
1059        }
1060    }
1061
1062    /**
1063     * A helper class to forward ErrorCallback to another thread.
1064     */
1065    private static class ErrorCallbackForward implements Camera.ErrorCallback {
1066        private final Handler mHandler;
1067        private final CameraProxy mCamera;
1068        private final CameraErrorCallback mCallback;
1069
1070        /**
1071         * Returns a new instance of {@link AFCallbackForward}.
1072         *
1073         * @param handler The handler in which the callback will be invoked in.
1074         * @param camera  The {@link CameraProxy} which the callback is from.
1075         * @param cb      The callback to be invoked.
1076         * @return        The instance of the {@link AFCallbackForward},
1077         *                or null if any parameter is null.
1078         */
1079        public static ErrorCallbackForward getNewInstance(
1080                Handler handler, CameraProxy camera, CameraErrorCallback cb) {
1081            if (handler == null || camera == null || cb == null) {
1082                return null;
1083            }
1084            return new ErrorCallbackForward(handler, camera, cb);
1085        }
1086
1087        private ErrorCallbackForward(
1088                Handler h, CameraProxy camera, CameraErrorCallback cb) {
1089            mHandler = h;
1090            mCamera = camera;
1091            mCallback = cb;
1092        }
1093
1094        @Override
1095        public void onError(final int error, Camera camera) {
1096            mHandler.post(new Runnable() {
1097                @Override
1098                public void run() {
1099                    mCallback.onError(error, mCamera);
1100                }
1101            });
1102        }
1103    }
1104
1105    /** A helper class to forward AutoFocusMoveCallback to another thread. */
1106    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1107    private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
1108        private final Handler mHandler;
1109        private final CameraAFMoveCallback mCallback;
1110        private final CameraProxy mCamera;
1111
1112        /**
1113         * Returns a new instance of {@link AFMoveCallbackForward}.
1114         *
1115         * @param handler The handler in which the callback will be invoked in.
1116         * @param camera  The {@link CameraProxy} which the callback is from.
1117         * @param cb      The callback to be invoked.
1118         * @return        The instance of the {@link AFMoveCallbackForward},
1119         *                or null if any parameter is null.
1120         */
1121        public static AFMoveCallbackForward getNewInstance(
1122                Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
1123            if (handler == null || camera == null || cb == null) {
1124                return null;
1125            }
1126            return new AFMoveCallbackForward(handler, camera, cb);
1127        }
1128
1129        private AFMoveCallbackForward(
1130                Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
1131            mHandler = h;
1132            mCamera = camera;
1133            mCallback = cb;
1134        }
1135
1136        @Override
1137        public void onAutoFocusMoving(
1138                final boolean moving, android.hardware.Camera camera) {
1139            mHandler.post(new Runnable() {
1140                @Override
1141                public void run() {
1142                    mCallback.onAutoFocusMoving(moving, mCamera);
1143                }
1144            });
1145        }
1146    }
1147
1148    /**
1149     * A helper class to forward ShutterCallback to to another thread.
1150     */
1151    private static class ShutterCallbackForward implements ShutterCallback {
1152        private final Handler mHandler;
1153        private final CameraShutterCallback mCallback;
1154        private final CameraProxy mCamera;
1155
1156        /**
1157         * Returns a new instance of {@link ShutterCallbackForward}.
1158         *
1159         * @param handler The handler in which the callback will be invoked in.
1160         * @param camera  The {@link CameraProxy} which the callback is from.
1161         * @param cb      The callback to be invoked.
1162         * @return        The instance of the {@link ShutterCallbackForward},
1163         *                or null if any parameter is null.
1164         */
1165        public static ShutterCallbackForward getNewInstance(
1166                Handler handler, CameraProxy camera, CameraShutterCallback cb) {
1167            if (handler == null || camera == null || cb == null) {
1168                return null;
1169            }
1170            return new ShutterCallbackForward(handler, camera, cb);
1171        }
1172
1173        private ShutterCallbackForward(
1174                Handler h, CameraProxy camera, CameraShutterCallback cb) {
1175            mHandler = h;
1176            mCamera = camera;
1177            mCallback = cb;
1178        }
1179
1180        @Override
1181        public void onShutter() {
1182            mHandler.post(new Runnable() {
1183                @Override
1184                public void run() {
1185                    mCallback.onShutter(mCamera);
1186                }
1187            });
1188        }
1189    }
1190
1191    /**
1192     * A helper class to forward PictureCallback to another thread.
1193     */
1194    private static class PictureCallbackForward implements PictureCallback {
1195        private final Handler mHandler;
1196        private final CameraPictureCallback mCallback;
1197        private final CameraProxy mCamera;
1198
1199        /**
1200         * Returns a new instance of {@link PictureCallbackForward}.
1201         *
1202         * @param handler The handler in which the callback will be invoked in.
1203         * @param camera  The {@link CameraProxy} which the callback is from.
1204         * @param cb      The callback to be invoked.
1205         * @return        The instance of the {@link PictureCallbackForward},
1206         *                or null if any parameters is null.
1207         */
1208        public static PictureCallbackForward getNewInstance(
1209                Handler handler, CameraProxy camera, CameraPictureCallback cb) {
1210            if (handler == null || camera == null || cb == null) {
1211                return null;
1212            }
1213            return new PictureCallbackForward(handler, camera, cb);
1214        }
1215
1216        private PictureCallbackForward(
1217                Handler h, CameraProxy camera, CameraPictureCallback cb) {
1218            mHandler = h;
1219            mCamera = camera;
1220            mCallback = cb;
1221        }
1222
1223        @Override
1224        public void onPictureTaken(
1225                final byte[] data, android.hardware.Camera camera) {
1226            mHandler.post(new Runnable() {
1227                @Override
1228                public void run() {
1229                    mCallback.onPictureTaken(data, mCamera);
1230                }
1231            });
1232        }
1233    }
1234
1235    /**
1236     * A helper class to forward PreviewCallback to another thread.
1237     */
1238    private static class PreviewCallbackForward implements PreviewCallback {
1239        private final Handler mHandler;
1240        private final CameraPreviewDataCallback mCallback;
1241        private final CameraProxy mCamera;
1242
1243        /**
1244         * Returns a new instance of {@link PreviewCallbackForward}.
1245         *
1246         * @param handler The handler in which the callback will be invoked in.
1247         * @param camera  The {@link CameraProxy} which the callback is from.
1248         * @param cb      The callback to be invoked.
1249         * @return        The instance of the {@link PreviewCallbackForward},
1250         *                or null if any parameters is null.
1251         */
1252        public static PreviewCallbackForward getNewInstance(
1253                Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
1254            if (handler == null || camera == null || cb == null) {
1255                return null;
1256            }
1257            return new PreviewCallbackForward(handler, camera, cb);
1258        }
1259
1260        private PreviewCallbackForward(
1261                Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
1262            mHandler = h;
1263            mCamera = camera;
1264            mCallback = cb;
1265        }
1266
1267        @Override
1268        public void onPreviewFrame(
1269                final byte[] data, android.hardware.Camera camera) {
1270            mHandler.post(new Runnable() {
1271                @Override
1272                public void run() {
1273                    mCallback.onPreviewFrame(data, mCamera);
1274                }
1275            });
1276        }
1277    }
1278
1279    private static class FaceDetectionCallbackForward implements FaceDetectionListener {
1280        private final Handler mHandler;
1281        private final CameraFaceDetectionCallback mCallback;
1282        private final CameraProxy mCamera;
1283
1284        /**
1285         * Returns a new instance of {@link FaceDetectionCallbackForward}.
1286         *
1287         * @param handler The handler in which the callback will be invoked in.
1288         * @param camera  The {@link CameraProxy} which the callback is from.
1289         * @param cb      The callback to be invoked.
1290         * @return        The instance of the {@link FaceDetectionCallbackForward},
1291         *                or null if any parameter is null.
1292         */
1293        public static FaceDetectionCallbackForward getNewInstance(
1294                Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
1295            if (handler == null || camera == null || cb == null) {
1296                return null;
1297            }
1298            return new FaceDetectionCallbackForward(handler, camera, cb);
1299        }
1300
1301        private FaceDetectionCallbackForward(
1302                Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
1303            mHandler = h;
1304            mCamera = camera;
1305            mCallback = cb;
1306        }
1307
1308        @Override
1309        public void onFaceDetection(
1310                final Camera.Face[] faces, Camera camera) {
1311            mHandler.post(new Runnable() {
1312                @Override
1313                public void run() {
1314                    mCallback.onFaceDetection(faces, mCamera);
1315                }
1316            });
1317        }
1318    }
1319}
1320