CameraHolder.java revision 7add00693c1ec910bc8700fe046ee18cbe4e1148
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 android.hardware.Camera 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 CameraInfo[] mInfo; 53 54 // We store the camera parameters when we actually open the device, 55 // so we can restore them in the subsequent open() requests by the user. 56 // This prevents the parameters set by the Camera activity used by 57 // the VideoCamera activity inadvertently. 58 private Parameters mParameters; 59 60 // Use a singleton. 61 private static CameraHolder sHolder; 62 public static synchronized CameraHolder instance() { 63 if (sHolder == null) { 64 sHolder = new CameraHolder(); 65 } 66 return sHolder; 67 } 68 69 private static final int RELEASE_CAMERA = 1; 70 private class MyHandler extends Handler { 71 MyHandler(Looper looper) { 72 super(looper); 73 } 74 75 @Override 76 public void handleMessage(Message msg) { 77 switch(msg.what) { 78 case RELEASE_CAMERA: 79 synchronized (CameraHolder.this) { 80 // In 'CameraHolder.open', the 'RELEASE_CAMERA' message 81 // will be removed if it is found in the queue. However, 82 // there is a chance that this message has been handled 83 // before being removed. So, we need to add a check 84 // here: 85 if (CameraHolder.this.mUsers == 0) releaseCamera(); 86 } 87 break; 88 } 89 } 90 } 91 92 private CameraHolder() { 93 HandlerThread ht = new HandlerThread("CameraHolder"); 94 ht.start(); 95 mHandler = new MyHandler(ht.getLooper()); 96 mNumberOfCameras = android.hardware.Camera.getNumberOfCameras(); 97 mInfo = new CameraInfo[mNumberOfCameras]; 98 for (int i = 0; i < mNumberOfCameras; i++) { 99 mInfo[i] = new CameraInfo(); 100 android.hardware.Camera.getCameraInfo(i, mInfo[i]); 101 } 102 } 103 104 public int getNumberOfCameras() { 105 return mNumberOfCameras; 106 } 107 108 public CameraInfo[] getCameraInfo() { 109 return mInfo; 110 } 111 112 public synchronized android.hardware.Camera open(int cameraId) 113 throws CameraHardwareException { 114 Assert(mUsers == 0); 115 if (mCameraDevice == null) { 116 try { 117 Log.v(TAG, "open camera " + cameraId); 118 mCameraDevice = android.hardware.Camera.open(cameraId); 119 } catch (RuntimeException e) { 120 Log.e(TAG, "fail to connect Camera", e); 121 throw new CameraHardwareException(e); 122 } 123 mParameters = mCameraDevice.getParameters(); 124 } else { 125 try { 126 mCameraDevice.reconnect(); 127 } catch (IOException e) { 128 Log.e(TAG, "reconnect failed."); 129 throw new CameraHardwareException(e); 130 } 131 mCameraDevice.setParameters(mParameters); 132 } 133 ++mUsers; 134 mHandler.removeMessages(RELEASE_CAMERA); 135 mKeepBeforeTime = 0; 136 return mCameraDevice; 137 } 138 139 /** 140 * Tries to open the hardware camera. If the camera is being used or 141 * unavailable then return {@code null}. 142 */ 143 public synchronized android.hardware.Camera tryOpen(int cameraId) { 144 try { 145 return mUsers == 0 ? open(cameraId) : null; 146 } catch (CameraHardwareException e) { 147 // In eng build, we throw the exception so that test tool 148 // can detect it and report it 149 if ("eng".equals(Build.TYPE)) { 150 throw new RuntimeException(e); 151 } 152 return null; 153 } 154 } 155 156 public synchronized void release() { 157 Assert(mUsers == 1); 158 --mUsers; 159 mCameraDevice.stopPreview(); 160 releaseCamera(); 161 } 162 163 private synchronized void releaseCamera() { 164 Assert(mUsers == 0); 165 Assert(mCameraDevice != null); 166 long now = System.currentTimeMillis(); 167 if (now < mKeepBeforeTime) { 168 mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA, 169 mKeepBeforeTime - now); 170 return; 171 } 172 mCameraDevice.release(); 173 mCameraDevice = null; 174 } 175 176 public synchronized void keep() { 177 // We allow (mUsers == 0) for the convenience of the calling activity. 178 // The activity may not have a chance to call open() before the user 179 // choose the menu item to switch to another activity. 180 Assert(mUsers == 1 || mUsers == 0); 181 // Keep the camera instance for 3 seconds. 182 mKeepBeforeTime = System.currentTimeMillis() + 3000; 183 } 184} 185