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.CameraUtil.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 import java.text.SimpleDateFormat; 34 import java.util.ArrayList; 35 import java.util.Date; 36 37 /** 38 * The class is used to hold an {@code android.hardware.Camera} instance. 39 * 40 * <p>The {@code open()} and {@code release()} calls are similar to the ones 41 * in {@code android.hardware.Camera}. The difference is if {@code keep()} is 42 * called before {@code release()}, CameraHolder will try to hold the {@code 43 * android.hardware.Camera} instance for a while, so if {@code open()} is 44 * called soon after, we can avoid the cost of {@code open()} in {@code 45 * android.hardware.Camera}. 46 * 47 * <p>This is used in switching between different modules. 48 */ 49 public class CameraHolder { 50 private static final String TAG = "CameraHolder"; 51 private static final int KEEP_CAMERA_TIMEOUT = 3000; // 3 seconds 52 private CameraProxy mCameraDevice; 53 private long mKeepBeforeTime; // Keep the Camera before this time. 54 private final Handler mHandler; 55 private boolean mCameraOpened; // true if camera is opened 56 private final int mNumberOfCameras; 57 private int mCameraId = -1; // current camera id 58 private int mBackCameraId = -1; 59 private int mFrontCameraId = -1; 60 private final CameraInfo[] mInfo; 61 private static CameraProxy mMockCamera[]; 62 private static CameraInfo mMockCameraInfo[]; 63 64 /* Debug double-open issue */ 65 private static final boolean DEBUG_OPEN_RELEASE = true; 66 private static class OpenReleaseState { 67 long time; 68 int id; 69 String device; 70 String[] stack; 71 } 72 private static ArrayList<OpenReleaseState> sOpenReleaseStates = 73 new ArrayList<OpenReleaseState>(); 74 private static SimpleDateFormat sDateFormat = new SimpleDateFormat( 75 "yyyy-MM-dd HH:mm:ss.SSS"); 76 77 private static synchronized void collectState(int id, CameraProxy device) { 78 OpenReleaseState s = new OpenReleaseState(); 79 s.time = System.currentTimeMillis(); 80 s.id = id; 81 if (device == null) { 82 s.device = "(null)"; 83 } else { 84 s.device = device.toString(); 85 } 86 87 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 88 String[] lines = new String[stack.length]; 89 for (int i = 0; i < stack.length; i++) { 90 lines[i] = stack[i].toString(); 91 } 92 s.stack = lines; 93 94 if (sOpenReleaseStates.size() > 10) { 95 sOpenReleaseStates.remove(0); 96 } 97 sOpenReleaseStates.add(s); 98 } 99 100 private static synchronized void dumpStates() { 101 for (int i = sOpenReleaseStates.size() - 1; i >= 0; i--) { 102 OpenReleaseState s = sOpenReleaseStates.get(i); 103 String date = sDateFormat.format(new Date(s.time)); 104 Log.d(TAG, "State " + i + " at " + date); 105 Log.d(TAG, "mCameraId = " + s.id + ", mCameraDevice = " + s.device); 106 Log.d(TAG, "Stack:"); 107 for (int j = 0; j < s.stack.length; j++) { 108 Log.d(TAG, " " + s.stack[j]); 109 } 110 } 111 } 112 113 // We store the camera parameters when we actually open the device, 114 // so we can restore them in the subsequent open() requests by the user. 115 // This prevents the parameters set by PhotoModule used by VideoModule 116 // inadvertently. 117 private Parameters mParameters; 118 119 // Use a singleton. 120 private static CameraHolder sHolder; 121 public static synchronized CameraHolder instance() { 122 if (sHolder == null) { 123 sHolder = new CameraHolder(); 124 } 125 return sHolder; 126 } 127 128 private static final int RELEASE_CAMERA = 1; 129 private class MyHandler extends Handler { 130 MyHandler(Looper looper) { 131 super(looper); 132 } 133 134 @Override 135 public void handleMessage(Message msg) { 136 switch(msg.what) { 137 case RELEASE_CAMERA: 138 synchronized (CameraHolder.this) { 139 // In 'CameraHolder.open', the 'RELEASE_CAMERA' message 140 // will be removed if it is found in the queue. However, 141 // there is a chance that this message has been handled 142 // before being removed. So, we need to add a check 143 // here: 144 if (!mCameraOpened) release(); 145 } 146 break; 147 } 148 } 149 } 150 151 public static void injectMockCamera(CameraInfo[] info, CameraProxy[] camera) { 152 mMockCameraInfo = info; 153 mMockCamera = camera; 154 sHolder = new CameraHolder(); 155 } 156 157 private CameraHolder() { 158 HandlerThread ht = new HandlerThread("CameraHolder"); 159 ht.start(); 160 mHandler = new MyHandler(ht.getLooper()); 161 if (mMockCameraInfo != null) { 162 mNumberOfCameras = mMockCameraInfo.length; 163 mInfo = mMockCameraInfo; 164 } else { 165 mNumberOfCameras = android.hardware.Camera.getNumberOfCameras(); 166 mInfo = new CameraInfo[mNumberOfCameras]; 167 for (int i = 0; i < mNumberOfCameras; i++) { 168 mInfo[i] = new CameraInfo(); 169 android.hardware.Camera.getCameraInfo(i, mInfo[i]); 170 } 171 } 172 173 // get the first (smallest) back and first front camera id 174 for (int i = 0; i < mNumberOfCameras; i++) { 175 if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) { 176 mBackCameraId = i; 177 } else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) { 178 mFrontCameraId = i; 179 } 180 } 181 } 182 183 public int getNumberOfCameras() { 184 return mNumberOfCameras; 185 } 186 187 public CameraInfo[] getCameraInfo() { 188 return mInfo; 189 } 190 191 public synchronized CameraProxy open( 192 Handler handler, int cameraId, 193 CameraManager.CameraOpenErrorCallback cb) { 194 if (DEBUG_OPEN_RELEASE) { 195 collectState(cameraId, mCameraDevice); 196 if (mCameraOpened) { 197 Log.e(TAG, "double open"); 198 dumpStates(); 199 } 200 } 201 Assert(!mCameraOpened); 202 if (mCameraDevice != null && mCameraId != cameraId) { 203 mCameraDevice.release(); 204 mCameraDevice = null; 205 mCameraId = -1; 206 } 207 if (mCameraDevice == null) { 208 Log.v(TAG, "open camera " + cameraId); 209 if (mMockCameraInfo == null) { 210 mCameraDevice = CameraManagerFactory 211 .getAndroidCameraManager().cameraOpen(handler, cameraId, cb); 212 } else { 213 if (mMockCamera != null) { 214 mCameraDevice = mMockCamera[cameraId]; 215 } else { 216 Log.e(TAG, "MockCameraInfo found, but no MockCamera provided."); 217 mCameraDevice = null; 218 } 219 } 220 if (mCameraDevice == null) { 221 Log.e(TAG, "fail to connect Camera:" + mCameraId + ", aborting."); 222 return null; 223 } 224 mCameraId = cameraId; 225 mParameters = mCameraDevice.getParameters(); 226 } else { 227 if (!mCameraDevice.reconnect(handler, cb)) { 228 Log.e(TAG, "fail to reconnect Camera:" + mCameraId + ", aborting."); 229 return null; 230 } 231 mCameraDevice.setParameters(mParameters); 232 } 233 mCameraOpened = true; 234 mHandler.removeMessages(RELEASE_CAMERA); 235 mKeepBeforeTime = 0; 236 return mCameraDevice; 237 } 238 239 /** 240 * Tries to open the hardware camera. If the camera is being used or 241 * unavailable then return {@code null}. 242 */ 243 public synchronized CameraProxy tryOpen( 244 Handler handler, int cameraId, CameraManager.CameraOpenErrorCallback cb) { 245 return (!mCameraOpened ? open(handler, cameraId, cb) : null); 246 } 247 248 public synchronized void release() { 249 if (DEBUG_OPEN_RELEASE) { 250 collectState(mCameraId, mCameraDevice); 251 } 252 253 if (mCameraDevice == null) return; 254 255 long now = System.currentTimeMillis(); 256 if (now < mKeepBeforeTime) { 257 if (mCameraOpened) { 258 mCameraOpened = false; 259 mCameraDevice.stopPreview(); 260 } 261 mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA, 262 mKeepBeforeTime - now); 263 return; 264 } 265 strongRelease(); 266 } 267 268 public synchronized void strongRelease() { 269 if (mCameraDevice == null) return; 270 271 mCameraOpened = false; 272 mCameraDevice.release(); 273 mCameraDevice = null; 274 // We must set this to null because it has a reference to Camera. 275 // Camera has references to the listeners. 276 mParameters = null; 277 mCameraId = -1; 278 } 279 280 public void keep() { 281 keep(KEEP_CAMERA_TIMEOUT); 282 } 283 284 public synchronized void keep(int time) { 285 // We allow mCameraOpened in either state for the convenience of the 286 // calling activity. The activity may not have a chance to call open() 287 // before the user switches to another activity. 288 mKeepBeforeTime = System.currentTimeMillis() + time; 289 } 290 291 public int getBackCameraId() { 292 return mBackCameraId; 293 } 294 295 public int getFrontCameraId() { 296 return mFrontCameraId; 297 } 298 } 299