1/*
2 * Copyright (C) 2012 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.OnZoomChangeListener;
23import android.os.Build;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.view.SurfaceHolder;
28
29import com.android.ex.camera2.portability.debug.Log;
30
31/**
32 * An interface which provides possible camera device operations.
33 *
34 * The client should call {@code CameraAgent.openCamera} to get an instance
35 * of {@link CameraAgent.CameraProxy} to control the camera. Classes
36 * implementing this interface should have its own one unique {@code Thread}
37 * other than the main thread for camera operations. Camera device callbacks
38 * are wrapped since the client should not deal with
39 * {@code android.hardware.Camera} directly.
40 *
41 * TODO: provide callback interfaces for:
42 * {@code android.hardware.Camera.ErrorCallback},
43 * {@code android.hardware.Camera.OnZoomChangeListener}, and
44 */
45public abstract class CameraAgent {
46    public static final long CAMERA_OPERATION_TIMEOUT_MS = 3500;
47
48    private static final Log.Tag TAG = new Log.Tag("CamAgnt");
49
50    public static class CameraStartPreviewCallbackForward
51            implements CameraStartPreviewCallback {
52        private final Handler mHandler;
53        private final CameraStartPreviewCallback mCallback;
54
55        public static CameraStartPreviewCallbackForward getNewInstance(
56                Handler handler, CameraStartPreviewCallback cb) {
57            if (handler == null || cb == null) {
58                return null;
59            }
60            return new CameraStartPreviewCallbackForward(handler, cb);
61        }
62
63        private CameraStartPreviewCallbackForward(Handler h,
64                CameraStartPreviewCallback cb) {
65            mHandler = h;
66            mCallback = cb;
67        }
68
69        @Override
70        public void onPreviewStarted() {
71            mHandler.post(new Runnable() {
72                @Override
73                public void run() {
74                    mCallback.onPreviewStarted();
75                }});
76        }
77    }
78
79    /**
80     * A callback helps to invoke the original callback on another
81     * {@link android.os.Handler}.
82     */
83    public static class CameraOpenCallbackForward implements CameraOpenCallback {
84        private final Handler mHandler;
85        private final CameraOpenCallback mCallback;
86
87        /**
88         * Returns a new instance of {@link FaceDetectionCallbackForward}.
89         *
90         * @param handler The handler in which the callback will be invoked in.
91         * @param cb The callback to be invoked.
92         * @return The instance of the {@link FaceDetectionCallbackForward}, or
93         *         null if any parameter is null.
94         */
95        public static CameraOpenCallbackForward getNewInstance(
96                Handler handler, CameraOpenCallback cb) {
97            if (handler == null || cb == null) {
98                return null;
99            }
100            return new CameraOpenCallbackForward(handler, cb);
101        }
102
103        private CameraOpenCallbackForward(Handler h, CameraOpenCallback cb) {
104            // Given that we are using the main thread handler, we can create it
105            // here instead of holding onto the PhotoModule objects. In this
106            // way, we can avoid memory leak.
107            mHandler = new Handler(Looper.getMainLooper());
108            mCallback = cb;
109        }
110
111        @Override
112        public void onCameraOpened(final CameraProxy camera) {
113            mHandler.post(new Runnable() {
114                @Override
115                public void run() {
116                    mCallback.onCameraOpened(camera);
117                }});
118        }
119
120        @Override
121        public void onCameraDisabled(final int cameraId) {
122            mHandler.post(new Runnable() {
123                @Override
124                public void run() {
125                    mCallback.onCameraDisabled(cameraId);
126                }});
127        }
128
129        @Override
130        public void onDeviceOpenFailure(final int cameraId, final String info) {
131            mHandler.post(new Runnable() {
132                @Override
133                public void run() {
134                    mCallback.onDeviceOpenFailure(cameraId, info);
135                }});
136        }
137
138        @Override
139        public void onDeviceOpenedAlready(final int cameraId, final String info) {
140            mHandler.post(new Runnable() {
141                @Override
142                public void run() {
143                    mCallback.onDeviceOpenedAlready(cameraId, info);
144                }});
145        }
146
147        @Override
148        public void onReconnectionFailure(final CameraAgent mgr, final String info) {
149            mHandler.post(new Runnable() {
150                @Override
151                public void run() {
152                    mCallback.onReconnectionFailure(mgr, info);
153                }});
154        }
155    }
156
157    /**
158     * An interface which wraps
159     * {@link android.hardware.Camera.ErrorCallback}
160     */
161    public static interface CameraErrorCallback {
162        public void onError(int error, CameraProxy camera);
163    }
164
165    /**
166     * An interface which wraps
167     * {@link android.hardware.Camera.AutoFocusCallback}.
168     */
169    public static interface CameraAFCallback {
170        public void onAutoFocus(boolean focused, CameraProxy camera);
171    }
172
173    /**
174     * An interface which wraps
175     * {@link android.hardware.Camera.AutoFocusMoveCallback}.
176     */
177    public static interface CameraAFMoveCallback {
178        public void onAutoFocusMoving(boolean moving, CameraProxy camera);
179    }
180
181    /**
182     * An interface which wraps
183     * {@link android.hardware.Camera.ShutterCallback}.
184     */
185    public static interface CameraShutterCallback {
186        public void onShutter(CameraProxy camera);
187    }
188
189    /**
190     * An interface which wraps
191     * {@link android.hardware.Camera.PictureCallback}.
192     */
193    public static interface CameraPictureCallback {
194        public void onPictureTaken(byte[] data, CameraProxy camera);
195    }
196
197    /**
198     * An interface which wraps
199     * {@link android.hardware.Camera.PreviewCallback}.
200     */
201    public static interface CameraPreviewDataCallback {
202        public void onPreviewFrame(byte[] data, CameraProxy camera);
203    }
204
205    /**
206     * An interface which wraps
207     * {@link android.hardware.Camera.FaceDetectionListener}.
208     */
209    public static interface CameraFaceDetectionCallback {
210        /**
211         * Callback for face detection.
212         *
213         * @param faces   Recognized face in the preview.
214         * @param camera  The camera which the preview image comes from.
215         */
216        public void onFaceDetection(Camera.Face[] faces, CameraProxy camera);
217    }
218
219    /**
220     * An interface to be called when the camera preview has started.
221     */
222    public static interface CameraStartPreviewCallback {
223        /**
224         * Callback when the preview starts.
225         */
226        public void onPreviewStarted();
227    }
228
229    /**
230     * An interface to be called for any events when opening or closing the
231     * camera device. This error callback is different from the one defined
232     * in the framework, {@link android.hardware.Camera.ErrorCallback}, which
233     * is used after the camera is opened.
234     */
235    public static interface CameraOpenCallback {
236        /**
237         * Callback when camera open succeeds.
238         */
239        public void onCameraOpened(CameraProxy camera);
240
241        /**
242         * Callback when {@link com.android.camera.CameraDisabledException} is
243         * caught.
244         *
245         * @param cameraId The disabled camera.
246         */
247        public void onCameraDisabled(int cameraId);
248
249        /**
250         * Callback when {@link com.android.camera.CameraHardwareException} is
251         * caught.
252         *
253         * @param cameraId The camera with the hardware failure.
254         * @param info The extra info regarding this failure.
255         */
256        public void onDeviceOpenFailure(int cameraId, String info);
257
258        /**
259         * Callback when trying to open the camera which is already opened.
260         *
261         * @param cameraId The camera which is causing the open error.
262         */
263        public void onDeviceOpenedAlready(int cameraId, String info);
264
265        /**
266         * Callback when {@link java.io.IOException} is caught during
267         * {@link android.hardware.Camera#reconnect()}.
268         *
269         * @param mgr The {@link CameraAgent}
270         *            with the reconnect failure.
271         */
272        public void onReconnectionFailure(CameraAgent mgr, String info);
273    }
274
275    /**
276     * Opens the camera of the specified ID asynchronously. The camera device
277     * will be opened in the camera handler thread and will be returned through
278     * the {@link CameraAgent.CameraOpenCallback#
279     * onCameraOpened(com.android.camera.cameradevice.CameraAgent.CameraProxy)}.
280     *
281     * @param handler The {@link android.os.Handler} in which the callback
282     *                was handled.
283     * @param callback The callback for the result.
284     * @param cameraId The camera ID to open.
285     */
286    public void openCamera(final Handler handler, final int cameraId,
287                           final CameraOpenCallback callback) {
288        try {
289            getDispatchThread().runJob(new Runnable() {
290                @Override
291                public void run() {
292                    getCameraHandler().obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0,
293                            CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget();
294                }
295            });
296        } catch (final RuntimeException ex) {
297            getCameraExceptionHandler().onDispatchThreadException(ex);
298        }
299    }
300
301    /**
302     * Closes the camera device.
303     *
304     * @param camera The camera to close. {@code null} means all.
305     * @param synced Whether this call should be synchronous.
306     */
307    public void closeCamera(CameraProxy camera, boolean synced) {
308        try {
309            if (synced) {
310                // Don't bother to wait since camera is in bad state.
311                if (getCameraState().isInvalid()) {
312                    return;
313                }
314                final WaitDoneBundle bundle = new WaitDoneBundle();
315
316                getDispatchThread().runJobSync(new Runnable() {
317                    @Override
318                    public void run() {
319                        getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget();
320                        getCameraHandler().post(bundle.mUnlockRunnable);
321                    }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release");
322            } else {
323                getDispatchThread().runJob(new Runnable() {
324                    @Override
325                    public void run() {
326                        getCameraHandler().removeCallbacksAndMessages(null);
327                        getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget();
328                    }});
329            }
330        } catch (final RuntimeException ex) {
331            getCameraExceptionHandler().onDispatchThreadException(ex);
332        }
333    }
334
335    /**
336     * Sets a callback for handling camera api runtime exceptions on
337     * a handler.
338     */
339    public abstract void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler);
340
341    /**
342     * Recycles the resources used by this instance. CameraAgent will be in
343     * an unusable state after calling this.
344     */
345    public abstract void recycle();
346
347    /**
348     * @return The camera devices info.
349     */
350    public abstract CameraDeviceInfo getCameraDeviceInfo();
351
352    /**
353     * @return The handler to which camera tasks should be posted.
354     */
355    protected abstract Handler getCameraHandler();
356
357    /**
358     * @return The thread used on which client callbacks are served.
359     */
360    protected abstract DispatchThread getDispatchThread();
361
362    /**
363     * @return The state machine tracking the camera API's current status.
364     */
365    protected abstract CameraStateHolder getCameraState();
366
367    /**
368     * @return The exception handler.
369     */
370    protected abstract CameraExceptionHandler getCameraExceptionHandler();
371
372    /**
373     * An interface that takes camera operation requests and post messages to the
374     * camera handler thread. All camera operations made through this interface is
375     * asynchronous by default except those mentioned specifically.
376     */
377    public abstract static class CameraProxy {
378
379        /**
380         * Returns the underlying {@link android.hardware.Camera} object used
381         * by this proxy. This method should only be used when handing the
382         * camera device over to {@link android.media.MediaRecorder} for
383         * recording.
384         */
385        @Deprecated
386        public abstract android.hardware.Camera getCamera();
387
388        /**
389         * @return The camera ID associated to by this
390         * {@link CameraAgent.CameraProxy}.
391         */
392        public abstract int getCameraId();
393
394        /**
395         * @return The camera characteristics.
396         */
397        public abstract CameraDeviceInfo.Characteristics getCharacteristics();
398
399        /**
400         * @return The camera capabilities.
401         */
402        public abstract CameraCapabilities getCapabilities();
403
404        /**
405         * @return The camera agent which creates this proxy.
406         */
407        public abstract CameraAgent getAgent();
408
409        /**
410         * Reconnects to the camera device. On success, the camera device will
411         * be returned through {@link CameraAgent
412         * .CameraOpenCallback#onCameraOpened(com.android.camera.cameradevice.CameraAgent
413         * .CameraProxy)}.
414         * @see android.hardware.Camera#reconnect()
415         *
416         * @param handler The {@link android.os.Handler} in which the callback
417         *                was handled.
418         * @param cb The callback when any error happens.
419         */
420        public void reconnect(final Handler handler, final CameraOpenCallback cb) {
421            try {
422                getDispatchThread().runJob(new Runnable() {
423                    @Override
424                    public void run() {
425                        getCameraHandler().obtainMessage(CameraActions.RECONNECT, getCameraId(), 0,
426                                CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget();
427                    }});
428            } catch (final RuntimeException ex) {
429                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
430            }
431        }
432
433        /**
434         * Unlocks the camera device.
435         *
436         * @see android.hardware.Camera#unlock()
437         */
438        public void unlock() {
439            // Don't bother to wait since camera is in bad state.
440            if (getCameraState().isInvalid()) {
441                return;
442            }
443            final WaitDoneBundle bundle = new WaitDoneBundle();
444            try {
445                getDispatchThread().runJobSync(new Runnable() {
446                    @Override
447                    public void run() {
448                        getCameraHandler().sendEmptyMessage(CameraActions.UNLOCK);
449                        getCameraHandler().post(bundle.mUnlockRunnable);
450                    }
451                }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock");
452            } catch (final RuntimeException ex) {
453                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
454            }
455        }
456
457        /**
458         * Locks the camera device.
459         * @see android.hardware.Camera#lock()
460         */
461        public void lock() {
462            try {
463                getDispatchThread().runJob(new Runnable() {
464                    @Override
465                    public void run() {
466                        getCameraHandler().sendEmptyMessage(CameraActions.LOCK);
467                    }});
468            } catch (final RuntimeException ex) {
469                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
470            }
471        }
472
473        /**
474         * Sets the {@link android.graphics.SurfaceTexture} for preview.
475         *
476         * <p>Note that, once this operation has been performed, it is no longer
477         * possible to change the preview or photo sizes in the
478         * {@link CameraSettings} instance for this camera, and the mutators for
479         * these fields are allowed to ignore all further invocations until the
480         * preview is stopped with {@link #stopPreview}.</p>
481         *
482         * @param surfaceTexture The {@link SurfaceTexture} for preview.
483         *
484         * @see CameraSettings#setPhotoSize
485         * @see CameraSettings#setPreviewSize
486         */
487        // XXX: Despite the above documentation about locking the sizes, the API
488        // 1 implementation doesn't currently enforce this at all, although the
489        // Camera class warns that preview sizes shouldn't be changed while a
490        // preview is running. Furthermore, the API 2 implementation doesn't yet
491        // unlock the sizes when stopPreview() is invoked (see related FIXME on
492        // the STOP_PREVIEW case in its handler; in the meantime, changing API 2
493        // sizes would require closing and reopening the camera.
494        public void setPreviewTexture(final SurfaceTexture surfaceTexture) {
495            try {
496                getDispatchThread().runJob(new Runnable() {
497                    @Override
498                    public void run() {
499                        getCameraHandler()
500                                .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
501                                .sendToTarget();
502                    }});
503            } catch (final RuntimeException ex) {
504                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
505            }
506        }
507
508        /**
509         * Blocks until a {@link android.graphics.SurfaceTexture} has been set
510         * for preview.
511         *
512         * <p>Note that, once this operation has been performed, it is no longer
513         * possible to change the preview or photo sizes in the
514         * {@link CameraSettings} instance for this camera, and the mutators for
515         * these fields are allowed to ignore all further invocations.</p>
516         *
517         * @param surfaceTexture The {@link SurfaceTexture} for preview.
518         *
519         * @see CameraSettings#setPhotoSize
520         * @see CameraSettings#setPreviewSize
521         */
522        public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) {
523            // Don't bother to wait since camera is in bad state.
524            if (getCameraState().isInvalid()) {
525                return;
526            }
527            final WaitDoneBundle bundle = new WaitDoneBundle();
528            try {
529                getDispatchThread().runJobSync(new Runnable() {
530                    @Override
531                    public void run() {
532                        getCameraHandler()
533                                .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
534                                .sendToTarget();
535                        getCameraHandler().post(bundle.mUnlockRunnable);
536                    }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture");
537            } catch (final RuntimeException ex) {
538                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
539            }
540        }
541
542        /**
543         * Sets the {@link android.view.SurfaceHolder} for preview.
544         *
545         * @param surfaceHolder The {@link SurfaceHolder} for preview.
546         */
547        public void setPreviewDisplay(final SurfaceHolder surfaceHolder) {
548            try {
549                getDispatchThread().runJob(new Runnable() {
550                    @Override
551                    public void run() {
552                        getCameraHandler()
553                                .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder)
554                                .sendToTarget();
555                    }});
556            } catch (final RuntimeException ex) {
557                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
558            }
559        }
560
561        /**
562         * Starts the camera preview.
563         */
564        public void startPreview() {
565            try {
566            getDispatchThread().runJob(new Runnable() {
567                @Override
568                public void run() {
569                    getCameraHandler()
570                            .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget();
571                }});
572            } catch (final RuntimeException ex) {
573                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
574            }
575        }
576
577        /**
578         * Starts the camera preview and executes a callback on a handler once
579         * the preview starts.
580         */
581        public void startPreviewWithCallback(final Handler h, final CameraStartPreviewCallback cb) {
582            try {
583            getDispatchThread().runJob(new Runnable() {
584                @Override
585                public void run() {
586                    getCameraHandler().obtainMessage(CameraActions.START_PREVIEW_ASYNC,
587                            CameraStartPreviewCallbackForward.getNewInstance(h, cb))
588                                    .sendToTarget();
589                }});
590            } catch (final RuntimeException ex) {
591                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
592            }
593        }
594
595        /**
596         * Stops the camera preview synchronously.
597         * {@code stopPreview()} must be synchronous to ensure that the caller can
598         * continues to release resources related to camera preview.
599         */
600        public void stopPreview() {
601            // Don't bother to wait since camera is in bad state.
602            if (getCameraState().isInvalid()) {
603                return;
604            }
605            final WaitDoneBundle bundle = new WaitDoneBundle();
606            try {
607                getDispatchThread().runJobSync(new Runnable() {
608                    @Override
609                    public void run() {
610                        getCameraHandler().obtainMessage(CameraActions.STOP_PREVIEW, bundle)
611                                .sendToTarget();
612                    }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview");
613            } catch (final RuntimeException ex) {
614                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
615            }
616        }
617
618        /**
619         * Sets the callback for preview data.
620         *
621         * @param handler    The {@link android.os.Handler} in which the callback was handled.
622         * @param cb         The callback to be invoked when the preview data is available.
623         * @see  android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback)
624         */
625        public abstract void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb);
626
627        /**
628         * Sets the one-time callback for preview data.
629         *
630         * @param handler    The {@link android.os.Handler} in which the callback was handled.
631         * @param cb         The callback to be invoked when the preview data for
632         *                   next frame is available.
633         * @see  android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback)
634         */
635        public abstract void setOneShotPreviewCallback(Handler handler,
636                                                       CameraPreviewDataCallback cb);
637
638        /**
639         * Sets the callback for preview data.
640         *
641         * @param handler The handler in which the callback will be invoked.
642         * @param cb      The callback to be invoked when the preview data is available.
643         * @see android.hardware.Camera#setPreviewCallbackWithBuffer(android.hardware.Camera.PreviewCallback)
644         */
645        public abstract void setPreviewDataCallbackWithBuffer(Handler handler,
646                                                              CameraPreviewDataCallback cb);
647
648        /**
649         * Adds buffer for the preview callback.
650         *
651         * @param callbackBuffer The buffer allocated for the preview data.
652         */
653        public void addCallbackBuffer(final byte[] callbackBuffer) {
654            try {
655                getDispatchThread().runJob(new Runnable() {
656                    @Override
657                    public void run() {
658                        getCameraHandler()
659                                .obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer)
660                                .sendToTarget();
661                        }
662                    });
663            } catch (final RuntimeException ex) {
664                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
665            }
666        }
667
668        /**
669         * Starts the auto-focus process. The result will be returned through the callback.
670         *
671         * @param handler The handler in which the callback will be invoked.
672         * @param cb      The auto-focus callback.
673         */
674        public abstract void autoFocus(Handler handler, CameraAFCallback cb);
675
676        /**
677         * Cancels the auto-focus process.
678         *
679         * <p>This action has the highest priority and will get processed before anything
680         * else that is pending. Moreover, any pending auto-focuses that haven't yet
681         * began will also be ignored.</p>
682         */
683        public void cancelAutoFocus() {
684             // Do not use the dispatch thread since we want to avoid a wait-cycle
685             // between applySettingsHelper which waits until the state is not FOCUSING.
686             // cancelAutoFocus should get executed asap, set the state back to idle.
687            getCameraHandler().sendMessageAtFrontOfQueue(
688                    getCameraHandler().obtainMessage(CameraActions.CANCEL_AUTO_FOCUS));
689            getCameraHandler().sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS_FINISH);
690        }
691
692        /**
693         * Sets the auto-focus callback
694         *
695         * @param handler The handler in which the callback will be invoked.
696         * @param cb      The callback to be invoked when the preview data is available.
697         */
698        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
699        public abstract void setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb);
700
701        /**
702         * Instrument the camera to take a picture.
703         *
704         * @param handler   The handler in which the callback will be invoked.
705         * @param shutter   The callback for shutter action, may be null.
706         * @param raw       The callback for uncompressed data, may be null.
707         * @param postview  The callback for postview image data, may be null.
708         * @param jpeg      The callback for jpeg image data, may be null.
709         * @see android.hardware.Camera#takePicture(
710         *         android.hardware.Camera.ShutterCallback,
711         *         android.hardware.Camera.PictureCallback,
712         *         android.hardware.Camera.PictureCallback)
713         */
714        public abstract void takePicture(
715                Handler handler,
716                CameraShutterCallback shutter,
717                CameraPictureCallback raw,
718                CameraPictureCallback postview,
719                CameraPictureCallback jpeg);
720
721        /**
722         * Sets the display orientation for camera to adjust the preview and JPEG orientation.
723         *
724         * @param degrees The counterclockwise rotation in degrees, relative to the device's natural
725         *                orientation. Should be 0, 90, 180 or 270.
726         */
727        public void setDisplayOrientation(final int degrees) {
728            setDisplayOrientation(degrees, true);
729        }
730
731        /**
732         * Sets the display orientation for camera to adjust the preview&mdash;and, optionally,
733         * JPEG&mdash;orientations.
734         * <p>If capture rotation is not requested, future captures will be returned in the sensor's
735         * physical rotation, which does not necessarily match the device's natural orientation.</p>
736         *
737         * @param degrees The counterclockwise rotation in degrees, relative to the device's natural
738         *                orientation. Should be 0, 90, 180 or 270.
739         * @param capture Whether to adjust the JPEG capture orientation as well as the preview one.
740         */
741        public void setDisplayOrientation(final int degrees, final boolean capture) {
742            try {
743                getDispatchThread().runJob(new Runnable() {
744                    @Override
745                    public void run() {
746                        getCameraHandler()
747                                .obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees,
748                                        capture ? 1 : 0)
749                                .sendToTarget();
750                    }});
751            } catch (final RuntimeException ex) {
752                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
753            }
754        }
755
756        public void setJpegOrientation(final int degrees) {
757            try {
758                getDispatchThread().runJob(new Runnable() {
759                    @Override
760                    public void run() {
761                        getCameraHandler()
762                                .obtainMessage(CameraActions.SET_JPEG_ORIENTATION, degrees, 0)
763                                .sendToTarget();
764                    }});
765            } catch (final RuntimeException ex) {
766                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
767            }
768        }
769
770        /**
771         * Sets the listener for zoom change.
772         *
773         * @param listener The listener.
774         */
775        public abstract void setZoomChangeListener(OnZoomChangeListener listener);
776
777        /**
778         * Sets the face detection listener.
779         *
780         * @param handler  The handler in which the callback will be invoked.
781         * @param callback The callback for face detection results.
782         */
783        public abstract void setFaceDetectionCallback(Handler handler,
784                                                      CameraFaceDetectionCallback callback);
785
786        /**
787         * Starts the face detection.
788         */
789        public void startFaceDetection() {
790            try {
791                getDispatchThread().runJob(new Runnable() {
792                    @Override
793                    public void run() {
794                        getCameraHandler().sendEmptyMessage(CameraActions.START_FACE_DETECTION);
795                    }});
796            } catch (final RuntimeException ex) {
797                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
798            }
799        }
800
801        /**
802         * Stops the face detection.
803         */
804        public void stopFaceDetection() {
805            try {
806                getDispatchThread().runJob(new Runnable() {
807                    @Override
808                    public void run() {
809                        getCameraHandler().sendEmptyMessage(CameraActions.STOP_FACE_DETECTION);
810                    }});
811            } catch (final RuntimeException ex) {
812                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
813            }
814        }
815
816        /**
817         * Sets the camera parameters.
818         *
819         * @param params The camera parameters to use.
820         */
821        @Deprecated
822        public abstract void setParameters(Camera.Parameters params);
823
824        /**
825         * Gets the current camera parameters synchronously. This method is
826         * synchronous since the caller has to wait for the camera to return
827         * the parameters. If the parameters are already cached, it returns
828         * immediately.
829         */
830        @Deprecated
831        public abstract Camera.Parameters getParameters();
832
833        /**
834         * Gets the current camera settings synchronously.
835         * <p>This method is synchronous since the caller has to wait for the
836         * camera to return the parameters. If the parameters are already
837         * cached, it returns immediately.</p>
838         */
839        public abstract CameraSettings getSettings();
840
841        /**
842         * Default implementation of {@link #applySettings(CameraSettings)}
843         * that is only missing the set of states it needs to wait for
844         * before applying the settings.
845         *
846         * @param settings The settings to use on the device.
847         * @param statesToAwait Bitwise OR of the required camera states.
848         * @return Whether the settings can be applied.
849         */
850        protected boolean applySettingsHelper(CameraSettings settings,
851                                              final int statesToAwait) {
852            if (settings == null) {
853                Log.v(TAG, "null argument in applySettings()");
854                return false;
855            }
856            if (!getCapabilities().supports(settings)) {
857                Log.w(TAG, "Unsupported settings in applySettings()");
858                return false;
859            }
860
861            final CameraSettings copyOfSettings = settings.copy();
862            try {
863                getDispatchThread().runJob(new Runnable() {
864                    @Override
865                    public void run() {
866                        CameraStateHolder cameraState = getCameraState();
867                        // Don't bother to wait since camera is in bad state.
868                        if (cameraState.isInvalid()) {
869                            return;
870                        }
871                        cameraState.waitForStates(statesToAwait);
872                        getCameraHandler().obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings)
873                                .sendToTarget();
874                    }});
875            } catch (final RuntimeException ex) {
876                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
877            }
878            return true;
879        }
880
881        /**
882         * Applies the settings to the camera device.
883         *
884         * <p>If the camera is either focusing or capturing; settings applications
885         * will be (asynchronously) deferred until those operations complete.</p>
886         *
887         * @param settings The settings to use on the device.
888         * @return Whether the settings can be applied.
889         */
890        public abstract boolean applySettings(CameraSettings settings);
891
892        /**
893         * Forces {@code CameraProxy} to update the cached version of the camera
894         * settings regardless of the dirty bit.
895         */
896        public void refreshSettings() {
897            try {
898                getDispatchThread().runJob(new Runnable() {
899                    @Override
900                    public void run() {
901                        getCameraHandler().sendEmptyMessage(CameraActions.REFRESH_PARAMETERS);
902                    }});
903            } catch (final RuntimeException ex) {
904                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
905            }
906        }
907
908        /**
909         * Enables/Disables the camera shutter sound.
910         *
911         * @param enable   {@code true} to enable the shutter sound,
912         *                 {@code false} to disable it.
913         */
914        public void enableShutterSound(final boolean enable) {
915            try {
916                getDispatchThread().runJob(new Runnable() {
917                    @Override
918                    public void run() {
919                        getCameraHandler()
920                                .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0)
921                                .sendToTarget();
922                    }});
923            } catch (final RuntimeException ex) {
924                getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
925            }
926        }
927
928        /**
929         * Dumps the current settings of the camera device.
930         *
931         * <p>The content varies based on the underlying camera API settings
932         * implementation.</p>
933         *
934         * @return The content of the device settings represented by a string.
935         */
936        public abstract String dumpDeviceSettings();
937
938        /**
939         * @return The handler to which camera tasks should be posted.
940         */
941        public abstract Handler getCameraHandler();
942
943        /**
944         * @return The thread used on which client callbacks are served.
945         */
946        public abstract DispatchThread getDispatchThread();
947
948        /**
949         * @return The state machine tracking the camera API's current mode.
950         */
951        public abstract CameraStateHolder getCameraState();
952    }
953
954    public static class WaitDoneBundle {
955        public final Runnable mUnlockRunnable;
956        public final Object mWaitLock;
957
958        WaitDoneBundle() {
959            mWaitLock = new Object();
960            mUnlockRunnable = new Runnable() {
961                @Override
962                public void run() {
963                    synchronized (mWaitLock) {
964                        mWaitLock.notifyAll();
965                    }
966                }};
967        }
968
969        /**
970         * Notify all synchronous waiters waiting on message completion with {@link #mWaitLock}.
971         *
972         * <p>This assumes that the message was sent with {@code this} as the {@code Message#obj}.
973         * Otherwise the message is ignored.</p>
974         */
975        /*package*/ static void unblockSyncWaiters(Message msg) {
976            if (msg == null) return;
977
978            if (msg.obj instanceof WaitDoneBundle) {
979                WaitDoneBundle bundle = (WaitDoneBundle)msg.obj;
980                bundle.mUnlockRunnable.run();
981            }
982        }
983    }
984}
985