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