CameraDeviceUserShim.java revision 5398a676809faaf3c6c2875edc1907ad6b8e1c89
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 206 private final HandlerThread mHandlerThread; 207 private Handler mHandler; 208 209 private final ICameraDeviceCallbacks mCallbacks; 210 211 public CameraCallbackThread(ICameraDeviceCallbacks callbacks) { 212 mCallbacks = callbacks; 213 214 mHandlerThread = new HandlerThread("LegacyCameraCallback"); 215 mHandlerThread.start(); 216 } 217 218 public void close() { 219 mHandlerThread.quitSafely(); 220 } 221 222 @Override 223 public void onDeviceError(final int errorCode, final CaptureResultExtras resultExtras) { 224 Message msg = getHandler().obtainMessage(CAMERA_ERROR, 225 /*arg1*/ errorCode, /*arg2*/ 0, 226 /*obj*/ resultExtras); 227 getHandler().sendMessage(msg); 228 } 229 230 @Override 231 public void onDeviceIdle() { 232 Message msg = getHandler().obtainMessage(CAMERA_IDLE); 233 getHandler().sendMessage(msg); 234 } 235 236 @Override 237 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 238 Message msg = getHandler().obtainMessage(CAPTURE_STARTED, 239 /*arg1*/ (int) (timestamp & 0xFFFFFFFFL), 240 /*arg2*/ (int) ( (timestamp >> 32) & 0xFFFFFFFFL), 241 /*obj*/ resultExtras); 242 getHandler().sendMessage(msg); 243 } 244 245 @Override 246 public void onResultReceived(final CameraMetadataNative result, 247 final CaptureResultExtras resultExtras) { 248 Object[] resultArray = new Object[] { result, resultExtras }; 249 Message msg = getHandler().obtainMessage(RESULT_RECEIVED, 250 /*obj*/ resultArray); 251 getHandler().sendMessage(msg); 252 } 253 254 @Override 255 public IBinder asBinder() { 256 // This is solely intended to be used for in-process binding. 257 return null; 258 } 259 260 private Handler getHandler() { 261 if (mHandler == null) { 262 mHandler = new CallbackHandler(mHandlerThread.getLooper()); 263 } 264 return mHandler; 265 } 266 267 private class CallbackHandler extends Handler { 268 public CallbackHandler(Looper l) { 269 super(l); 270 } 271 272 @Override 273 public void handleMessage(Message msg) { 274 try { 275 switch (msg.what) { 276 case CAMERA_ERROR: { 277 int errorCode = msg.arg1; 278 CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj; 279 mCallbacks.onDeviceError(errorCode, resultExtras); 280 break; 281 } 282 case CAMERA_IDLE: 283 mCallbacks.onDeviceIdle(); 284 break; 285 case CAPTURE_STARTED: { 286 long timestamp = msg.arg2 & 0xFFFFFFFFL; 287 timestamp = (timestamp << 32) | (msg.arg1 & 0xFFFFFFFFL); 288 CaptureResultExtras resultExtras = (CaptureResultExtras) msg.obj; 289 mCallbacks.onCaptureStarted(resultExtras, timestamp); 290 break; 291 } 292 case RESULT_RECEIVED: { 293 Object[] resultArray = (Object[]) msg.obj; 294 CameraMetadataNative result = (CameraMetadataNative) resultArray[0]; 295 CaptureResultExtras resultExtras = (CaptureResultExtras) resultArray[1]; 296 mCallbacks.onResultReceived(result, resultExtras); 297 break; 298 } 299 default: 300 throw new IllegalArgumentException( 301 "Unknown callback message " + msg.what); 302 } 303 } catch (RemoteException e) { 304 throw new IllegalStateException( 305 "Received remote exception during camera callback " + msg.what, e); 306 } 307 } 308 } 309 } 310 311 public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks, 312 int cameraId) { 313 if (DEBUG) { 314 Log.d(TAG, "Opening shim Camera device"); 315 } 316 317 /* 318 * Put the camera open on a separate thread with its own looper; otherwise 319 * if the main thread is used then the callbacks might never get delivered 320 * (e.g. in CTS which run its own default looper only after tests) 321 */ 322 323 CameraLooper init = new CameraLooper(cameraId); 324 325 CameraCallbackThread threadCallbacks = new CameraCallbackThread(callbacks); 326 327 // TODO: Make this async instead of blocking 328 int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS); 329 Camera legacyCamera = init.getCamera(); 330 331 // Check errors old HAL initialization 332 CameraBinderDecorator.throwOnError(initErrors); 333 334 // Disable shutter sounds (this will work unconditionally) for api2 clients 335 legacyCamera.disableShutterSound(); 336 337 CameraInfo info = new CameraInfo(); 338 Camera.getCameraInfo(cameraId, info); 339 340 Camera.Parameters legacyParameters = null; 341 try { 342 legacyParameters = legacyCamera.getParameters(); 343 } catch (RuntimeException e) { 344 throw new CameraRuntimeException(CameraAccessException.CAMERA_ERROR, 345 "Unable to get initial parameters", e); 346 } 347 348 CameraCharacteristics characteristics = 349 LegacyMetadataMapper.createCharacteristics(legacyParameters, info); 350 LegacyCameraDevice device = new LegacyCameraDevice( 351 cameraId, legacyCamera, characteristics, threadCallbacks); 352 return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks); 353 } 354 355 @Override 356 public void disconnect() { 357 if (DEBUG) { 358 Log.d(TAG, "disconnect called."); 359 } 360 361 if (mLegacyDevice.isClosed()) { 362 Log.w(TAG, "Cannot disconnect, device has already been closed."); 363 } 364 365 try { 366 mLegacyDevice.close(); 367 } finally { 368 mCameraInit.close(); 369 mCameraCallbacks.close(); 370 } 371 } 372 373 @Override 374 public int submitRequest(CaptureRequest request, boolean streaming, 375 /*out*/LongParcelable lastFrameNumber) { 376 if (DEBUG) { 377 Log.d(TAG, "submitRequest called."); 378 } 379 if (mLegacyDevice.isClosed()) { 380 Log.e(TAG, "Cannot submit request, device has been closed."); 381 return CameraBinderDecorator.ENODEV; 382 } 383 384 synchronized(mConfigureLock) { 385 if (mConfiguring) { 386 Log.e(TAG, "Cannot submit request, configuration change in progress."); 387 return CameraBinderDecorator.INVALID_OPERATION; 388 } 389 } 390 return mLegacyDevice.submitRequest(request, streaming, lastFrameNumber); 391 } 392 393 @Override 394 public int submitRequestList(List<CaptureRequest> request, boolean streaming, 395 /*out*/LongParcelable lastFrameNumber) { 396 if (DEBUG) { 397 Log.d(TAG, "submitRequestList called."); 398 } 399 if (mLegacyDevice.isClosed()) { 400 Log.e(TAG, "Cannot submit request list, device has been closed."); 401 return CameraBinderDecorator.ENODEV; 402 } 403 404 synchronized(mConfigureLock) { 405 if (mConfiguring) { 406 Log.e(TAG, "Cannot submit request, configuration change in progress."); 407 return CameraBinderDecorator.INVALID_OPERATION; 408 } 409 } 410 return mLegacyDevice.submitRequestList(request, streaming, lastFrameNumber); 411 } 412 413 @Override 414 public int cancelRequest(int requestId, /*out*/LongParcelable lastFrameNumber) { 415 if (DEBUG) { 416 Log.d(TAG, "cancelRequest called."); 417 } 418 if (mLegacyDevice.isClosed()) { 419 Log.e(TAG, "Cannot cancel request, device has been closed."); 420 return CameraBinderDecorator.ENODEV; 421 } 422 423 synchronized(mConfigureLock) { 424 if (mConfiguring) { 425 Log.e(TAG, "Cannot cancel request, configuration change in progress."); 426 return CameraBinderDecorator.INVALID_OPERATION; 427 } 428 } 429 long lastFrame = mLegacyDevice.cancelRequest(requestId); 430 lastFrameNumber.setNumber(lastFrame); 431 return CameraBinderDecorator.NO_ERROR; 432 } 433 434 @Override 435 public int beginConfigure() { 436 if (DEBUG) { 437 Log.d(TAG, "beginConfigure called."); 438 } 439 if (mLegacyDevice.isClosed()) { 440 Log.e(TAG, "Cannot begin configure, device has been closed."); 441 return CameraBinderDecorator.ENODEV; 442 } 443 444 synchronized(mConfigureLock) { 445 if (mConfiguring) { 446 Log.e(TAG, "Cannot begin configure, configuration change already in progress."); 447 return CameraBinderDecorator.INVALID_OPERATION; 448 } 449 mConfiguring = true; 450 } 451 return CameraBinderDecorator.NO_ERROR; 452 } 453 454 @Override 455 public int endConfigure() { 456 if (DEBUG) { 457 Log.d(TAG, "endConfigure called."); 458 } 459 if (mLegacyDevice.isClosed()) { 460 Log.e(TAG, "Cannot end configure, device has been closed."); 461 return CameraBinderDecorator.ENODEV; 462 } 463 464 ArrayList<Surface> surfaces = null; 465 synchronized(mConfigureLock) { 466 if (!mConfiguring) { 467 Log.e(TAG, "Cannot end configure, no configuration change in progress."); 468 return CameraBinderDecorator.INVALID_OPERATION; 469 } 470 int numSurfaces = mSurfaces.size(); 471 if (numSurfaces > 0) { 472 surfaces = new ArrayList<>(); 473 for (int i = 0; i < numSurfaces; ++i) { 474 surfaces.add(mSurfaces.valueAt(i)); 475 } 476 } 477 mConfiguring = false; 478 } 479 return mLegacyDevice.configureOutputs(surfaces); 480 } 481 482 @Override 483 public int deleteStream(int streamId) { 484 if (DEBUG) { 485 Log.d(TAG, "deleteStream called."); 486 } 487 if (mLegacyDevice.isClosed()) { 488 Log.e(TAG, "Cannot delete stream, device has been closed."); 489 return CameraBinderDecorator.ENODEV; 490 } 491 492 synchronized(mConfigureLock) { 493 if (!mConfiguring) { 494 Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet."); 495 return CameraBinderDecorator.INVALID_OPERATION; 496 } 497 int index = mSurfaces.indexOfKey(streamId); 498 if (index < 0) { 499 Log.e(TAG, "Cannot delete stream, stream id " + streamId + " doesn't exist."); 500 return CameraBinderDecorator.BAD_VALUE; 501 } 502 mSurfaces.removeAt(index); 503 } 504 return CameraBinderDecorator.NO_ERROR; 505 } 506 507 @Override 508 public int createStream(OutputConfiguration outputConfiguration) { 509 if (DEBUG) { 510 Log.d(TAG, "createStream called."); 511 } 512 if (mLegacyDevice.isClosed()) { 513 Log.e(TAG, "Cannot create stream, device has been closed."); 514 return CameraBinderDecorator.ENODEV; 515 } 516 517 synchronized(mConfigureLock) { 518 if (!mConfiguring) { 519 Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet."); 520 return CameraBinderDecorator.INVALID_OPERATION; 521 } 522 if (outputConfiguration.getRotation() != OutputConfiguration.ROTATION_0) { 523 Log.e(TAG, "Cannot create stream, stream rotation is not supported."); 524 return CameraBinderDecorator.INVALID_OPERATION; 525 } 526 int id = ++mSurfaceIdCounter; 527 mSurfaces.put(id, outputConfiguration.getSurface()); 528 return id; 529 } 530 } 531 532 @Override 533 public int createInputStream(int width, int height, int format) { 534 Log.e(TAG, "creating input stream is not supported on legacy devices"); 535 return CameraBinderDecorator.INVALID_OPERATION; 536 } 537 538 @Override 539 public int getInputSurface(/*out*/ Surface surface) { 540 Log.e(TAG, "getting input surface is not supported on legacy devices"); 541 return CameraBinderDecorator.INVALID_OPERATION; 542 } 543 544 @Override 545 public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) { 546 if (DEBUG) { 547 Log.d(TAG, "createDefaultRequest called."); 548 } 549 if (mLegacyDevice.isClosed()) { 550 Log.e(TAG, "Cannot create default request, device has been closed."); 551 return CameraBinderDecorator.ENODEV; 552 } 553 554 CameraMetadataNative template; 555 try { 556 template = 557 LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId); 558 } catch (IllegalArgumentException e) { 559 Log.e(TAG, "createDefaultRequest - invalid templateId specified"); 560 return CameraBinderDecorator.BAD_VALUE; 561 } 562 563 request.swap(template); 564 return CameraBinderDecorator.NO_ERROR; 565 } 566 567 @Override 568 public int getCameraInfo(/*out*/CameraMetadataNative info) { 569 if (DEBUG) { 570 Log.d(TAG, "getCameraInfo called."); 571 } 572 // TODO: implement getCameraInfo. 573 Log.e(TAG, "getCameraInfo unimplemented."); 574 return CameraBinderDecorator.NO_ERROR; 575 } 576 577 @Override 578 public int waitUntilIdle() throws RemoteException { 579 if (DEBUG) { 580 Log.d(TAG, "waitUntilIdle called."); 581 } 582 if (mLegacyDevice.isClosed()) { 583 Log.e(TAG, "Cannot wait until idle, device has been closed."); 584 return CameraBinderDecorator.ENODEV; 585 } 586 587 synchronized(mConfigureLock) { 588 if (mConfiguring) { 589 Log.e(TAG, "Cannot wait until idle, configuration change in progress."); 590 return CameraBinderDecorator.INVALID_OPERATION; 591 } 592 } 593 mLegacyDevice.waitUntilIdle(); 594 return CameraBinderDecorator.NO_ERROR; 595 } 596 597 @Override 598 public int flush(/*out*/LongParcelable lastFrameNumber) { 599 if (DEBUG) { 600 Log.d(TAG, "flush called."); 601 } 602 if (mLegacyDevice.isClosed()) { 603 Log.e(TAG, "Cannot flush, device has been closed."); 604 return CameraBinderDecorator.ENODEV; 605 } 606 607 synchronized(mConfigureLock) { 608 if (mConfiguring) { 609 Log.e(TAG, "Cannot flush, configuration change in progress."); 610 return CameraBinderDecorator.INVALID_OPERATION; 611 } 612 } 613 long lastFrame = mLegacyDevice.flush(); 614 if (lastFrameNumber != null) { 615 lastFrameNumber.setNumber(lastFrame); 616 } 617 return CameraBinderDecorator.NO_ERROR; 618 } 619 620 @Override 621 public IBinder asBinder() { 622 // This is solely intended to be used for in-process binding. 623 return null; 624 } 625} 626