CameraDeviceUserShim.java revision df6242e374b81e802a38cb891477f05d3e4b3cbc
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( 203 cameraId, legacyCamera, characteristics, callbacks); 204 return new CameraDeviceUserShim(cameraId, device, characteristics, init); 205 } 206 207 @Override 208 public void disconnect() { 209 if (DEBUG) { 210 Log.d(TAG, "disconnect called."); 211 } 212 213 try { 214 mLegacyDevice.close(); 215 } finally { 216 mCameraInit.close(); 217 } 218 } 219 220 @Override 221 public int submitRequest(CaptureRequest request, boolean streaming, 222 /*out*/LongParcelable lastFrameNumber) { 223 if (DEBUG) { 224 Log.d(TAG, "submitRequest called."); 225 } 226 synchronized(mConfigureLock) { 227 if (mConfiguring) { 228 Log.e(TAG, "Cannot submit request, configuration change in progress."); 229 return CameraBinderDecorator.INVALID_OPERATION; 230 } 231 } 232 return mLegacyDevice.submitRequest(request, streaming, lastFrameNumber); 233 } 234 235 @Override 236 public int submitRequestList(List<CaptureRequest> request, boolean streaming, 237 /*out*/LongParcelable lastFrameNumber) { 238 if (DEBUG) { 239 Log.d(TAG, "submitRequestList called."); 240 } 241 synchronized(mConfigureLock) { 242 if (mConfiguring) { 243 Log.e(TAG, "Cannot submit request, configuration change in progress."); 244 return CameraBinderDecorator.INVALID_OPERATION; 245 } 246 } 247 return mLegacyDevice.submitRequestList(request, streaming, lastFrameNumber); 248 } 249 250 @Override 251 public int cancelRequest(int requestId, /*out*/LongParcelable lastFrameNumber) { 252 if (DEBUG) { 253 Log.d(TAG, "cancelRequest called."); 254 } 255 synchronized(mConfigureLock) { 256 if (mConfiguring) { 257 Log.e(TAG, "Cannot cancel request, configuration change in progress."); 258 return CameraBinderDecorator.INVALID_OPERATION; 259 } 260 } 261 long lastFrame = mLegacyDevice.cancelRequest(requestId); 262 lastFrameNumber.setNumber(lastFrame); 263 return CameraBinderDecorator.NO_ERROR; 264 } 265 266 @Override 267 public int beginConfigure() { 268 if (DEBUG) { 269 Log.d(TAG, "beginConfigure called."); 270 } 271 synchronized(mConfigureLock) { 272 if (mConfiguring) { 273 Log.e(TAG, "Cannot begin configure, configuration change already in progress."); 274 return CameraBinderDecorator.INVALID_OPERATION; 275 } 276 mConfiguring = true; 277 } 278 return CameraBinderDecorator.NO_ERROR; 279 } 280 281 @Override 282 public int endConfigure() { 283 if (DEBUG) { 284 Log.d(TAG, "endConfigure called."); 285 } 286 ArrayList<Surface> surfaces = null; 287 synchronized(mConfigureLock) { 288 if (!mConfiguring) { 289 Log.e(TAG, "Cannot end configure, no configuration change in progress."); 290 return CameraBinderDecorator.INVALID_OPERATION; 291 } 292 int numSurfaces = mSurfaces.size(); 293 if (numSurfaces > 0) { 294 surfaces = new ArrayList<>(); 295 for (int i = 0; i < numSurfaces; ++i) { 296 surfaces.add(mSurfaces.valueAt(i)); 297 } 298 } 299 mConfiguring = false; 300 } 301 return mLegacyDevice.configureOutputs(surfaces); 302 } 303 304 @Override 305 public int deleteStream(int streamId) { 306 if (DEBUG) { 307 Log.d(TAG, "deleteStream called."); 308 } 309 synchronized(mConfigureLock) { 310 if (!mConfiguring) { 311 Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet."); 312 return CameraBinderDecorator.INVALID_OPERATION; 313 } 314 int index = mSurfaces.indexOfKey(streamId); 315 if (index < 0) { 316 Log.e(TAG, "Cannot delete stream, stream id " + streamId + " doesn't exist."); 317 return CameraBinderDecorator.BAD_VALUE; 318 } 319 mSurfaces.removeAt(index); 320 } 321 return CameraBinderDecorator.NO_ERROR; 322 } 323 324 @Override 325 public int createStream(int width, int height, int format, Surface surface) { 326 if (DEBUG) { 327 Log.d(TAG, "createStream called."); 328 } 329 synchronized(mConfigureLock) { 330 if (!mConfiguring) { 331 Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet."); 332 return CameraBinderDecorator.INVALID_OPERATION; 333 } 334 int id = ++mSurfaceIdCounter; 335 mSurfaces.put(id, surface); 336 return id; 337 } 338 } 339 340 @Override 341 public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) { 342 if (DEBUG) { 343 Log.d(TAG, "createDefaultRequest called."); 344 } 345 346 CameraMetadataNative template; 347 try { 348 template = 349 LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId); 350 } catch (IllegalArgumentException e) { 351 Log.e(TAG, "createDefaultRequest - invalid templateId specified"); 352 return CameraBinderDecorator.BAD_VALUE; 353 } 354 355 request.swap(template); 356 return CameraBinderDecorator.NO_ERROR; 357 } 358 359 @Override 360 public int getCameraInfo(/*out*/CameraMetadataNative info) { 361 if (DEBUG) { 362 Log.d(TAG, "getCameraInfo called."); 363 } 364 // TODO: implement getCameraInfo. 365 Log.e(TAG, "getCameraInfo unimplemented."); 366 return CameraBinderDecorator.NO_ERROR; 367 } 368 369 @Override 370 public int waitUntilIdle() throws RemoteException { 371 if (DEBUG) { 372 Log.d(TAG, "waitUntilIdle called."); 373 } 374 synchronized(mConfigureLock) { 375 if (mConfiguring) { 376 Log.e(TAG, "Cannot wait until idle, configuration change in progress."); 377 return CameraBinderDecorator.INVALID_OPERATION; 378 } 379 } 380 mLegacyDevice.waitUntilIdle(); 381 return CameraBinderDecorator.NO_ERROR; 382 } 383 384 @Override 385 public int flush(/*out*/LongParcelable lastFrameNumber) { 386 if (DEBUG) { 387 Log.d(TAG, "flush called."); 388 } 389 synchronized(mConfigureLock) { 390 if (mConfiguring) { 391 Log.e(TAG, "Cannot flush, configuration change in progress."); 392 return CameraBinderDecorator.INVALID_OPERATION; 393 } 394 } 395 // TODO: implement flush. 396 return CameraBinderDecorator.NO_ERROR; 397 } 398 399 @Override 400 public IBinder asBinder() { 401 // This is solely intended to be used for in-process binding. 402 return null; 403 } 404} 405