Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.camera;
     18 
     19 import static com.android.camera.Util.Assert;
     20 
     21 import android.hardware.Camera.CameraInfo;
     22 import android.hardware.Camera.Parameters;
     23 import android.os.Build;
     24 import android.os.Handler;
     25 import android.os.HandlerThread;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.util.Log;
     29 
     30 import com.android.camera.CameraManager.CameraProxy;
     31 
     32 import java.io.IOException;
     33 
     34 /**
     35  * The class is used to hold an {@code android.hardware.Camera} instance.
     36  *
     37  * <p>The {@code open()} and {@code release()} calls are similar to the ones
     38  * in {@code android.hardware.Camera}. The difference is if {@code keep()} is
     39  * called before {@code release()}, CameraHolder will try to hold the {@code
     40  * android.hardware.Camera} instance for a while, so if {@code open()} is
     41  * called soon after, we can avoid the cost of {@code open()} in {@code
     42  * android.hardware.Camera}.
     43  *
     44  * <p>This is used in switching between {@code Camera} and {@code VideoCamera}
     45  * activities.
     46  */
     47 public class CameraHolder {
     48     private static final String TAG = "CameraHolder";
     49     private CameraProxy mCameraDevice;
     50     private long mKeepBeforeTime;  // Keep the Camera before this time.
     51     private final Handler mHandler;
     52     private boolean mCameraOpened;  // true if camera is opened
     53     private final int mNumberOfCameras;
     54     private int mCameraId = -1;  // current camera id
     55     private int mBackCameraId = -1;
     56     private int mFrontCameraId = -1;
     57     private final CameraInfo[] mInfo;
     58     private static CameraProxy mMockCamera[];
     59     private static CameraInfo mMockCameraInfo[];
     60 
     61     // We store the camera parameters when we actually open the device,
     62     // so we can restore them in the subsequent open() requests by the user.
     63     // This prevents the parameters set by the Camera activity used by
     64     // the VideoCamera activity inadvertently.
     65     private Parameters mParameters;
     66 
     67     // Use a singleton.
     68     private static CameraHolder sHolder;
     69     public static synchronized CameraHolder instance() {
     70         if (sHolder == null) {
     71             sHolder = new CameraHolder();
     72         }
     73         return sHolder;
     74     }
     75 
     76     private static final int RELEASE_CAMERA = 1;
     77     private class MyHandler extends Handler {
     78         MyHandler(Looper looper) {
     79             super(looper);
     80         }
     81 
     82         @Override
     83         public void handleMessage(Message msg) {
     84             switch(msg.what) {
     85                 case RELEASE_CAMERA:
     86                     synchronized (CameraHolder.this) {
     87                         // In 'CameraHolder.open', the 'RELEASE_CAMERA' message
     88                         // will be removed if it is found in the queue. However,
     89                         // there is a chance that this message has been handled
     90                         // before being removed. So, we need to add a check
     91                         // here:
     92                         if (!mCameraOpened) release();
     93                     }
     94                     break;
     95             }
     96         }
     97     }
     98 
     99     public static void injectMockCamera(CameraInfo[] info, CameraProxy[] camera) {
    100         mMockCameraInfo = info;
    101         mMockCamera = camera;
    102         sHolder = new CameraHolder();
    103     }
    104 
    105     private CameraHolder() {
    106         HandlerThread ht = new HandlerThread("CameraHolder");
    107         ht.start();
    108         mHandler = new MyHandler(ht.getLooper());
    109         if (mMockCameraInfo != null) {
    110             mNumberOfCameras = mMockCameraInfo.length;
    111             mInfo = mMockCameraInfo;
    112         } else {
    113             mNumberOfCameras = android.hardware.Camera.getNumberOfCameras();
    114             mInfo = new CameraInfo[mNumberOfCameras];
    115             for (int i = 0; i < mNumberOfCameras; i++) {
    116                 mInfo[i] = new CameraInfo();
    117                 android.hardware.Camera.getCameraInfo(i, mInfo[i]);
    118             }
    119         }
    120 
    121         // get the first (smallest) back and first front camera id
    122         for (int i = 0; i < mNumberOfCameras; i++) {
    123             if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) {
    124                 mBackCameraId = i;
    125             } else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) {
    126                 mFrontCameraId = i;
    127             }
    128         }
    129     }
    130 
    131     public int getNumberOfCameras() {
    132         return mNumberOfCameras;
    133     }
    134 
    135     public CameraInfo[] getCameraInfo() {
    136         return mInfo;
    137     }
    138 
    139     public synchronized CameraProxy open(int cameraId)
    140             throws CameraHardwareException {
    141         Assert(!mCameraOpened);
    142         if (mCameraDevice != null && mCameraId != cameraId) {
    143             mCameraDevice.release();
    144             mCameraDevice = null;
    145             mCameraId = -1;
    146         }
    147         if (mCameraDevice == null) {
    148             try {
    149                 Log.v(TAG, "open camera " + cameraId);
    150                 if (mMockCameraInfo == null) {
    151                     mCameraDevice = CameraManager.instance().cameraOpen(cameraId);
    152                 } else {
    153                     if (mMockCamera == null)
    154                         throw new RuntimeException();
    155                     mCameraDevice = mMockCamera[cameraId];
    156                 }
    157                 mCameraId = cameraId;
    158             } catch (RuntimeException e) {
    159                 Log.e(TAG, "fail to connect Camera", e);
    160                 throw new CameraHardwareException(e);
    161             }
    162             mParameters = mCameraDevice.getParameters();
    163         } else {
    164             try {
    165                 mCameraDevice.reconnect();
    166             } catch (IOException e) {
    167                 Log.e(TAG, "reconnect failed.");
    168                 throw new CameraHardwareException(e);
    169             }
    170             mCameraDevice.setParameters(mParameters);
    171         }
    172         mCameraOpened = true;
    173         mHandler.removeMessages(RELEASE_CAMERA);
    174         mKeepBeforeTime = 0;
    175         return mCameraDevice;
    176     }
    177 
    178     /**
    179      * Tries to open the hardware camera. If the camera is being used or
    180      * unavailable then return {@code null}.
    181      */
    182     public synchronized CameraProxy tryOpen(int cameraId) {
    183         try {
    184             return !mCameraOpened ? open(cameraId) : null;
    185         } catch (CameraHardwareException e) {
    186             // In eng build, we throw the exception so that test tool
    187             // can detect it and report it
    188             if ("eng".equals(Build.TYPE)) {
    189                 throw new RuntimeException(e);
    190             }
    191             return null;
    192         }
    193     }
    194 
    195     public synchronized void release() {
    196         Assert(mCameraDevice != null);
    197 
    198         long now = System.currentTimeMillis();
    199         if (now < mKeepBeforeTime) {
    200             if (mCameraOpened) {
    201                 mCameraOpened = false;
    202                 mCameraDevice.stopPreview();
    203             }
    204             mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA,
    205                     mKeepBeforeTime - now);
    206             return;
    207         }
    208         mCameraOpened = false;
    209         mCameraDevice.release();
    210         mCameraDevice = null;
    211         // We must set this to null because it has a reference to Camera.
    212         // Camera has references to the listeners.
    213         mParameters = null;
    214         mCameraId = -1;
    215     }
    216 
    217     public synchronized void keep() {
    218         // We allow mCameraOpened in either state for the convenience of the
    219         // calling activity. The activity may not have a chance to call open()
    220         // before the user switches to another activity.
    221 
    222         // Keep the camera instance for 3 seconds.
    223         mKeepBeforeTime = System.currentTimeMillis() + 3000;
    224     }
    225 
    226     public int getBackCameraId() {
    227         return mBackCameraId;
    228     }
    229 
    230     public int getFrontCameraId() {
    231         return mFrontCameraId;
    232     }
    233 }
    234