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