CameraHolder.java revision 75aa8e0970c88c1ffbe30ea5cf50a3e1ad80c5f5
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 java.io.IOException; 31 32/** 33 * The class is used to hold an {@code android.hardware.Camera} instance. 34 * 35 * <p>The {@code open()} and {@code release()} calls are similar to the ones 36 * in {@code android.hardware.Camera}. The difference is if {@code keep()} is 37 * called before {@code release()}, CameraHolder will try to hold the {@code 38 * android.hardware.Camera} instance for a while, so if {@code open()} is 39 * called soon after, we can avoid the cost of {@code open()} in {@code 40 * android.hardware.Camera}. 41 * 42 * <p>This is used in switching between {@code Camera} and {@code VideoCamera} 43 * activities. 44 */ 45public class CameraHolder { 46 private static final String TAG = "CameraHolder"; 47 private CameraDevice mCameraDevice; 48 private long mKeepBeforeTime = 0; // Keep the Camera before this time. 49 private final Handler mHandler; 50 private int mUsers = 0; // number of open() - number of release() 51 private int mNumberOfCameras; 52 private int mCameraId = -1; // current camera id 53 private int mBackCameraId = -1, mFrontCameraId = -1; 54 private CameraInfo[] mInfo; 55 private static CameraDevice mMockCamera[]; 56 private static CameraInfo mMockCameraInfo[]; 57 58 // We store the camera parameters when we actually open the device, 59 // so we can restore them in the subsequent open() requests by the user. 60 // This prevents the parameters set by the Camera activity used by 61 // the VideoCamera activity inadvertently. 62 private Parameters mParameters; 63 64 // Use a singleton. 65 private static CameraHolder sHolder; 66 public static synchronized CameraHolder instance() { 67 if (sHolder == null) { 68 sHolder = new CameraHolder(); 69 } 70 return sHolder; 71 } 72 73 private static final int RELEASE_CAMERA = 1; 74 private class MyHandler extends Handler { 75 MyHandler(Looper looper) { 76 super(looper); 77 } 78 79 @Override 80 public void handleMessage(Message msg) { 81 switch(msg.what) { 82 case RELEASE_CAMERA: 83 synchronized (CameraHolder.this) { 84 // In 'CameraHolder.open', the 'RELEASE_CAMERA' message 85 // will be removed if it is found in the queue. However, 86 // there is a chance that this message has been handled 87 // before being removed. So, we need to add a check 88 // here: 89 if (CameraHolder.this.mUsers == 0) releaseCamera(); 90 } 91 break; 92 } 93 } 94 } 95 96 public static void injectMockCamera(CameraInfo[] info, CameraDevice[] camera) { 97 mMockCameraInfo = info; 98 mMockCamera = camera; 99 sHolder = new CameraHolder(); 100 } 101 102 private CameraHolder() { 103 HandlerThread ht = new HandlerThread("CameraHolder"); 104 ht.start(); 105 mHandler = new MyHandler(ht.getLooper()); 106 if (mMockCameraInfo != null) { 107 mNumberOfCameras = mMockCameraInfo.length; 108 mInfo = mMockCameraInfo; 109 } else { 110 mNumberOfCameras = android.hardware.Camera.getNumberOfCameras(); 111 mInfo = new CameraInfo[mNumberOfCameras]; 112 for (int i = 0; i < mNumberOfCameras; i++) { 113 mInfo[i] = new CameraInfo(); 114 android.hardware.Camera.getCameraInfo(i, mInfo[i]); 115 } 116 } 117 for (int i = 0; i < mNumberOfCameras; i++) { 118 if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) { 119 mBackCameraId = i; 120 } 121 if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) { 122 mFrontCameraId = i; 123 } 124 } 125 } 126 127 public int getNumberOfCameras() { 128 return mNumberOfCameras; 129 } 130 131 public CameraInfo[] getCameraInfo() { 132 return mInfo; 133 } 134 135 public synchronized CameraDevice open(int cameraId) 136 throws CameraHardwareException { 137 Assert(mUsers == 0); 138 if (mCameraDevice != null && mCameraId != cameraId) { 139 mCameraDevice.release(); 140 mCameraDevice = null; 141 mCameraId = -1; 142 } 143 if (mCameraDevice == null) { 144 try { 145 Log.v(TAG, "open camera " + cameraId); 146 if (mMockCameraInfo == null) { 147 android.hardware.Camera camera = android.hardware.Camera.open(cameraId); 148 if (camera != null) 149 mCameraDevice = new RealCamera(camera); 150 } else { 151 if (mMockCamera == null) 152 throw new RuntimeException(); 153 mCameraDevice = mMockCamera[cameraId]; 154 } 155 mCameraId = cameraId; 156 } catch (RuntimeException e) { 157 Log.e(TAG, "fail to connect Camera", e); 158 throw new CameraHardwareException(e); 159 } 160 mParameters = mCameraDevice.getParameters(); 161 } else { 162 try { 163 mCameraDevice.reconnect(); 164 } catch (IOException e) { 165 Log.e(TAG, "reconnect failed."); 166 throw new CameraHardwareException(e); 167 } 168 mCameraDevice.setParameters(mParameters); 169 } 170 ++mUsers; 171 mHandler.removeMessages(RELEASE_CAMERA); 172 mKeepBeforeTime = 0; 173 return mCameraDevice; 174 } 175 176 /** 177 * Tries to open the hardware camera. If the camera is being used or 178 * unavailable then return {@code null}. 179 */ 180 public synchronized CameraDevice tryOpen(int cameraId) { 181 try { 182 return mUsers == 0 ? open(cameraId) : null; 183 } catch (CameraHardwareException e) { 184 // In eng build, we throw the exception so that test tool 185 // can detect it and report it 186 if ("eng".equals(Build.TYPE)) { 187 throw new RuntimeException(e); 188 } 189 return null; 190 } 191 } 192 193 public synchronized void release() { 194 Assert(mUsers == 1); 195 --mUsers; 196 mCameraDevice.stopPreview(); 197 releaseCamera(); 198 } 199 200 private synchronized void releaseCamera() { 201 Assert(mUsers == 0); 202 Assert(mCameraDevice != null); 203 long now = System.currentTimeMillis(); 204 if (now < mKeepBeforeTime) { 205 mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA, 206 mKeepBeforeTime - now); 207 return; 208 } 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 (mUsers == 0) for the convenience of the calling activity. 219 // The activity may not have a chance to call open() before the user 220 // choose the menu item to switch to another activity. 221 Assert(mUsers == 1 || mUsers == 0); 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