1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
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.AutoFocusCallback;
23 import android.hardware.Camera.AutoFocusMoveCallback;
24 import android.hardware.Camera.ErrorCallback;
25 import android.hardware.Camera.FaceDetectionListener;
26 import android.hardware.Camera.OnZoomChangeListener;
27 import android.hardware.Camera.Parameters;
28 import android.hardware.Camera.PictureCallback;
29 import android.hardware.Camera.PreviewCallback;
30 import android.hardware.Camera.ShutterCallback;
31 import android.os.Build;
32 import android.os.Handler;
33 import android.os.HandlerThread;
34 import android.os.Looper;
35 import android.os.Message;
36 import android.view.SurfaceHolder;
37 
38 import com.android.ex.camera2.portability.debug.Log;
39 
40 import java.io.IOException;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.StringTokenizer;
44 
45 /**
46  * A class to implement {@link CameraAgent} of the Android camera framework.
47  */
48 class AndroidCameraAgentImpl extends CameraAgent {
49     private static final Log.Tag TAG = new Log.Tag("AndCamAgntImp");
50 
51     private CameraDeviceInfo.Characteristics mCharacteristics;
52     private AndroidCameraCapabilities mCapabilities;
53 
54     private final CameraHandler mCameraHandler;
55     private final HandlerThread mCameraHandlerThread;
56     private final CameraStateHolder mCameraState;
57     private final DispatchThread mDispatchThread;
58 
59     private static final CameraExceptionHandler sDefaultExceptionHandler =
60             new CameraExceptionHandler(null) {
61         @Override
62         public void onCameraError(int errorCode) {
63             Log.w(TAG, "onCameraError called with no handler set: " + errorCode);
64         }
65 
66         @Override
67         public void onCameraException(RuntimeException ex, String commandHistory, int action,
68                 int state) {
69             Log.w(TAG, "onCameraException called with no handler set", ex);
70         }
71 
72         @Override
73         public void onDispatchThreadException(RuntimeException ex) {
74             Log.w(TAG, "onDispatchThreadException called with no handler set", ex);
75         }
76     };
77 
78     private CameraExceptionHandler mExceptionHandler = sDefaultExceptionHandler;
79 
AndroidCameraAgentImpl()80     AndroidCameraAgentImpl() {
81         mCameraHandlerThread = new HandlerThread("Camera Handler Thread");
82         mCameraHandlerThread.start();
83         mCameraHandler = new CameraHandler(this, mCameraHandlerThread.getLooper());
84         mExceptionHandler = new CameraExceptionHandler(mCameraHandler);
85         mCameraState = new AndroidCameraStateHolder();
86         mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread);
87         mDispatchThread.start();
88     }
89 
90     @Override
recycle()91     public void recycle() {
92         closeCamera(null, true);
93         mDispatchThread.end();
94         mCameraState.invalidate();
95     }
96 
97     @Override
getCameraDeviceInfo()98     public CameraDeviceInfo getCameraDeviceInfo() {
99         return AndroidCameraDeviceInfo.create();
100     }
101 
102     @Override
getCameraHandler()103     protected Handler getCameraHandler() {
104         return mCameraHandler;
105     }
106 
107     @Override
getDispatchThread()108     protected DispatchThread getDispatchThread() {
109         return mDispatchThread;
110     }
111 
112     @Override
getCameraState()113     protected CameraStateHolder getCameraState() {
114         return mCameraState;
115     }
116 
117     @Override
getCameraExceptionHandler()118     protected CameraExceptionHandler getCameraExceptionHandler() {
119         return mExceptionHandler;
120     }
121 
122     @Override
setCameraExceptionHandler(CameraExceptionHandler exceptionHandler)123     public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) {
124         // In case of null set the default handler to route exceptions to logs
125         mExceptionHandler = exceptionHandler != null ? exceptionHandler : sDefaultExceptionHandler;
126     }
127 
128     private static class AndroidCameraDeviceInfo implements CameraDeviceInfo {
129         private final Camera.CameraInfo[] mCameraInfos;
130         private final int mNumberOfCameras;
131         private final int mFirstBackCameraId;
132         private final int mFirstFrontCameraId;
133 
AndroidCameraDeviceInfo(Camera.CameraInfo[] info, int numberOfCameras, int firstBackCameraId, int firstFrontCameraId)134         private AndroidCameraDeviceInfo(Camera.CameraInfo[] info, int numberOfCameras,
135                 int firstBackCameraId, int firstFrontCameraId) {
136 
137             mCameraInfos = info;
138             mNumberOfCameras = numberOfCameras;
139             mFirstBackCameraId = firstBackCameraId;
140             mFirstFrontCameraId = firstFrontCameraId;
141         }
142 
create()143         public static AndroidCameraDeviceInfo create() {
144             int numberOfCameras;
145             Camera.CameraInfo[] cameraInfos;
146             try {
147                 numberOfCameras = Camera.getNumberOfCameras();
148                 cameraInfos = new Camera.CameraInfo[numberOfCameras];
149                 for (int i = 0; i < numberOfCameras; i++) {
150                     cameraInfos[i] = new Camera.CameraInfo();
151                     Camera.getCameraInfo(i, cameraInfos[i]);
152                 }
153             } catch (RuntimeException ex) {
154                 Log.e(TAG, "Exception while creating CameraDeviceInfo", ex);
155                 return null;
156             }
157 
158             int firstFront = NO_DEVICE;
159             int firstBack = NO_DEVICE;
160             // Get the first (smallest) back and first front camera id.
161             for (int i = numberOfCameras - 1; i >= 0; i--) {
162                 if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
163                     firstBack = i;
164                 } else {
165                     if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
166                         firstFront = i;
167                     }
168                 }
169             }
170 
171             return new AndroidCameraDeviceInfo(cameraInfos, numberOfCameras, firstBack, firstFront);
172         }
173 
174         @Override
getCharacteristics(int cameraId)175         public Characteristics getCharacteristics(int cameraId) {
176             Camera.CameraInfo info = mCameraInfos[cameraId];
177             if (info != null) {
178                 return new AndroidCharacteristics(info);
179             } else {
180                 return null;
181             }
182         }
183 
184         @Override
getNumberOfCameras()185         public int getNumberOfCameras() {
186             return mNumberOfCameras;
187         }
188 
189         @Override
getFirstBackCameraId()190         public int getFirstBackCameraId() {
191             return mFirstBackCameraId;
192         }
193 
194         @Override
getFirstFrontCameraId()195         public int getFirstFrontCameraId() {
196             return mFirstFrontCameraId;
197         }
198 
199         private static class AndroidCharacteristics extends Characteristics {
200             private Camera.CameraInfo mCameraInfo;
201 
AndroidCharacteristics(Camera.CameraInfo cameraInfo)202             AndroidCharacteristics(Camera.CameraInfo cameraInfo) {
203                 mCameraInfo = cameraInfo;
204             }
205 
206             @Override
isFacingBack()207             public boolean isFacingBack() {
208                 return mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK;
209             }
210 
211             @Override
isFacingFront()212             public boolean isFacingFront() {
213                 return mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT;
214             }
215 
216             @Override
getSensorOrientation()217             public int getSensorOrientation() {
218                 return mCameraInfo.orientation;
219             }
220 
221             @Override
canDisableShutterSound()222             public boolean canDisableShutterSound() {
223                 return mCameraInfo.canDisableShutterSound;
224             }
225         }
226     }
227 
228     private static class ParametersCache {
229         private Parameters mParameters;
230         private Camera mCamera;
231 
ParametersCache(Camera camera)232         public ParametersCache(Camera camera) {
233             mCamera = camera;
234         }
235 
invalidate()236         public synchronized void invalidate() {
237             mParameters = null;
238         }
239 
240         /**
241          * Access parameters from the cache. If cache is empty, block by
242          * retrieving parameters directly from Camera, but if cache is present,
243          * returns immediately.
244          */
getBlocking()245         public synchronized Parameters getBlocking() {
246             if (mParameters == null) {
247                 mParameters = mCamera.getParameters();
248                 if (mParameters == null) {
249                     Log.e(TAG, "Camera object returned null parameters!");
250                     throw new IllegalStateException("camera.getParameters returned null");
251                 }
252             }
253             return mParameters;
254         }
255     }
256 
257     /**
258      * The handler on which the actual camera operations happen.
259      */
260     private class CameraHandler extends HistoryHandler implements Camera.ErrorCallback {
261         private CameraAgent mAgent;
262         private Camera mCamera;
263         private int mCameraId = -1;
264         private ParametersCache mParameterCache;
265         private int mCancelAfPending = 0;
266 
267         private class CaptureCallbacks {
268             public final ShutterCallback mShutter;
269             public final PictureCallback mRaw;
270             public final PictureCallback mPostView;
271             public final PictureCallback mJpeg;
272 
CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView, PictureCallback jpeg)273             CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView,
274                     PictureCallback jpeg) {
275                 mShutter = shutter;
276                 mRaw = raw;
277                 mPostView = postView;
278                 mJpeg = jpeg;
279             }
280         }
281 
CameraHandler(CameraAgent agent, Looper looper)282         CameraHandler(CameraAgent agent, Looper looper) {
283             super(looper);
284             mAgent = agent;
285         }
286 
startFaceDetection()287         private void startFaceDetection() {
288             mCamera.startFaceDetection();
289         }
290 
stopFaceDetection()291         private void stopFaceDetection() {
292             mCamera.stopFaceDetection();
293         }
294 
setFaceDetectionListener(FaceDetectionListener listener)295         private void setFaceDetectionListener(FaceDetectionListener listener) {
296             mCamera.setFaceDetectionListener(listener);
297         }
298 
setPreviewTexture(Object surfaceTexture)299         private void setPreviewTexture(Object surfaceTexture) {
300             try {
301                 mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
302             } catch (IOException e) {
303                 Log.e(TAG, "Could not set preview texture", e);
304             }
305         }
306 
307         @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
enableShutterSound(boolean enable)308         private void enableShutterSound(boolean enable) {
309             mCamera.enableShutterSound(enable);
310         }
311 
312         @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
setAutoFocusMoveCallback( android.hardware.Camera camera, Object cb)313         private void setAutoFocusMoveCallback(
314                 android.hardware.Camera camera, Object cb) {
315             try {
316                 camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
317             } catch (RuntimeException ex) {
318                 Log.w(TAG, ex.getMessage());
319             }
320         }
321 
requestTakePicture( final ShutterCallback shutter, final PictureCallback raw, final PictureCallback postView, final PictureCallback jpeg)322         public void requestTakePicture(
323                 final ShutterCallback shutter,
324                 final PictureCallback raw,
325                 final PictureCallback postView,
326                 final PictureCallback jpeg) {
327             final CaptureCallbacks callbacks = new CaptureCallbacks(shutter, raw, postView, jpeg);
328             obtainMessage(CameraActions.CAPTURE_PHOTO, callbacks).sendToTarget();
329         }
330 
331         @Override
onError(final int errorCode, Camera camera)332         public void onError(final int errorCode, Camera camera) {
333             mExceptionHandler.onCameraError(errorCode);
334             if (errorCode == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
335                 int lastCameraAction = getCurrentMessage();
336                 mExceptionHandler.onCameraException(
337                         new RuntimeException("Media server died."),
338                         generateHistoryString(mCameraId),
339                         lastCameraAction,
340                         mCameraState.getState());
341             }
342         }
343 
344         /**
345          * This method does not deal with the API level check.  Everyone should
346          * check first for supported operations before sending message to this handler.
347          */
348         @Override
handleMessage(final Message msg)349         public void handleMessage(final Message msg) {
350             super.handleMessage(msg);
351 
352             if (getCameraState().isInvalid()) {
353                 Log.v(TAG, "Skip handleMessage - action = '" + CameraActions.stringify(msg.what) + "'");
354                 return;
355             }
356             Log.v(TAG, "handleMessage - action = '" + CameraActions.stringify(msg.what) + "'");
357 
358             int cameraAction = msg.what;
359             try {
360                 switch (cameraAction) {
361                     case CameraActions.OPEN_CAMERA: {
362                         final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
363                         final int cameraId = msg.arg1;
364                         if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_UNOPENED) {
365                             openCallback.onDeviceOpenedAlready(cameraId, generateHistoryString(cameraId));
366                             break;
367                         }
368 
369                         Log.i(TAG, "Opening camera " + cameraId + " with camera1 API");
370                         mCamera = android.hardware.Camera.open(cameraId);
371                         if (mCamera != null) {
372                             mCameraId = cameraId;
373                             mParameterCache = new ParametersCache(mCamera);
374 
375                             mCharacteristics =
376                                     AndroidCameraDeviceInfo.create().getCharacteristics(cameraId);
377                             mCapabilities = new AndroidCameraCapabilities(
378                                     mParameterCache.getBlocking());
379 
380                             mCamera.setErrorCallback(this);
381 
382                             mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
383                             if (openCallback != null) {
384                                 CameraProxy cameraProxy = new AndroidCameraProxyImpl(
385                                         mAgent, cameraId, mCamera, mCharacteristics, mCapabilities);
386                                 openCallback.onCameraOpened(cameraProxy);
387                             }
388                         } else {
389                             if (openCallback != null) {
390                                 openCallback.onDeviceOpenFailure(cameraId, generateHistoryString(cameraId));
391                             }
392                         }
393                         break;
394                     }
395 
396                     case CameraActions.RELEASE: {
397                         if (mCamera != null) {
398                             mCamera.release();
399                             mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED);
400                             mCamera = null;
401                             mCameraId = -1;
402                         } else {
403                             Log.w(TAG, "Releasing camera without any camera opened.");
404                         }
405                         break;
406                     }
407 
408                     case CameraActions.RECONNECT: {
409                         final CameraOpenCallbackForward cbForward =
410                                 (CameraOpenCallbackForward) msg.obj;
411                         final int cameraId = msg.arg1;
412                         try {
413                             mCamera.reconnect();
414                         } catch (IOException ex) {
415                             if (cbForward != null) {
416                                 cbForward.onReconnectionFailure(mAgent, generateHistoryString(mCameraId));
417                             }
418                             break;
419                         }
420 
421                         mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
422                         if (cbForward != null) {
423                             cbForward.onCameraOpened(
424                                     new AndroidCameraProxyImpl(AndroidCameraAgentImpl.this,
425                                             cameraId, mCamera, mCharacteristics, mCapabilities));
426                         }
427                         break;
428                     }
429 
430                     case CameraActions.UNLOCK: {
431                         mCamera.unlock();
432                         mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNLOCKED);
433                         break;
434                     }
435 
436                     case CameraActions.LOCK: {
437                         mCamera.lock();
438                         mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
439                         break;
440                     }
441 
442                     // TODO: Lock the CameraSettings object's sizes
443                     case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: {
444                         setPreviewTexture(msg.obj);
445                         break;
446                     }
447 
448                     case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: {
449                         try {
450                             mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
451                         } catch (IOException e) {
452                             throw new RuntimeException(e);
453                         }
454                         break;
455                     }
456 
457                     case CameraActions.START_PREVIEW_ASYNC: {
458                         final CameraStartPreviewCallbackForward cbForward =
459                             (CameraStartPreviewCallbackForward) msg.obj;
460                         mCamera.startPreview();
461                         if (cbForward != null) {
462                             cbForward.onPreviewStarted();
463                         }
464                         break;
465                     }
466 
467                     // TODO: Unlock the CameraSettings object's sizes
468                     case CameraActions.STOP_PREVIEW: {
469                         mCamera.stopPreview();
470                         break;
471                     }
472 
473                     case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: {
474                         mCamera.setPreviewCallbackWithBuffer((PreviewCallback) msg.obj);
475                         break;
476                     }
477 
478                     case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: {
479                         mCamera.setOneShotPreviewCallback((PreviewCallback) msg.obj);
480                         break;
481                     }
482 
483                     case CameraActions.ADD_CALLBACK_BUFFER: {
484                         mCamera.addCallbackBuffer((byte[]) msg.obj);
485                         break;
486                     }
487 
488                     case CameraActions.AUTO_FOCUS: {
489                         if (mCancelAfPending > 0) {
490                             Log.v(TAG, "handleMessage - Ignored AUTO_FOCUS because there was "
491                                     + mCancelAfPending + " pending CANCEL_AUTO_FOCUS messages");
492                             break; // ignore AF because a CANCEL_AF is queued after this
493                         }
494                         mCameraState.setState(AndroidCameraStateHolder.CAMERA_FOCUSING);
495                         mCamera.autoFocus((AutoFocusCallback) msg.obj);
496                         break;
497                     }
498 
499                     case CameraActions.CANCEL_AUTO_FOCUS: {
500                         // Ignore all AFs that were already queued until we see
501                         // a CANCEL_AUTO_FOCUS_FINISH
502                         mCancelAfPending++;
503                         mCamera.cancelAutoFocus();
504                         mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
505                         break;
506                     }
507 
508                     case CameraActions.CANCEL_AUTO_FOCUS_FINISH: {
509                         // Stop ignoring AUTO_FOCUS messages unless there are additional
510                         // CANCEL_AUTO_FOCUSes that were added
511                         mCancelAfPending--;
512                         break;
513                     }
514 
515                     case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: {
516                         setAutoFocusMoveCallback(mCamera, msg.obj);
517                         break;
518                     }
519 
520                     case CameraActions.SET_DISPLAY_ORIENTATION: {
521                         // Update preview orientation
522                         mCamera.setDisplayOrientation(
523                                 mCharacteristics.getPreviewOrientation(msg.arg1));
524                         // Only set the JPEG capture orientation if requested to do so; otherwise,
525                         // capture in the sensor's physical orientation. (e.g., JPEG rotation is
526                         // necessary in auto-rotate mode.
527                         Parameters parameters = mParameterCache.getBlocking();
528                         parameters.setRotation(
529                                 msg.arg2 > 0 ? mCharacteristics.getJpegOrientation(msg.arg1) : 0);
530                         mCamera.setParameters(parameters);
531                         mParameterCache.invalidate();
532                         break;
533                     }
534 
535                     case CameraActions.SET_JPEG_ORIENTATION: {
536                         Parameters parameters = mParameterCache.getBlocking();
537                         parameters.setRotation(msg.arg1);
538                         mCamera.setParameters(parameters);
539                         mParameterCache.invalidate();
540                         break;
541                     }
542 
543                     case CameraActions.SET_ZOOM_CHANGE_LISTENER: {
544                         mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj);
545                         break;
546                     }
547 
548                     case CameraActions.SET_FACE_DETECTION_LISTENER: {
549                         setFaceDetectionListener((FaceDetectionListener) msg.obj);
550                         break;
551                     }
552 
553                     case CameraActions.START_FACE_DETECTION: {
554                         startFaceDetection();
555                         break;
556                     }
557 
558                     case CameraActions.STOP_FACE_DETECTION: {
559                         stopFaceDetection();
560                         break;
561                     }
562 
563                     case CameraActions.APPLY_SETTINGS: {
564                         Parameters parameters = mParameterCache.getBlocking();
565                         CameraSettings settings = (CameraSettings) msg.obj;
566                         applySettingsToParameters(settings, parameters);
567                         mCamera.setParameters(parameters);
568                         mParameterCache.invalidate();
569                         break;
570                     }
571 
572                     case CameraActions.SET_PARAMETERS: {
573                         Parameters parameters = mParameterCache.getBlocking();
574                         parameters.unflatten((String) msg.obj);
575                         mCamera.setParameters(parameters);
576                         mParameterCache.invalidate();
577                         break;
578                     }
579 
580                     case CameraActions.GET_PARAMETERS: {
581                         Parameters[] parametersHolder = (Parameters[]) msg.obj;
582                         Parameters parameters = mParameterCache.getBlocking();
583                         parametersHolder[0] = parameters;
584                         break;
585                     }
586 
587                     case CameraActions.SET_PREVIEW_CALLBACK: {
588                         mCamera.setPreviewCallback((PreviewCallback) msg.obj);
589                         break;
590                     }
591 
592                     case CameraActions.ENABLE_SHUTTER_SOUND: {
593                         enableShutterSound((msg.arg1 == 1) ? true : false);
594                         break;
595                     }
596 
597                     case CameraActions.REFRESH_PARAMETERS: {
598                         mParameterCache.invalidate();;
599                         break;
600                     }
601 
602                     case CameraActions.CAPTURE_PHOTO: {
603                         mCameraState.setState(AndroidCameraStateHolder.CAMERA_CAPTURING);
604                         CaptureCallbacks captureCallbacks = (CaptureCallbacks) msg.obj;
605                         mCamera.takePicture(
606                                 captureCallbacks.mShutter,
607                                 captureCallbacks.mRaw,
608                                 captureCallbacks.mPostView,
609                                 captureCallbacks.mJpeg);
610                         break;
611                     }
612 
613                     default: {
614                         Log.e(TAG, "Invalid CameraProxy message=" + msg.what);
615                     }
616                 }
617             } catch (final RuntimeException ex) {
618                 int cameraState = mCameraState.getState();
619                 String errorContext = "CameraAction[" + CameraActions.stringify(cameraAction) +
620                         "] at CameraState[" + cameraState + "]";
621                 Log.e(TAG, "RuntimeException during " + errorContext, ex);
622 
623                 if (mCamera != null) {
624                     Log.i(TAG, "Release camera since mCamera is not null.");
625                     try {
626                         mCamera.release();
627                     } catch (Exception e) {
628                         Log.e(TAG, "Fail when calling Camera.release().", e);
629                     } finally {
630                         mCamera = null;
631                     }
632                 }
633 
634                 // Invoke error callback.
635                 if (msg.what == CameraActions.OPEN_CAMERA && mCamera == null) {
636                     final int cameraId = msg.arg1;
637                     if (msg.obj != null) {
638                         ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(
639                                 msg.arg1, generateHistoryString(cameraId));
640                     }
641                 } else {
642                     CameraExceptionHandler exceptionHandler = mAgent.getCameraExceptionHandler();
643                     exceptionHandler.onCameraException(
644                             ex, generateHistoryString(mCameraId), cameraAction, cameraState);
645                 }
646             } finally {
647                 WaitDoneBundle.unblockSyncWaiters(msg);
648             }
649         }
650 
applySettingsToParameters(final CameraSettings settings, final Parameters parameters)651         private void applySettingsToParameters(final CameraSettings settings,
652                 final Parameters parameters) {
653             final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier();
654             Size photoSize = settings.getCurrentPhotoSize();
655             parameters.setPictureSize(photoSize.width(), photoSize.height());
656             Size previewSize = settings.getCurrentPreviewSize();
657             parameters.setPreviewSize(previewSize.width(), previewSize.height());
658             if (settings.getPreviewFrameRate() == -1) {
659                 parameters.setPreviewFpsRange(settings.getPreviewFpsRangeMin(),
660                         settings.getPreviewFpsRangeMax());
661             } else {
662                 parameters.setPreviewFrameRate(settings.getPreviewFrameRate());
663             }
664             parameters.setPreviewFormat(settings.getCurrentPreviewFormat());
665             parameters.setJpegQuality(settings.getPhotoJpegCompressionQuality());
666             if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
667                 parameters.setZoom(zoomRatioToIndex(settings.getCurrentZoomRatio(),
668                         parameters.getZoomRatios()));
669             }
670             parameters.setExposureCompensation(settings.getExposureCompensationIndex());
671             if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) {
672                 parameters.setAutoExposureLock(settings.isAutoExposureLocked());
673             }
674             parameters.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode()));
675             if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) {
676                 parameters.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked());
677             }
678             if (settings.getWhiteBalance() != null) {
679                 parameters.setWhiteBalance(stringifier.stringify(settings.getWhiteBalance()));
680             }
681             if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) {
682                 if (settings.getFocusAreas().size() != 0) {
683                     parameters.setFocusAreas(settings.getFocusAreas());
684                 } else {
685                     parameters.setFocusAreas(null);
686                 }
687             }
688             if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) {
689                 if (settings.getMeteringAreas().size() != 0) {
690                     parameters.setMeteringAreas(settings.getMeteringAreas());
691                 } else {
692                     parameters.setMeteringAreas(null);
693                 }
694             }
695             if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) {
696                 parameters.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode()));
697             }
698             if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) {
699                 if (settings.getCurrentSceneMode() != null) {
700                     parameters
701                             .setSceneMode(stringifier.stringify(settings.getCurrentSceneMode()));
702                 }
703             }
704             parameters.setRecordingHint(settings.isRecordingHintEnabled());
705             Size jpegThumbSize = settings.getExifThumbnailSize();
706             if (jpegThumbSize != null) {
707                 parameters.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
708             }
709             parameters.setPictureFormat(settings.getCurrentPhotoFormat());
710 
711             CameraSettings.GpsData gpsData = settings.getGpsData();
712             if (gpsData == null) {
713                 parameters.removeGpsData();
714             } else {
715                 parameters.setGpsTimestamp(gpsData.timeStamp);
716                 if (gpsData.processingMethod != null) {
717                     // It's a hack since we always use GPS time stamp but does
718                     // not use other fields sometimes. Setting processing
719                     // method to null means the other fields should not be used.
720                     parameters.setGpsAltitude(gpsData.altitude);
721                     parameters.setGpsLatitude(gpsData.latitude);
722                     parameters.setGpsLongitude(gpsData.longitude);
723                     parameters.setGpsProcessingMethod(gpsData.processingMethod);
724                 }
725             }
726 
727         }
728 
729         /**
730          * @param ratio Desired zoom ratio, in [1.0f,+Inf).
731          * @param percentages Available zoom ratios, as percentages.
732          * @return Index of the closest corresponding ratio, rounded up toward
733          *         that of the maximum available ratio.
734          */
zoomRatioToIndex(float ratio, List<Integer> percentages)735         private int zoomRatioToIndex(float ratio, List<Integer> percentages) {
736             int percent = (int) (ratio * AndroidCameraCapabilities.ZOOM_MULTIPLIER);
737             int index = Collections.binarySearch(percentages, percent);
738             if (index >= 0) {
739                 // Found the desired ratio in the supported list
740                 return index;
741             } else {
742                 // Didn't find an exact match. Where would it have been?
743                 index = -(index + 1);
744                 if (index == percentages.size()) {
745                     // Put it back in bounds by setting to the maximum allowable zoom
746                     --index;
747                 }
748                 return index;
749             }
750         }
751     }
752 
753     /**
754      * A class which implements {@link CameraAgent.CameraProxy} and
755      * camera handler thread.
756      */
757     private class AndroidCameraProxyImpl extends CameraAgent.CameraProxy {
758         private final CameraAgent mCameraAgent;
759         private final int mCameraId;
760         /* TODO: remove this Camera instance. */
761         private final Camera mCamera;
762         private final CameraDeviceInfo.Characteristics mCharacteristics;
763         private final AndroidCameraCapabilities mCapabilities;
764 
AndroidCameraProxyImpl( CameraAgent cameraAgent, int cameraId, Camera camera, CameraDeviceInfo.Characteristics characteristics, AndroidCameraCapabilities capabilities)765         private AndroidCameraProxyImpl(
766                 CameraAgent cameraAgent,
767                 int cameraId,
768                 Camera camera,
769                 CameraDeviceInfo.Characteristics characteristics,
770                 AndroidCameraCapabilities capabilities) {
771             mCameraAgent = cameraAgent;
772             mCamera = camera;
773             mCameraId = cameraId;
774             mCharacteristics = characteristics;
775             mCapabilities = capabilities;
776         }
777 
778         @Deprecated
779         @Override
getCamera()780         public android.hardware.Camera getCamera() {
781             if (getCameraState().isInvalid()) {
782                 return null;
783             }
784             return mCamera;
785         }
786 
787         @Override
getCameraId()788         public int getCameraId() {
789             return mCameraId;
790         }
791 
792         @Override
getCharacteristics()793         public CameraDeviceInfo.Characteristics getCharacteristics() {
794             return mCharacteristics;
795         }
796 
797         @Override
getCapabilities()798         public CameraCapabilities getCapabilities() {
799             return new AndroidCameraCapabilities(mCapabilities);
800         }
801 
802         @Override
getAgent()803         public CameraAgent getAgent() {
804             return mCameraAgent;
805         }
806 
807         @Override
setPreviewDataCallback( final Handler handler, final CameraPreviewDataCallback cb)808         public void setPreviewDataCallback(
809                 final Handler handler, final CameraPreviewDataCallback cb) {
810             mDispatchThread.runJob(new Runnable() {
811                 @Override
812                 public void run() {
813                     mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK,
814                             PreviewCallbackForward.getNewInstance(
815                                     handler, AndroidCameraProxyImpl.this, cb))
816                             .sendToTarget();
817                 }
818             });
819         }
820 
821         @Override
setOneShotPreviewCallback(final Handler handler, final CameraPreviewDataCallback cb)822         public void setOneShotPreviewCallback(final Handler handler,
823                 final CameraPreviewDataCallback cb) {
824             mDispatchThread.runJob(new Runnable() {
825                 @Override
826                 public void run() {
827                     mCameraHandler.obtainMessage(CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK,
828                             PreviewCallbackForward
829                                     .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
830                             .sendToTarget();
831                 }
832             });
833         }
834 
835         @Override
setPreviewDataCallbackWithBuffer( final Handler handler, final CameraPreviewDataCallback cb)836         public void setPreviewDataCallbackWithBuffer(
837                 final Handler handler, final CameraPreviewDataCallback cb) {
838             mDispatchThread.runJob(new Runnable() {
839                 @Override
840                 public void run() {
841                     mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER,
842                             PreviewCallbackForward
843                                     .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
844                             .sendToTarget();
845                 }
846             });
847         }
848 
849         @Override
autoFocus(final Handler handler, final CameraAFCallback cb)850         public void autoFocus(final Handler handler, final CameraAFCallback cb) {
851             final AutoFocusCallback afCallback = new AutoFocusCallback() {
852                 @Override
853                 public void onAutoFocus(final boolean b, Camera camera) {
854                     if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_FOCUSING) {
855                         Log.w(TAG, "onAutoFocus callback returning when not focusing");
856                     } else {
857                         mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
858                     }
859                     handler.post(new Runnable() {
860                         @Override
861                         public void run() {
862                             cb.onAutoFocus(b, AndroidCameraProxyImpl.this);
863                         }
864                     });
865                 }
866             };
867             mDispatchThread.runJob(new Runnable() {
868                 @Override
869                 public void run() {
870                     // Don't bother to wait since camera is in bad state.
871                     if (getCameraState().isInvalid()) {
872                         return;
873                     }
874                     mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE);
875                     mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback)
876                             .sendToTarget();
877                 }
878             });
879         }
880 
881         @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
882         @Override
setAutoFocusMoveCallback( final Handler handler, final CameraAFMoveCallback cb)883         public void setAutoFocusMoveCallback(
884                 final Handler handler, final CameraAFMoveCallback cb) {
885             try {
886                 mDispatchThread.runJob(new Runnable() {
887                     @Override
888                     public void run() {
889                         mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
890                                 AFMoveCallbackForward.getNewInstance(
891                                         handler, AndroidCameraProxyImpl.this, cb))
892                                 .sendToTarget();
893                     }
894                 });
895             } catch (final RuntimeException ex) {
896                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
897             }
898         }
899 
900         @Override
takePicture( final Handler handler, final CameraShutterCallback shutter, final CameraPictureCallback raw, final CameraPictureCallback post, final CameraPictureCallback jpeg)901         public void takePicture(
902                 final Handler handler, final CameraShutterCallback shutter,
903                 final CameraPictureCallback raw, final CameraPictureCallback post,
904                 final CameraPictureCallback jpeg) {
905             final PictureCallback jpegCallback = new PictureCallback() {
906                 @Override
907                 public void onPictureTaken(final byte[] data, Camera camera) {
908                     if (mCameraState.getState() != AndroidCameraStateHolder.CAMERA_CAPTURING) {
909                         Log.w(TAG, "picture callback returning when not capturing");
910                     } else {
911                         mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
912                     }
913                     handler.post(new Runnable() {
914                         @Override
915                         public void run() {
916                             jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this);
917                         }
918                     });
919                 }
920             };
921 
922             try {
923                 mDispatchThread.runJob(new Runnable() {
924                     @Override
925                     public void run() {
926                         // Don't bother to wait since camera is in bad state.
927                         if (getCameraState().isInvalid()) {
928                             return;
929                         }
930                         mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
931                                 AndroidCameraStateHolder.CAMERA_UNLOCKED);
932                         mCameraHandler.requestTakePicture(ShutterCallbackForward
933                                         .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
934                                 PictureCallbackForward
935                                         .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
936                                 PictureCallbackForward
937                                         .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
938                                 jpegCallback
939                         );
940                     }
941                 });
942             } catch (final RuntimeException ex) {
943                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
944             }
945         }
946 
947         @Override
setZoomChangeListener(final OnZoomChangeListener listener)948         public void setZoomChangeListener(final OnZoomChangeListener listener) {
949             try {
950                 mDispatchThread.runJob(new Runnable() {
951                     @Override
952                     public void run() {
953                         mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener)
954                                 .sendToTarget();
955                     }
956                 });
957             } catch (final RuntimeException ex) {
958                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
959             }
960         }
961 
962         @Override
setFaceDetectionCallback(final Handler handler, final CameraFaceDetectionCallback cb)963         public void setFaceDetectionCallback(final Handler handler,
964                 final CameraFaceDetectionCallback cb) {
965             try {
966                 mDispatchThread.runJob(new Runnable() {
967                     @Override
968                     public void run() {
969                         mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER,
970                                 FaceDetectionCallbackForward
971                                         .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
972                                 .sendToTarget();
973                     }
974                 });
975             } catch (final RuntimeException ex) {
976                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
977             }
978         }
979 
980         @Deprecated
981         @Override
setParameters(final Parameters params)982         public void setParameters(final Parameters params) {
983             if (params == null) {
984                 Log.v(TAG, "null parameters in setParameters()");
985                 return;
986             }
987             final String flattenedParameters = params.flatten();
988             try {
989                 mDispatchThread.runJob(new Runnable() {
990                     @Override
991                     public void run() {
992                         mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
993                                 AndroidCameraStateHolder.CAMERA_UNLOCKED);
994                         mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters)
995                                 .sendToTarget();
996                     }
997                 });
998             } catch (final RuntimeException ex) {
999                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
1000             }
1001         }
1002 
1003         @Deprecated
1004         @Override
getParameters()1005         public Parameters getParameters() {
1006             final WaitDoneBundle bundle = new WaitDoneBundle();
1007             final Parameters[] parametersHolder = new Parameters[1];
1008             try {
1009                 mDispatchThread.runJobSync(new Runnable() {
1010                     @Override
1011                     public void run() {
1012                         mCameraHandler.obtainMessage(
1013                                 CameraActions.GET_PARAMETERS, parametersHolder).sendToTarget();
1014                         mCameraHandler.post(bundle.mUnlockRunnable);
1015                     }
1016                 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
1017             } catch (final RuntimeException ex) {
1018                 mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
1019             }
1020             return parametersHolder[0];
1021         }
1022 
1023         @Override
getSettings()1024         public CameraSettings getSettings() {
1025             return new AndroidCameraSettings(mCapabilities, getParameters());
1026         }
1027 
1028         @Override
applySettings(CameraSettings settings)1029         public boolean applySettings(CameraSettings settings) {
1030             return applySettingsHelper(settings, AndroidCameraStateHolder.CAMERA_IDLE |
1031                     AndroidCameraStateHolder.CAMERA_UNLOCKED);
1032         }
1033 
1034         @Override
dumpDeviceSettings()1035         public String dumpDeviceSettings() {
1036             Parameters parameters = getParameters();
1037             if (parameters != null) {
1038                 String flattened = getParameters().flatten();
1039                 StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
1040                 String dumpedSettings = new String();
1041                 while (tokenizer.hasMoreElements()) {
1042                     dumpedSettings += tokenizer.nextToken() + '\n';
1043                 }
1044 
1045                 return dumpedSettings;
1046             } else {
1047                 return "[no parameters retrieved]";
1048             }
1049         }
1050 
1051         @Override
getCameraHandler()1052         public Handler getCameraHandler() {
1053             return AndroidCameraAgentImpl.this.getCameraHandler();
1054         }
1055 
1056         @Override
getDispatchThread()1057         public DispatchThread getDispatchThread() {
1058             return AndroidCameraAgentImpl.this.getDispatchThread();
1059         }
1060 
1061         @Override
getCameraState()1062         public CameraStateHolder getCameraState() {
1063             return mCameraState;
1064         }
1065     }
1066 
1067     private static class AndroidCameraStateHolder extends CameraStateHolder {
1068         /* Camera states */
1069         // These states are defined bitwise so we can easily to specify a set of
1070         // states together.
1071         public static final int CAMERA_UNOPENED = 1;
1072         public static final int CAMERA_IDLE = 1 << 1;
1073         public static final int CAMERA_UNLOCKED = 1 << 2;
1074         public static final int CAMERA_CAPTURING = 1 << 3;
1075         public static final int CAMERA_FOCUSING = 1 << 4;
1076 
AndroidCameraStateHolder()1077         public AndroidCameraStateHolder() {
1078             this(CAMERA_UNOPENED);
1079         }
1080 
AndroidCameraStateHolder(int state)1081         public AndroidCameraStateHolder(int state) {
1082             super(state);
1083         }
1084     }
1085 
1086     /**
1087      * A helper class to forward AutoFocusCallback to another thread.
1088      */
1089     private static class AFCallbackForward implements AutoFocusCallback {
1090         private final Handler mHandler;
1091         private final CameraProxy mCamera;
1092         private final CameraAFCallback mCallback;
1093 
1094         /**
1095          * Returns a new instance of {@link AFCallbackForward}.
1096          *
1097          * @param handler The handler in which the callback will be invoked in.
1098          * @param camera  The {@link CameraProxy} which the callback is from.
1099          * @param cb      The callback to be invoked.
1100          * @return        The instance of the {@link AFCallbackForward},
1101          *                or null if any parameter is null.
1102          */
getNewInstance( Handler handler, CameraProxy camera, CameraAFCallback cb)1103         public static AFCallbackForward getNewInstance(
1104                 Handler handler, CameraProxy camera, CameraAFCallback cb) {
1105             if (handler == null || camera == null || cb == null) {
1106                 return null;
1107             }
1108             return new AFCallbackForward(handler, camera, cb);
1109         }
1110 
AFCallbackForward( Handler h, CameraProxy camera, CameraAFCallback cb)1111         private AFCallbackForward(
1112                 Handler h, CameraProxy camera, CameraAFCallback cb) {
1113             mHandler = h;
1114             mCamera = camera;
1115             mCallback = cb;
1116         }
1117 
1118         @Override
onAutoFocus(final boolean b, Camera camera)1119         public void onAutoFocus(final boolean b, Camera camera) {
1120             mHandler.post(new Runnable() {
1121                 @Override
1122                 public void run() {
1123                     mCallback.onAutoFocus(b, mCamera);
1124                 }
1125             });
1126         }
1127     }
1128 
1129     /** A helper class to forward AutoFocusMoveCallback to another thread. */
1130     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
1131     private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
1132         private final Handler mHandler;
1133         private final CameraAFMoveCallback mCallback;
1134         private final CameraProxy mCamera;
1135 
1136         /**
1137          * Returns a new instance of {@link AFMoveCallbackForward}.
1138          *
1139          * @param handler The handler in which the callback will be invoked in.
1140          * @param camera  The {@link CameraProxy} which the callback is from.
1141          * @param cb      The callback to be invoked.
1142          * @return        The instance of the {@link AFMoveCallbackForward},
1143          *                or null if any parameter is null.
1144          */
getNewInstance( Handler handler, CameraProxy camera, CameraAFMoveCallback cb)1145         public static AFMoveCallbackForward getNewInstance(
1146                 Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
1147             if (handler == null || camera == null || cb == null) {
1148                 return null;
1149             }
1150             return new AFMoveCallbackForward(handler, camera, cb);
1151         }
1152 
AFMoveCallbackForward( Handler h, CameraProxy camera, CameraAFMoveCallback cb)1153         private AFMoveCallbackForward(
1154                 Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
1155             mHandler = h;
1156             mCamera = camera;
1157             mCallback = cb;
1158         }
1159 
1160         @Override
onAutoFocusMoving( final boolean moving, android.hardware.Camera camera)1161         public void onAutoFocusMoving(
1162                 final boolean moving, android.hardware.Camera camera) {
1163             mHandler.post(new Runnable() {
1164                 @Override
1165                 public void run() {
1166                     mCallback.onAutoFocusMoving(moving, mCamera);
1167                 }
1168             });
1169         }
1170     }
1171 
1172     /**
1173      * A helper class to forward ShutterCallback to to another thread.
1174      */
1175     private static class ShutterCallbackForward implements ShutterCallback {
1176         private final Handler mHandler;
1177         private final CameraShutterCallback mCallback;
1178         private final CameraProxy mCamera;
1179 
1180         /**
1181          * Returns a new instance of {@link ShutterCallbackForward}.
1182          *
1183          * @param handler The handler in which the callback will be invoked in.
1184          * @param camera  The {@link CameraProxy} which the callback is from.
1185          * @param cb      The callback to be invoked.
1186          * @return        The instance of the {@link ShutterCallbackForward},
1187          *                or null if any parameter is null.
1188          */
getNewInstance( Handler handler, CameraProxy camera, CameraShutterCallback cb)1189         public static ShutterCallbackForward getNewInstance(
1190                 Handler handler, CameraProxy camera, CameraShutterCallback cb) {
1191             if (handler == null || camera == null || cb == null) {
1192                 return null;
1193             }
1194             return new ShutterCallbackForward(handler, camera, cb);
1195         }
1196 
ShutterCallbackForward( Handler h, CameraProxy camera, CameraShutterCallback cb)1197         private ShutterCallbackForward(
1198                 Handler h, CameraProxy camera, CameraShutterCallback cb) {
1199             mHandler = h;
1200             mCamera = camera;
1201             mCallback = cb;
1202         }
1203 
1204         @Override
onShutter()1205         public void onShutter() {
1206             mHandler.post(new Runnable() {
1207                 @Override
1208                 public void run() {
1209                     mCallback.onShutter(mCamera);
1210                 }
1211             });
1212         }
1213     }
1214 
1215     /**
1216      * A helper class to forward PictureCallback to another thread.
1217      */
1218     private static class PictureCallbackForward implements PictureCallback {
1219         private final Handler mHandler;
1220         private final CameraPictureCallback mCallback;
1221         private final CameraProxy mCamera;
1222 
1223         /**
1224          * Returns a new instance of {@link PictureCallbackForward}.
1225          *
1226          * @param handler The handler in which the callback will be invoked in.
1227          * @param camera  The {@link CameraProxy} which the callback is from.
1228          * @param cb      The callback to be invoked.
1229          * @return        The instance of the {@link PictureCallbackForward},
1230          *                or null if any parameters is null.
1231          */
getNewInstance( Handler handler, CameraProxy camera, CameraPictureCallback cb)1232         public static PictureCallbackForward getNewInstance(
1233                 Handler handler, CameraProxy camera, CameraPictureCallback cb) {
1234             if (handler == null || camera == null || cb == null) {
1235                 return null;
1236             }
1237             return new PictureCallbackForward(handler, camera, cb);
1238         }
1239 
PictureCallbackForward( Handler h, CameraProxy camera, CameraPictureCallback cb)1240         private PictureCallbackForward(
1241                 Handler h, CameraProxy camera, CameraPictureCallback cb) {
1242             mHandler = h;
1243             mCamera = camera;
1244             mCallback = cb;
1245         }
1246 
1247         @Override
onPictureTaken( final byte[] data, android.hardware.Camera camera)1248         public void onPictureTaken(
1249                 final byte[] data, android.hardware.Camera camera) {
1250             mHandler.post(new Runnable() {
1251                 @Override
1252                 public void run() {
1253                     mCallback.onPictureTaken(data, mCamera);
1254                 }
1255             });
1256         }
1257     }
1258 
1259     /**
1260      * A helper class to forward PreviewCallback to another thread.
1261      */
1262     private static class PreviewCallbackForward implements PreviewCallback {
1263         private final Handler mHandler;
1264         private final CameraPreviewDataCallback mCallback;
1265         private final CameraProxy mCamera;
1266 
1267         /**
1268          * Returns a new instance of {@link PreviewCallbackForward}.
1269          *
1270          * @param handler The handler in which the callback will be invoked in.
1271          * @param camera  The {@link CameraProxy} which the callback is from.
1272          * @param cb      The callback to be invoked.
1273          * @return        The instance of the {@link PreviewCallbackForward},
1274          *                or null if any parameters is null.
1275          */
getNewInstance( Handler handler, CameraProxy camera, CameraPreviewDataCallback cb)1276         public static PreviewCallbackForward getNewInstance(
1277                 Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
1278             if (handler == null || camera == null || cb == null) {
1279                 return null;
1280             }
1281             return new PreviewCallbackForward(handler, camera, cb);
1282         }
1283 
PreviewCallbackForward( Handler h, CameraProxy camera, CameraPreviewDataCallback cb)1284         private PreviewCallbackForward(
1285                 Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
1286             mHandler = h;
1287             mCamera = camera;
1288             mCallback = cb;
1289         }
1290 
1291         @Override
onPreviewFrame( final byte[] data, android.hardware.Camera camera)1292         public void onPreviewFrame(
1293                 final byte[] data, android.hardware.Camera camera) {
1294             mHandler.post(new Runnable() {
1295                 @Override
1296                 public void run() {
1297                     mCallback.onPreviewFrame(data, mCamera);
1298                 }
1299             });
1300         }
1301     }
1302 
1303     private static class FaceDetectionCallbackForward implements FaceDetectionListener {
1304         private final Handler mHandler;
1305         private final CameraFaceDetectionCallback mCallback;
1306         private final CameraProxy mCamera;
1307 
1308         /**
1309          * Returns a new instance of {@link FaceDetectionCallbackForward}.
1310          *
1311          * @param handler The handler in which the callback will be invoked in.
1312          * @param camera  The {@link CameraProxy} which the callback is from.
1313          * @param cb      The callback to be invoked.
1314          * @return        The instance of the {@link FaceDetectionCallbackForward},
1315          *                or null if any parameter is null.
1316          */
getNewInstance( Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb)1317         public static FaceDetectionCallbackForward getNewInstance(
1318                 Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
1319             if (handler == null || camera == null || cb == null) {
1320                 return null;
1321             }
1322             return new FaceDetectionCallbackForward(handler, camera, cb);
1323         }
1324 
FaceDetectionCallbackForward( Handler h, CameraProxy camera, CameraFaceDetectionCallback cb)1325         private FaceDetectionCallbackForward(
1326                 Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
1327             mHandler = h;
1328             mCamera = camera;
1329             mCallback = cb;
1330         }
1331 
1332         @Override
onFaceDetection( final Camera.Face[] faces, Camera camera)1333         public void onFaceDetection(
1334                 final Camera.Face[] faces, Camera camera) {
1335             mHandler.post(new Runnable() {
1336                 @Override
1337                 public void run() {
1338                     mCallback.onFaceDetection(faces, mCamera);
1339                 }
1340             });
1341         }
1342     }
1343 }
1344