Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 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 | |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 19 | import android.hardware.Camera.CameraInfo; |
| 20 | import android.hardware.Camera.Parameters; |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 21 | import android.os.Handler; |
| 22 | import android.os.HandlerThread; |
| 23 | import android.os.Looper; |
| 24 | import android.os.Message; |
| 25 | import android.util.Log; |
| 26 | |
Angus Kong | 20fad24 | 2013-11-11 18:23:46 -0800 | [diff] [blame^] | 27 | import com.android.camera.app.CameraManager; |
| 28 | import com.android.camera.app.CameraManager.CameraProxy; |
| 29 | import com.android.camera.app.CameraManagerFactory; |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 30 | |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 31 | import java.text.SimpleDateFormat; |
| 32 | import java.util.ArrayList; |
| 33 | import java.util.Date; |
| 34 | |
Angus Kong | 20fad24 | 2013-11-11 18:23:46 -0800 | [diff] [blame^] | 35 | import static com.android.camera.util.CameraUtil.Assert; |
| 36 | |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 37 | /** |
| 38 | * The class is used to hold an {@code android.hardware.Camera} instance. |
| 39 | * |
| 40 | * <p>The {@code open()} and {@code release()} calls are similar to the ones |
| 41 | * in {@code android.hardware.Camera}. The difference is if {@code keep()} is |
| 42 | * called before {@code release()}, CameraHolder will try to hold the {@code |
| 43 | * android.hardware.Camera} instance for a while, so if {@code open()} is |
| 44 | * called soon after, we can avoid the cost of {@code open()} in {@code |
| 45 | * android.hardware.Camera}. |
| 46 | * |
| 47 | * <p>This is used in switching between different modules. |
| 48 | */ |
| 49 | public class CameraHolder { |
| 50 | private static final String TAG = "CameraHolder"; |
| 51 | private static final int KEEP_CAMERA_TIMEOUT = 3000; // 3 seconds |
| 52 | private CameraProxy mCameraDevice; |
| 53 | private long mKeepBeforeTime; // Keep the Camera before this time. |
| 54 | private final Handler mHandler; |
| 55 | private boolean mCameraOpened; // true if camera is opened |
| 56 | private final int mNumberOfCameras; |
| 57 | private int mCameraId = -1; // current camera id |
| 58 | private int mBackCameraId = -1; |
| 59 | private int mFrontCameraId = -1; |
| 60 | private final CameraInfo[] mInfo; |
| 61 | private static CameraProxy mMockCamera[]; |
| 62 | private static CameraInfo mMockCameraInfo[]; |
| 63 | |
| 64 | /* Debug double-open issue */ |
| 65 | private static final boolean DEBUG_OPEN_RELEASE = true; |
| 66 | private static class OpenReleaseState { |
| 67 | long time; |
| 68 | int id; |
| 69 | String device; |
| 70 | String[] stack; |
| 71 | } |
| 72 | private static ArrayList<OpenReleaseState> sOpenReleaseStates = |
| 73 | new ArrayList<OpenReleaseState>(); |
| 74 | private static SimpleDateFormat sDateFormat = new SimpleDateFormat( |
| 75 | "yyyy-MM-dd HH:mm:ss.SSS"); |
| 76 | |
| 77 | private static synchronized void collectState(int id, CameraProxy device) { |
| 78 | OpenReleaseState s = new OpenReleaseState(); |
| 79 | s.time = System.currentTimeMillis(); |
| 80 | s.id = id; |
| 81 | if (device == null) { |
| 82 | s.device = "(null)"; |
| 83 | } else { |
| 84 | s.device = device.toString(); |
| 85 | } |
| 86 | |
| 87 | StackTraceElement[] stack = Thread.currentThread().getStackTrace(); |
| 88 | String[] lines = new String[stack.length]; |
| 89 | for (int i = 0; i < stack.length; i++) { |
| 90 | lines[i] = stack[i].toString(); |
| 91 | } |
| 92 | s.stack = lines; |
| 93 | |
| 94 | if (sOpenReleaseStates.size() > 10) { |
| 95 | sOpenReleaseStates.remove(0); |
| 96 | } |
| 97 | sOpenReleaseStates.add(s); |
| 98 | } |
| 99 | |
| 100 | private static synchronized void dumpStates() { |
| 101 | for (int i = sOpenReleaseStates.size() - 1; i >= 0; i--) { |
| 102 | OpenReleaseState s = sOpenReleaseStates.get(i); |
| 103 | String date = sDateFormat.format(new Date(s.time)); |
| 104 | Log.d(TAG, "State " + i + " at " + date); |
| 105 | Log.d(TAG, "mCameraId = " + s.id + ", mCameraDevice = " + s.device); |
| 106 | Log.d(TAG, "Stack:"); |
| 107 | for (int j = 0; j < s.stack.length; j++) { |
| 108 | Log.d(TAG, " " + s.stack[j]); |
| 109 | } |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | // We store the camera parameters when we actually open the device, |
| 114 | // so we can restore them in the subsequent open() requests by the user. |
| 115 | // This prevents the parameters set by PhotoModule used by VideoModule |
| 116 | // inadvertently. |
| 117 | private Parameters mParameters; |
| 118 | |
| 119 | // Use a singleton. |
| 120 | private static CameraHolder sHolder; |
| 121 | public static synchronized CameraHolder instance() { |
| 122 | if (sHolder == null) { |
| 123 | sHolder = new CameraHolder(); |
| 124 | } |
| 125 | return sHolder; |
| 126 | } |
| 127 | |
| 128 | private static final int RELEASE_CAMERA = 1; |
| 129 | private class MyHandler extends Handler { |
| 130 | MyHandler(Looper looper) { |
| 131 | super(looper); |
| 132 | } |
| 133 | |
| 134 | @Override |
| 135 | public void handleMessage(Message msg) { |
| 136 | switch(msg.what) { |
| 137 | case RELEASE_CAMERA: |
| 138 | synchronized (CameraHolder.this) { |
| 139 | // In 'CameraHolder.open', the 'RELEASE_CAMERA' message |
| 140 | // will be removed if it is found in the queue. However, |
| 141 | // there is a chance that this message has been handled |
| 142 | // before being removed. So, we need to add a check |
| 143 | // here: |
| 144 | if (!mCameraOpened) release(); |
| 145 | } |
| 146 | break; |
| 147 | } |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | public static void injectMockCamera(CameraInfo[] info, CameraProxy[] camera) { |
| 152 | mMockCameraInfo = info; |
| 153 | mMockCamera = camera; |
| 154 | sHolder = new CameraHolder(); |
| 155 | } |
| 156 | |
| 157 | private CameraHolder() { |
| 158 | HandlerThread ht = new HandlerThread("CameraHolder"); |
| 159 | ht.start(); |
| 160 | mHandler = new MyHandler(ht.getLooper()); |
| 161 | if (mMockCameraInfo != null) { |
| 162 | mNumberOfCameras = mMockCameraInfo.length; |
| 163 | mInfo = mMockCameraInfo; |
| 164 | } else { |
| 165 | mNumberOfCameras = android.hardware.Camera.getNumberOfCameras(); |
| 166 | mInfo = new CameraInfo[mNumberOfCameras]; |
| 167 | for (int i = 0; i < mNumberOfCameras; i++) { |
| 168 | mInfo[i] = new CameraInfo(); |
| 169 | android.hardware.Camera.getCameraInfo(i, mInfo[i]); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | // get the first (smallest) back and first front camera id |
| 174 | for (int i = 0; i < mNumberOfCameras; i++) { |
| 175 | if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) { |
| 176 | mBackCameraId = i; |
| 177 | } else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) { |
| 178 | mFrontCameraId = i; |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | public int getNumberOfCameras() { |
| 184 | return mNumberOfCameras; |
| 185 | } |
| 186 | |
| 187 | public CameraInfo[] getCameraInfo() { |
| 188 | return mInfo; |
| 189 | } |
| 190 | |
Angus Kong | 4f795b8 | 2013-09-16 14:25:35 -0700 | [diff] [blame] | 191 | public synchronized CameraProxy open( |
| 192 | Handler handler, int cameraId, |
Angus Kong | 6284815 | 2013-11-08 17:25:29 -0800 | [diff] [blame] | 193 | CameraManager.CameraOpenCallback cb) { |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 194 | if (DEBUG_OPEN_RELEASE) { |
| 195 | collectState(cameraId, mCameraDevice); |
| 196 | if (mCameraOpened) { |
| 197 | Log.e(TAG, "double open"); |
| 198 | dumpStates(); |
| 199 | } |
| 200 | } |
| 201 | Assert(!mCameraOpened); |
| 202 | if (mCameraDevice != null && mCameraId != cameraId) { |
Angus Kong | 20fad24 | 2013-11-11 18:23:46 -0800 | [diff] [blame^] | 203 | mCameraDevice.release(true); |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 204 | mCameraDevice = null; |
| 205 | mCameraId = -1; |
| 206 | } |
| 207 | if (mCameraDevice == null) { |
Angus Kong | 4f795b8 | 2013-09-16 14:25:35 -0700 | [diff] [blame] | 208 | Log.v(TAG, "open camera " + cameraId); |
| 209 | if (mMockCameraInfo == null) { |
| 210 | mCameraDevice = CameraManagerFactory |
Angus Kong | 20fad24 | 2013-11-11 18:23:46 -0800 | [diff] [blame^] | 211 | .getAndroidCameraManager().cameraOpenOld(handler, cameraId, cb); |
Angus Kong | 4f795b8 | 2013-09-16 14:25:35 -0700 | [diff] [blame] | 212 | } else { |
| 213 | if (mMockCamera != null) { |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 214 | mCameraDevice = mMockCamera[cameraId]; |
Angus Kong | 4f795b8 | 2013-09-16 14:25:35 -0700 | [diff] [blame] | 215 | } else { |
| 216 | Log.e(TAG, "MockCameraInfo found, but no MockCamera provided."); |
| 217 | mCameraDevice = null; |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 218 | } |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 219 | } |
Angus Kong | 4f795b8 | 2013-09-16 14:25:35 -0700 | [diff] [blame] | 220 | if (mCameraDevice == null) { |
| 221 | Log.e(TAG, "fail to connect Camera:" + mCameraId + ", aborting."); |
| 222 | return null; |
| 223 | } |
| 224 | mCameraId = cameraId; |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 225 | mParameters = mCameraDevice.getParameters(); |
| 226 | } else { |
Angus Kong | 20fad24 | 2013-11-11 18:23:46 -0800 | [diff] [blame^] | 227 | if (!mCameraDevice.reconnectOld(handler, cb)) { |
Angus Kong | 4f795b8 | 2013-09-16 14:25:35 -0700 | [diff] [blame] | 228 | Log.e(TAG, "fail to reconnect Camera:" + mCameraId + ", aborting."); |
| 229 | return null; |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 230 | } |
| 231 | mCameraDevice.setParameters(mParameters); |
| 232 | } |
| 233 | mCameraOpened = true; |
| 234 | mHandler.removeMessages(RELEASE_CAMERA); |
| 235 | mKeepBeforeTime = 0; |
| 236 | return mCameraDevice; |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Tries to open the hardware camera. If the camera is being used or |
| 241 | * unavailable then return {@code null}. |
| 242 | */ |
Angus Kong | 4f795b8 | 2013-09-16 14:25:35 -0700 | [diff] [blame] | 243 | public synchronized CameraProxy tryOpen( |
Angus Kong | 6284815 | 2013-11-08 17:25:29 -0800 | [diff] [blame] | 244 | Handler handler, int cameraId, CameraManager.CameraOpenCallback cb) { |
Angus Kong | 4f795b8 | 2013-09-16 14:25:35 -0700 | [diff] [blame] | 245 | return (!mCameraOpened ? open(handler, cameraId, cb) : null); |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | public synchronized void release() { |
| 249 | if (DEBUG_OPEN_RELEASE) { |
| 250 | collectState(mCameraId, mCameraDevice); |
| 251 | } |
| 252 | |
| 253 | if (mCameraDevice == null) return; |
| 254 | |
Ruben Brunk | a9d66bd | 2013-09-06 11:56:32 -0700 | [diff] [blame] | 255 | strongRelease(); |
| 256 | } |
| 257 | |
| 258 | public synchronized void strongRelease() { |
| 259 | if (mCameraDevice == null) return; |
| 260 | |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 261 | mCameraOpened = false; |
Angus Kong | 20fad24 | 2013-11-11 18:23:46 -0800 | [diff] [blame^] | 262 | mCameraDevice.release(true); |
Michael Kolb | 8872c23 | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 263 | mCameraDevice = null; |
| 264 | // We must set this to null because it has a reference to Camera. |
| 265 | // Camera has references to the listeners. |
| 266 | mParameters = null; |
| 267 | mCameraId = -1; |
| 268 | } |
| 269 | |
| 270 | public void keep() { |
| 271 | keep(KEEP_CAMERA_TIMEOUT); |
| 272 | } |
| 273 | |
| 274 | public synchronized void keep(int time) { |
| 275 | // We allow mCameraOpened in either state for the convenience of the |
| 276 | // calling activity. The activity may not have a chance to call open() |
| 277 | // before the user switches to another activity. |
| 278 | mKeepBeforeTime = System.currentTimeMillis() + time; |
| 279 | } |
| 280 | |
| 281 | public int getBackCameraId() { |
| 282 | return mBackCameraId; |
| 283 | } |
| 284 | |
| 285 | public int getFrontCameraId() { |
| 286 | return mFrontCameraId; |
| 287 | } |
| 288 | } |