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