1 /* 2 * Copyright (C) 2013 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.app; 18 19 import android.content.Context; 20 import android.os.Handler; 21 22 import com.android.camera.CameraDisabledException; 23 import com.android.camera.debug.Log; 24 import com.android.camera.util.CameraUtil; 25 import com.android.camera.util.GservicesHelper; 26 import com.android.ex.camera2.portability.CameraAgent; 27 import com.android.ex.camera2.portability.CameraDeviceInfo; 28 import com.android.ex.camera2.portability.CameraExceptionHandler; 29 30 /** 31 * A class which implements {@link com.android.camera.app.CameraProvider} used 32 * by {@link com.android.camera.CameraActivity}. 33 * TODO: Make this class package private. 34 */ 35 public class CameraController implements CameraAgent.CameraOpenCallback, CameraProvider { 36 private static final Log.Tag TAG = new Log.Tag("CameraController"); 37 private static final int EMPTY_REQUEST = -1; 38 private final Context mContext; 39 private CameraAgent.CameraOpenCallback mCallbackReceiver; 40 private final Handler mCallbackHandler; 41 private final CameraAgent mCameraAgent; 42 private final CameraAgent mCameraAgentNg; 43 44 /** The one for the API that is currently in use (deprecated one by default). */ 45 private CameraDeviceInfo mInfo; 46 47 private CameraAgent.CameraProxy mCameraProxy; 48 private int mRequestingCameraId = EMPTY_REQUEST; 49 50 /** 51 * Determines which of mCameraAgent and mCameraAgentNg is currently in use. 52 * <p>It's only possible to enable this if the new API is actually 53 * supported.</p> 54 */ 55 private boolean mUsingNewApi = false; 56 57 /** 58 * Constructor. 59 * 60 * @param context The {@link android.content.Context} used to check if the 61 * camera is disabled. 62 * @param handler The {@link android.os.Handler} to post the camera 63 * callbacks to. 64 * @param cameraManager Used for camera open/close. 65 * @param cameraManagerNg Used for camera open/close with the new API. If 66 * {@code null} or the same object as 67 * {@code cameraManager}, the new API will not be 68 * exposed and requests for it will get the old one. 69 */ 70 public CameraController(Context context, CameraAgent.CameraOpenCallback callbackReceiver, 71 Handler handler, CameraAgent cameraManager, CameraAgent cameraManagerNg) { 72 mContext = context; 73 mCallbackReceiver = callbackReceiver; 74 mCallbackHandler = handler; 75 mCameraAgent = cameraManager; 76 // If the new implementation is the same as the old, the 77 // CameraAgentFactory decided this device doesn't support the new API. 78 mCameraAgentNg = cameraManagerNg != cameraManager ? cameraManagerNg : null; 79 mInfo = mCameraAgent.getCameraDeviceInfo(); 80 if (mInfo == null && mCallbackReceiver != null) { 81 mCallbackReceiver.onDeviceOpenFailure(-1, "GETTING_CAMERA_INFO"); 82 } 83 } 84 85 @Override 86 public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) { 87 mCameraAgent.setCameraExceptionHandler(exceptionHandler); 88 if (mCameraAgentNg != null) { 89 mCameraAgentNg.setCameraExceptionHandler(exceptionHandler); 90 } 91 } 92 93 @Override 94 public CameraDeviceInfo.Characteristics getCharacteristics(int cameraId) { 95 if (mInfo == null) { 96 return null; 97 } 98 return mInfo.getCharacteristics(cameraId); 99 } 100 101 @Override 102 public int getCurrentCameraId() { 103 if (mCameraProxy != null) { 104 return mCameraProxy.getCameraId(); 105 } else { 106 Log.v(TAG, "getCurrentCameraId without an open camera... returning requested id"); 107 return mRequestingCameraId; 108 } 109 } 110 111 @Override 112 public int getNumberOfCameras() { 113 if (mInfo == null) { 114 return 0; 115 } 116 return mInfo.getNumberOfCameras(); 117 } 118 119 @Override 120 public int getFirstBackCameraId() { 121 if (mInfo == null) { 122 return -1; 123 } 124 return mInfo.getFirstBackCameraId(); 125 } 126 127 @Override 128 public int getFirstFrontCameraId() { 129 if (mInfo == null) { 130 return -1; 131 } 132 return mInfo.getFirstFrontCameraId(); 133 } 134 135 @Override 136 public boolean isFrontFacingCamera(int id) { 137 if (mInfo == null) { 138 return false; 139 } 140 if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) { 141 Log.e(TAG, "Camera info not available:" + id); 142 return false; 143 } 144 return mInfo.getCharacteristics(id).isFacingFront(); 145 } 146 147 @Override 148 public boolean isBackFacingCamera(int id) { 149 if (mInfo == null) { 150 return false; 151 } 152 if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) { 153 Log.e(TAG, "Camera info not available:" + id); 154 return false; 155 } 156 return mInfo.getCharacteristics(id).isFacingBack(); 157 } 158 159 @Override 160 public void onCameraOpened(CameraAgent.CameraProxy camera) { 161 Log.v(TAG, "onCameraOpened"); 162 if (mRequestingCameraId != camera.getCameraId()) { 163 return; 164 } 165 mCameraProxy = camera; 166 mRequestingCameraId = EMPTY_REQUEST; 167 if (mCallbackReceiver != null) { 168 mCallbackReceiver.onCameraOpened(camera); 169 } 170 } 171 172 @Override 173 public void onCameraDisabled(int cameraId) { 174 if (mCallbackReceiver != null) { 175 mCallbackReceiver.onCameraDisabled(cameraId); 176 } 177 } 178 179 @Override 180 public void onDeviceOpenFailure(int cameraId, String info) { 181 if (mCallbackReceiver != null) { 182 mCallbackReceiver.onDeviceOpenFailure(cameraId, info); 183 } 184 } 185 186 @Override 187 public void onDeviceOpenedAlready(int cameraId, String info) { 188 if (mCallbackReceiver != null) { 189 mCallbackReceiver.onDeviceOpenedAlready(cameraId, info); 190 } 191 } 192 193 @Override 194 public void onReconnectionFailure(CameraAgent mgr, String info) { 195 if (mCallbackReceiver != null) { 196 mCallbackReceiver.onReconnectionFailure(mgr, info); 197 } 198 } 199 200 @Override 201 public void requestCamera(int id) { 202 requestCamera(id, false); 203 } 204 205 @Override 206 public void requestCamera(int id, boolean useNewApi) { 207 Log.v(TAG, "requestCamera"); 208 // Based on 209 // (mRequestingCameraId == id, mRequestingCameraId == EMPTY_REQUEST), 210 // we have (T, T), (T, F), (F, T), (F, F). 211 // (T, T): implies id == EMPTY_REQUEST. We don't allow this to happen 212 // here. Return. 213 // (F, F): A previous request hasn't been fulfilled yet. Return. 214 // (T, F): Already requested the same camera. No-op. Return. 215 // (F, T): Nothing is going on. Continue. 216 if (mRequestingCameraId != EMPTY_REQUEST || mRequestingCameraId == id) { 217 return; 218 } 219 if (mInfo == null) { 220 return; 221 } 222 mRequestingCameraId = id; 223 224 // Only actually use the new API if it's supported on this device. 225 useNewApi = mCameraAgentNg != null && useNewApi; 226 CameraAgent cameraManager = useNewApi ? mCameraAgentNg : mCameraAgent; 227 228 if (mCameraProxy == null) { 229 // No camera yet. 230 checkAndOpenCamera(mContext, cameraManager, id, mCallbackHandler, this); 231 } else if (mCameraProxy.getCameraId() != id || mUsingNewApi != useNewApi) { 232 boolean syncClose = GservicesHelper.useCamera2ApiThroughPortabilityLayer(mContext); 233 Log.v(TAG, "different camera already opened, closing then reopening"); 234 // Already has camera opened, and is switching cameras and/or APIs. 235 if (mUsingNewApi) { 236 mCameraAgentNg.closeCamera(mCameraProxy, true); 237 } else { 238 // if using API2 ensure API1 usage is also synced 239 mCameraAgent.closeCamera(mCameraProxy, syncClose); 240 } 241 checkAndOpenCamera(mContext, cameraManager, id, mCallbackHandler, this); 242 } else { 243 // The same camera, just do a reconnect. 244 Log.v(TAG, "reconnecting to use the existing camera"); 245 mCameraProxy.reconnect(mCallbackHandler, this); 246 mCameraProxy = null; 247 } 248 249 mUsingNewApi = useNewApi; 250 mInfo = cameraManager.getCameraDeviceInfo(); 251 } 252 253 @Override 254 public boolean waitingForCamera() { 255 return mRequestingCameraId != EMPTY_REQUEST; 256 } 257 258 @Override 259 public void releaseCamera(int id) { 260 if (mCameraProxy == null) { 261 if (mRequestingCameraId == EMPTY_REQUEST) { 262 // Camera not requested yet. 263 Log.w(TAG, "Trying to release the camera before requesting"); 264 } 265 // Camera requested but not available yet. 266 mRequestingCameraId = EMPTY_REQUEST; 267 return; 268 } 269 if (mCameraProxy.getCameraId() != id) { 270 throw new IllegalStateException("Trying to release an unopened camera."); 271 } 272 mRequestingCameraId = EMPTY_REQUEST; 273 } 274 275 public void removeCallbackReceiver() { 276 mCallbackReceiver = null; 277 } 278 279 /** 280 * Closes the opened camera device. 281 * TODO: Make this method package private. 282 */ 283 public void closeCamera(boolean synced) { 284 Log.v(TAG, "Closing camera"); 285 mCameraProxy = null; 286 if (mUsingNewApi) { 287 mCameraAgentNg.closeCamera(mCameraProxy, synced); 288 } else { 289 mCameraAgent.closeCamera(mCameraProxy, synced); 290 } 291 mRequestingCameraId = EMPTY_REQUEST; 292 mUsingNewApi = false; 293 } 294 295 private static void checkAndOpenCamera(Context context, CameraAgent cameraManager, 296 final int cameraId, Handler handler, final CameraAgent.CameraOpenCallback cb) { 297 Log.v(TAG, "checkAndOpenCamera"); 298 try { 299 CameraUtil.throwIfCameraDisabled(context); 300 cameraManager.openCamera(handler, cameraId, cb); 301 } catch (CameraDisabledException ex) { 302 handler.post(new Runnable() { 303 @Override 304 public void run() { 305 cb.onCameraDisabled(cameraId); 306 } 307 }); 308 } 309 } 310 311 public void setOneShotPreviewCallback(Handler handler, 312 CameraAgent.CameraPreviewDataCallback cb) { 313 mCameraProxy.setOneShotPreviewCallback(handler, cb); 314 } 315 } 316