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; 33import java.text.SimpleDateFormat; 34import java.util.ArrayList; 35import 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 */ 49public 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(int cameraId) 192 throws CameraHardwareException { 193 if (DEBUG_OPEN_RELEASE) { 194 collectState(cameraId, mCameraDevice); 195 if (mCameraOpened) { 196 Log.e(TAG, "double open"); 197 dumpStates(); 198 } 199 } 200 Assert(!mCameraOpened); 201 if (mCameraDevice != null && mCameraId != cameraId) { 202 mCameraDevice.release(); 203 mCameraDevice = null; 204 mCameraId = -1; 205 } 206 if (mCameraDevice == null) { 207 try { 208 Log.v(TAG, "open camera " + cameraId); 209 if (mMockCameraInfo == null) { 210 mCameraDevice = CameraManager.instance().cameraOpen(cameraId); 211 } else { 212 if (mMockCamera == null) 213 throw new RuntimeException(); 214 mCameraDevice = mMockCamera[cameraId]; 215 } 216 mCameraId = cameraId; 217 } catch (RuntimeException e) { 218 Log.e(TAG, "fail to connect Camera", e); 219 throw new CameraHardwareException(e); 220 } 221 mParameters = mCameraDevice.getParameters(); 222 } else { 223 try { 224 mCameraDevice.reconnect(); 225 } catch (IOException e) { 226 Log.e(TAG, "reconnect failed."); 227 throw new CameraHardwareException(e); 228 } 229 mCameraDevice.setParameters(mParameters); 230 } 231 mCameraOpened = true; 232 mHandler.removeMessages(RELEASE_CAMERA); 233 mKeepBeforeTime = 0; 234 return mCameraDevice; 235 } 236 237 /** 238 * Tries to open the hardware camera. If the camera is being used or 239 * unavailable then return {@code null}. 240 */ 241 public synchronized CameraProxy tryOpen(int cameraId) { 242 try { 243 return !mCameraOpened ? open(cameraId) : null; 244 } catch (CameraHardwareException e) { 245 // In eng build, we throw the exception so that test tool 246 // can detect it and report it 247 if ("eng".equals(Build.TYPE)) { 248 throw new RuntimeException(e); 249 } 250 return null; 251 } 252 } 253 254 public synchronized void release() { 255 if (DEBUG_OPEN_RELEASE) { 256 collectState(mCameraId, mCameraDevice); 257 } 258 259 if (mCameraDevice == null) return; 260 261 long now = System.currentTimeMillis(); 262 if (now < mKeepBeforeTime) { 263 if (mCameraOpened) { 264 mCameraOpened = false; 265 mCameraDevice.stopPreview(); 266 } 267 mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA, 268 mKeepBeforeTime - now); 269 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