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; instance()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 { MyHandler(Looper looper)78 MyHandler(Looper looper) { 79 super(looper); 80 } 81 82 @Override handleMessage(Message msg)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 injectMockCamera(CameraInfo[] info, CameraProxy[] camera)99 public static void injectMockCamera(CameraInfo[] info, CameraProxy[] camera) { 100 mMockCameraInfo = info; 101 mMockCamera = camera; 102 sHolder = new CameraHolder(); 103 } 104 CameraHolder()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 getNumberOfCameras()131 public int getNumberOfCameras() { 132 return mNumberOfCameras; 133 } 134 getCameraInfo()135 public CameraInfo[] getCameraInfo() { 136 return mInfo; 137 } 138 open(int cameraId)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 */ tryOpen(int cameraId)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 release()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 keep()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 getBackCameraId()226 public int getBackCameraId() { 227 return mBackCameraId; 228 } 229 getFrontCameraId()230 public int getFrontCameraId() { 231 return mFrontCameraId; 232 } 233 } 234