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