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