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