AndroidCameraAgentImpl.java revision a0842b40441db5332a5290f941021636b1182761
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 implements 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                        mCamera.setDisplayOrientation(msg.arg1);
441                        break;
442                    }
443
444                    case CameraActions.SET_ZOOM_CHANGE_LISTENER: {
445                        mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj);
446                        break;
447                    }
448
449                    case CameraActions.SET_FACE_DETECTION_LISTENER: {
450                        setFaceDetectionListener((FaceDetectionListener) msg.obj);
451                        break;
452                    }
453
454                    case CameraActions.START_FACE_DETECTION: {
455                        startFaceDetection();
456                        break;
457                    }
458
459                    case CameraActions.STOP_FACE_DETECTION: {
460                        stopFaceDetection();
461                        break;
462                    }
463
464                    case CameraActions.SET_ERROR_CALLBACK: {
465                        mCamera.setErrorCallback((ErrorCallback) msg.obj);
466                        break;
467                    }
468
469                    case CameraActions.APPLY_SETTINGS: {
470                        mParametersIsDirty = true;
471                        CameraSettings settings = (CameraSettings) msg.obj;
472                        applyToParameters(settings);
473                        mCamera.setParameters(mParamsToSet);
474                        break;
475                    }
476
477                    case CameraActions.SET_PARAMETERS: {
478                        mParametersIsDirty = true;
479                        mParamsToSet.unflatten((String) msg.obj);
480                        mCamera.setParameters(mParamsToSet);
481                        break;
482                    }
483
484                    case CameraActions.GET_PARAMETERS: {
485                        if (mParametersIsDirty) {
486                            mParameters = mCamera.getParameters();
487                            mParametersIsDirty = false;
488                        }
489                        break;
490                    }
491
492                    case CameraActions.SET_PREVIEW_CALLBACK: {
493                        mCamera.setPreviewCallback((PreviewCallback) msg.obj);
494                        break;
495                    }
496
497                    case CameraActions.ENABLE_SHUTTER_SOUND: {
498                        enableShutterSound((msg.arg1 == 1) ? true : false);
499                        break;
500                    }
501
502                    case CameraActions.REFRESH_PARAMETERS: {
503                        mParametersIsDirty = true;
504                        break;
505                    }
506
507                    case CameraActions.CAPTURE_PHOTO: {
508                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_CAPTURING);
509                        capture((CaptureCallbacks) msg.obj);
510                        break;
511                    }
512
513                    default: {
514                        throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
515                    }
516                }
517            } catch (final RuntimeException e) {
518                if (msg.what != CameraActions.RELEASE && mCamera != null) {
519                    try {
520                        mCamera.release();
521                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED);
522                    } catch (Exception ex) {
523                        Log.e(TAG, "Fail to release the camera.");
524                    }
525                    mCamera = null;
526                } else {
527                    if (mCamera == null) {
528                        if (msg.what == CameraActions.OPEN_CAMERA) {
529                            final int cameraId = msg.arg1;
530                            if (msg.obj != null) {
531                                ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(
532                                        msg.arg1, generateHistoryString(cameraId));
533                            }
534                        } else {
535                            Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null.");
536                        }
537                        return;
538                    }
539                }
540                synchronized (mCameraExceptionCallback) {
541                    mCameraExceptionCallbackHandler.post(new Runnable() {
542                        @Override
543                        public void run() {
544                                mCameraExceptionCallback.onCameraException(e);
545                            }
546                        });
547                }
548            }
549        }
550
551        private void applyToParameters(final CameraSettings settings) {
552            final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier();
553            Size photoSize = settings.getCurrentPhotoSize();
554            mParamsToSet.setPictureSize(photoSize.width(), photoSize.height());
555            Size previewSize = settings.getCurrentPreviewSize();
556            mParamsToSet.setPreviewSize(previewSize.width(), previewSize.height());
557            if (settings.getPreviewFrameRate() == -1) {
558                mParamsToSet.setPreviewFpsRange(settings.getPreviewFpsRangeMin(),
559                        settings.getPreviewFpsRangeMax());
560            } else {
561                mParamsToSet.setPreviewFrameRate(settings.getPreviewFrameRate());
562            }
563            mParamsToSet.setPreviewFormat(settings.getCurrentPreviewFormat());
564            mParamsToSet.setJpegQuality(settings.getPhotoJpegCompressionQuality());
565            if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
566                // Should use settings.getCurrentZoomRatio() instead here.
567                mParamsToSet.setZoom(settings.getCurrentZoomIndex());
568            }
569            mParamsToSet.setRotation((int) settings.getCurrentPhotoRotationDegrees());
570            mParamsToSet.setExposureCompensation(settings.getExposureCompensationIndex());
571            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) {
572                mParamsToSet.setAutoExposureLock(settings.isAutoExposureLocked());
573            }
574            mParamsToSet.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode()));
575            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) {
576                mParamsToSet.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked());
577            }
578            if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) {
579                if (settings.getFocusAreas().size() != 0) {
580                    mParamsToSet.setFocusAreas(settings.getFocusAreas());
581                }
582            }
583            if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) {
584                if (settings.getMeteringAreas().size() != 0) {
585                    mParamsToSet.setMeteringAreas(settings.getMeteringAreas());
586                }
587            }
588            if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) {
589                mParamsToSet.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode()));
590            }
591            if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) {
592                if (settings.getCurrentSceneMode() != null) {
593                    mParamsToSet
594                            .setSceneMode(stringifier.stringify(settings.getCurrentSceneMode()));
595                }
596            }
597            mParamsToSet.setRecordingHint(settings.isRecordingHintEnabled());
598            Size jpegThumbSize = settings.getExifThumbnailSize();
599            mParamsToSet.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
600            mParamsToSet.setPictureFormat(settings.getCurrentPhotoFormat());
601
602            CameraSettings.GpsData gpsData = settings.getGpsData();
603            if (gpsData == null) {
604                mParamsToSet.removeGpsData();
605            } else {
606                mParamsToSet.setGpsTimestamp(gpsData.timeStamp);
607                if (gpsData.processingMethod != null) {
608                    // It's a hack since we always use GPS time stamp but does
609                    // not use other fields sometimes. Setting processing
610                    // method to null means the other fields should not be used.
611                    mParamsToSet.setGpsAltitude(gpsData.altitude);
612                    mParamsToSet.setGpsLatitude(gpsData.latitude);
613                    mParamsToSet.setGpsLongitude(gpsData.longitude);
614                    mParamsToSet.setGpsProcessingMethod(gpsData.processingMethod);
615                }
616            }
617
618        }
619    }
620
621    /**
622     * A class which implements {@link CameraAgent.CameraProxy} and
623     * camera handler thread.
624     */
625    private class AndroidCameraProxyImpl extends CameraAgent.CameraProxy {
626        private final int mCameraId;
627        /* TODO: remove this Camera instance. */
628        private final Camera mCamera;
629        private final CameraDeviceInfo.Characteristics mCharacteristics;
630        private final AndroidCameraCapabilities mCapabilities;
631
632        private AndroidCameraProxyImpl(int cameraId, Camera camera,
633                CameraDeviceInfo.Characteristics characteristics,
634                AndroidCameraCapabilities capabilities) {
635            mCamera = camera;
636            mCameraId = cameraId;
637            mCharacteristics = characteristics;
638            mCapabilities = capabilities;
639        }
640
641        @Override
642        public android.hardware.Camera getCamera() {
643            return mCamera;
644        }
645
646        @Override
647        public int getCameraId() {
648            return mCameraId;
649        }
650
651        @Override
652        public CameraDeviceInfo.Characteristics getCharacteristics() {
653            return mCharacteristics;
654        }
655
656        @Override
657        public CameraCapabilities getCapabilities() {
658            return new AndroidCameraCapabilities(mCapabilities);
659        }
660
661        @Override
662        public void setPreviewDataCallback(
663                final Handler handler, final CameraPreviewDataCallback cb) {
664            mDispatchThread.runJob(new Runnable() {
665                @Override
666                public void run() {
667                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK,
668                            PreviewCallbackForward.getNewInstance(
669                                    handler, AndroidCameraProxyImpl.this, cb))
670                            .sendToTarget();
671                }
672            });
673        }
674
675        @Override
676        public void setOneShotPreviewCallback(final Handler handler,
677                final CameraPreviewDataCallback cb) {
678            mDispatchThread.runJob(new Runnable() {
679                @Override
680                public void run() {
681                    mCameraHandler.obtainMessage(CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK,
682                            PreviewCallbackForward
683                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
684                            .sendToTarget();
685                }
686            });
687        }
688
689        @Override
690        public void setPreviewDataCallbackWithBuffer(
691                final Handler handler, final CameraPreviewDataCallback cb) {
692            mDispatchThread.runJob(new Runnable() {
693                @Override
694                public void run() {
695                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER,
696                            PreviewCallbackForward
697                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
698                            .sendToTarget();
699                }
700            });
701        }
702
703        @Override
704        public void autoFocus(final Handler handler, final CameraAFCallback cb) {
705            final AutoFocusCallback afCallback = new AutoFocusCallback() {
706                @Override
707                public void onAutoFocus(final boolean b, Camera camera) {
708                    if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_FOCUSING) {
709                        Log.w(TAG, "onAutoFocus callback returning when not focusing");
710                    } else {
711                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
712                    }
713                    handler.post(new Runnable() {
714                        @Override
715                        public void run() {
716                            cb.onAutoFocus(b, AndroidCameraProxyImpl.this);
717                        }
718                    });
719                }
720            };
721            mDispatchThread.runJob(new Runnable() {
722                @Override
723                public void run() {
724                    mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE);
725                    mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback)
726                            .sendToTarget();
727                }
728            });
729        }
730
731        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
732        @Override
733        public void setAutoFocusMoveCallback(
734                final Handler handler, final CameraAFMoveCallback cb) {
735            mDispatchThread.runJob(new Runnable() {
736                @Override
737                public void run() {
738                    mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
739                            AFMoveCallbackForward.getNewInstance(
740                                    handler, AndroidCameraProxyImpl.this, cb))
741                            .sendToTarget();
742                }
743            });
744        }
745
746        @Override
747        public void takePicture(
748                final Handler handler, final CameraShutterCallback shutter,
749                final CameraPictureCallback raw, final CameraPictureCallback post,
750                final CameraPictureCallback jpeg) {
751            final PictureCallback jpegCallback = new PictureCallback() {
752                @Override
753                public void onPictureTaken(final byte[] data, Camera camera) {
754                    if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_CAPTURING) {
755                        Log.w(TAG, "picture callback returning when not capturing");
756                    } else {
757                        mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
758                    }
759                    handler.post(new Runnable() {
760                        @Override
761                        public void run() {
762                            jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this);
763                        }
764                    });
765                }
766            };
767
768            mDispatchThread.runJob(new Runnable() {
769                @Override
770                public void run() {
771                    mCameraState.waitForStates(CameraStateHolder.CAMERA_IDLE |
772                            AndroidCameraStateHolder.CAMERA_UNLOCKED);
773                    mCameraHandler.requestTakePicture(ShutterCallbackForward
774                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
775                            PictureCallbackForward
776                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
777                            PictureCallbackForward
778                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
779                            jpegCallback
780                    );
781                }
782            });
783        }
784
785        @Override
786        public void setZoomChangeListener(final OnZoomChangeListener listener) {
787            mDispatchThread.runJob(new Runnable() {
788                @Override
789                public void run() {
790                    mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener)
791                            .sendToTarget();
792                }
793            });
794        }
795
796        @Override
797        public void setFaceDetectionCallback(final Handler handler,
798                final CameraFaceDetectionCallback cb) {
799            mDispatchThread.runJob(new Runnable() {
800                @Override
801                public void run() {
802                    mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER,
803                            FaceDetectionCallbackForward
804                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
805                            .sendToTarget();
806                }
807            });
808        }
809
810        @Override
811        public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) {
812            mDispatchThread.runJob(new Runnable() {
813                @Override
814                public void run() {
815                    mCameraHandler.obtainMessage(CameraActions.SET_ERROR_CALLBACK,
816                            ErrorCallbackForward.getNewInstance(
817                                    handler, AndroidCameraProxyImpl.this, cb))
818                            .sendToTarget();
819                }
820            });
821        }
822
823        @Override
824        public void setParameters(final Parameters params) {
825            if (params == null) {
826                Log.v(TAG, "null parameters in setParameters()");
827                return;
828            }
829            final String flattenedParameters = params.flatten();
830            mDispatchThread.runJob(new Runnable() {
831                @Override
832                public void run() {
833                    mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
834                            AndroidCameraStateHolder.CAMERA_UNLOCKED);
835                    mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters)
836                            .sendToTarget();
837                }
838            });
839        }
840
841        @Override
842        public Parameters getParameters() {
843            final WaitDoneBundle bundle = new WaitDoneBundle();
844            mDispatchThread.runJobSync(new Runnable() {
845                @Override
846                public void run() {
847                    mCameraHandler.sendEmptyMessage(CameraActions.GET_PARAMETERS);
848                    mCameraHandler.post(bundle.mUnlockRunnable);
849                }
850            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
851            return mParameters;
852        }
853
854        @Override
855        public CameraSettings getSettings() {
856            return new AndroidCameraSettings(mCapabilities, getParameters());
857        }
858
859        @Override
860        public boolean applySettings(CameraSettings settings) {
861            return applySettingsHelper(settings, AndroidCameraStateHolder.CAMERA_IDLE |
862                    AndroidCameraStateHolder.CAMERA_UNLOCKED);
863        }
864
865        @Override
866        public String dumpDeviceSettings() {
867            String flattened = mParameters.flatten();
868            StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
869            String dumpedSettings = new String();
870            while (tokenizer.hasMoreElements()) {
871                dumpedSettings += tokenizer.nextToken() + '\n';
872            }
873
874            return dumpedSettings;
875        }
876
877        @Override
878        public Handler getCameraHandler() {
879            return AndroidCameraAgentImpl.this.getCameraHandler();
880        }
881
882        @Override
883        public DispatchThread getDispatchThread() {
884            return AndroidCameraAgentImpl.this.getDispatchThread();
885        }
886
887        @Override
888        public CameraStateHolder getCameraState() {
889            return mCameraState;
890        }
891    }
892
893    private static class AndroidCameraStateHolder extends CameraStateHolder {
894        /* Camera states */
895        // These states are defined bitwise so we can easily to specify a set of
896        // states together.
897        public static final int CAMERA_UNOPENED = 1;
898        public static final int CAMERA_IDLE = 1 << 1;
899        public static final int CAMERA_UNLOCKED = 1 << 2;
900        public static final int CAMERA_CAPTURING = 1 << 3;
901        public static final int CAMERA_FOCUSING = 1 << 4;
902
903        private int mState;
904
905        public AndroidCameraStateHolder() {
906            this(CAMERA_UNOPENED);
907        }
908
909        public AndroidCameraStateHolder(int state) {
910            setState(state);
911        }
912
913        @Override
914        public synchronized void setState(int state) {
915            mState = state;
916            this.notifyAll();
917        }
918
919        @Override
920        public synchronized int getState() {
921            return mState;
922        }
923    }
924
925    /**
926     * A helper class to forward AutoFocusCallback to another thread.
927     */
928    private static class AFCallbackForward implements AutoFocusCallback {
929        private final Handler mHandler;
930        private final CameraProxy mCamera;
931        private final CameraAFCallback mCallback;
932
933        /**
934         * Returns a new instance of {@link AFCallbackForward}.
935         *
936         * @param handler The handler in which the callback will be invoked in.
937         * @param camera  The {@link CameraProxy} which the callback is from.
938         * @param cb      The callback to be invoked.
939         * @return        The instance of the {@link AFCallbackForward},
940         *                or null if any parameter is null.
941         */
942        public static AFCallbackForward getNewInstance(
943                Handler handler, CameraProxy camera, CameraAFCallback cb) {
944            if (handler == null || camera == null || cb == null) {
945                return null;
946            }
947            return new AFCallbackForward(handler, camera, cb);
948        }
949
950        private AFCallbackForward(
951                Handler h, CameraProxy camera, CameraAFCallback cb) {
952            mHandler = h;
953            mCamera = camera;
954            mCallback = cb;
955        }
956
957        @Override
958        public void onAutoFocus(final boolean b, Camera camera) {
959            mHandler.post(new Runnable() {
960                @Override
961                public void run() {
962                    mCallback.onAutoFocus(b, mCamera);
963                }
964            });
965        }
966    }
967
968    /**
969     * A helper class to forward ErrorCallback to another thread.
970     */
971    private static class ErrorCallbackForward implements Camera.ErrorCallback {
972        private final Handler mHandler;
973        private final CameraProxy mCamera;
974        private final CameraErrorCallback mCallback;
975
976        /**
977         * Returns a new instance of {@link AFCallbackForward}.
978         *
979         * @param handler The handler in which the callback will be invoked in.
980         * @param camera  The {@link CameraProxy} which the callback is from.
981         * @param cb      The callback to be invoked.
982         * @return        The instance of the {@link AFCallbackForward},
983         *                or null if any parameter is null.
984         */
985        public static ErrorCallbackForward getNewInstance(
986                Handler handler, CameraProxy camera, CameraErrorCallback cb) {
987            if (handler == null || camera == null || cb == null) {
988                return null;
989            }
990            return new ErrorCallbackForward(handler, camera, cb);
991        }
992
993        private ErrorCallbackForward(
994                Handler h, CameraProxy camera, CameraErrorCallback cb) {
995            mHandler = h;
996            mCamera = camera;
997            mCallback = cb;
998        }
999
1000        @Override
1001        public void onError(final int error, Camera camera) {
1002            mHandler.post(new Runnable() {
1003                @Override
1004                public void run() {
1005                    mCallback.onError(error, mCamera);
1006                }
1007            });
1008        }
1009    }
1010
1011    /** A helper class to forward AutoFocusMoveCallback to another thread. */
1012    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1013    private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
1014        private final Handler mHandler;
1015        private final CameraAFMoveCallback mCallback;
1016        private final CameraProxy mCamera;
1017
1018        /**
1019         * Returns a new instance of {@link AFMoveCallbackForward}.
1020         *
1021         * @param handler The handler in which the callback will be invoked in.
1022         * @param camera  The {@link CameraProxy} which the callback is from.
1023         * @param cb      The callback to be invoked.
1024         * @return        The instance of the {@link AFMoveCallbackForward},
1025         *                or null if any parameter is null.
1026         */
1027        public static AFMoveCallbackForward getNewInstance(
1028                Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
1029            if (handler == null || camera == null || cb == null) {
1030                return null;
1031            }
1032            return new AFMoveCallbackForward(handler, camera, cb);
1033        }
1034
1035        private AFMoveCallbackForward(
1036                Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
1037            mHandler = h;
1038            mCamera = camera;
1039            mCallback = cb;
1040        }
1041
1042        @Override
1043        public void onAutoFocusMoving(
1044                final boolean moving, android.hardware.Camera camera) {
1045            mHandler.post(new Runnable() {
1046                @Override
1047                public void run() {
1048                    mCallback.onAutoFocusMoving(moving, mCamera);
1049                }
1050            });
1051        }
1052    }
1053
1054    /**
1055     * A helper class to forward ShutterCallback to to another thread.
1056     */
1057    private static class ShutterCallbackForward implements ShutterCallback {
1058        private final Handler mHandler;
1059        private final CameraShutterCallback mCallback;
1060        private final CameraProxy mCamera;
1061
1062        /**
1063         * Returns a new instance of {@link ShutterCallbackForward}.
1064         *
1065         * @param handler The handler in which the callback will be invoked in.
1066         * @param camera  The {@link CameraProxy} which the callback is from.
1067         * @param cb      The callback to be invoked.
1068         * @return        The instance of the {@link ShutterCallbackForward},
1069         *                or null if any parameter is null.
1070         */
1071        public static ShutterCallbackForward getNewInstance(
1072                Handler handler, CameraProxy camera, CameraShutterCallback cb) {
1073            if (handler == null || camera == null || cb == null) {
1074                return null;
1075            }
1076            return new ShutterCallbackForward(handler, camera, cb);
1077        }
1078
1079        private ShutterCallbackForward(
1080                Handler h, CameraProxy camera, CameraShutterCallback cb) {
1081            mHandler = h;
1082            mCamera = camera;
1083            mCallback = cb;
1084        }
1085
1086        @Override
1087        public void onShutter() {
1088            mHandler.post(new Runnable() {
1089                @Override
1090                public void run() {
1091                    mCallback.onShutter(mCamera);
1092                }
1093            });
1094        }
1095    }
1096
1097    /**
1098     * A helper class to forward PictureCallback to another thread.
1099     */
1100    private static class PictureCallbackForward implements PictureCallback {
1101        private final Handler mHandler;
1102        private final CameraPictureCallback mCallback;
1103        private final CameraProxy mCamera;
1104
1105        /**
1106         * Returns a new instance of {@link PictureCallbackForward}.
1107         *
1108         * @param handler The handler in which the callback will be invoked in.
1109         * @param camera  The {@link CameraProxy} which the callback is from.
1110         * @param cb      The callback to be invoked.
1111         * @return        The instance of the {@link PictureCallbackForward},
1112         *                or null if any parameters is null.
1113         */
1114        public static PictureCallbackForward getNewInstance(
1115                Handler handler, CameraProxy camera, CameraPictureCallback cb) {
1116            if (handler == null || camera == null || cb == null) {
1117                return null;
1118            }
1119            return new PictureCallbackForward(handler, camera, cb);
1120        }
1121
1122        private PictureCallbackForward(
1123                Handler h, CameraProxy camera, CameraPictureCallback cb) {
1124            mHandler = h;
1125            mCamera = camera;
1126            mCallback = cb;
1127        }
1128
1129        @Override
1130        public void onPictureTaken(
1131                final byte[] data, android.hardware.Camera camera) {
1132            mHandler.post(new Runnable() {
1133                @Override
1134                public void run() {
1135                    mCallback.onPictureTaken(data, mCamera);
1136                }
1137            });
1138        }
1139    }
1140
1141    /**
1142     * A helper class to forward PreviewCallback to another thread.
1143     */
1144    private static class PreviewCallbackForward implements PreviewCallback {
1145        private final Handler mHandler;
1146        private final CameraPreviewDataCallback mCallback;
1147        private final CameraProxy mCamera;
1148
1149        /**
1150         * Returns a new instance of {@link PreviewCallbackForward}.
1151         *
1152         * @param handler The handler in which the callback will be invoked in.
1153         * @param camera  The {@link CameraProxy} which the callback is from.
1154         * @param cb      The callback to be invoked.
1155         * @return        The instance of the {@link PreviewCallbackForward},
1156         *                or null if any parameters is null.
1157         */
1158        public static PreviewCallbackForward getNewInstance(
1159                Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
1160            if (handler == null || camera == null || cb == null) {
1161                return null;
1162            }
1163            return new PreviewCallbackForward(handler, camera, cb);
1164        }
1165
1166        private PreviewCallbackForward(
1167                Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
1168            mHandler = h;
1169            mCamera = camera;
1170            mCallback = cb;
1171        }
1172
1173        @Override
1174        public void onPreviewFrame(
1175                final byte[] data, android.hardware.Camera camera) {
1176            mHandler.post(new Runnable() {
1177                @Override
1178                public void run() {
1179                    mCallback.onPreviewFrame(data, mCamera);
1180                }
1181            });
1182        }
1183    }
1184
1185    private static class FaceDetectionCallbackForward implements FaceDetectionListener {
1186        private final Handler mHandler;
1187        private final CameraFaceDetectionCallback mCallback;
1188        private final CameraProxy mCamera;
1189
1190        /**
1191         * Returns a new instance of {@link FaceDetectionCallbackForward}.
1192         *
1193         * @param handler The handler in which the callback will be invoked in.
1194         * @param camera  The {@link CameraProxy} which the callback is from.
1195         * @param cb      The callback to be invoked.
1196         * @return        The instance of the {@link FaceDetectionCallbackForward},
1197         *                or null if any parameter is null.
1198         */
1199        public static FaceDetectionCallbackForward getNewInstance(
1200                Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
1201            if (handler == null || camera == null || cb == null) {
1202                return null;
1203            }
1204            return new FaceDetectionCallbackForward(handler, camera, cb);
1205        }
1206
1207        private FaceDetectionCallbackForward(
1208                Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
1209            mHandler = h;
1210            mCamera = camera;
1211            mCallback = cb;
1212        }
1213
1214        @Override
1215        public void onFaceDetection(
1216                final Camera.Face[] faces, Camera camera) {
1217            mHandler.post(new Runnable() {
1218                @Override
1219                public void run() {
1220                    mCallback.onFaceDetection(faces, mCamera);
1221                }
1222            });
1223        }
1224    }
1225}
1226