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