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