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