191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang/* 291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Copyright (C) 2009 The Android Open Source Project 391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * 491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Licensed under the Apache License, Version 2.0 (the "License"); 591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * you may not use this file except in compliance with the License. 691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * You may obtain a copy of the License at 791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * 891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * http://www.apache.org/licenses/LICENSE-2.0 991acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * 1091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * Unless required by applicable law or agreed to in writing, software 1191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * distributed under the License is distributed on an "AS IS" BASIS, 1291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * See the License for the specific language governing permissions and 1491acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang * limitations under the License. 1591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang */ 1691acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang 17cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changpackage com.android.camera; 18cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 193f3c857e3f34650c15d764810335024654b0fcc3Owen Linimport static com.android.camera.Util.Assert; 203f3c857e3f34650c15d764810335024654b0fcc3Owen Lin 217add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Liimport android.hardware.Camera.CameraInfo; 224c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Changimport android.hardware.Camera.Parameters; 23f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Linimport android.os.Build; 24cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Handler; 25cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.HandlerThread; 26cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Looper; 27cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.os.Message; 28cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport android.util.Log; 29cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 30cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changimport java.io.IOException; 31cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 32271b3095b9f763421c0547109da9de774795072dChih-Chung Chang/** 33271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * The class is used to hold an {@code android.hardware.Camera} instance. 34271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * 35271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>The {@code open()} and {@code release()} calls are similar to the ones 36271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * in {@code android.hardware.Camera}. The difference is if {@code keep()} is 37271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called before {@code release()}, CameraHolder will try to hold the {@code 38271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera} instance for a while, so if {@code open()} is 39271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * called soon after, we can avoid the cost of {@code open()} in {@code 40271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * android.hardware.Camera}. 41271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * 42271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * <p>This is used in switching between {@code Camera} and {@code VideoCamera} 43271b3095b9f763421c0547109da9de774795072dChih-Chung Chang * activities. 44271b3095b9f763421c0547109da9de774795072dChih-Chung Chang */ 45cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Changpublic class CameraHolder { 46cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang private static final String TAG = "CameraHolder"; 47cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang private android.hardware.Camera mCameraDevice; 4891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang private long mKeepBeforeTime = 0; // Keep the Camera before this time. 493f3c857e3f34650c15d764810335024654b0fcc3Owen Lin private final Handler mHandler; 5091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang private int mUsers = 0; // number of open() - number of release() 51ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang private int mNumberOfCameras; 52003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li private int mCameraId = -1; // current camera id 53003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li private int mBackCameraId = -1, mFrontCameraId = -1; 547add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li private CameraInfo[] mInfo; 55cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 564c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang // We store the camera parameters when we actually open the device, 574c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang // so we can restore them in the subsequent open() requests by the user. 584c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang // This prevents the parameters set by the Camera activity used by 594c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang // the VideoCamera activity inadvertently. 604c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang private Parameters mParameters; 614c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang 62cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang // Use a singleton. 63cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang private static CameraHolder sHolder; 64cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang public static synchronized CameraHolder instance() { 65cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang if (sHolder == null) { 66cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang sHolder = new CameraHolder(); 67cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 68cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang return sHolder; 69cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 70cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 71cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang private static final int RELEASE_CAMERA = 1; 72cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang private class MyHandler extends Handler { 73cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang MyHandler(Looper looper) { 74cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang super(looper); 75cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 76cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 77cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang @Override 78cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang public void handleMessage(Message msg) { 79cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang switch(msg.what) { 80cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang case RELEASE_CAMERA: 81dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin synchronized (CameraHolder.this) { 82dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin // In 'CameraHolder.open', the 'RELEASE_CAMERA' message 83dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin // will be removed if it is found in the queue. However, 84dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin // there is a chance that this message has been handled 85dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin // before being removed. So, we need to add a check 86dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin // here: 87dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin if (CameraHolder.this.mUsers == 0) releaseCamera(); 88dd6600e7f0adf322e5a8fcb0ed5389b14655106eOwen Lin } 89cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang break; 90cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 91cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 92cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 93cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 94cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang private CameraHolder() { 95cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang HandlerThread ht = new HandlerThread("CameraHolder"); 96cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang ht.start(); 97cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang mHandler = new MyHandler(ht.getLooper()); 98ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang mNumberOfCameras = android.hardware.Camera.getNumberOfCameras(); 997add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li mInfo = new CameraInfo[mNumberOfCameras]; 100ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang for (int i = 0; i < mNumberOfCameras; i++) { 1017add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li mInfo[i] = new CameraInfo(); 1027add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li android.hardware.Camera.getCameraInfo(i, mInfo[i]); 103003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) { 104003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li mBackCameraId = i; 105003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li } 106003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) { 107003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li mFrontCameraId = i; 108003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li } 109ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang } 110ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang } 111ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang 112ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang public int getNumberOfCameras() { 113ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang return mNumberOfCameras; 114cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 115cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 1167add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li public CameraInfo[] getCameraInfo() { 1177add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li return mInfo; 1187add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li } 1197add00693c1ec910bc8700fe046ee18cbe4e1148Wu-cheng Li 120ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang public synchronized android.hardware.Camera open(int cameraId) 1213f3c857e3f34650c15d764810335024654b0fcc3Owen Lin throws CameraHardwareException { 12291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang Assert(mUsers == 0); 12343b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li if (mCameraDevice != null && mCameraId != cameraId) { 12443b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li mCameraDevice.release(); 12543b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li mCameraDevice = null; 12643b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li mCameraId = -1; 12743b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li } 128cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang if (mCameraDevice == null) { 1293f3c857e3f34650c15d764810335024654b0fcc3Owen Lin try { 130ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang Log.v(TAG, "open camera " + cameraId); 131ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang mCameraDevice = android.hardware.Camera.open(cameraId); 13243b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li mCameraId = cameraId; 1333f3c857e3f34650c15d764810335024654b0fcc3Owen Lin } catch (RuntimeException e) { 1343f3c857e3f34650c15d764810335024654b0fcc3Owen Lin Log.e(TAG, "fail to connect Camera", e); 135f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin throw new CameraHardwareException(e); 1363f3c857e3f34650c15d764810335024654b0fcc3Owen Lin } 1374c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang mParameters = mCameraDevice.getParameters(); 138cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } else { 139cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang try { 140cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang mCameraDevice.reconnect(); 141cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } catch (IOException e) { 142cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang Log.e(TAG, "reconnect failed."); 143f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin throw new CameraHardwareException(e); 144cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 1454c9266ef8f43c6b057b6f560645475272c66ff8aChih-Chung Chang mCameraDevice.setParameters(mParameters); 146cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 14791acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang ++mUsers; 148cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang mHandler.removeMessages(RELEASE_CAMERA); 14991acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang mKeepBeforeTime = 0; 150cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang return mCameraDevice; 151cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 152cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 153d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin /** 154d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin * Tries to open the hardware camera. If the camera is being used or 155d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin * unavailable then return {@code null}. 156d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin */ 157ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang public synchronized android.hardware.Camera tryOpen(int cameraId) { 158d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin try { 159ac9d0a1ce538eb4bd50cba3b257737a05b9ac4e5Chih-Chung Chang return mUsers == 0 ? open(cameraId) : null; 160d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin } catch (CameraHardwareException e) { 161f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin // In eng build, we throw the exception so that test tool 162f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin // can detect it and report it 163f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin if ("eng".equals(Build.TYPE)) { 164f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin throw new RuntimeException(e); 165f6ef7b960f610d557f0bab924b9f7928b46f6f0aOwen Lin } 166d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin return null; 167d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin } 168d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin } 169d9e32402bbc5ebaac40ccd2c4b734f8e5743343eOwen Lin 170cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang public synchronized void release() { 17191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang Assert(mUsers == 1); 17291acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang --mUsers; 173cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang mCameraDevice.stopPreview(); 174cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang releaseCamera(); 175cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 176cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 177cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang private synchronized void releaseCamera() { 17891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang Assert(mUsers == 0); 179cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang Assert(mCameraDevice != null); 180cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang long now = System.currentTimeMillis(); 18191acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang if (now < mKeepBeforeTime) { 182cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang mHandler.sendEmptyMessageDelayed(RELEASE_CAMERA, 18391acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang mKeepBeforeTime - now); 184cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang return; 185cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 186cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang mCameraDevice.release(); 187cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang mCameraDevice = null; 188788aad96ea4abcda2469867464139daa8087af65Wu-cheng Li // We must set this to null because it has a reference to Camera. 189788aad96ea4abcda2469867464139daa8087af65Wu-cheng Li // Camera has references to the listeners. 190788aad96ea4abcda2469867464139daa8087af65Wu-cheng Li mParameters = null; 19143b6525b4aedc5e177163cab7b6f26698a19097fWu-cheng Li mCameraId = -1; 192cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 193cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang 194cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang public synchronized void keep() { 19591acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang // We allow (mUsers == 0) for the convenience of the calling activity. 1965075f53df86f3dcb0a46fd6353057260ad480f43Chih-Chung Chang // The activity may not have a chance to call open() before the user 1975075f53df86f3dcb0a46fd6353057260ad480f43Chih-Chung Chang // choose the menu item to switch to another activity. 19891acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang Assert(mUsers == 1 || mUsers == 0); 199cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang // Keep the camera instance for 3 seconds. 20091acfc99279d5ece7ac9cb2d7a2980eb0d3b50daChih-Chung Chang mKeepBeforeTime = System.currentTimeMillis() + 3000; 201cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang } 202003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li 203003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li public int getBackCameraId() { 204003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li return mBackCameraId; 205003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li } 206003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li 207003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li public int getFrontCameraId() { 208003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li return mFrontCameraId; 209003dd5a52457c024a0f99a2bb222bfc6ad70bbe5Wu-cheng Li } 210cd65be31531717fb032b7423f8d5a77df465cfcaChih-Chung Chang} 211