CameraDeviceUserShim.java revision a296fece2b974a11bc624fd67b275863f17df867
1/* 2 * Copyright (C) 2014 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 android.hardware.camera2.legacy; 18 19import android.hardware.Camera; 20import android.hardware.Camera.CameraInfo; 21import android.hardware.camera2.CameraAccessException; 22import android.hardware.camera2.CameraCharacteristics; 23import android.hardware.camera2.CaptureRequest; 24import android.hardware.camera2.ICameraDeviceCallbacks; 25import android.hardware.camera2.ICameraDeviceUser; 26import android.hardware.camera2.utils.LongParcelable; 27import android.hardware.camera2.impl.CameraMetadataNative; 28import android.hardware.camera2.utils.CameraBinderDecorator; 29import android.hardware.camera2.utils.CameraRuntimeException; 30import android.os.ConditionVariable; 31import android.os.IBinder; 32import android.os.Looper; 33import android.os.RemoteException; 34import android.util.Log; 35import android.util.SparseArray; 36import android.view.Surface; 37 38import java.util.ArrayList; 39import java.util.List; 40 41/** 42 * Compatibility implementation of the Camera2 API binder interface. 43 * 44 * <p> 45 * This is intended to be called from the same process as client 46 * {@link android.hardware.camera2.CameraDevice}, and wraps a 47 * {@link android.hardware.camera2.legacy.LegacyCameraDevice} that emulates Camera2 service using 48 * the Camera1 API. 49 * </p> 50 * 51 * <p> 52 * Keep up to date with ICameraDeviceUser.aidl. 53 * </p> 54 */ 55public class CameraDeviceUserShim implements ICameraDeviceUser { 56 private static final String TAG = "CameraDeviceUserShim"; 57 58 private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG); 59 private static final int OPEN_CAMERA_TIMEOUT_MS = 5000; // 5 sec (same as api1 cts timeout) 60 61 private final LegacyCameraDevice mLegacyDevice; 62 63 private final Object mConfigureLock = new Object(); 64 private int mSurfaceIdCounter; 65 private boolean mConfiguring; 66 private final SparseArray<Surface> mSurfaces; 67 private final CameraCharacteristics mCameraCharacteristics; 68 private final CameraLooper mCameraInit; 69 70 protected CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera, 71 CameraCharacteristics characteristics, CameraLooper cameraInit) { 72 mLegacyDevice = legacyCamera; 73 mConfiguring = false; 74 mSurfaces = new SparseArray<Surface>(); 75 mCameraCharacteristics = characteristics; 76 mCameraInit = cameraInit; 77 78 mSurfaceIdCounter = 0; 79 } 80 81 /** 82 * Create a separate looper/thread for the camera to run on; open the camera. 83 * 84 * <p>Since the camera automatically latches on to the current thread's looper, 85 * it's important that we have our own thread with our own looper to guarantee 86 * that the camera callbacks get correctly posted to our own thread.</p> 87 */ 88 private static class CameraLooper implements Runnable, AutoCloseable { 89 private final int mCameraId; 90 private Looper mLooper; 91 private volatile int mInitErrors; 92 private final Camera mCamera = Camera.openUninitialized(); 93 private final ConditionVariable mStartDone = new ConditionVariable(); 94 private final Thread mThread; 95 96 /** 97 * Spin up a new thread, immediately open the camera in the background. 98 * 99 * <p>Use {@link #waitForOpen} to block until the camera is finished opening.</p> 100 * 101 * @param cameraId numeric camera Id 102 * 103 * @see #waitForOpen 104 */ 105 public CameraLooper(int cameraId) { 106 mCameraId = cameraId; 107 108 mThread = new Thread(this); 109 mThread.start(); 110 } 111 112 public Camera getCamera() { 113 return mCamera; 114 } 115 116 @Override 117 public void run() { 118 // Set up a looper to be used by camera. 119 Looper.prepare(); 120 121 // Save the looper so that we can terminate this thread 122 // after we are done with it. 123 mLooper = Looper.myLooper(); 124 mInitErrors = mCamera.cameraInitUnspecified(mCameraId); 125 126 mStartDone.open(); 127 Looper.loop(); // Blocks forever until #close is called. 128 } 129 130 /** 131 * Quit the looper safely; then join until the thread shuts down. 132 */ 133 @Override 134 public void close() { 135 if (mLooper == null) { 136 return; 137 } 138 139 mLooper.quitSafely(); 140 try { 141 mThread.join(); 142 } catch (InterruptedException e) { 143 throw new AssertionError(e); 144 } 145 146 mLooper = null; 147 } 148 149 /** 150 * Block until the camera opens; then return its initialization error code (if any). 151 * 152 * @param timeoutMs timeout in milliseconds 153 * 154 * @return int error code 155 * 156 * @throws CameraRuntimeException if the camera open times out with ({@code CAMERA_ERROR}) 157 */ 158 public int waitForOpen(int timeoutMs) { 159 // Block until the camera is open asynchronously 160 if (!mStartDone.block(timeoutMs)) { 161 Log.e(TAG, "waitForOpen - Camera failed to open after timeout of " 162 + OPEN_CAMERA_TIMEOUT_MS + " ms"); 163 try { 164 mCamera.release(); 165 } catch (RuntimeException e) { 166 Log.e(TAG, "connectBinderShim - Failed to release camera after timeout ", e); 167 } 168 169 throw new CameraRuntimeException(CameraAccessException.CAMERA_ERROR); 170 } 171 172 return mInitErrors; 173 } 174 } 175 176 public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks, 177 int cameraId) { 178 if (DEBUG) { 179 Log.d(TAG, "Opening shim Camera device"); 180 } 181 182 /* 183 * Put the camera open on a separate thread with its own looper; otherwise 184 * if the main thread is used then the callbacks might never get delivered 185 * (e.g. in CTS which run its own default looper only after tests) 186 */ 187 188 CameraLooper init = new CameraLooper(cameraId); 189 190 // TODO: Make this async instead of blocking 191 int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS); 192 Camera legacyCamera = init.getCamera(); 193 194 // Check errors old HAL initialization 195 CameraBinderDecorator.throwOnError(initErrors); 196 197 CameraInfo info = new CameraInfo(); 198 Camera.getCameraInfo(cameraId, info); 199 200 CameraCharacteristics characteristics = 201 LegacyMetadataMapper.createCharacteristics(legacyCamera.getParameters(), info); 202 LegacyCameraDevice device = new LegacyCameraDevice(cameraId, legacyCamera, callbacks); 203 return new CameraDeviceUserShim(cameraId, device, characteristics, init); 204 } 205 206 @Override 207 public void disconnect() { 208 if (DEBUG) { 209 Log.d(TAG, "disconnect called."); 210 } 211 212 try { 213 mLegacyDevice.close(); 214 } finally { 215 mCameraInit.close(); 216 } 217 } 218 219 @Override 220 public int submitRequest(CaptureRequest request, boolean streaming, 221 /*out*/LongParcelable lastFrameNumber) { 222 if (DEBUG) { 223 Log.d(TAG, "submitRequest called."); 224 } 225 synchronized(mConfigureLock) { 226 if (mConfiguring) { 227 Log.e(TAG, "Cannot submit request, configuration change in progress."); 228 return CameraBinderDecorator.INVALID_OPERATION; 229 } 230 } 231 return mLegacyDevice.submitRequest(request, streaming, lastFrameNumber); 232 } 233 234 @Override 235 public int submitRequestList(List<CaptureRequest> request, boolean streaming, 236 /*out*/LongParcelable lastFrameNumber) { 237 if (DEBUG) { 238 Log.d(TAG, "submitRequestList called."); 239 } 240 synchronized(mConfigureLock) { 241 if (mConfiguring) { 242 Log.e(TAG, "Cannot submit request, configuration change in progress."); 243 return CameraBinderDecorator.INVALID_OPERATION; 244 } 245 } 246 return mLegacyDevice.submitRequestList(request, streaming, lastFrameNumber); 247 } 248 249 @Override 250 public int cancelRequest(int requestId, /*out*/LongParcelable lastFrameNumber) { 251 if (DEBUG) { 252 Log.d(TAG, "cancelRequest called."); 253 } 254 synchronized(mConfigureLock) { 255 if (mConfiguring) { 256 Log.e(TAG, "Cannot cancel request, configuration change in progress."); 257 return CameraBinderDecorator.INVALID_OPERATION; 258 } 259 } 260 long lastFrame = mLegacyDevice.cancelRequest(requestId); 261 lastFrameNumber.setNumber(lastFrame); 262 return CameraBinderDecorator.NO_ERROR; 263 } 264 265 @Override 266 public int beginConfigure() { 267 if (DEBUG) { 268 Log.d(TAG, "beginConfigure called."); 269 } 270 synchronized(mConfigureLock) { 271 if (mConfiguring) { 272 Log.e(TAG, "Cannot begin configure, configuration change already in progress."); 273 return CameraBinderDecorator.INVALID_OPERATION; 274 } 275 mConfiguring = true; 276 } 277 return CameraBinderDecorator.NO_ERROR; 278 } 279 280 @Override 281 public int endConfigure() { 282 if (DEBUG) { 283 Log.d(TAG, "endConfigure called."); 284 } 285 ArrayList<Surface> surfaces = null; 286 synchronized(mConfigureLock) { 287 if (!mConfiguring) { 288 Log.e(TAG, "Cannot end configure, no configuration change in progress."); 289 return CameraBinderDecorator.INVALID_OPERATION; 290 } 291 int numSurfaces = mSurfaces.size(); 292 if (numSurfaces > 0) { 293 surfaces = new ArrayList<>(); 294 for (int i = 0; i < numSurfaces; ++i) { 295 surfaces.add(mSurfaces.valueAt(i)); 296 } 297 } 298 mConfiguring = false; 299 } 300 return mLegacyDevice.configureOutputs(surfaces); 301 } 302 303 @Override 304 public int deleteStream(int streamId) { 305 if (DEBUG) { 306 Log.d(TAG, "deleteStream called."); 307 } 308 synchronized(mConfigureLock) { 309 if (!mConfiguring) { 310 Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet."); 311 return CameraBinderDecorator.INVALID_OPERATION; 312 } 313 int index = mSurfaces.indexOfKey(streamId); 314 if (index < 0) { 315 Log.e(TAG, "Cannot delete stream, stream id " + streamId + " doesn't exist."); 316 return CameraBinderDecorator.BAD_VALUE; 317 } 318 mSurfaces.removeAt(index); 319 } 320 return CameraBinderDecorator.NO_ERROR; 321 } 322 323 @Override 324 public int createStream(int width, int height, int format, Surface surface) { 325 if (DEBUG) { 326 Log.d(TAG, "createStream called."); 327 } 328 synchronized(mConfigureLock) { 329 if (!mConfiguring) { 330 Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet."); 331 return CameraBinderDecorator.INVALID_OPERATION; 332 } 333 int id = ++mSurfaceIdCounter; 334 mSurfaces.put(id, surface); 335 return id; 336 } 337 } 338 339 @Override 340 public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) { 341 if (DEBUG) { 342 Log.d(TAG, "createDefaultRequest called."); 343 } 344 345 CameraMetadataNative template; 346 try { 347 template = 348 LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId); 349 } catch (IllegalArgumentException e) { 350 Log.e(TAG, "createDefaultRequest - invalid templateId specified"); 351 return CameraBinderDecorator.BAD_VALUE; 352 } 353 354 request.swap(template); 355 return CameraBinderDecorator.NO_ERROR; 356 } 357 358 @Override 359 public int getCameraInfo(/*out*/CameraMetadataNative info) { 360 if (DEBUG) { 361 Log.d(TAG, "getCameraInfo called."); 362 } 363 // TODO: implement getCameraInfo. 364 Log.e(TAG, "getCameraInfo unimplemented."); 365 return CameraBinderDecorator.NO_ERROR; 366 } 367 368 @Override 369 public int waitUntilIdle() throws RemoteException { 370 if (DEBUG) { 371 Log.d(TAG, "waitUntilIdle called."); 372 } 373 synchronized(mConfigureLock) { 374 if (mConfiguring) { 375 Log.e(TAG, "Cannot wait until idle, configuration change in progress."); 376 return CameraBinderDecorator.INVALID_OPERATION; 377 } 378 } 379 mLegacyDevice.waitUntilIdle(); 380 return CameraBinderDecorator.NO_ERROR; 381 } 382 383 @Override 384 public int flush(/*out*/LongParcelable lastFrameNumber) { 385 if (DEBUG) { 386 Log.d(TAG, "flush called."); 387 } 388 synchronized(mConfigureLock) { 389 if (mConfiguring) { 390 Log.e(TAG, "Cannot flush, configuration change in progress."); 391 return CameraBinderDecorator.INVALID_OPERATION; 392 } 393 } 394 // TODO: implement flush. 395 return CameraBinderDecorator.NO_ERROR; 396 } 397 398 @Override 399 public IBinder asBinder() { 400 // This is solely intended to be used for in-process binding. 401 return null; 402 } 403} 404