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 
17 package com.android.ex.camera2.portability;
18 
19 import android.annotation.TargetApi;
20 import android.graphics.SurfaceTexture;
21 import android.hardware.Camera;
22 import android.hardware.Camera.OnZoomChangeListener;
23 import android.os.Build;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.view.SurfaceHolder;
28 
29 import 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  */
45 public 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 
getNewInstance( Handler handler, CameraStartPreviewCallback cb)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 
CameraStartPreviewCallbackForward(Handler h, CameraStartPreviewCallback cb)63         private CameraStartPreviewCallbackForward(Handler h,
64                 CameraStartPreviewCallback cb) {
65             mHandler = h;
66             mCallback = cb;
67         }
68 
69         @Override
onPreviewStarted()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          */
getNewInstance( Handler handler, CameraOpenCallback cb)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 
CameraOpenCallbackForward(Handler h, CameraOpenCallback cb)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
onCameraOpened(final CameraProxy camera)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
onCameraDisabled(final int cameraId)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
onDeviceOpenFailure(final int cameraId, final String info)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
onDeviceOpenedAlready(final int cameraId, final String info)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
onReconnectionFailure(final CameraAgent mgr, final String info)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 {
onError(int error, CameraProxy camera)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 {
onAutoFocus(boolean focused, CameraProxy camera)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 {
onAutoFocusMoving(boolean moving, CameraProxy camera)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 {
onShutter(CameraProxy camera)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 {
onPictureTaken(byte[] data, CameraProxy camera)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 {
onPreviewFrame(byte[] data, CameraProxy camera)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          */
onFaceDetection(Camera.Face[] faces, CameraProxy camera)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          */
onPreviewStarted()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          */
onCameraOpened(CameraProxy camera)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          */
onCameraDisabled(int cameraId)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          */
onDeviceOpenFailure(int cameraId, String info)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          */
onDeviceOpenedAlready(int cameraId, String info)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          */
onReconnectionFailure(CameraAgent mgr, String info)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      */
openCamera(final Handler handler, final int cameraId, final CameraOpenCallback callback)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      */
closeCamera(CameraProxy camera, boolean synced)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      */
setCameraExceptionHandler(CameraExceptionHandler exceptionHandler)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      */
recycle()345     public abstract void recycle();
346 
347     /**
348      * @return The camera devices info.
349      */
getCameraDeviceInfo()350     public abstract CameraDeviceInfo getCameraDeviceInfo();
351 
352     /**
353      * @return The handler to which camera tasks should be posted.
354      */
getCameraHandler()355     protected abstract Handler getCameraHandler();
356 
357     /**
358      * @return The thread used on which client callbacks are served.
359      */
getDispatchThread()360     protected abstract DispatchThread getDispatchThread();
361 
362     /**
363      * @return The state machine tracking the camera API's current status.
364      */
getCameraState()365     protected abstract CameraStateHolder getCameraState();
366 
367     /**
368      * @return The exception handler.
369      */
getCameraExceptionHandler()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
getCamera()386         public abstract android.hardware.Camera getCamera();
387 
388         /**
389          * @return The camera ID associated to by this
390          * {@link CameraAgent.CameraProxy}.
391          */
getCameraId()392         public abstract int getCameraId();
393 
394         /**
395          * @return The camera characteristics.
396          */
getCharacteristics()397         public abstract CameraDeviceInfo.Characteristics getCharacteristics();
398 
399         /**
400          * @return The camera capabilities.
401          */
getCapabilities()402         public abstract CameraCapabilities getCapabilities();
403 
404         /**
405          * @return The camera agent which creates this proxy.
406          */
getAgent()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          */
reconnect(final Handler handler, final CameraOpenCallback cb)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          */
unlock()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          */
lock()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.
setPreviewTexture(final SurfaceTexture surfaceTexture)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          */
setPreviewTextureSync(final SurfaceTexture surfaceTexture)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          */
setPreviewDisplay(final SurfaceHolder surfaceHolder)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          */
startPreview()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          */
startPreviewWithCallback(final Handler h, final CameraStartPreviewCallback cb)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          */
stopPreview()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          */
setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb)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          */
setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb)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          */
setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb)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          */
addCallbackBuffer(final byte[] callbackBuffer)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          */
autoFocus(Handler handler, CameraAFCallback cb)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          */
cancelAutoFocus()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)
setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb)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          */
takePicture( Handler handler, CameraShutterCallback shutter, CameraPictureCallback raw, CameraPictureCallback postview, CameraPictureCallback jpeg)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          */
setDisplayOrientation(final int degrees)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          */
setDisplayOrientation(final int degrees, final boolean capture)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 
setJpegOrientation(final int degrees)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          */
setZoomChangeListener(OnZoomChangeListener listener)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          */
setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback)783         public abstract void setFaceDetectionCallback(Handler handler,
784                                                       CameraFaceDetectionCallback callback);
785 
786         /**
787          * Starts the face detection.
788          */
startFaceDetection()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          */
stopFaceDetection()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
setParameters(Camera.Parameters params)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
getParameters()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          */
getSettings()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          */
applySettingsHelper(CameraSettings settings, final int statesToAwait)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          */
applySettings(CameraSettings settings)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          */
refreshSettings()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          */
enableShutterSound(final boolean enable)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          */
dumpDeviceSettings()936         public abstract String dumpDeviceSettings();
937 
938         /**
939          * @return The handler to which camera tasks should be posted.
940          */
getCameraHandler()941         public abstract Handler getCameraHandler();
942 
943         /**
944          * @return The thread used on which client callbacks are served.
945          */
getDispatchThread()946         public abstract DispatchThread getDispatchThread();
947 
948         /**
949          * @return The state machine tracking the camera API's current mode.
950          */
getCameraState()951         public abstract CameraStateHolder getCameraState();
952     }
953 
954     public static class WaitDoneBundle {
955         public final Runnable mUnlockRunnable;
956         public final Object mWaitLock;
957 
WaitDoneBundle()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          */
unblockSyncWaiters(Message msg)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