AndroidCameraAgentImpl.java revision de48004068f8c16f9a56c60b0ed2485a67687b4b
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.StringTokenizer;
42
43/**
44 * A class to implement {@link CameraAgent} of the Android camera framework.
45 */
46class AndroidCameraAgentImpl extends CameraAgent {
47    private static final Log.Tag TAG = new Log.Tag("AndCamAgntImp");
48
49    private Parameters mParameters;
50    private boolean mParametersIsDirty;
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                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    /**
208     * The handler on which the actual camera operations happen.
209     */
210    private class CameraHandler extends HistoryHandler {
211
212        // Used to retain a copy of Parameters for setting parameters.
213        private Parameters mParamsToSet;
214        private Camera mCamera;
215        private int mCameraId;
216
217        private class CaptureCallbacks {
218            public final ShutterCallback mShutter;
219            public final PictureCallback mRaw;
220            public final PictureCallback mPostView;
221            public final PictureCallback mJpeg;
222
223            CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView,
224                    PictureCallback jpeg) {
225                mShutter = shutter;
226                mRaw = raw;
227                mPostView = postView;
228                mJpeg = jpeg;
229            }
230        }
231
232        CameraHandler(Looper looper) {
233            super(looper);
234        }
235
236        private void startFaceDetection() {
237            mCamera.startFaceDetection();
238        }
239
240        private void stopFaceDetection() {
241            mCamera.stopFaceDetection();
242        }
243
244        private void setFaceDetectionListener(FaceDetectionListener listener) {
245            mCamera.setFaceDetectionListener(listener);
246        }
247
248        private void setPreviewTexture(Object surfaceTexture) {
249            try {
250                mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
251            } catch (IOException e) {
252                Log.e(TAG, "Could not set preview texture", e);
253            }
254        }
255
256        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
257        private void enableShutterSound(boolean enable) {
258            mCamera.enableShutterSound(enable);
259        }
260
261        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
262        private void setAutoFocusMoveCallback(
263                android.hardware.Camera camera, Object cb) {
264            try {
265                camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
266            } catch (RuntimeException ex) {
267                Log.w(TAG, ex.getMessage());
268            }
269        }
270
271        private void capture(final CaptureCallbacks cb) {
272            try {
273                mCamera.takePicture(cb.mShutter, cb.mRaw, cb.mPostView, cb.mJpeg);
274            } catch (RuntimeException e) {
275                // TODO: output camera state and focus state for debugging.
276                Log.e(TAG, "take picture failed.");
277                throw e;
278            }
279        }
280
281        public void requestTakePicture(
282                final ShutterCallback shutter,
283                final PictureCallback raw,
284                final PictureCallback postView,
285                final PictureCallback jpeg) {
286            final CaptureCallbacks callbacks = new CaptureCallbacks(shutter, raw, postView, jpeg);
287            obtainMessage(CameraActions.CAPTURE_PHOTO, callbacks).sendToTarget();
288        }
289
290        /**
291         * This method does not deal with the API level check.  Everyone should
292         * check first for supported operations before sending message to this handler.
293         */
294        @Override
295        public void handleMessage(final Message msg) {
296            super.handleMessage(msg);
297            try {
298                switch (msg.what) {
299                    case CameraActions.OPEN_CAMERA: {
300                        final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
301                        final int cameraId = msg.arg1;
302                        if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_UNOPENED) {
303                            openCallback.onDeviceOpenedAlready(cameraId, generateHistoryString(cameraId));
304                            break;
305                        }
306
307                        mCamera = android.hardware.Camera.open(cameraId);
308                        if (mCamera != null) {
309                            mCameraId = cameraId;
310                            mParametersIsDirty = true;
311
312                            // Get an instance of Camera.Parameters for later use.
313                            mParamsToSet = mCamera.getParameters();
314                            mCharacteristics =
315                                    AndroidCameraDeviceInfo.create().getCharacteristics(cameraId);
316                            mCapabilities = new AndroidCameraCapabilities(mParamsToSet);
317
318                            mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
319                            if (openCallback != null) {
320                                openCallback.onCameraOpened(
321                                        new AndroidCameraProxyImpl(cameraId, mCamera,
322                                                mCharacteristics, mCapabilities));
323                            }
324                        } else {
325                            if (openCallback != null) {
326                                openCallback.onDeviceOpenFailure(cameraId, generateHistoryString(cameraId));
327                            }
328                        }
329                        break;
330                    }
331
332                    case CameraActions.RELEASE: {
333                        if (mCamera != null) {
334                            mCamera.release();
335                            mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED);
336                            mCamera = null;
337                        } else {
338                            Log.w(TAG, "Releasing camera without any camera opened.");
339                        }
340                        break;
341                    }
342
343                    case CameraActions.RECONNECT: {
344                        final CameraOpenCallbackForward cbForward =
345                                (CameraOpenCallbackForward) msg.obj;
346                        final int cameraId = msg.arg1;
347                        try {
348                            mCamera.reconnect();
349                        } catch (IOException ex) {
350                            if (cbForward != null) {
351                                cbForward.onReconnectionFailure(AndroidCameraAgentImpl.this,
352                                        generateHistoryString(mCameraId));
353                            }
354                            break;
355                        }
356
357                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
358                        if (cbForward != null) {
359                            cbForward.onCameraOpened(
360                                    new AndroidCameraProxyImpl(cameraId, mCamera, mCharacteristics,
361                                            mCapabilities));
362                        }
363                        break;
364                    }
365
366                    case CameraActions.UNLOCK: {
367                        mCamera.unlock();
368                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNLOCKED);
369                        break;
370                    }
371
372                    case CameraActions.LOCK: {
373                        mCamera.lock();
374                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
375                        break;
376                    }
377
378                    case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: {
379                        setPreviewTexture(msg.obj);
380                        break;
381                    }
382
383                    case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: {
384                        try {
385                            mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
386                        } catch (IOException e) {
387                            throw new RuntimeException(e);
388                        }
389                        break;
390                    }
391
392                    case CameraActions.START_PREVIEW_ASYNC: {
393                        final CameraStartPreviewCallbackForward cbForward =
394                            (CameraStartPreviewCallbackForward) msg.obj;
395                        mCamera.startPreview();
396                        if (cbForward != null) {
397                            cbForward.onPreviewStarted();
398                        }
399                        break;
400                    }
401
402                    case CameraActions.STOP_PREVIEW: {
403                        mCamera.stopPreview();
404                        break;
405                    }
406
407                    case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: {
408                        mCamera.setPreviewCallbackWithBuffer((PreviewCallback) msg.obj);
409                        break;
410                    }
411
412                    case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: {
413                        mCamera.setOneShotPreviewCallback((PreviewCallback) msg.obj);
414                        break;
415                    }
416
417                    case CameraActions.ADD_CALLBACK_BUFFER: {
418                        mCamera.addCallbackBuffer((byte[]) msg.obj);
419                        break;
420                    }
421
422                    case CameraActions.AUTO_FOCUS: {
423                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_FOCUSING);
424                        mCamera.autoFocus((AutoFocusCallback) msg.obj);
425                        break;
426                    }
427
428                    case CameraActions.CANCEL_AUTO_FOCUS: {
429                        mCamera.cancelAutoFocus();
430                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
431                        break;
432                    }
433
434                    case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: {
435                        setAutoFocusMoveCallback(mCamera, msg.obj);
436                        break;
437                    }
438
439                    case CameraActions.SET_DISPLAY_ORIENTATION: {
440                        // Update preview orientation
441                        mCamera.setDisplayOrientation(
442                                mCharacteristics.getPreviewOrientation(msg.arg1));
443                        // Only set the JPEG capture orientation if requested to do so; otherwise,
444                        // capture in the sensor's physical orientation
445                        mParamsToSet.setRotation(
446                                msg.arg2 > 0 ? mCharacteristics.getJpegOrientation(msg.arg1) : 0);
447                        mCamera.setParameters(mParamsToSet);
448                        break;
449                    }
450
451                    case CameraActions.SET_ZOOM_CHANGE_LISTENER: {
452                        mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj);
453                        break;
454                    }
455
456                    case CameraActions.SET_FACE_DETECTION_LISTENER: {
457                        setFaceDetectionListener((FaceDetectionListener) msg.obj);
458                        break;
459                    }
460
461                    case CameraActions.START_FACE_DETECTION: {
462                        startFaceDetection();
463                        break;
464                    }
465
466                    case CameraActions.STOP_FACE_DETECTION: {
467                        stopFaceDetection();
468                        break;
469                    }
470
471                    case CameraActions.SET_ERROR_CALLBACK: {
472                        mCamera.setErrorCallback((ErrorCallback) msg.obj);
473                        break;
474                    }
475
476                    case CameraActions.APPLY_SETTINGS: {
477                        mParametersIsDirty = true;
478                        CameraSettings settings = (CameraSettings) msg.obj;
479                        applyToParameters(settings);
480                        mCamera.setParameters(mParamsToSet);
481                        break;
482                    }
483
484                    case CameraActions.SET_PARAMETERS: {
485                        mParametersIsDirty = true;
486                        mParamsToSet.unflatten((String) msg.obj);
487                        mCamera.setParameters(mParamsToSet);
488                        break;
489                    }
490
491                    case CameraActions.GET_PARAMETERS: {
492                        if (mParametersIsDirty) {
493                            mParameters = mCamera.getParameters();
494                            mParametersIsDirty = false;
495                        }
496                        break;
497                    }
498
499                    case CameraActions.SET_PREVIEW_CALLBACK: {
500                        mCamera.setPreviewCallback((PreviewCallback) msg.obj);
501                        break;
502                    }
503
504                    case CameraActions.ENABLE_SHUTTER_SOUND: {
505                        enableShutterSound((msg.arg1 == 1) ? true : false);
506                        break;
507                    }
508
509                    case CameraActions.REFRESH_PARAMETERS: {
510                        mParametersIsDirty = true;
511                        break;
512                    }
513
514                    case CameraActions.CAPTURE_PHOTO: {
515                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_CAPTURING);
516                        capture((CaptureCallbacks) msg.obj);
517                        break;
518                    }
519
520                    default: {
521                        throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
522                    }
523                }
524            } catch (final RuntimeException e) {
525                if (msg.what != CameraActions.RELEASE && mCamera != null) {
526                    try {
527                        mCamera.release();
528                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED);
529                    } catch (Exception ex) {
530                        Log.e(TAG, "Fail to release the camera.");
531                    }
532                    mCamera = null;
533                } else {
534                    if (mCamera == null) {
535                        if (msg.what == CameraActions.OPEN_CAMERA) {
536                            final int cameraId = msg.arg1;
537                            if (msg.obj != null) {
538                                ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(
539                                        msg.arg1, generateHistoryString(cameraId));
540                            }
541                        } else {
542                            Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null.");
543                        }
544                        return;
545                    }
546                }
547                synchronized (mCameraExceptionCallback) {
548                    mCameraExceptionCallbackHandler.post(new Runnable() {
549                        @Override
550                        public void run() {
551                                mCameraExceptionCallback.onCameraException(e);
552                            }
553                        });
554                }
555            }
556        }
557
558        private void applyToParameters(final CameraSettings settings) {
559            final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier();
560            Size photoSize = settings.getCurrentPhotoSize();
561            mParamsToSet.setPictureSize(photoSize.width(), photoSize.height());
562            Size previewSize = settings.getCurrentPreviewSize();
563            mParamsToSet.setPreviewSize(previewSize.width(), previewSize.height());
564            if (settings.getPreviewFrameRate() == -1) {
565                mParamsToSet.setPreviewFpsRange(settings.getPreviewFpsRangeMin(),
566                        settings.getPreviewFpsRangeMax());
567            } else {
568                mParamsToSet.setPreviewFrameRate(settings.getPreviewFrameRate());
569            }
570            mParamsToSet.setPreviewFormat(settings.getCurrentPreviewFormat());
571            mParamsToSet.setJpegQuality(settings.getPhotoJpegCompressionQuality());
572            if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
573                // Should use settings.getCurrentZoomRatio() instead here.
574                mParamsToSet.setZoom(settings.getCurrentZoomIndex());
575            }
576            mParamsToSet.setExposureCompensation(settings.getExposureCompensationIndex());
577            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) {
578                mParamsToSet.setAutoExposureLock(settings.isAutoExposureLocked());
579            }
580            mParamsToSet.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode()));
581            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) {
582                mParamsToSet.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked());
583            }
584            if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) {
585                if (settings.getFocusAreas().size() != 0) {
586                    mParamsToSet.setFocusAreas(settings.getFocusAreas());
587                }
588            }
589            if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) {
590                if (settings.getMeteringAreas().size() != 0) {
591                    mParamsToSet.setMeteringAreas(settings.getMeteringAreas());
592                }
593            }
594            if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) {
595                mParamsToSet.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode()));
596            }
597            if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) {
598                if (settings.getCurrentSceneMode() != null) {
599                    mParamsToSet
600                            .setSceneMode(stringifier.stringify(settings.getCurrentSceneMode()));
601                }
602            }
603            mParamsToSet.setRecordingHint(settings.isRecordingHintEnabled());
604            Size jpegThumbSize = settings.getExifThumbnailSize();
605            mParamsToSet.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
606            mParamsToSet.setPictureFormat(settings.getCurrentPhotoFormat());
607
608            CameraSettings.GpsData gpsData = settings.getGpsData();
609            if (gpsData == null) {
610                mParamsToSet.removeGpsData();
611            } else {
612                mParamsToSet.setGpsTimestamp(gpsData.timeStamp);
613                if (gpsData.processingMethod != null) {
614                    // It's a hack since we always use GPS time stamp but does
615                    // not use other fields sometimes. Setting processing
616                    // method to null means the other fields should not be used.
617                    mParamsToSet.setGpsAltitude(gpsData.altitude);
618                    mParamsToSet.setGpsLatitude(gpsData.latitude);
619                    mParamsToSet.setGpsLongitude(gpsData.longitude);
620                    mParamsToSet.setGpsProcessingMethod(gpsData.processingMethod);
621                }
622            }
623
624        }
625    }
626
627    /**
628     * A class which implements {@link CameraAgent.CameraProxy} and
629     * camera handler thread.
630     */
631    private class AndroidCameraProxyImpl extends CameraAgent.CameraProxy {
632        private final int mCameraId;
633        /* TODO: remove this Camera instance. */
634        private final Camera mCamera;
635        private final CameraDeviceInfo.Characteristics mCharacteristics;
636        private final AndroidCameraCapabilities mCapabilities;
637
638        private AndroidCameraProxyImpl(int cameraId, Camera camera,
639                CameraDeviceInfo.Characteristics characteristics,
640                AndroidCameraCapabilities capabilities) {
641            mCamera = camera;
642            mCameraId = cameraId;
643            mCharacteristics = characteristics;
644            mCapabilities = capabilities;
645        }
646
647        @Override
648        public android.hardware.Camera getCamera() {
649            return mCamera;
650        }
651
652        @Override
653        public int getCameraId() {
654            return mCameraId;
655        }
656
657        @Override
658        public CameraDeviceInfo.Characteristics getCharacteristics() {
659            return mCharacteristics;
660        }
661
662        @Override
663        public CameraCapabilities getCapabilities() {
664            return new AndroidCameraCapabilities(mCapabilities);
665        }
666
667        @Override
668        public void setPreviewDataCallback(
669                final Handler handler, final CameraPreviewDataCallback cb) {
670            mDispatchThread.runJob(new Runnable() {
671                @Override
672                public void run() {
673                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK,
674                            PreviewCallbackForward.getNewInstance(
675                                    handler, AndroidCameraProxyImpl.this, cb))
676                            .sendToTarget();
677                }
678            });
679        }
680
681        @Override
682        public void setOneShotPreviewCallback(final Handler handler,
683                final CameraPreviewDataCallback cb) {
684            mDispatchThread.runJob(new Runnable() {
685                @Override
686                public void run() {
687                    mCameraHandler.obtainMessage(CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK,
688                            PreviewCallbackForward
689                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
690                            .sendToTarget();
691                }
692            });
693        }
694
695        @Override
696        public void setPreviewDataCallbackWithBuffer(
697                final Handler handler, final CameraPreviewDataCallback cb) {
698            mDispatchThread.runJob(new Runnable() {
699                @Override
700                public void run() {
701                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER,
702                            PreviewCallbackForward
703                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
704                            .sendToTarget();
705                }
706            });
707        }
708
709        @Override
710        public void autoFocus(final Handler handler, final CameraAFCallback cb) {
711            final AutoFocusCallback afCallback = new AutoFocusCallback() {
712                @Override
713                public void onAutoFocus(final boolean b, Camera camera) {
714                    if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_FOCUSING) {
715                        Log.w(TAG, "onAutoFocus callback returning when not focusing");
716                    } else {
717                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
718                    }
719                    handler.post(new Runnable() {
720                        @Override
721                        public void run() {
722                            cb.onAutoFocus(b, AndroidCameraProxyImpl.this);
723                        }
724                    });
725                }
726            };
727            mDispatchThread.runJob(new Runnable() {
728                @Override
729                public void run() {
730                    mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE);
731                    mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback)
732                            .sendToTarget();
733                }
734            });
735        }
736
737        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
738        @Override
739        public void setAutoFocusMoveCallback(
740                final Handler handler, final CameraAFMoveCallback cb) {
741            mDispatchThread.runJob(new Runnable() {
742                @Override
743                public void run() {
744                    mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
745                            AFMoveCallbackForward.getNewInstance(
746                                    handler, AndroidCameraProxyImpl.this, cb))
747                            .sendToTarget();
748                }
749            });
750        }
751
752        @Override
753        public void takePicture(
754                final Handler handler, final CameraShutterCallback shutter,
755                final CameraPictureCallback raw, final CameraPictureCallback post,
756                final CameraPictureCallback jpeg) {
757            final PictureCallback jpegCallback = new PictureCallback() {
758                @Override
759                public void onPictureTaken(final byte[] data, Camera camera) {
760                    if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_CAPTURING) {
761                        Log.w(TAG, "picture callback returning when not capturing");
762                    } else {
763                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
764                    }
765                    handler.post(new Runnable() {
766                        @Override
767                        public void run() {
768                            jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this);
769                        }
770                    });
771                }
772            };
773
774            mDispatchThread.runJob(new Runnable() {
775                @Override
776                public void run() {
777                    mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
778                            AndroidCameraStateHolder.CAMERA_UNLOCKED);
779                    mCameraHandler.requestTakePicture(ShutterCallbackForward
780                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
781                            PictureCallbackForward
782                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
783                            PictureCallbackForward
784                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
785                            jpegCallback
786                    );
787                }
788            });
789        }
790
791        @Override
792        public void setZoomChangeListener(final OnZoomChangeListener listener) {
793            mDispatchThread.runJob(new Runnable() {
794                @Override
795                public void run() {
796                    mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener)
797                            .sendToTarget();
798                }
799            });
800        }
801
802        @Override
803        public void setFaceDetectionCallback(final Handler handler,
804                final CameraFaceDetectionCallback cb) {
805            mDispatchThread.runJob(new Runnable() {
806                @Override
807                public void run() {
808                    mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER,
809                            FaceDetectionCallbackForward
810                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
811                            .sendToTarget();
812                }
813            });
814        }
815
816        @Override
817        public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) {
818            mDispatchThread.runJob(new Runnable() {
819                @Override
820                public void run() {
821                    mCameraHandler.obtainMessage(CameraActions.SET_ERROR_CALLBACK,
822                            ErrorCallbackForward.getNewInstance(
823                                    handler, AndroidCameraProxyImpl.this, cb))
824                            .sendToTarget();
825                }
826            });
827        }
828
829        @Override
830        public void setParameters(final Parameters params) {
831            if (params == null) {
832                Log.v(TAG, "null parameters in setParameters()");
833                return;
834            }
835            final String flattenedParameters = params.flatten();
836            mDispatchThread.runJob(new Runnable() {
837                @Override
838                public void run() {
839                    mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
840                            AndroidCameraStateHolder.CAMERA_UNLOCKED);
841                    mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters)
842                            .sendToTarget();
843                }
844            });
845        }
846
847        @Override
848        public Parameters getParameters() {
849            final WaitDoneBundle bundle = new WaitDoneBundle();
850            mDispatchThread.runJobSync(new Runnable() {
851                @Override
852                public void run() {
853                    mCameraHandler.sendEmptyMessage(CameraActions.GET_PARAMETERS);
854                    mCameraHandler.post(bundle.mUnlockRunnable);
855                }
856            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
857            return mParameters;
858        }
859
860        @Override
861        public CameraSettings getSettings() {
862            return new AndroidCameraSettings(mCapabilities, getParameters());
863        }
864
865        @Override
866        public boolean applySettings(CameraSettings settings) {
867            return applySettingsHelper(settings, AndroidCameraStateHolder.CAMERA_IDLE |
868                    AndroidCameraStateHolder.CAMERA_UNLOCKED);
869        }
870
871        @Override
872        public String dumpDeviceSettings() {
873            String flattened = mParameters.flatten();
874            StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
875            String dumpedSettings = new String();
876            while (tokenizer.hasMoreElements()) {
877                dumpedSettings += tokenizer.nextToken() + '\n';
878            }
879
880            return dumpedSettings;
881        }
882
883        @Override
884        public Handler getCameraHandler() {
885            return AndroidCameraAgentImpl.this.getCameraHandler();
886        }
887
888        @Override
889        public DispatchThread getDispatchThread() {
890            return AndroidCameraAgentImpl.this.getDispatchThread();
891        }
892
893        @Override
894        public CameraStateHolder getCameraState() {
895            return mCameraState;
896        }
897    }
898
899    private static class AndroidCameraStateHolder extends CameraStateHolder {
900        /* Camera states */
901        // These states are defined bitwise so we can easily to specify a set of
902        // states together.
903        public static final int CAMERA_UNOPENED = 1;
904        public static final int CAMERA_IDLE = 1 << 1;
905        public static final int CAMERA_UNLOCKED = 1 << 2;
906        public static final int CAMERA_CAPTURING = 1 << 3;
907        public static final int CAMERA_FOCUSING = 1 << 4;
908
909        public AndroidCameraStateHolder() {
910            this(CAMERA_UNOPENED);
911        }
912
913        public AndroidCameraStateHolder(int state) {
914            super(state);
915        }
916    }
917
918    /**
919     * A helper class to forward AutoFocusCallback to another thread.
920     */
921    private static class AFCallbackForward implements AutoFocusCallback {
922        private final Handler mHandler;
923        private final CameraProxy mCamera;
924        private final CameraAFCallback mCallback;
925
926        /**
927         * Returns a new instance of {@link AFCallbackForward}.
928         *
929         * @param handler The handler in which the callback will be invoked in.
930         * @param camera  The {@link CameraProxy} which the callback is from.
931         * @param cb      The callback to be invoked.
932         * @return        The instance of the {@link AFCallbackForward},
933         *                or null if any parameter is null.
934         */
935        public static AFCallbackForward getNewInstance(
936                Handler handler, CameraProxy camera, CameraAFCallback cb) {
937            if (handler == null || camera == null || cb == null) {
938                return null;
939            }
940            return new AFCallbackForward(handler, camera, cb);
941        }
942
943        private AFCallbackForward(
944                Handler h, CameraProxy camera, CameraAFCallback cb) {
945            mHandler = h;
946            mCamera = camera;
947            mCallback = cb;
948        }
949
950        @Override
951        public void onAutoFocus(final boolean b, Camera camera) {
952            mHandler.post(new Runnable() {
953                @Override
954                public void run() {
955                    mCallback.onAutoFocus(b, mCamera);
956                }
957            });
958        }
959    }
960
961    /**
962     * A helper class to forward ErrorCallback to another thread.
963     */
964    private static class ErrorCallbackForward implements Camera.ErrorCallback {
965        private final Handler mHandler;
966        private final CameraProxy mCamera;
967        private final CameraErrorCallback mCallback;
968
969        /**
970         * Returns a new instance of {@link AFCallbackForward}.
971         *
972         * @param handler The handler in which the callback will be invoked in.
973         * @param camera  The {@link CameraProxy} which the callback is from.
974         * @param cb      The callback to be invoked.
975         * @return        The instance of the {@link AFCallbackForward},
976         *                or null if any parameter is null.
977         */
978        public static ErrorCallbackForward getNewInstance(
979                Handler handler, CameraProxy camera, CameraErrorCallback cb) {
980            if (handler == null || camera == null || cb == null) {
981                return null;
982            }
983            return new ErrorCallbackForward(handler, camera, cb);
984        }
985
986        private ErrorCallbackForward(
987                Handler h, CameraProxy camera, CameraErrorCallback cb) {
988            mHandler = h;
989            mCamera = camera;
990            mCallback = cb;
991        }
992
993        @Override
994        public void onError(final int error, Camera camera) {
995            mHandler.post(new Runnable() {
996                @Override
997                public void run() {
998                    mCallback.onError(error, mCamera);
999                }
1000            });
1001        }
1002    }
1003
1004    /** A helper class to forward AutoFocusMoveCallback to another thread. */
1005    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1006    private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
1007        private final Handler mHandler;
1008        private final CameraAFMoveCallback mCallback;
1009        private final CameraProxy mCamera;
1010
1011        /**
1012         * Returns a new instance of {@link AFMoveCallbackForward}.
1013         *
1014         * @param handler The handler in which the callback will be invoked in.
1015         * @param camera  The {@link CameraProxy} which the callback is from.
1016         * @param cb      The callback to be invoked.
1017         * @return        The instance of the {@link AFMoveCallbackForward},
1018         *                or null if any parameter is null.
1019         */
1020        public static AFMoveCallbackForward getNewInstance(
1021                Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
1022            if (handler == null || camera == null || cb == null) {
1023                return null;
1024            }
1025            return new AFMoveCallbackForward(handler, camera, cb);
1026        }
1027
1028        private AFMoveCallbackForward(
1029                Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
1030            mHandler = h;
1031            mCamera = camera;
1032            mCallback = cb;
1033        }
1034
1035        @Override
1036        public void onAutoFocusMoving(
1037                final boolean moving, android.hardware.Camera camera) {
1038            mHandler.post(new Runnable() {
1039                @Override
1040                public void run() {
1041                    mCallback.onAutoFocusMoving(moving, mCamera);
1042                }
1043            });
1044        }
1045    }
1046
1047    /**
1048     * A helper class to forward ShutterCallback to to another thread.
1049     */
1050    private static class ShutterCallbackForward implements ShutterCallback {
1051        private final Handler mHandler;
1052        private final CameraShutterCallback mCallback;
1053        private final CameraProxy mCamera;
1054
1055        /**
1056         * Returns a new instance of {@link ShutterCallbackForward}.
1057         *
1058         * @param handler The handler in which the callback will be invoked in.
1059         * @param camera  The {@link CameraProxy} which the callback is from.
1060         * @param cb      The callback to be invoked.
1061         * @return        The instance of the {@link ShutterCallbackForward},
1062         *                or null if any parameter is null.
1063         */
1064        public static ShutterCallbackForward getNewInstance(
1065                Handler handler, CameraProxy camera, CameraShutterCallback cb) {
1066            if (handler == null || camera == null || cb == null) {
1067                return null;
1068            }
1069            return new ShutterCallbackForward(handler, camera, cb);
1070        }
1071
1072        private ShutterCallbackForward(
1073                Handler h, CameraProxy camera, CameraShutterCallback cb) {
1074            mHandler = h;
1075            mCamera = camera;
1076            mCallback = cb;
1077        }
1078
1079        @Override
1080        public void onShutter() {
1081            mHandler.post(new Runnable() {
1082                @Override
1083                public void run() {
1084                    mCallback.onShutter(mCamera);
1085                }
1086            });
1087        }
1088    }
1089
1090    /**
1091     * A helper class to forward PictureCallback to another thread.
1092     */
1093    private static class PictureCallbackForward implements PictureCallback {
1094        private final Handler mHandler;
1095        private final CameraPictureCallback mCallback;
1096        private final CameraProxy mCamera;
1097
1098        /**
1099         * Returns a new instance of {@link PictureCallbackForward}.
1100         *
1101         * @param handler The handler in which the callback will be invoked in.
1102         * @param camera  The {@link CameraProxy} which the callback is from.
1103         * @param cb      The callback to be invoked.
1104         * @return        The instance of the {@link PictureCallbackForward},
1105         *                or null if any parameters is null.
1106         */
1107        public static PictureCallbackForward getNewInstance(
1108                Handler handler, CameraProxy camera, CameraPictureCallback cb) {
1109            if (handler == null || camera == null || cb == null) {
1110                return null;
1111            }
1112            return new PictureCallbackForward(handler, camera, cb);
1113        }
1114
1115        private PictureCallbackForward(
1116                Handler h, CameraProxy camera, CameraPictureCallback cb) {
1117            mHandler = h;
1118            mCamera = camera;
1119            mCallback = cb;
1120        }
1121
1122        @Override
1123        public void onPictureTaken(
1124                final byte[] data, android.hardware.Camera camera) {
1125            mHandler.post(new Runnable() {
1126                @Override
1127                public void run() {
1128                    mCallback.onPictureTaken(data, mCamera);
1129                }
1130            });
1131        }
1132    }
1133
1134    /**
1135     * A helper class to forward PreviewCallback to another thread.
1136     */
1137    private static class PreviewCallbackForward implements PreviewCallback {
1138        private final Handler mHandler;
1139        private final CameraPreviewDataCallback mCallback;
1140        private final CameraProxy mCamera;
1141
1142        /**
1143         * Returns a new instance of {@link PreviewCallbackForward}.
1144         *
1145         * @param handler The handler in which the callback will be invoked in.
1146         * @param camera  The {@link CameraProxy} which the callback is from.
1147         * @param cb      The callback to be invoked.
1148         * @return        The instance of the {@link PreviewCallbackForward},
1149         *                or null if any parameters is null.
1150         */
1151        public static PreviewCallbackForward getNewInstance(
1152                Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
1153            if (handler == null || camera == null || cb == null) {
1154                return null;
1155            }
1156            return new PreviewCallbackForward(handler, camera, cb);
1157        }
1158
1159        private PreviewCallbackForward(
1160                Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
1161            mHandler = h;
1162            mCamera = camera;
1163            mCallback = cb;
1164        }
1165
1166        @Override
1167        public void onPreviewFrame(
1168                final byte[] data, android.hardware.Camera camera) {
1169            mHandler.post(new Runnable() {
1170                @Override
1171                public void run() {
1172                    mCallback.onPreviewFrame(data, mCamera);
1173                }
1174            });
1175        }
1176    }
1177
1178    private static class FaceDetectionCallbackForward implements FaceDetectionListener {
1179        private final Handler mHandler;
1180        private final CameraFaceDetectionCallback mCallback;
1181        private final CameraProxy mCamera;
1182
1183        /**
1184         * Returns a new instance of {@link FaceDetectionCallbackForward}.
1185         *
1186         * @param handler The handler in which the callback will be invoked in.
1187         * @param camera  The {@link CameraProxy} which the callback is from.
1188         * @param cb      The callback to be invoked.
1189         * @return        The instance of the {@link FaceDetectionCallbackForward},
1190         *                or null if any parameter is null.
1191         */
1192        public static FaceDetectionCallbackForward getNewInstance(
1193                Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
1194            if (handler == null || camera == null || cb == null) {
1195                return null;
1196            }
1197            return new FaceDetectionCallbackForward(handler, camera, cb);
1198        }
1199
1200        private FaceDetectionCallbackForward(
1201                Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
1202            mHandler = h;
1203            mCamera = camera;
1204            mCallback = cb;
1205        }
1206
1207        @Override
1208        public void onFaceDetection(
1209                final Camera.Face[] faces, Camera camera) {
1210            mHandler.post(new Runnable() {
1211                @Override
1212                public void run() {
1213                    mCallback.onFaceDetection(faces, mCamera);
1214                }
1215            });
1216        }
1217    }
1218}
1219