CameraDeviceUserShim.java revision e663cb77281c4c76241b820f6126543f1c2d859f
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.impl.CaptureResultExtras; 29import android.hardware.camera2.utils.CameraBinderDecorator; 30import android.hardware.camera2.utils.CameraRuntimeException; 31import android.os.ConditionVariable; 32import android.os.IBinder; 33import android.os.Looper; 34import android.os.Handler; 35import android.os.HandlerThread; 36import android.os.Message; 37import android.os.RemoteException; 38import android.util.Log; 39import android.util.SparseArray; 40import android.view.Surface; 41 42import java.util.ArrayList; 43import java.util.List; 44 45/** 46 * Compatibility implementation of the Camera2 API binder interface. 47 * 48 * <p> 49 * This is intended to be called from the same process as client 50 * {@link android.hardware.camera2.CameraDevice}, and wraps a 51 * {@link android.hardware.camera2.legacy.LegacyCameraDevice} that emulates Camera2 service using 52 * the Camera1 API. 53 * </p> 54 * 55 * <p> 56 * Keep up to date with ICameraDeviceUser.aidl. 57 * </p> 58 */ 59@SuppressWarnings("deprecation") 60public class CameraDeviceUserShim implements ICameraDeviceUser { 61 private static final String TAG = "CameraDeviceUserShim"; 62 63 private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG); 64 private static final int OPEN_CAMERA_TIMEOUT_MS = 5000; // 5 sec (same as api1 cts timeout) 65 66 private final LegacyCameraDevice mLegacyDevice; 67 68 private final Object mConfigureLock = new Object(); 69 private int mSurfaceIdCounter; 70 private boolean mConfiguring; 71 private final SparseArray<Surface> mSurfaces; 72 private final CameraCharacteristics mCameraCharacteristics; 73 private final CameraLooper mCameraInit; 74 private final CameraCallbackThread mCameraCallbacks; 75 76 77 protected CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera, 78 CameraCharacteristics characteristics, CameraLooper cameraInit, 79 CameraCallbackThread cameraCallbacks) { 80 mLegacyDevice = legacyCamera; 81 mConfiguring = false; 82 mSurfaces = new SparseArray<Surface>(); 83 mCameraCharacteristics = characteristics; 84 mCameraInit = cameraInit; 85 mCameraCallbacks = cameraCallbacks; 86 87 mSurfaceIdCounter = 0; 88 } 89 90 /** 91 * Create a separate looper/thread for the camera to run on; open the camera. 92 * 93 * <p>Since the camera automatically latches on to the current thread's looper, 94 * it's important that we have our own thread with our own looper to guarantee 95 * that the camera callbacks get correctly posted to our own thread.</p> 96 */ 97 private static class CameraLooper implements Runnable, AutoCloseable { 98 private final int mCameraId; 99 private Looper mLooper; 100 private volatile int mInitErrors; 101 private final Camera mCamera = Camera.openUninitialized(); 102 private final ConditionVariable mStartDone = new ConditionVariable(); 103 private final Thread mThread; 104 105 /** 106 * Spin up a new thread, immediately open the camera in the background. 107 * 108 * <p>Use {@link #waitForOpen} to block until the camera is finished opening.</p> 109 * 110 * @param cameraId numeric camera Id 111 * 112 * @see #waitForOpen 113 */ 114 public CameraLooper(int cameraId) { 115 mCameraId = cameraId; 116 117 mThread = new Thread(this); 118 mThread.start(); 119 } 120 121 public Camera getCamera() { 122 return mCamera; 123 } 124 125 @Override 126 public void run() { 127 // Set up a looper to be used by camera. 128 Looper.prepare(); 129 130 // Save the looper so that we can terminate this thread 131 // after we are done with it. 132 mLooper = Looper.myLooper(); 133 mInitErrors = mCamera.cameraInitUnspecified(mCameraId); 134 135 mStartDone.open(); 136 Looper.loop(); // Blocks forever until #close is called. 137 } 138 139 /** 140 * Quit the looper safely; then join until the thread shuts down. 141 */ 142 @Override 143 public void close() { 144 if (mLooper == null) { 145 return; 146 } 147 148 mLooper.quitSafely(); 149 try { 150 mThread.join(); 151 } catch (InterruptedException e) { 152 throw new AssertionError(e); 153 } 154 155 mLooper = null; 156 } 157 158 /** 159 * Block until the camera opens; then return its initialization error code (if any). 160 * 161 * @param timeoutMs timeout in milliseconds 162 * 163 * @return int error code 164 * 165 * @throws CameraRuntimeException if the camera open times out with ({@code CAMERA_ERROR}) 166 */ 167 public int waitForOpen(int timeoutMs) { 168 // Block until the camera is open asynchronously 169 if (!mStartDone.block(timeoutMs)) { 170 Log.e(TAG, "waitForOpen - Camera failed to open after timeout of " 171 + OPEN_CAMERA_TIMEOUT_MS + " ms"); 172 try { 173 mCamera.release(); 174 } catch (RuntimeException e) { 175 Log.e(TAG, "connectBinderShim - Failed to release camera after timeout ", e); 176 } 177 178 throw new CameraRuntimeException(CameraAccessException.CAMERA_ERROR); 179 } 180 181 return mInitErrors; 182 } 183 } 184 185 /** 186 * A thread to process callbacks to send back to the camera client. 187 * 188 * <p>This effectively emulates one-way binder semantics when in the same process as the 189 * callee.</p> 190 */ 191 private static class CameraCallbackThread implements ICameraDeviceCallbacks { 192 private static final int CAMERA_ERROR = 0; 193 private static final int CAMERA_IDLE = 1; 194 private static final int CAPTURE_STARTED = 2; 195 private static final int RESULT_RECEIVED = 3; 196 197 private final HandlerThread mHandlerThread; 198 private Handler mHandler; 199 200 private final ICameraDeviceCallbacks mCallbacks; 201 202 public CameraCallbackThread(ICameraDeviceCallbacks callbacks) { 203 mCallbacks = callbacks; 204 205 mHandlerThread = new HandlerThread("LegacyCameraCallback"); 206 mHandlerThread.start(); 207 } 208 209 public void close() { 210 mHandlerThread.quitSafely(); 211 } 212 213 @Override 214 public void onDeviceError(final int errorCode, final CaptureResultExtras resultExtras) { 215 Message msg = getHandler().obtainMessage(CAMERA_ERROR, 216 /*arg1*/ errorCode, /*arg2*/ 0, 217 /*obj*/ resultExtras); 218 getHandler().sendMessage(msg); 219 } 220 221 @Override 222 public void onDeviceIdle() { 223 Message msg = getHandler().obtainMessage(CAMERA_IDLE); 224 getHandler().sendMessage(msg); 225 } 226 227 @Override 228 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 229 Message msg = getHandler().obtainMessage(CAPTURE_STARTED, 230 /*arg1*/ (int) (timestamp & 0xFFFFFFFFL), 231 /*arg2*/ (int) ( (timestamp >> 32) & 0xFFFFFFFFL), 232 /*obj*/ resultExtras); 233 getHandler().sendMessage(msg); 234 } 235 236 @Override 237 public void onResultReceived(final CameraMetadataNative result, 238 final CaptureResultExtras resultExtras) { 239 Object[] resultArray = new Object[] { result, resultExtras }; 240 Message msg = getHandler().obtainMessage(RESULT_RECEIVED, 241 /*obj*/ resultArray); 242 getHandler().sendMessage(msg); 243 } 244 245 @Override 246 public IBinder asBinder() { 247 // This is solely intended to be used for in-process binding. 248 return null; 249 } 250 251 private Handler getHandler() { 252 if (mHandler == null) { 253 mHandler = new CallbackHandler(mHandlerThread.getLooper()); 254 } 255 return mHandler; 256 } 257 258 private class CallbackHandler extends Handler { 259 public CallbackHandler(Looper l) { 260 super(l); 261 } 262 263 @Override 264 public void handleMessage(Message msg) { 265 try { 266 switch (msg.what) { 267 case CAMERA_ERROR: { 268 int errorCode = msg.arg1; 269 CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj; 270 mCallbacks.onDeviceError(errorCode, resultExtras); 271 break; 272 } 273 case CAMERA_IDLE: 274 mCallbacks.onDeviceIdle(); 275 break; 276 case CAPTURE_STARTED: { 277 long timestamp = msg.arg2 & 0xFFFFFFFFL; 278 timestamp = (timestamp << 32) | (msg.arg1 & 0xFFFFFFFFL); 279 CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj; 280 mCallbacks.onCaptureStarted(resultExtras, timestamp); 281 break; 282 } 283 case RESULT_RECEIVED: { 284 Object[] resultArray = (Object[]) msg.obj; 285 CameraMetadataNative result = (CameraMetadataNative) resultArray[0]; 286 CaptureResultExtras resultExtras = (CaptureResultExtras) resultArray[1]; 287 mCallbacks.onResultReceived(result, resultExtras); 288 break; 289 } 290 default: 291 throw new IllegalArgumentException( 292 "Unknown callback message " + msg.what); 293 } 294 } catch (RemoteException e) { 295 throw new IllegalStateException( 296 "Received remote exception during camera callback " + msg.what, e); 297 } 298 } 299 } 300 } 301 302 public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks, 303 int cameraId) { 304 if (DEBUG) { 305 Log.d(TAG, "Opening shim Camera device"); 306 } 307 308 /* 309 * Put the camera open on a separate thread with its own looper; otherwise 310 * if the main thread is used then the callbacks might never get delivered 311 * (e.g. in CTS which run its own default looper only after tests) 312 */ 313 314 CameraLooper init = new CameraLooper(cameraId); 315 316 CameraCallbackThread threadCallbacks = new CameraCallbackThread(callbacks); 317 318 // TODO: Make this async instead of blocking 319 int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS); 320 Camera legacyCamera = init.getCamera(); 321 322 // Check errors old HAL initialization 323 CameraBinderDecorator.throwOnError(initErrors); 324 325 // Disable shutter sounds (this will work unconditionally) for api2 clients 326 legacyCamera.disableShutterSound(); 327 328 CameraInfo info = new CameraInfo(); 329 Camera.getCameraInfo(cameraId, info); 330 331 CameraCharacteristics characteristics = 332 LegacyMetadataMapper.createCharacteristics(legacyCamera.getParameters(), info); 333 LegacyCameraDevice device = new LegacyCameraDevice( 334 cameraId, legacyCamera, characteristics, threadCallbacks); 335 return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks); 336 } 337 338 @Override 339 public void disconnect() { 340 if (DEBUG) { 341 Log.d(TAG, "disconnect called."); 342 } 343 344 if (mLegacyDevice.isClosed()) { 345 Log.w(TAG, "Cannot disconnect, device has already been closed."); 346 } 347 348 try { 349 mLegacyDevice.close(); 350 } finally { 351 mCameraInit.close(); 352 mCameraCallbacks.close(); 353 } 354 } 355 356 @Override 357 public int submitRequest(CaptureRequest request, boolean streaming, 358 /*out*/LongParcelable lastFrameNumber) { 359 if (DEBUG) { 360 Log.d(TAG, "submitRequest called."); 361 } 362 if (mLegacyDevice.isClosed()) { 363 Log.e(TAG, "Cannot submit request, device has been closed."); 364 return CameraBinderDecorator.ENODEV; 365 } 366 367 synchronized(mConfigureLock) { 368 if (mConfiguring) { 369 Log.e(TAG, "Cannot submit request, configuration change in progress."); 370 return CameraBinderDecorator.INVALID_OPERATION; 371 } 372 } 373 return mLegacyDevice.submitRequest(request, streaming, lastFrameNumber); 374 } 375 376 @Override 377 public int submitRequestList(List<CaptureRequest> request, boolean streaming, 378 /*out*/LongParcelable lastFrameNumber) { 379 if (DEBUG) { 380 Log.d(TAG, "submitRequestList called."); 381 } 382 if (mLegacyDevice.isClosed()) { 383 Log.e(TAG, "Cannot submit request list, device has been closed."); 384 return CameraBinderDecorator.ENODEV; 385 } 386 387 synchronized(mConfigureLock) { 388 if (mConfiguring) { 389 Log.e(TAG, "Cannot submit request, configuration change in progress."); 390 return CameraBinderDecorator.INVALID_OPERATION; 391 } 392 } 393 return mLegacyDevice.submitRequestList(request, streaming, lastFrameNumber); 394 } 395 396 @Override 397 public int cancelRequest(int requestId, /*out*/LongParcelable lastFrameNumber) { 398 if (DEBUG) { 399 Log.d(TAG, "cancelRequest called."); 400 } 401 if (mLegacyDevice.isClosed()) { 402 Log.e(TAG, "Cannot cancel request, device has been closed."); 403 return CameraBinderDecorator.ENODEV; 404 } 405 406 synchronized(mConfigureLock) { 407 if (mConfiguring) { 408 Log.e(TAG, "Cannot cancel request, configuration change in progress."); 409 return CameraBinderDecorator.INVALID_OPERATION; 410 } 411 } 412 long lastFrame = mLegacyDevice.cancelRequest(requestId); 413 lastFrameNumber.setNumber(lastFrame); 414 return CameraBinderDecorator.NO_ERROR; 415 } 416 417 @Override 418 public int beginConfigure() { 419 if (DEBUG) { 420 Log.d(TAG, "beginConfigure called."); 421 } 422 if (mLegacyDevice.isClosed()) { 423 Log.e(TAG, "Cannot begin configure, device has been closed."); 424 return CameraBinderDecorator.ENODEV; 425 } 426 427 synchronized(mConfigureLock) { 428 if (mConfiguring) { 429 Log.e(TAG, "Cannot begin configure, configuration change already in progress."); 430 return CameraBinderDecorator.INVALID_OPERATION; 431 } 432 mConfiguring = true; 433 } 434 return CameraBinderDecorator.NO_ERROR; 435 } 436 437 @Override 438 public int endConfigure() { 439 if (DEBUG) { 440 Log.d(TAG, "endConfigure called."); 441 } 442 if (mLegacyDevice.isClosed()) { 443 Log.e(TAG, "Cannot end configure, device has been closed."); 444 return CameraBinderDecorator.ENODEV; 445 } 446 447 ArrayList<Surface> surfaces = null; 448 synchronized(mConfigureLock) { 449 if (!mConfiguring) { 450 Log.e(TAG, "Cannot end configure, no configuration change in progress."); 451 return CameraBinderDecorator.INVALID_OPERATION; 452 } 453 int numSurfaces = mSurfaces.size(); 454 if (numSurfaces > 0) { 455 surfaces = new ArrayList<>(); 456 for (int i = 0; i < numSurfaces; ++i) { 457 surfaces.add(mSurfaces.valueAt(i)); 458 } 459 } 460 mConfiguring = false; 461 } 462 return mLegacyDevice.configureOutputs(surfaces); 463 } 464 465 @Override 466 public int deleteStream(int streamId) { 467 if (DEBUG) { 468 Log.d(TAG, "deleteStream called."); 469 } 470 if (mLegacyDevice.isClosed()) { 471 Log.e(TAG, "Cannot delete stream, device has been closed."); 472 return CameraBinderDecorator.ENODEV; 473 } 474 475 synchronized(mConfigureLock) { 476 if (!mConfiguring) { 477 Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet."); 478 return CameraBinderDecorator.INVALID_OPERATION; 479 } 480 int index = mSurfaces.indexOfKey(streamId); 481 if (index < 0) { 482 Log.e(TAG, "Cannot delete stream, stream id " + streamId + " doesn't exist."); 483 return CameraBinderDecorator.BAD_VALUE; 484 } 485 mSurfaces.removeAt(index); 486 } 487 return CameraBinderDecorator.NO_ERROR; 488 } 489 490 @Override 491 public int createStream(int width, int height, int format, Surface surface) { 492 if (DEBUG) { 493 Log.d(TAG, "createStream called."); 494 } 495 if (mLegacyDevice.isClosed()) { 496 Log.e(TAG, "Cannot create stream, device has been closed."); 497 return CameraBinderDecorator.ENODEV; 498 } 499 500 synchronized(mConfigureLock) { 501 if (!mConfiguring) { 502 Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet."); 503 return CameraBinderDecorator.INVALID_OPERATION; 504 } 505 int id = ++mSurfaceIdCounter; 506 mSurfaces.put(id, surface); 507 return id; 508 } 509 } 510 511 @Override 512 public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) { 513 if (DEBUG) { 514 Log.d(TAG, "createDefaultRequest called."); 515 } 516 if (mLegacyDevice.isClosed()) { 517 Log.e(TAG, "Cannot create default request, device has been closed."); 518 return CameraBinderDecorator.ENODEV; 519 } 520 521 CameraMetadataNative template; 522 try { 523 template = 524 LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId); 525 } catch (IllegalArgumentException e) { 526 Log.e(TAG, "createDefaultRequest - invalid templateId specified"); 527 return CameraBinderDecorator.BAD_VALUE; 528 } 529 530 request.swap(template); 531 return CameraBinderDecorator.NO_ERROR; 532 } 533 534 @Override 535 public int getCameraInfo(/*out*/CameraMetadataNative info) { 536 if (DEBUG) { 537 Log.d(TAG, "getCameraInfo called."); 538 } 539 // TODO: implement getCameraInfo. 540 Log.e(TAG, "getCameraInfo unimplemented."); 541 return CameraBinderDecorator.NO_ERROR; 542 } 543 544 @Override 545 public int waitUntilIdle() throws RemoteException { 546 if (DEBUG) { 547 Log.d(TAG, "waitUntilIdle called."); 548 } 549 if (mLegacyDevice.isClosed()) { 550 Log.e(TAG, "Cannot wait until idle, device has been closed."); 551 return CameraBinderDecorator.ENODEV; 552 } 553 554 synchronized(mConfigureLock) { 555 if (mConfiguring) { 556 Log.e(TAG, "Cannot wait until idle, configuration change in progress."); 557 return CameraBinderDecorator.INVALID_OPERATION; 558 } 559 } 560 mLegacyDevice.waitUntilIdle(); 561 return CameraBinderDecorator.NO_ERROR; 562 } 563 564 @Override 565 public int flush(/*out*/LongParcelable lastFrameNumber) { 566 if (DEBUG) { 567 Log.d(TAG, "flush called."); 568 } 569 if (mLegacyDevice.isClosed()) { 570 Log.e(TAG, "Cannot flush, device has been closed."); 571 return CameraBinderDecorator.ENODEV; 572 } 573 574 synchronized(mConfigureLock) { 575 if (mConfiguring) { 576 Log.e(TAG, "Cannot flush, configuration change in progress."); 577 return CameraBinderDecorator.INVALID_OPERATION; 578 } 579 } 580 long lastFrame = mLegacyDevice.flush(); 581 if (lastFrameNumber != null) { 582 lastFrameNumber.setNumber(lastFrame); 583 } 584 return CameraBinderDecorator.NO_ERROR; 585 } 586 587 @Override 588 public IBinder asBinder() { 589 // This is solely intended to be used for in-process binding. 590 return null; 591 } 592} 593