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