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.utils.CameraBinderDecorator; 30 import android.hardware.camera2.utils.CameraRuntimeException; 31 import android.os.ConditionVariable; 32 import android.os.IBinder; 33 import android.os.Looper; 34 import android.os.Handler; 35 import android.os.HandlerThread; 36 import android.os.Message; 37 import android.os.RemoteException; 38 import android.util.Log; 39 import android.util.SparseArray; 40 import android.view.Surface; 41 42 import java.util.ArrayList; 43 import 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") 60 public 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 CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera, CameraCharacteristics characteristics, CameraLooper cameraInit, CameraCallbackThread cameraCallbacks)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 translateErrorsFromCamera1(int errorCode)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 */ CameraLooper(int cameraId)123 public CameraLooper(int cameraId) { 124 mCameraId = cameraId; 125 126 mThread = new Thread(this); 127 mThread.start(); 128 } 129 getCamera()130 public Camera getCamera() { 131 return mCamera; 132 } 133 134 @Override run()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 close()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 */ waitForOpen(int timeoutMs)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 CameraCallbackThread(ICameraDeviceCallbacks callbacks)210 public CameraCallbackThread(ICameraDeviceCallbacks callbacks) { 211 mCallbacks = callbacks; 212 213 mHandlerThread = new HandlerThread("LegacyCameraCallback"); 214 mHandlerThread.start(); 215 } 216 close()217 public void close() { 218 mHandlerThread.quitSafely(); 219 } 220 221 @Override onDeviceError(final int errorCode, final CaptureResultExtras resultExtras)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 onDeviceIdle()230 public void onDeviceIdle() { 231 Message msg = getHandler().obtainMessage(CAMERA_IDLE); 232 getHandler().sendMessage(msg); 233 } 234 235 @Override onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp)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 onResultReceived(final CameraMetadataNative result, final CaptureResultExtras resultExtras)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 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 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 connectBinderShim(ICameraDeviceCallbacks callbacks, int cameraId)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 Camera.Parameters legacyParameters = null; 340 try { 341 legacyParameters = legacyCamera.getParameters(); 342 } catch (RuntimeException e) { 343 throw new CameraRuntimeException(CameraAccessException.CAMERA_ERROR, 344 "Unable to get initial parameters", e); 345 } 346 347 CameraCharacteristics characteristics = 348 LegacyMetadataMapper.createCharacteristics(legacyParameters, info); 349 LegacyCameraDevice device = new LegacyCameraDevice( 350 cameraId, legacyCamera, characteristics, threadCallbacks); 351 return new CameraDeviceUserShim(cameraId, device, characteristics, init, threadCallbacks); 352 } 353 354 @Override disconnect()355 public void disconnect() { 356 if (DEBUG) { 357 Log.d(TAG, "disconnect called."); 358 } 359 360 if (mLegacyDevice.isClosed()) { 361 Log.w(TAG, "Cannot disconnect, device has already been closed."); 362 } 363 364 try { 365 mLegacyDevice.close(); 366 } finally { 367 mCameraInit.close(); 368 mCameraCallbacks.close(); 369 } 370 } 371 372 @Override submitRequest(CaptureRequest request, boolean streaming, LongParcelable lastFrameNumber)373 public int submitRequest(CaptureRequest request, boolean streaming, 374 /*out*/LongParcelable lastFrameNumber) { 375 if (DEBUG) { 376 Log.d(TAG, "submitRequest called."); 377 } 378 if (mLegacyDevice.isClosed()) { 379 Log.e(TAG, "Cannot submit request, device has been closed."); 380 return CameraBinderDecorator.ENODEV; 381 } 382 383 synchronized(mConfigureLock) { 384 if (mConfiguring) { 385 Log.e(TAG, "Cannot submit request, configuration change in progress."); 386 return CameraBinderDecorator.INVALID_OPERATION; 387 } 388 } 389 return mLegacyDevice.submitRequest(request, streaming, lastFrameNumber); 390 } 391 392 @Override submitRequestList(List<CaptureRequest> request, boolean streaming, LongParcelable lastFrameNumber)393 public int submitRequestList(List<CaptureRequest> request, boolean streaming, 394 /*out*/LongParcelable lastFrameNumber) { 395 if (DEBUG) { 396 Log.d(TAG, "submitRequestList called."); 397 } 398 if (mLegacyDevice.isClosed()) { 399 Log.e(TAG, "Cannot submit request list, device has been closed."); 400 return CameraBinderDecorator.ENODEV; 401 } 402 403 synchronized(mConfigureLock) { 404 if (mConfiguring) { 405 Log.e(TAG, "Cannot submit request, configuration change in progress."); 406 return CameraBinderDecorator.INVALID_OPERATION; 407 } 408 } 409 return mLegacyDevice.submitRequestList(request, streaming, lastFrameNumber); 410 } 411 412 @Override cancelRequest(int requestId, LongParcelable lastFrameNumber)413 public int cancelRequest(int requestId, /*out*/LongParcelable lastFrameNumber) { 414 if (DEBUG) { 415 Log.d(TAG, "cancelRequest called."); 416 } 417 if (mLegacyDevice.isClosed()) { 418 Log.e(TAG, "Cannot cancel request, device has been closed."); 419 return CameraBinderDecorator.ENODEV; 420 } 421 422 synchronized(mConfigureLock) { 423 if (mConfiguring) { 424 Log.e(TAG, "Cannot cancel request, configuration change in progress."); 425 return CameraBinderDecorator.INVALID_OPERATION; 426 } 427 } 428 long lastFrame = mLegacyDevice.cancelRequest(requestId); 429 lastFrameNumber.setNumber(lastFrame); 430 return CameraBinderDecorator.NO_ERROR; 431 } 432 433 @Override beginConfigure()434 public int beginConfigure() { 435 if (DEBUG) { 436 Log.d(TAG, "beginConfigure called."); 437 } 438 if (mLegacyDevice.isClosed()) { 439 Log.e(TAG, "Cannot begin configure, device has been closed."); 440 return CameraBinderDecorator.ENODEV; 441 } 442 443 synchronized(mConfigureLock) { 444 if (mConfiguring) { 445 Log.e(TAG, "Cannot begin configure, configuration change already in progress."); 446 return CameraBinderDecorator.INVALID_OPERATION; 447 } 448 mConfiguring = true; 449 } 450 return CameraBinderDecorator.NO_ERROR; 451 } 452 453 @Override endConfigure()454 public int endConfigure() { 455 if (DEBUG) { 456 Log.d(TAG, "endConfigure called."); 457 } 458 if (mLegacyDevice.isClosed()) { 459 Log.e(TAG, "Cannot end configure, device has been closed."); 460 return CameraBinderDecorator.ENODEV; 461 } 462 463 ArrayList<Surface> surfaces = null; 464 synchronized(mConfigureLock) { 465 if (!mConfiguring) { 466 Log.e(TAG, "Cannot end configure, no configuration change in progress."); 467 return CameraBinderDecorator.INVALID_OPERATION; 468 } 469 int numSurfaces = mSurfaces.size(); 470 if (numSurfaces > 0) { 471 surfaces = new ArrayList<>(); 472 for (int i = 0; i < numSurfaces; ++i) { 473 surfaces.add(mSurfaces.valueAt(i)); 474 } 475 } 476 mConfiguring = false; 477 } 478 return mLegacyDevice.configureOutputs(surfaces); 479 } 480 481 @Override deleteStream(int streamId)482 public int deleteStream(int streamId) { 483 if (DEBUG) { 484 Log.d(TAG, "deleteStream called."); 485 } 486 if (mLegacyDevice.isClosed()) { 487 Log.e(TAG, "Cannot delete stream, device has been closed."); 488 return CameraBinderDecorator.ENODEV; 489 } 490 491 synchronized(mConfigureLock) { 492 if (!mConfiguring) { 493 Log.e(TAG, "Cannot delete stream, beginConfigure hasn't been called yet."); 494 return CameraBinderDecorator.INVALID_OPERATION; 495 } 496 int index = mSurfaces.indexOfKey(streamId); 497 if (index < 0) { 498 Log.e(TAG, "Cannot delete stream, stream id " + streamId + " doesn't exist."); 499 return CameraBinderDecorator.BAD_VALUE; 500 } 501 mSurfaces.removeAt(index); 502 } 503 return CameraBinderDecorator.NO_ERROR; 504 } 505 506 @Override createStream(int width, int height, int format, Surface surface)507 public int createStream(int width, int height, int format, Surface surface) { 508 if (DEBUG) { 509 Log.d(TAG, "createStream called."); 510 } 511 if (mLegacyDevice.isClosed()) { 512 Log.e(TAG, "Cannot create stream, device has been closed."); 513 return CameraBinderDecorator.ENODEV; 514 } 515 516 synchronized(mConfigureLock) { 517 if (!mConfiguring) { 518 Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet."); 519 return CameraBinderDecorator.INVALID_OPERATION; 520 } 521 int id = ++mSurfaceIdCounter; 522 mSurfaces.put(id, surface); 523 return id; 524 } 525 } 526 527 @Override createDefaultRequest(int templateId, CameraMetadataNative request)528 public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) { 529 if (DEBUG) { 530 Log.d(TAG, "createDefaultRequest called."); 531 } 532 if (mLegacyDevice.isClosed()) { 533 Log.e(TAG, "Cannot create default request, device has been closed."); 534 return CameraBinderDecorator.ENODEV; 535 } 536 537 CameraMetadataNative template; 538 try { 539 template = 540 LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId); 541 } catch (IllegalArgumentException e) { 542 Log.e(TAG, "createDefaultRequest - invalid templateId specified"); 543 return CameraBinderDecorator.BAD_VALUE; 544 } 545 546 request.swap(template); 547 return CameraBinderDecorator.NO_ERROR; 548 } 549 550 @Override getCameraInfo( CameraMetadataNative info)551 public int getCameraInfo(/*out*/CameraMetadataNative info) { 552 if (DEBUG) { 553 Log.d(TAG, "getCameraInfo called."); 554 } 555 // TODO: implement getCameraInfo. 556 Log.e(TAG, "getCameraInfo unimplemented."); 557 return CameraBinderDecorator.NO_ERROR; 558 } 559 560 @Override waitUntilIdle()561 public int waitUntilIdle() throws RemoteException { 562 if (DEBUG) { 563 Log.d(TAG, "waitUntilIdle called."); 564 } 565 if (mLegacyDevice.isClosed()) { 566 Log.e(TAG, "Cannot wait until idle, device has been closed."); 567 return CameraBinderDecorator.ENODEV; 568 } 569 570 synchronized(mConfigureLock) { 571 if (mConfiguring) { 572 Log.e(TAG, "Cannot wait until idle, configuration change in progress."); 573 return CameraBinderDecorator.INVALID_OPERATION; 574 } 575 } 576 mLegacyDevice.waitUntilIdle(); 577 return CameraBinderDecorator.NO_ERROR; 578 } 579 580 @Override flush( LongParcelable lastFrameNumber)581 public int flush(/*out*/LongParcelable lastFrameNumber) { 582 if (DEBUG) { 583 Log.d(TAG, "flush called."); 584 } 585 if (mLegacyDevice.isClosed()) { 586 Log.e(TAG, "Cannot flush, device has been closed."); 587 return CameraBinderDecorator.ENODEV; 588 } 589 590 synchronized(mConfigureLock) { 591 if (mConfiguring) { 592 Log.e(TAG, "Cannot flush, configuration change in progress."); 593 return CameraBinderDecorator.INVALID_OPERATION; 594 } 595 } 596 long lastFrame = mLegacyDevice.flush(); 597 if (lastFrameNumber != null) { 598 lastFrameNumber.setNumber(lastFrame); 599 } 600 return CameraBinderDecorator.NO_ERROR; 601 } 602 603 @Override asBinder()604 public IBinder asBinder() { 605 // This is solely intended to be used for in-process binding. 606 return null; 607 } 608 } 609