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 17 package android.hardware.camera2.legacy; 18 19 import android.hardware.Camera; 20 import android.hardware.Camera.CameraInfo; 21 import android.hardware.camera2.CameraAccessException; 22 import android.hardware.camera2.CameraCharacteristics; 23 import android.hardware.camera2.CaptureRequest; 24 import android.hardware.camera2.ICameraDeviceCallbacks; 25 import android.hardware.camera2.ICameraDeviceUser; 26 import android.hardware.camera2.utils.LongParcelable; 27 import android.hardware.camera2.impl.CameraMetadataNative; 28 import android.hardware.camera2.impl.CaptureResultExtras; 29 import android.hardware.camera2.params.OutputConfiguration; 30 import android.hardware.camera2.utils.CameraBinderDecorator; 31 import android.hardware.camera2.utils.CameraRuntimeException; 32 import android.os.ConditionVariable; 33 import android.os.IBinder; 34 import android.os.Looper; 35 import android.os.Handler; 36 import android.os.HandlerThread; 37 import android.os.Message; 38 import android.os.RemoteException; 39 import android.util.Log; 40 import android.util.SparseArray; 41 import android.view.Surface; 42 43 import java.util.ArrayList; 44 import 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") 61 public class CameraDeviceUserShim implements ICameraDeviceUser { 62 private static final String TAG = "CameraDeviceUserShim"; 63 64 private static final boolean DEBUG = false; 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 CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera, CameraCharacteristics characteristics, CameraLooper cameraInit, CameraCallbackThread cameraCallbacks)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 /** 92 * Create a separate looper/thread for the camera to run on; open the camera. 93 * 94 * <p>Since the camera automatically latches on to the current thread's looper, 95 * it's important that we have our own thread with our own looper to guarantee 96 * that the camera callbacks get correctly posted to our own thread.</p> 97 */ 98 private static class CameraLooper implements Runnable, AutoCloseable { 99 private final int mCameraId; 100 private Looper mLooper; 101 private volatile int mInitErrors; 102 private final Camera mCamera = Camera.openUninitialized(); 103 private final ConditionVariable mStartDone = new ConditionVariable(); 104 private final Thread mThread; 105 106 /** 107 * Spin up a new thread, immediately open the camera in the background. 108 * 109 * <p>Use {@link #waitForOpen} to block until the camera is finished opening.</p> 110 * 111 * @param cameraId numeric camera Id 112 * 113 * @see #waitForOpen 114 */ CameraLooper(int cameraId)115 public CameraLooper(int cameraId) { 116 mCameraId = cameraId; 117 118 mThread = new Thread(this); 119 mThread.start(); 120 } 121 getCamera()122 public Camera getCamera() { 123 return mCamera; 124 } 125 126 @Override run()127 public void run() { 128 // Set up a looper to be used by camera. 129 Looper.prepare(); 130 131 // Save the looper so that we can terminate this thread 132 // after we are done with it. 133 mLooper = Looper.myLooper(); 134 mInitErrors = mCamera.cameraInitUnspecified(mCameraId); 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 close()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 */ waitForOpen(int timeoutMs)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 private static final int PREPARED = 4; 197 198 private final HandlerThread mHandlerThread; 199 private Handler mHandler; 200 201 private final ICameraDeviceCallbacks mCallbacks; 202 CameraCallbackThread(ICameraDeviceCallbacks callbacks)203 public CameraCallbackThread(ICameraDeviceCallbacks callbacks) { 204 mCallbacks = callbacks; 205 206 mHandlerThread = new HandlerThread("LegacyCameraCallback"); 207 mHandlerThread.start(); 208 } 209 close()210 public void close() { 211 mHandlerThread.quitSafely(); 212 } 213 214 @Override onDeviceError(final int errorCode, final CaptureResultExtras resultExtras)215 public void onDeviceError(final int errorCode, final CaptureResultExtras resultExtras) { 216 Message msg = getHandler().obtainMessage(CAMERA_ERROR, 217 /*arg1*/ errorCode, /*arg2*/ 0, 218 /*obj*/ resultExtras); 219 getHandler().sendMessage(msg); 220 } 221 222 @Override onDeviceIdle()223 public void onDeviceIdle() { 224 Message msg = getHandler().obtainMessage(CAMERA_IDLE); 225 getHandler().sendMessage(msg); 226 } 227 228 @Override onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)229 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { 230 Message msg = getHandler().obtainMessage(CAPTURE_STARTED, 231 /*arg1*/ (int) (timestamp & 0xFFFFFFFFL), 232 /*arg2*/ (int) ( (timestamp >> 32) & 0xFFFFFFFFL), 233 /*obj*/ resultExtras); 234 getHandler().sendMessage(msg); 235 } 236 237 @Override onResultReceived(final CameraMetadataNative result, final CaptureResultExtras resultExtras)238 public void onResultReceived(final CameraMetadataNative result, 239 final CaptureResultExtras resultExtras) { 240 Object[] resultArray = new Object[] { result, resultExtras }; 241 Message msg = getHandler().obtainMessage(RESULT_RECEIVED, 242 /*obj*/ resultArray); 243 getHandler().sendMessage(msg); 244 } 245 246 @Override onPrepared(int streamId)247 public void onPrepared(int streamId) { 248 Message msg = getHandler().obtainMessage(PREPARED, 249 /*arg1*/ streamId, /*arg2*/ 0); 250 getHandler().sendMessage(msg); 251 } 252 253 @Override asBinder()254 public IBinder asBinder() { 255 // This is solely intended to be used for in-process binding. 256 return null; 257 } 258 getHandler()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 { CallbackHandler(Looper l)267 public CallbackHandler(Looper l) { 268 super(l); 269 } 270 271 @Override handleMessage(Message msg)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 case PREPARED: { 299 int streamId = msg.arg1; 300 mCallbacks.onPrepared(streamId); 301 break; 302 } 303 default: 304 throw new IllegalArgumentException( 305 "Unknown callback message " + msg.what); 306 } 307 } catch (RemoteException e) { 308 throw new IllegalStateException( 309 "Received remote exception during camera callback " + msg.what, e); 310 } 311 } 312 } 313 } 314 connectBinderShim(ICameraDeviceCallbacks callbacks, int cameraId)315 public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks, 316 int cameraId) { 317 if (DEBUG) { 318 Log.d(TAG, "Opening shim Camera device"); 319 } 320 321 /* 322 * Put the camera open on a separate thread with its own looper; otherwise 323 * if the main thread is used then the callbacks might never get delivered 324 * (e.g. in CTS which run its own default looper only after tests) 325 */ 326 327 CameraLooper init = new CameraLooper(cameraId); 328 329 CameraCallbackThread threadCallbacks = new CameraCallbackThread(callbacks); 330 331 // TODO: Make this async instead of blocking 332 int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS); 333 Camera legacyCamera = init.getCamera(); 334 335 // Check errors old HAL initialization 336 CameraBinderDecorator.throwOnError(initErrors); 337 338 // Disable shutter sounds (this will work unconditionally) for api2 clients 339 legacyCamera.disableShutterSound(); 340 341 CameraInfo info = new CameraInfo(); 342 Camera.getCameraInfo(cameraId, info); 343 344 Camera.Parameters legacyParameters = null; 345 try { 346 legacyParameters = legacyCamera.getParameters(); 347 } catch (RuntimeException e) { 348 throw new CameraRuntimeException(CameraAccessException.CAMERA_ERROR, 349 "Unable to get initial parameters", e); 350 } 351 352 CameraCharacteristics characteristics = 353 LegacyMetadataMapper.createCharacteristics(legacyParameters, info); 354 LegacyCameraDevice device = new LegacyCameraDevice( 355 cameraId, legacyCamera, characteristics, threadCallbacks); 356 return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks); 357 } 358 359 @Override disconnect()360 public void disconnect() { 361 if (DEBUG) { 362 Log.d(TAG, "disconnect called."); 363 } 364 365 if (mLegacyDevice.isClosed()) { 366 Log.w(TAG, "Cannot disconnect, device has already been closed."); 367 } 368 369 try { 370 mLegacyDevice.close(); 371 } finally { 372 mCameraInit.close(); 373 mCameraCallbacks.close(); 374 } 375 } 376 377 @Override submitRequest(CaptureRequest request, boolean streaming, LongParcelable lastFrameNumber)378 public int submitRequest(CaptureRequest request, boolean streaming, 379 /*out*/LongParcelable lastFrameNumber) { 380 if (DEBUG) { 381 Log.d(TAG, "submitRequest called."); 382 } 383 if (mLegacyDevice.isClosed()) { 384 Log.e(TAG, "Cannot submit request, device has been closed."); 385 return CameraBinderDecorator.ENODEV; 386 } 387 388 synchronized(mConfigureLock) { 389 if (mConfiguring) { 390 Log.e(TAG, "Cannot submit request, configuration change in progress."); 391 return CameraBinderDecorator.INVALID_OPERATION; 392 } 393 } 394 return mLegacyDevice.submitRequest(request, streaming, lastFrameNumber); 395 } 396 397 @Override submitRequestList(List<CaptureRequest> request, boolean streaming, LongParcelable lastFrameNumber)398 public int submitRequestList(List<CaptureRequest> request, boolean streaming, 399 /*out*/LongParcelable lastFrameNumber) { 400 if (DEBUG) { 401 Log.d(TAG, "submitRequestList called."); 402 } 403 if (mLegacyDevice.isClosed()) { 404 Log.e(TAG, "Cannot submit request list, device has been closed."); 405 return CameraBinderDecorator.ENODEV; 406 } 407 408 synchronized(mConfigureLock) { 409 if (mConfiguring) { 410 Log.e(TAG, "Cannot submit request, configuration change in progress."); 411 return CameraBinderDecorator.INVALID_OPERATION; 412 } 413 } 414 return mLegacyDevice.submitRequestList(request, streaming, lastFrameNumber); 415 } 416 417 @Override cancelRequest(int requestId, LongParcelable lastFrameNumber)418 public int cancelRequest(int requestId, /*out*/LongParcelable lastFrameNumber) { 419 if (DEBUG) { 420 Log.d(TAG, "cancelRequest called."); 421 } 422 if (mLegacyDevice.isClosed()) { 423 Log.e(TAG, "Cannot cancel request, device has been closed."); 424 return CameraBinderDecorator.ENODEV; 425 } 426 427 synchronized(mConfigureLock) { 428 if (mConfiguring) { 429 Log.e(TAG, "Cannot cancel request, configuration change in progress."); 430 return CameraBinderDecorator.INVALID_OPERATION; 431 } 432 } 433 long lastFrame = mLegacyDevice.cancelRequest(requestId); 434 lastFrameNumber.setNumber(lastFrame); 435 return CameraBinderDecorator.NO_ERROR; 436 } 437 438 @Override beginConfigure()439 public int beginConfigure() { 440 if (DEBUG) { 441 Log.d(TAG, "beginConfigure called."); 442 } 443 if (mLegacyDevice.isClosed()) { 444 Log.e(TAG, "Cannot begin configure, device has been closed."); 445 return CameraBinderDecorator.ENODEV; 446 } 447 448 synchronized(mConfigureLock) { 449 if (mConfiguring) { 450 Log.e(TAG, "Cannot begin configure, configuration change already in progress."); 451 return CameraBinderDecorator.INVALID_OPERATION; 452 } 453 mConfiguring = true; 454 } 455 return CameraBinderDecorator.NO_ERROR; 456 } 457 458 @Override endConfigure(boolean isConstrainedHighSpeed)459 public int endConfigure(boolean isConstrainedHighSpeed) { 460 if (DEBUG) { 461 Log.d(TAG, "endConfigure called."); 462 } 463 if (mLegacyDevice.isClosed()) { 464 Log.e(TAG, "Cannot end configure, device has been closed."); 465 return CameraBinderDecorator.ENODEV; 466 } 467 468 ArrayList<Surface> surfaces = null; 469 synchronized(mConfigureLock) { 470 if (!mConfiguring) { 471 Log.e(TAG, "Cannot end configure, no configuration change in progress."); 472 return CameraBinderDecorator.INVALID_OPERATION; 473 } 474 int numSurfaces = mSurfaces.size(); 475 if (numSurfaces > 0) { 476 surfaces = new ArrayList<>(); 477 for (int i = 0; i < numSurfaces; ++i) { 478 surfaces.add(mSurfaces.valueAt(i)); 479 } 480 } 481 mConfiguring = false; 482 } 483 return mLegacyDevice.configureOutputs(surfaces); 484 } 485 486 @Override deleteStream(int streamId)487 public int deleteStream(int streamId) { 488 if (DEBUG) { 489 Log.d(TAG, "deleteStream called."); 490 } 491 if (mLegacyDevice.isClosed()) { 492 Log.e(TAG, "Cannot delete stream, device has been closed."); 493 return CameraBinderDecorator.ENODEV; 494 } 495 496 synchronized(mConfigureLock) { 497 if (!mConfiguring) { 498 Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet."); 499 return CameraBinderDecorator.INVALID_OPERATION; 500 } 501 int index = mSurfaces.indexOfKey(streamId); 502 if (index < 0) { 503 Log.e(TAG, "Cannot delete stream, stream id " + streamId + " doesn't exist."); 504 return CameraBinderDecorator.BAD_VALUE; 505 } 506 mSurfaces.removeAt(index); 507 } 508 return CameraBinderDecorator.NO_ERROR; 509 } 510 511 @Override createStream(OutputConfiguration outputConfiguration)512 public int createStream(OutputConfiguration outputConfiguration) { 513 if (DEBUG) { 514 Log.d(TAG, "createStream called."); 515 } 516 if (mLegacyDevice.isClosed()) { 517 Log.e(TAG, "Cannot create stream, device has been closed."); 518 return CameraBinderDecorator.ENODEV; 519 } 520 521 synchronized(mConfigureLock) { 522 if (!mConfiguring) { 523 Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet."); 524 return CameraBinderDecorator.INVALID_OPERATION; 525 } 526 if (outputConfiguration.getRotation() != OutputConfiguration.ROTATION_0) { 527 Log.e(TAG, "Cannot create stream, stream rotation is not supported."); 528 return CameraBinderDecorator.INVALID_OPERATION; 529 } 530 int id = ++mSurfaceIdCounter; 531 mSurfaces.put(id, outputConfiguration.getSurface()); 532 return id; 533 } 534 } 535 536 @Override createInputStream(int width, int height, int format)537 public int createInputStream(int width, int height, int format) { 538 Log.e(TAG, "creating input stream is not supported on legacy devices"); 539 return CameraBinderDecorator.INVALID_OPERATION; 540 } 541 542 @Override getInputSurface( Surface surface)543 public int getInputSurface(/*out*/ Surface surface) { 544 Log.e(TAG, "getting input surface is not supported on legacy devices"); 545 return CameraBinderDecorator.INVALID_OPERATION; 546 } 547 548 @Override createDefaultRequest(int templateId, CameraMetadataNative request)549 public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) { 550 if (DEBUG) { 551 Log.d(TAG, "createDefaultRequest called."); 552 } 553 if (mLegacyDevice.isClosed()) { 554 Log.e(TAG, "Cannot create default request, device has been closed."); 555 return CameraBinderDecorator.ENODEV; 556 } 557 558 CameraMetadataNative template; 559 try { 560 template = 561 LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId); 562 } catch (IllegalArgumentException e) { 563 Log.e(TAG, "createDefaultRequest - invalid templateId specified"); 564 return CameraBinderDecorator.BAD_VALUE; 565 } 566 567 request.swap(template); 568 return CameraBinderDecorator.NO_ERROR; 569 } 570 571 @Override getCameraInfo( CameraMetadataNative info)572 public int getCameraInfo(/*out*/CameraMetadataNative info) { 573 if (DEBUG) { 574 Log.d(TAG, "getCameraInfo called."); 575 } 576 // TODO: implement getCameraInfo. 577 Log.e(TAG, "getCameraInfo unimplemented."); 578 return CameraBinderDecorator.NO_ERROR; 579 } 580 581 @Override waitUntilIdle()582 public int waitUntilIdle() throws RemoteException { 583 if (DEBUG) { 584 Log.d(TAG, "waitUntilIdle called."); 585 } 586 if (mLegacyDevice.isClosed()) { 587 Log.e(TAG, "Cannot wait until idle, device has been closed."); 588 return CameraBinderDecorator.ENODEV; 589 } 590 591 synchronized(mConfigureLock) { 592 if (mConfiguring) { 593 Log.e(TAG, "Cannot wait until idle, configuration change in progress."); 594 return CameraBinderDecorator.INVALID_OPERATION; 595 } 596 } 597 mLegacyDevice.waitUntilIdle(); 598 return CameraBinderDecorator.NO_ERROR; 599 } 600 601 @Override flush( LongParcelable lastFrameNumber)602 public int flush(/*out*/LongParcelable lastFrameNumber) { 603 if (DEBUG) { 604 Log.d(TAG, "flush called."); 605 } 606 if (mLegacyDevice.isClosed()) { 607 Log.e(TAG, "Cannot flush, device has been closed."); 608 return CameraBinderDecorator.ENODEV; 609 } 610 611 synchronized(mConfigureLock) { 612 if (mConfiguring) { 613 Log.e(TAG, "Cannot flush, configuration change in progress."); 614 return CameraBinderDecorator.INVALID_OPERATION; 615 } 616 } 617 long lastFrame = mLegacyDevice.flush(); 618 if (lastFrameNumber != null) { 619 lastFrameNumber.setNumber(lastFrame); 620 } 621 return CameraBinderDecorator.NO_ERROR; 622 } 623 prepare(int streamId)624 public int prepare(int streamId) { 625 if (DEBUG) { 626 Log.d(TAG, "prepare called."); 627 } 628 if (mLegacyDevice.isClosed()) { 629 Log.e(TAG, "Cannot prepare stream, device has been closed."); 630 return CameraBinderDecorator.ENODEV; 631 } 632 633 // LEGACY doesn't support actual prepare, just signal success right away 634 mCameraCallbacks.onPrepared(streamId); 635 636 return CameraBinderDecorator.NO_ERROR; 637 } 638 tearDown(int streamId)639 public int tearDown(int streamId) { 640 if (DEBUG) { 641 Log.d(TAG, "tearDown called."); 642 } 643 if (mLegacyDevice.isClosed()) { 644 Log.e(TAG, "Cannot tear down stream, device has been closed."); 645 return CameraBinderDecorator.ENODEV; 646 } 647 648 // LEGACY doesn't support actual teardown, so just a no-op 649 650 return CameraBinderDecorator.NO_ERROR; 651 } 652 653 @Override asBinder()654 public IBinder asBinder() { 655 // This is solely intended to be used for in-process binding. 656 return null; 657 } 658 } 659