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