Home | History | Annotate | Download | only in portability
      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 
     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
     91     public void recycle() {
     92         closeCamera(null, true);
     93         mDispatchThread.end();
     94         mCameraState.invalidate();
     95     }
     96 
     97     @Override
     98     public CameraDeviceInfo getCameraDeviceInfo() {
     99         return AndroidCameraDeviceInfo.create();
    100     }
    101 
    102     @Override
    103     protected Handler getCameraHandler() {
    104         return mCameraHandler;
    105     }
    106 
    107     @Override
    108     protected DispatchThread getDispatchThread() {
    109         return mDispatchThread;
    110     }
    111 
    112     @Override
    113     protected CameraStateHolder getCameraState() {
    114         return mCameraState;
    115     }
    116 
    117     @Override
    118     protected CameraExceptionHandler getCameraExceptionHandler() {
    119         return mExceptionHandler;
    120     }
    121 
    122     @Override
    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 
    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 
    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
    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
    185         public int getNumberOfCameras() {
    186             return mNumberOfCameras;
    187         }
    188 
    189         @Override
    190         public int getFirstBackCameraId() {
    191             return mFirstBackCameraId;
    192         }
    193 
    194         @Override
    195         public int getFirstFrontCameraId() {
    196             return mFirstFrontCameraId;
    197         }
    198 
    199         private static class AndroidCharacteristics extends Characteristics {
    200             private Camera.CameraInfo mCameraInfo;
    201 
    202             AndroidCharacteristics(Camera.CameraInfo cameraInfo) {
    203                 mCameraInfo = cameraInfo;
    204             }
    205 
    206             @Override
    207             public boolean isFacingBack() {
    208                 return mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK;
    209             }
    210 
    211             @Override
    212             public boolean isFacingFront() {
    213                 return mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT;
    214             }
    215 
    216             @Override
    217             public int getSensorOrientation() {
    218                 return mCameraInfo.orientation;
    219             }
    220 
    221             @Override
    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 
    232         public ParametersCache(Camera camera) {
    233             mCamera = camera;
    234         }
    235 
    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          */
    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 
    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 
    282         CameraHandler(CameraAgent agent, Looper looper) {
    283             super(looper);
    284             mAgent = agent;
    285         }
    286 
    287         private void startFaceDetection() {
    288             mCamera.startFaceDetection();
    289         }
    290 
    291         private void stopFaceDetection() {
    292             mCamera.stopFaceDetection();
    293         }
    294 
    295         private void setFaceDetectionListener(FaceDetectionListener listener) {
    296             mCamera.setFaceDetectionListener(listener);
    297         }
    298 
    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)
    308         private void enableShutterSound(boolean enable) {
    309             mCamera.enableShutterSound(enable);
    310         }
    311 
    312         @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    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 
    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
    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
    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 
    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          */
    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 
    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
    780         public android.hardware.Camera getCamera() {
    781             if (getCameraState().isInvalid()) {
    782                 return null;
    783             }
    784             return mCamera;
    785         }
    786 
    787         @Override
    788         public int getCameraId() {
    789             return mCameraId;
    790         }
    791 
    792         @Override
    793         public CameraDeviceInfo.Characteristics getCharacteristics() {
    794             return mCharacteristics;
    795         }
    796 
    797         @Override
    798         public CameraCapabilities getCapabilities() {
    799             return new AndroidCameraCapabilities(mCapabilities);
    800         }
    801 
    802         @Override
    803         public CameraAgent getAgent() {
    804             return mCameraAgent;
    805         }
    806 
    807         @Override
    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
    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
    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
    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
    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
    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
    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
    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
    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
   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
   1024         public CameraSettings getSettings() {
   1025             return new AndroidCameraSettings(mCapabilities, getParameters());
   1026         }
   1027 
   1028         @Override
   1029         public boolean applySettings(CameraSettings settings) {
   1030             return applySettingsHelper(settings, AndroidCameraStateHolder.CAMERA_IDLE |
   1031                     AndroidCameraStateHolder.CAMERA_UNLOCKED);
   1032         }
   1033 
   1034         @Override
   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
   1052         public Handler getCameraHandler() {
   1053             return AndroidCameraAgentImpl.this.getCameraHandler();
   1054         }
   1055 
   1056         @Override
   1057         public DispatchThread getDispatchThread() {
   1058             return AndroidCameraAgentImpl.this.getDispatchThread();
   1059         }
   1060 
   1061         @Override
   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 
   1077         public AndroidCameraStateHolder() {
   1078             this(CAMERA_UNOPENED);
   1079         }
   1080 
   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          */
   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 
   1111         private AFCallbackForward(
   1112                 Handler h, CameraProxy camera, CameraAFCallback cb) {
   1113             mHandler = h;
   1114             mCamera = camera;
   1115             mCallback = cb;
   1116         }
   1117 
   1118         @Override
   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          */
   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 
   1153         private AFMoveCallbackForward(
   1154                 Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
   1155             mHandler = h;
   1156             mCamera = camera;
   1157             mCallback = cb;
   1158         }
   1159 
   1160         @Override
   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          */
   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 
   1197         private ShutterCallbackForward(
   1198                 Handler h, CameraProxy camera, CameraShutterCallback cb) {
   1199             mHandler = h;
   1200             mCamera = camera;
   1201             mCallback = cb;
   1202         }
   1203 
   1204         @Override
   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          */
   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 
   1240         private PictureCallbackForward(
   1241                 Handler h, CameraProxy camera, CameraPictureCallback cb) {
   1242             mHandler = h;
   1243             mCamera = camera;
   1244             mCallback = cb;
   1245         }
   1246 
   1247         @Override
   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          */
   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 
   1284         private PreviewCallbackForward(
   1285                 Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
   1286             mHandler = h;
   1287             mCamera = camera;
   1288             mCallback = cb;
   1289         }
   1290 
   1291         @Override
   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          */
   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 
   1325         private FaceDetectionCallbackForward(
   1326                 Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
   1327             mHandler = h;
   1328             mCamera = camera;
   1329             mCallback = cb;
   1330         }
   1331 
   1332         @Override
   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