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