CameraHolder.java revision dd6600e7f0adf322e5a8fcb0ed5389b14655106e
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.Parameters; 22import android.os.Build; 23import android.os.Handler; 24import android.os.HandlerThread; 25import android.os.Looper; 26import android.os.Message; 27import android.util.Log; 28 29import java.io.IOException; 30 31/** 32 * The class is used to hold an {@code android.hardware.Camera} instance. 33 * 34 * <p>The {@code open()} and {@code release()} calls are similar to the ones 35 * in {@code android.hardware.Camera}. The difference is if {@code keep()} is 36 * called before {@code release()}, CameraHolder will try to hold the {@code 37 * android.hardware.Camera} instance for a while, so if {@code open()} is 38 * called soon after, we can avoid the cost of {@code open()} in {@code 39 * android.hardware.Camera}. 40 * 41 * <p>This is used in switching between {@code Camera} and {@code VideoCamera} 42 * activities. 43 */ 44public class CameraHolder { 45 private static final String TAG = "CameraHolder"; 46 private android.hardware.Camera mCameraDevice; 47 private long mKeepBeforeTime = 0; // Keep the Camera before this time. 48 private final Handler mHandler; 49 private int mUsers = 0; // number of open() - number of release() 50 51 // We store the camera parameters when we actually open the device, 52 // so we can restore them in the subsequent open() requests by the user. 53 // This prevents the parameters set by the Camera activity used by 54 // the VideoCamera activity inadvertently. 55 private Parameters mParameters; 56 57 // Use a singleton. 58 private static CameraHolder sHolder; 59 public static synchronized CameraHolder instance() { 60 if (sHolder == null) { 61 sHolder = new CameraHolder(); 62 } 63 return sHolder; 64 } 65 66 private static final int RELEASE_CAMERA = 1; 67 private class MyHandler extends Handler { 68 MyHandler(Looper looper) { 69 super(looper); 70 } 71 72 @Override 73 public void handleMessage(Message msg) { 74 switch(msg.what) { 75 case RELEASE_CAMERA: 76 synchronized (CameraHolder.this) { 77 // In 'CameraHolder.open', the 'RELEASE_CAMERA' message 78 // will be removed if it is found in the queue. However, 79 // there is a chance that this message has been handled 80 // before being removed. So, we need to add a check 81 // here: 82 if (CameraHolder.this.mUsers == 0) releaseCamera(); 83 } 84 break; 85 } 86 } 87 } 88 89 private CameraHolder() { 90 HandlerThread ht = new HandlerThread("CameraHolder"); 91 ht.start(); 92 mHandler = new MyHandler(ht.getLooper()); 93 } 94 95 public synchronized android.hardware.Camera open() 96 throws CameraHardwareException { 97 Assert(mUsers == 0); 98 if (mCameraDevice == null) { 99 try { 100 mCameraDevice = android.hardware.Camera.open(); 101 } catch (RuntimeException e) { 102 Log.e(TAG, "fail to connect Camera", e); 103 throw new CameraHardwareException(e); 104 } 105 mParameters = mCameraDevice.getParameters(); 106 } else { 107 try { 108 mCameraDevice.reconnect(); 109 } catch (IOException e) { 110 Log.e(TAG, "reconnect failed."); 111 throw new CameraHardwareException(e); 112 } 113 mCameraDevice.setParameters(mParameters); 114 } 115 ++mUsers; 116 mHandler.removeMessages(RELEASE_CAMERA); 117 mKeepBeforeTime = 0; 118 return mCameraDevice; 119 } 120 121 /** 122 * Tries to open the hardware camera. If the camera is being used or 123 * unavailable then return {@code null}. 124 */ 125 public synchronized android.hardware.Camera tryOpen() { 126 try { 127 return mUsers == 0 ? open() : null; 128 } catch (CameraHardwareException e) { 129 // In eng build, we throw the exception so that test tool 130 // can detect it and report it 131 if ("eng".equals(Build.TYPE)) { 132 throw new RuntimeException(e); 133 } 134 return null; 135 } 136 } 137 138 public synchronized void release() { 139 Assert(mUsers == 1); 140 --mUsers; 141 mCameraDevice.stopPreview(); 142 releaseCamera(); 143 } 144 145 private synchronized void releaseCamera() { 146 Assert(mUsers == 0); 147 Assert(mCameraDevice != null); 148 long now = System.currentTimeMillis(); 149 if (now < mKeepBeforeTime) { 150 mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA, 151 mKeepBeforeTime - now); 152 return; 153 } 154 mCameraDevice.release(); 155 mCameraDevice = null; 156 } 157 158 public synchronized void keep() { 159 // We allow (mUsers == 0) for the convenience of the calling activity. 160 // The activity may not have a chance to call open() before the user 161 // choose the menu item to switch to another activity. 162 Assert(mUsers == 1 || mUsers == 0); 163 // Keep the camera instance for 3 seconds. 164 mKeepBeforeTime = System.currentTimeMillis() + 3000; 165 } 166} 167