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.camera;
18 
19 import static com.android.camera.Util.Assert;
20 
21 import android.annotation.TargetApi;
22 import android.graphics.SurfaceTexture;
23 import android.hardware.Camera.AutoFocusCallback;
24 import android.hardware.Camera.AutoFocusMoveCallback;
25 import android.hardware.Camera.ErrorCallback;
26 import android.hardware.Camera.FaceDetectionListener;
27 import android.hardware.Camera.OnZoomChangeListener;
28 import android.hardware.Camera.Parameters;
29 import android.hardware.Camera.PictureCallback;
30 import android.hardware.Camera.PreviewCallback;
31 import android.hardware.Camera.ShutterCallback;
32 import android.os.ConditionVariable;
33 import android.os.Handler;
34 import android.os.HandlerThread;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.view.SurfaceHolder;
38 import android.util.Log;
39 
40 import com.android.gallery3d.common.ApiHelper;
41 
42 import java.io.IOException;
43 
44 public class CameraManager {
45     private static final String TAG = "CameraManager";
46     private static CameraManager sCameraManager = new CameraManager();
47 
48     // Thread progress signals
49     private ConditionVariable mSig = new ConditionVariable();
50 
51     private Parameters mParameters;
52     private IOException mReconnectException;
53 
54     private static final int RELEASE = 1;
55     private static final int RECONNECT = 2;
56     private static final int UNLOCK = 3;
57     private static final int LOCK = 4;
58     private static final int SET_PREVIEW_TEXTURE_ASYNC = 5;
59     private static final int START_PREVIEW_ASYNC = 6;
60     private static final int STOP_PREVIEW = 7;
61     private static final int SET_PREVIEW_CALLBACK_WITH_BUFFER = 8;
62     private static final int ADD_CALLBACK_BUFFER = 9;
63     private static final int AUTO_FOCUS = 10;
64     private static final int CANCEL_AUTO_FOCUS = 11;
65     private static final int SET_AUTO_FOCUS_MOVE_CALLBACK = 12;
66     private static final int SET_DISPLAY_ORIENTATION = 13;
67     private static final int SET_ZOOM_CHANGE_LISTENER = 14;
68     private static final int SET_FACE_DETECTION_LISTENER = 15;
69     private static final int START_FACE_DETECTION = 16;
70     private static final int STOP_FACE_DETECTION = 17;
71     private static final int SET_ERROR_CALLBACK = 18;
72     private static final int SET_PARAMETERS = 19;
73     private static final int GET_PARAMETERS = 20;
74     private static final int SET_PARAMETERS_ASYNC = 21;
75     private static final int WAIT_FOR_IDLE = 22;
76     private static final int SET_PREVIEW_DISPLAY_ASYNC = 23;
77     private static final int SET_PREVIEW_CALLBACK = 24;
78     private static final int ENABLE_SHUTTER_SOUND = 25;
79 
80     private Handler mCameraHandler;
81     private CameraProxy mCameraProxy;
82     private android.hardware.Camera mCamera;
83 
instance()84     public static CameraManager instance() {
85         return sCameraManager;
86     }
87 
CameraManager()88     private CameraManager() {
89         HandlerThread ht = new HandlerThread("Camera Handler Thread");
90         ht.start();
91         mCameraHandler = new CameraHandler(ht.getLooper());
92     }
93 
94     private class CameraHandler extends Handler {
CameraHandler(Looper looper)95         CameraHandler(Looper looper) {
96             super(looper);
97         }
98 
99         @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
startFaceDetection()100         private void startFaceDetection() {
101             mCamera.startFaceDetection();
102         }
103 
104         @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
stopFaceDetection()105         private void stopFaceDetection() {
106             mCamera.stopFaceDetection();
107         }
108 
109         @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
setFaceDetectionListener(FaceDetectionListener listener)110         private void setFaceDetectionListener(FaceDetectionListener listener) {
111             mCamera.setFaceDetectionListener(listener);
112         }
113 
114         @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
setPreviewTexture(Object surfaceTexture)115         private void setPreviewTexture(Object surfaceTexture) {
116             try {
117                 mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
118             } catch(IOException e) {
119                 throw new RuntimeException(e);
120             }
121         }
122 
123         @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN_MR1)
enableShutterSound(boolean enable)124         private void enableShutterSound(boolean enable) {
125             mCamera.enableShutterSound(enable);
126         }
127 
128         /*
129          * This method does not deal with the build version check.  Everyone should
130          * check first before sending message to this handler.
131          */
132         @Override
handleMessage(final Message msg)133         public void handleMessage(final Message msg) {
134             try {
135                 switch (msg.what) {
136                     case RELEASE:
137                         mCamera.release();
138                         mCamera = null;
139                         mCameraProxy = null;
140                         break;
141 
142                     case RECONNECT:
143                         mReconnectException = null;
144                         try {
145                             mCamera.reconnect();
146                         } catch (IOException ex) {
147                             mReconnectException = ex;
148                         }
149                         break;
150 
151                     case UNLOCK:
152                         mCamera.unlock();
153                         break;
154 
155                     case LOCK:
156                         mCamera.lock();
157                         break;
158 
159                     case SET_PREVIEW_TEXTURE_ASYNC:
160                         setPreviewTexture(msg.obj);
161                         return;  // no need to call mSig.open()
162 
163                     case SET_PREVIEW_DISPLAY_ASYNC:
164                         try {
165                             mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
166                         } catch(IOException e) {
167                             throw new RuntimeException(e);
168                         }
169                         return;  // no need to call mSig.open()
170 
171                     case START_PREVIEW_ASYNC:
172                         mCamera.startPreview();
173                         return;  // no need to call mSig.open()
174 
175                     case STOP_PREVIEW:
176                         mCamera.stopPreview();
177                         break;
178 
179                     case SET_PREVIEW_CALLBACK_WITH_BUFFER:
180                         mCamera.setPreviewCallbackWithBuffer(
181                             (PreviewCallback) msg.obj);
182                         break;
183 
184                     case ADD_CALLBACK_BUFFER:
185                         mCamera.addCallbackBuffer((byte[]) msg.obj);
186                         break;
187 
188                     case AUTO_FOCUS:
189                         mCamera.autoFocus((AutoFocusCallback) msg.obj);
190                         break;
191 
192                     case CANCEL_AUTO_FOCUS:
193                         mCamera.cancelAutoFocus();
194                         break;
195 
196                     case SET_AUTO_FOCUS_MOVE_CALLBACK:
197                         setAutoFocusMoveCallback(mCamera, msg.obj);
198                         break;
199 
200                     case SET_DISPLAY_ORIENTATION:
201                         mCamera.setDisplayOrientation(msg.arg1);
202                         break;
203 
204                     case SET_ZOOM_CHANGE_LISTENER:
205                         mCamera.setZoomChangeListener(
206                             (OnZoomChangeListener) msg.obj);
207                         break;
208 
209                     case SET_FACE_DETECTION_LISTENER:
210                         setFaceDetectionListener((FaceDetectionListener) msg.obj);
211                         break;
212 
213                     case START_FACE_DETECTION:
214                         startFaceDetection();
215                         break;
216 
217                     case STOP_FACE_DETECTION:
218                         stopFaceDetection();
219                         break;
220 
221                     case SET_ERROR_CALLBACK:
222                         mCamera.setErrorCallback((ErrorCallback) msg.obj);
223                         break;
224 
225                     case SET_PARAMETERS:
226                         mCamera.setParameters((Parameters) msg.obj);
227                         break;
228 
229                     case GET_PARAMETERS:
230                         mParameters = mCamera.getParameters();
231                         break;
232 
233                     case SET_PARAMETERS_ASYNC:
234                         mCamera.setParameters((Parameters) msg.obj);
235                         return;  // no need to call mSig.open()
236 
237                     case SET_PREVIEW_CALLBACK:
238                         mCamera.setPreviewCallback((PreviewCallback) msg.obj);
239                         break;
240 
241                     case ENABLE_SHUTTER_SOUND:
242                         enableShutterSound((msg.arg1 == 1) ? true : false);
243                         break;
244 
245                     case WAIT_FOR_IDLE:
246                         // do nothing
247                         break;
248 
249                     default:
250                         throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
251                 }
252             } catch (RuntimeException e) {
253                 if (msg.what != RELEASE && mCamera != null) {
254                     try {
255                         mCamera.release();
256                     } catch (Exception ex) {
257                         Log.e(TAG, "Fail to release the camera.");
258                     }
259                     mCamera = null;
260                     mCameraProxy = null;
261                 }
262                 throw e;
263             }
264             mSig.open();
265         }
266     }
267 
268     @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
setAutoFocusMoveCallback(android.hardware.Camera camera, Object cb)269     private void setAutoFocusMoveCallback(android.hardware.Camera camera,
270             Object cb) {
271         camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
272     }
273 
274     // Open camera synchronously. This method is invoked in the context of a
275     // background thread.
cameraOpen(int cameraId)276     CameraProxy cameraOpen(int cameraId) {
277         // Cannot open camera in mCameraHandler, otherwise all camera events
278         // will be routed to mCameraHandler looper, which in turn will call
279         // event handler like Camera.onFaceDetection, which in turn will modify
280         // UI and cause exception like this:
281         // CalledFromWrongThreadException: Only the original thread that created
282         // a view hierarchy can touch its views.
283         mCamera = android.hardware.Camera.open(cameraId);
284         if (mCamera != null) {
285             mCameraProxy = new CameraProxy();
286             return mCameraProxy;
287         } else {
288             return null;
289         }
290     }
291 
292     public class CameraProxy {
CameraProxy()293         private CameraProxy() {
294             Assert(mCamera != null);
295         }
296 
getCamera()297         public android.hardware.Camera getCamera() {
298             return mCamera;
299         }
300 
release()301         public void release() {
302             mSig.close();
303             mCameraHandler.sendEmptyMessage(RELEASE);
304             mSig.block();
305         }
306 
reconnect()307         public void reconnect() throws IOException {
308             mSig.close();
309             mCameraHandler.sendEmptyMessage(RECONNECT);
310             mSig.block();
311             if (mReconnectException != null) {
312                 throw mReconnectException;
313             }
314         }
315 
unlock()316         public void unlock() {
317             mSig.close();
318             mCameraHandler.sendEmptyMessage(UNLOCK);
319             mSig.block();
320         }
321 
lock()322         public void lock() {
323             mSig.close();
324             mCameraHandler.sendEmptyMessage(LOCK);
325             mSig.block();
326         }
327 
328         @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
setPreviewTextureAsync(final SurfaceTexture surfaceTexture)329         public void setPreviewTextureAsync(final SurfaceTexture surfaceTexture) {
330             mCameraHandler.obtainMessage(SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture).sendToTarget();
331         }
332 
setPreviewDisplayAsync(final SurfaceHolder surfaceHolder)333         public void setPreviewDisplayAsync(final SurfaceHolder surfaceHolder) {
334             mCameraHandler.obtainMessage(SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder).sendToTarget();
335         }
336 
startPreviewAsync()337         public void startPreviewAsync() {
338             mCameraHandler.sendEmptyMessage(START_PREVIEW_ASYNC);
339         }
340 
stopPreview()341         public void stopPreview() {
342             mSig.close();
343             mCameraHandler.sendEmptyMessage(STOP_PREVIEW);
344             mSig.block();
345         }
346 
setPreviewCallback(final PreviewCallback cb)347         public void setPreviewCallback(final PreviewCallback cb) {
348             mSig.close();
349             mCameraHandler.obtainMessage(SET_PREVIEW_CALLBACK, cb).sendToTarget();
350             mSig.block();
351         }
352 
setPreviewCallbackWithBuffer(final PreviewCallback cb)353         public void setPreviewCallbackWithBuffer(final PreviewCallback cb) {
354             mSig.close();
355             mCameraHandler.obtainMessage(SET_PREVIEW_CALLBACK_WITH_BUFFER, cb).sendToTarget();
356             mSig.block();
357         }
358 
addCallbackBuffer(byte[] callbackBuffer)359         public void addCallbackBuffer(byte[] callbackBuffer) {
360             mSig.close();
361             mCameraHandler.obtainMessage(ADD_CALLBACK_BUFFER, callbackBuffer).sendToTarget();
362             mSig.block();
363         }
364 
autoFocus(AutoFocusCallback cb)365         public void autoFocus(AutoFocusCallback cb) {
366             mSig.close();
367             mCameraHandler.obtainMessage(AUTO_FOCUS, cb).sendToTarget();
368             mSig.block();
369         }
370 
cancelAutoFocus()371         public void cancelAutoFocus() {
372             mSig.close();
373             mCameraHandler.sendEmptyMessage(CANCEL_AUTO_FOCUS);
374             mSig.block();
375         }
376 
377         @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN)
setAutoFocusMoveCallback(AutoFocusMoveCallback cb)378         public void setAutoFocusMoveCallback(AutoFocusMoveCallback cb) {
379             mSig.close();
380             mCameraHandler.obtainMessage(SET_AUTO_FOCUS_MOVE_CALLBACK, cb).sendToTarget();
381             mSig.block();
382         }
383 
takePicture(final ShutterCallback shutter, final PictureCallback raw, final PictureCallback postview, final PictureCallback jpeg)384         public void takePicture(final ShutterCallback shutter, final PictureCallback raw,
385                 final PictureCallback postview, final PictureCallback jpeg) {
386             mSig.close();
387             // Too many parameters, so use post for simplicity
388             mCameraHandler.post(new Runnable() {
389                 @Override
390                 public void run() {
391                     mCamera.takePicture(shutter, raw, postview, jpeg);
392                     mSig.open();
393                 }
394             });
395             mSig.block();
396         }
397 
takePicture2(final ShutterCallback shutter, final PictureCallback raw, final PictureCallback postview, final PictureCallback jpeg, final int cameraState, final int focusState)398         public void takePicture2(final ShutterCallback shutter, final PictureCallback raw,
399                 final PictureCallback postview, final PictureCallback jpeg,
400                 final int cameraState, final int focusState) {
401             mSig.close();
402             // Too many parameters, so use post for simplicity
403             mCameraHandler.post(new Runnable() {
404                 @Override
405                 public void run() {
406                     try {
407                         mCamera.takePicture(shutter, raw, postview, jpeg);
408                     } catch (RuntimeException e) {
409                         Log.w(TAG, "take picture failed; cameraState:" + cameraState
410                             + ", focusState:" + focusState);
411                         throw e;
412                     }
413                     mSig.open();
414                 }
415             });
416             mSig.block();
417         }
418 
setDisplayOrientation(int degrees)419         public void setDisplayOrientation(int degrees) {
420             mSig.close();
421             mCameraHandler.obtainMessage(SET_DISPLAY_ORIENTATION, degrees, 0)
422                     .sendToTarget();
423             mSig.block();
424         }
425 
setZoomChangeListener(OnZoomChangeListener listener)426         public void setZoomChangeListener(OnZoomChangeListener listener) {
427             mSig.close();
428             mCameraHandler.obtainMessage(SET_ZOOM_CHANGE_LISTENER, listener).sendToTarget();
429             mSig.block();
430         }
431 
432         @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH)
setFaceDetectionListener(FaceDetectionListener listener)433         public void setFaceDetectionListener(FaceDetectionListener listener) {
434             mSig.close();
435             mCameraHandler.obtainMessage(SET_FACE_DETECTION_LISTENER, listener).sendToTarget();
436             mSig.block();
437         }
438 
startFaceDetection()439         public void startFaceDetection() {
440             mSig.close();
441             mCameraHandler.sendEmptyMessage(START_FACE_DETECTION);
442             mSig.block();
443         }
444 
stopFaceDetection()445         public void stopFaceDetection() {
446             mSig.close();
447             mCameraHandler.sendEmptyMessage(STOP_FACE_DETECTION);
448             mSig.block();
449         }
450 
setErrorCallback(ErrorCallback cb)451         public void setErrorCallback(ErrorCallback cb) {
452             mSig.close();
453             mCameraHandler.obtainMessage(SET_ERROR_CALLBACK, cb).sendToTarget();
454             mSig.block();
455         }
456 
setParameters(Parameters params)457         public void setParameters(Parameters params) {
458             mSig.close();
459             mCameraHandler.obtainMessage(SET_PARAMETERS, params).sendToTarget();
460             mSig.block();
461         }
462 
setParametersAsync(Parameters params)463         public void setParametersAsync(Parameters params) {
464             mCameraHandler.removeMessages(SET_PARAMETERS_ASYNC);
465             mCameraHandler.obtainMessage(SET_PARAMETERS_ASYNC, params).sendToTarget();
466         }
467 
getParameters()468         public Parameters getParameters() {
469             mSig.close();
470             mCameraHandler.sendEmptyMessage(GET_PARAMETERS);
471             mSig.block();
472             Parameters parameters = mParameters;
473             mParameters = null;
474             return parameters;
475         }
476 
enableShutterSound(boolean enable)477         public void enableShutterSound(boolean enable) {
478             mSig.close();
479             mCameraHandler.obtainMessage(
480                     ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0).sendToTarget();
481             mSig.block();
482         }
483 
waitForIdle()484         public void waitForIdle() {
485             mSig.close();
486             mCameraHandler.sendEmptyMessage(WAIT_FOR_IDLE);
487             mSig.block();
488         }
489     }
490 }
491