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