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