CameraHolder.java revision 7717d2f0fc0e1393971cab44b3bba8a0f1297835
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 17package com.android.camera; 18 19import static com.android.camera.Util.Assert; 20 21import android.hardware.Camera.CameraInfo; 22import android.hardware.Camera.Parameters; 23import android.os.Build; 24import android.os.Handler; 25import android.os.HandlerThread; 26import android.os.Looper; 27import android.os.Message; 28import android.util.Log; 29 30import com.android.camera.CameraManager.CameraProxy; 31 32import 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 */ 47public class CameraHolder { 48 private static final String TAG = "CameraHolder"; 49 private static final int KEEP_CAMERA_TIMEOUT = 3000; // 3 seconds 50 private CameraProxy mCameraDevice; 51 private long mKeepBeforeTime; // Keep the Camera before this time. 52 private final Handler mHandler; 53 private boolean mCameraOpened; // true if camera is opened 54 private final int mNumberOfCameras; 55 private int mCameraId = -1; // current camera id 56 private int mBackCameraId = -1; 57 private int mFrontCameraId = -1; 58 private final CameraInfo[] mInfo; 59 private static CameraProxy mMockCamera[]; 60 private static CameraInfo mMockCameraInfo[]; 61 62 // We store the camera parameters when we actually open the device, 63 // so we can restore them in the subsequent open() requests by the user. 64 // This prevents the parameters set by the Camera activity used by 65 // the VideoCamera activity inadvertently. 66 private Parameters mParameters; 67 68 // Use a singleton. 69 private static CameraHolder sHolder; 70 public static synchronized CameraHolder instance() { 71 if (sHolder == null) { 72 sHolder = new CameraHolder(); 73 } 74 return sHolder; 75 } 76 77 private static final int RELEASE_CAMERA = 1; 78 private class MyHandler extends Handler { 79 MyHandler(Looper looper) { 80 super(looper); 81 } 82 83 @Override 84 public void handleMessage(Message msg) { 85 switch(msg.what) { 86 case RELEASE_CAMERA: 87 synchronized (CameraHolder.this) { 88 // In 'CameraHolder.open', the 'RELEASE_CAMERA' message 89 // will be removed if it is found in the queue. However, 90 // there is a chance that this message has been handled 91 // before being removed. So, we need to add a check 92 // here: 93 if (!mCameraOpened) release(); 94 } 95 break; 96 } 97 } 98 } 99 100 public static void injectMockCamera(CameraInfo[] info, CameraProxy[] camera) { 101 mMockCameraInfo = info; 102 mMockCamera = camera; 103 sHolder = new CameraHolder(); 104 } 105 106 private CameraHolder() { 107 HandlerThread ht = new HandlerThread("CameraHolder"); 108 ht.start(); 109 mHandler = new MyHandler(ht.getLooper()); 110 if (mMockCameraInfo != null) { 111 mNumberOfCameras = mMockCameraInfo.length; 112 mInfo = mMockCameraInfo; 113 } else { 114 mNumberOfCameras = android.hardware.Camera.getNumberOfCameras(); 115 mInfo = new CameraInfo[mNumberOfCameras]; 116 for (int i = 0; i < mNumberOfCameras; i++) { 117 mInfo[i] = new CameraInfo(); 118 android.hardware.Camera.getCameraInfo(i, mInfo[i]); 119 } 120 } 121 122 // get the first (smallest) back and first front camera id 123 for (int i = 0; i < mNumberOfCameras; i++) { 124 if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) { 125 mBackCameraId = i; 126 } else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) { 127 mFrontCameraId = i; 128 } 129 } 130 } 131 132 public int getNumberOfCameras() { 133 return mNumberOfCameras; 134 } 135 136 public CameraInfo[] getCameraInfo() { 137 return mInfo; 138 } 139 140 public synchronized CameraProxy open(int cameraId) 141 throws CameraHardwareException { 142 Assert(!mCameraOpened); 143 if (mCameraDevice != null && mCameraId != cameraId) { 144 mCameraDevice.release(); 145 mCameraDevice = null; 146 mCameraId = -1; 147 } 148 if (mCameraDevice == null) { 149 try { 150 Log.v(TAG, "open camera " + cameraId); 151 if (mMockCameraInfo == null) { 152 mCameraDevice = CameraManager.instance().cameraOpen(cameraId); 153 } else { 154 if (mMockCamera == null) 155 throw new RuntimeException(); 156 mCameraDevice = mMockCamera[cameraId]; 157 } 158 mCameraId = cameraId; 159 } catch (RuntimeException e) { 160 Log.e(TAG, "fail to connect Camera", e); 161 throw new CameraHardwareException(e); 162 } 163 mParameters = mCameraDevice.getParameters(); 164 } else { 165 try { 166 mCameraDevice.reconnect(); 167 } catch (IOException e) { 168 Log.e(TAG, "reconnect failed."); 169 throw new CameraHardwareException(e); 170 } 171 mCameraDevice.setParameters(mParameters); 172 } 173 mCameraOpened = true; 174 mHandler.removeMessages(RELEASE_CAMERA); 175 mKeepBeforeTime = 0; 176 return mCameraDevice; 177 } 178 179 /** 180 * Tries to open the hardware camera. If the camera is being used or 181 * unavailable then return {@code null}. 182 */ 183 public synchronized CameraProxy tryOpen(int cameraId) { 184 try { 185 return !mCameraOpened ? open(cameraId) : null; 186 } catch (CameraHardwareException e) { 187 // In eng build, we throw the exception so that test tool 188 // can detect it and report it 189 if ("eng".equals(Build.TYPE)) { 190 throw new RuntimeException(e); 191 } 192 return null; 193 } 194 } 195 196 public synchronized void release() { 197 if (mCameraDevice == null) return; 198 199 long now = System.currentTimeMillis(); 200 if (now < mKeepBeforeTime) { 201 if (mCameraOpened) { 202 mCameraOpened = false; 203 mCameraDevice.stopPreview(); 204 } 205 mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA, 206 mKeepBeforeTime - now); 207 return; 208 } 209 mCameraOpened = false; 210 mCameraDevice.release(); 211 mCameraDevice = null; 212 // We must set this to null because it has a reference to Camera. 213 // Camera has references to the listeners. 214 mParameters = null; 215 mCameraId = -1; 216 } 217 218 public void keep() { 219 keep(KEEP_CAMERA_TIMEOUT); 220 } 221 222 public synchronized void keep(int time) { 223 // We allow mCameraOpened in either state for the convenience of the 224 // calling activity. The activity may not have a chance to call open() 225 // before the user switches to another activity. 226 mKeepBeforeTime = System.currentTimeMillis() + time; 227 } 228 229 public int getBackCameraId() { 230 return mBackCameraId; 231 } 232 233 public int getFrontCameraId() { 234 return mFrontCameraId; 235 } 236} 237