AndroidCamera2AgentImpl.java revision 7e0d39bf7b6e0f0df606e3f6c15f673f70fed3f7
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 17package com.android.ex.camera2.portability; 18 19import android.annotation.TargetApi; 20import android.content.Context; 21import android.graphics.SurfaceTexture; 22import android.hardware.camera2.CameraAccessException; 23import android.hardware.camera2.CameraCaptureSession; 24import android.hardware.camera2.CameraCharacteristics; 25import android.hardware.camera2.CameraDevice; 26import android.hardware.camera2.CameraManager; 27import android.hardware.camera2.CaptureRequest; 28import android.os.Build; 29import android.os.Handler; 30import android.os.HandlerThread; 31import android.os.Looper; 32import android.os.Message; 33import android.view.Surface; 34 35import com.android.ex.camera2.portability.debug.Log; 36 37import java.util.ArrayList; 38import java.util.Arrays; 39import java.util.HashSet; 40import java.util.List; 41import java.util.Set; 42 43/** 44 * A class to implement {@link CameraAgent} of the Android camera2 framework. 45 */ 46class AndroidCamera2AgentImpl extends CameraAgent { 47 private static final Log.Tag TAG = new Log.Tag("AndCam2AgntImp"); 48 49 private final Camera2Handler mCameraHandler; 50 private final HandlerThread mCameraHandlerThread; 51 private final CameraStateHolder mCameraState; 52 private final DispatchThread mDispatchThread; 53 private final CameraManager mCameraManager; 54 55 /** 56 * Number of camera devices. The length of {@code mCameraDevices} does not reveal this 57 * information because that list may contain since-invalidated indices. 58 */ 59 private int mNumCameraDevices; 60 61 /** 62 * Transformation between integral camera indices and the {@link java.lang.String} indices used 63 * by the underlying API. Note that devices may disappear because they've been disconnected or 64 * have otherwise gone offline. Because we need to keep the meanings of whatever indices we 65 * expose stable, we cannot simply remove them in such a case; instead, we insert {@code null}s 66 * to invalidate any such indices. Whenever new devices appear, they are appended to the end of 67 * the list, and thereby assigned the lowest index that has never yet been used. 68 */ 69 private final List<String> mCameraDevices; 70 71 AndroidCamera2AgentImpl(Context context) { 72 mCameraHandlerThread = new HandlerThread("Camera2 Handler Thread"); 73 mCameraHandlerThread.start(); 74 mCameraHandler = new Camera2Handler(mCameraHandlerThread.getLooper()); 75 mCameraState = new AndroidCamera2StateHolder(); 76 mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread); 77 mDispatchThread.start(); 78 mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 79 80 mNumCameraDevices = 0; 81 mCameraDevices = new ArrayList<String>(); 82 updateCameraDevices(); 83 } 84 85 /** 86 * Updates the camera device index assignments stored in {@link mCameraDevices}, without 87 * reappropriating any currently-assigned index. 88 * @return Whether the operation was successful 89 */ 90 private boolean updateCameraDevices() { 91 try { 92 String[] currentCameraDevices = mCameraManager.getCameraIdList(); 93 Set<String> currentSet = new HashSet<String>(Arrays.asList(currentCameraDevices)); 94 95 // Invalidate the indices assigned to any camera devices that are no longer present 96 for (int index = 0; index < mCameraDevices.size(); ++index) { 97 if (!currentSet.contains(mCameraDevices.get(index))) { 98 mCameraDevices.set(index, null); 99 --mNumCameraDevices; 100 } 101 } 102 103 // Assign fresh indices to any new camera devices 104 currentSet.removeAll(mCameraDevices); // The devices we didn't know about 105 for (String device : currentCameraDevices) { 106 if (currentSet.contains(device)) { 107 mCameraDevices.add(device); 108 ++mNumCameraDevices; 109 } 110 } 111 112 return true; 113 } catch (CameraAccessException ex) { 114 Log.e(TAG, "Could not get device listing from camera subsystem", ex); 115 return false; 116 } 117 } 118 119 // TODO: Implement 120 @Override 121 public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback, 122 Handler handler) {} 123 124 // TODO: Implement 125 @Override 126 public void recycle() {} 127 128 // TODO: Some indices may now be invalid; ensure everyone can handle that and update the docs 129 @Override 130 public CameraDeviceInfo getCameraDeviceInfo() { 131 updateCameraDevices(); 132 return new AndroidCamera2DeviceInfo(mCameraManager, mCameraDevices.toArray(new String[0]), 133 mNumCameraDevices); 134 } 135 136 @Override 137 protected Handler getCameraHandler() { 138 return mCameraHandler; 139 } 140 141 @Override 142 protected DispatchThread getDispatchThread() { 143 return mDispatchThread; 144 } 145 146 private class Camera2Handler extends HistoryHandler { 147 private CameraOpenCallback mOpenCallback; 148 private int mCameraIndex; 149 private String mCameraId; 150 private CameraDevice mCamera; 151 private AndroidCamera2ProxyImpl mCameraProxy; 152 private CaptureRequest.Builder mPreviewRequestBuilder; 153 private Size mPreviewSize; 154 private Size mPhotoSize; 155 private SurfaceTexture mPreviewTexture; 156 private Surface mPreviewSurface; 157 private CameraCaptureSession mPreviewSession; 158 159 Camera2Handler(Looper looper) { 160 super(looper); 161 } 162 163 @Override 164 public void handleMessage(final Message msg) { 165 super.handleMessage(msg); 166 try { 167 switch(msg.what) { 168 case CameraActions.OPEN_CAMERA: 169 case CameraActions.RECONNECT: { 170 CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj; 171 int cameraIndex = msg.arg1; 172 173 if (mCameraState.getState() != AndroidCamera2StateHolder.CAMERA_UNOPENED) { 174 openCallback.onDeviceOpenedAlready(cameraIndex, 175 generateHistoryString(cameraIndex)); 176 break; 177 } 178 179 mOpenCallback = openCallback; 180 mCameraIndex = cameraIndex; 181 mCameraId = mCameraDevices.get(mCameraIndex); 182 183 if (mCameraId == null) { 184 mOpenCallback.onCameraDisabled(msg.arg1); 185 break; 186 } 187 mCameraManager.openCamera(mCameraId, mCameraDeviceStateListener, this); 188 189 break; 190 } 191 192 case CameraActions.RELEASE: { 193 if (mCameraState.getState() == AndroidCamera2StateHolder.CAMERA_UNOPENED) { 194 Log.e(TAG, "Ignoring release at inappropriate time"); 195 } 196 197 if (mCameraState.getState() == 198 AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) { 199 closePreviewSession(); 200 } 201 if (mCamera != null) { 202 mCamera.close(); 203 mCamera = null; 204 } 205 mCameraProxy = null; 206 mPreviewRequestBuilder = null; 207 if (mPreviewSurface != null) { 208 mPreviewSurface.release(); 209 mPreviewSurface = null; 210 } 211 mPreviewTexture = null; 212 mPreviewSize = null; 213 mPhotoSize = null; 214 mCameraIndex = 0; 215 mCameraId = null; 216 mCameraState.setState(AndroidCamera2StateHolder.CAMERA_UNOPENED); 217 break; 218 } 219 220 /*case CameraActions.UNLOCK: { 221 break; 222 } 223 224 case CameraActions.LOCK: { 225 break; 226 }*/ 227 228 case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: { 229 setPreviewTexture((SurfaceTexture) msg.obj); 230 break; 231 } 232 233 case CameraActions.START_PREVIEW_ASYNC: { 234 if (mCameraState.getState() != 235 AndroidCamera2StateHolder.CAMERA_PREVIEW_READY || 236 mPreviewSession == null) { 237 // TODO: Provide better feedback here? 238 Log.e(TAG, "Refusing to start preview at inappropriate time"); 239 break; 240 } 241 242 final CameraStartPreviewCallbackForward cbForward = 243 (CameraStartPreviewCallbackForward) msg.obj; 244 CameraCaptureSession.CaptureListener cbDispatcher = null; 245 if (cbForward != null) { 246 cbDispatcher = new CameraCaptureSession.CaptureListener() { 247 @Override 248 public void onCaptureStarted(CameraCaptureSession session, 249 CaptureRequest request, 250 long timestamp) { 251 cbForward.onPreviewStarted(); 252 }}; 253 } 254 mPreviewSession.setRepeatingRequest(mPreviewRequestBuilder.build(), 255 cbDispatcher/*listener*/, this/*handler*/); 256 mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE); 257 break; 258 } 259 260 case CameraActions.STOP_PREVIEW: { 261 if (mCameraState.getState() != 262 AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE || 263 mPreviewSession == null) { 264 Log.e(TAG, "Refusing to stop preview at inappropriate time"); 265 } 266 267 mPreviewSession.stopRepeating(); 268 mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); 269 break; 270 } 271 272 /*case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: { 273 break; 274 } 275 276 case CameraActions.ADD_CALLBACK_BUFFER: { 277 break; 278 } 279 280 case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: { 281 break; 282 } 283 284 case CameraActions.SET_PREVIEW_CALLBACK: { 285 break; 286 } 287 288 case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: { 289 break; 290 } 291 292 case CameraActions.SET_PARAMETERS: { 293 break; 294 } 295 296 case CameraActions.GET_PARAMETERS: { 297 break; 298 } 299 300 case CameraActions.REFRESH_PARAMETERS: { 301 break; 302 }*/ 303 304 case CameraActions.APPLY_SETTINGS: { 305 CameraSettings settings = (CameraSettings) msg.obj; 306 applyToRequest(settings); 307 break; 308 } 309 310 /*case CameraActions.AUTO_FOCUS: { 311 break; 312 } 313 314 case CameraActions.CANCEL_AUTO_FOCUS: { 315 break; 316 } 317 318 case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: { 319 break; 320 } 321 322 case CameraActions.SET_ZOOM_CHANGE_LISTENER: { 323 break; 324 } 325 326 case CameraActions.SET_FACE_DETECTION_LISTENER: { 327 break; 328 } 329 330 case CameraActions.START_FACE_DETECTION: { 331 break; 332 } 333 334 case CameraActions.STOP_FACE_DETECTION: { 335 break; 336 } 337 338 case CameraActions.SET_ERROR_CALLBACK: { 339 break; 340 } 341 342 case CameraActions.ENABLE_SHUTTER_SOUND: { 343 break; 344 } 345 346 case CameraActions.SET_DISPLAY_ORIENTATION: { 347 break; 348 } 349 350 case CameraActions.CAPTURE_PHOTO: { 351 break; 352 }*/ 353 354 default: { 355 // TODO: Rephrase once everything has been implemented 356 throw new RuntimeException("Unimplemented CameraProxy message=" + msg.what); 357 } 358 } 359 } catch (final Exception ex) { 360 if (msg.what != CameraActions.RELEASE && mCamera != null) { 361 mCamera.close(); 362 mCamera = null; 363 } else if (mCamera == null) { 364 if (msg.what == CameraActions.OPEN_CAMERA) { 365 if (mOpenCallback != null) { 366 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 367 generateHistoryString(mCameraIndex)); 368 } 369 } else { 370 Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null"); 371 } 372 return; 373 } 374 375 if (ex instanceof RuntimeException) { 376 post(new Runnable() { 377 @Override 378 public void run() { 379 sCameraExceptionCallback.onCameraException((RuntimeException) ex); 380 }}); 381 } 382 } 383 } 384 385 public CameraSettings buildSettings(AndroidCamera2Capabilities caps) { 386 return new AndroidCamera2Settings(caps, mPreviewRequestBuilder, mPreviewSize, 387 mPhotoSize); 388 } 389 390 // TODO: Finish implementation to add support for all settings 391 private void applyToRequest(CameraSettings settings) { 392 // TODO: If invoked when in PREVIEW_READY state, a new preview size will not take effect 393 AndroidCamera2Capabilities.IntegralStringifier intifier = 394 mCameraProxy.getSpecializedCapabilities().getIntegralStringifier(); 395 mPreviewSize = settings.getCurrentPreviewSize(); 396 mPhotoSize = settings.getCurrentPhotoSize(); 397 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, 398 intifier.intify(settings.getCurrentFocusMode())); 399 if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) { 400 mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, 401 intifier.intify(settings.getCurrentFlashMode())); 402 } 403 if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) { 404 mPreviewRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, 405 intifier.intify(settings.getCurrentSceneMode())); 406 } 407 408 // If we're already ready to preview, this doesn't regress our state 409 if (mCameraState.getState() != AndroidCamera2StateHolder.CAMERA_PREVIEW_READY) { 410 mCameraState.setState(AndroidCamera2StateHolder.CAMERA_CONFIGURED); 411 } 412 } 413 414 private void setPreviewTexture(SurfaceTexture surfaceTexture) { 415 // TODO: Must be called after providing a .*Settings populated with sizes 416 // TODO: We don't technically offer a selection of sizes tailored to SurfaceTextures! 417 418 // TODO: Handle this error condition with a callback or exception 419 if (mCameraState.getState() != AndroidCamera2StateHolder.CAMERA_CONFIGURED) { 420 Log.e(TAG, "Ignoring texture setting at inappropriate time"); 421 return; 422 } 423 424 // Avoid initializing another capture session unless we absolutely have to 425 if (surfaceTexture == mPreviewTexture) { 426 Log.i(TAG, "Optimizing out redundant preview texture setting"); 427 return; 428 } 429 430 if (mPreviewSession != null) { 431 closePreviewSession(); 432 } 433 434 mPreviewTexture = surfaceTexture; 435 surfaceTexture.setDefaultBufferSize(mPreviewSize.width(), mPreviewSize.height()); 436 437 if (mPreviewSurface != null) { 438 mPreviewRequestBuilder.removeTarget(mPreviewSurface); 439 mPreviewSurface.release(); 440 } 441 mPreviewSurface = new Surface(surfaceTexture); 442 mPreviewRequestBuilder.addTarget(mPreviewSurface); 443 444 try { 445 mCamera.createCaptureSession(Arrays.asList(mPreviewSurface), 446 mCameraPreviewStateListener, this); 447 } catch (CameraAccessException ex) { 448 Log.e(TAG, "Failed to create camera capture session", ex); 449 } 450 } 451 452 private void closePreviewSession() { 453 try { 454 mPreviewSession.abortCaptures(); 455 mPreviewSession = null; 456 } catch (CameraAccessException ex) { 457 Log.e(TAG, "Failed to close existing camera capture session", ex); 458 } 459 mCameraState.setState(AndroidCamera2StateHolder.CAMERA_CONFIGURED); 460 } 461 462 private CameraDevice.StateListener mCameraDeviceStateListener = 463 new CameraDevice.StateListener() { 464 @Override 465 public void onOpened(CameraDevice camera) { 466 mCamera = camera; 467 if (mOpenCallback != null) { 468 try { 469 CameraCharacteristics props = 470 mCameraManager.getCameraCharacteristics(mCameraId); 471 mCameraProxy = new AndroidCamera2ProxyImpl(mCameraIndex, mCamera, 472 getCameraDeviceInfo().getCharacteristics(mCameraIndex), props); 473 mPreviewRequestBuilder = 474 camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 475 mCameraState.setState(AndroidCamera2StateHolder.CAMERA_UNCONFIGURED); 476 mOpenCallback.onCameraOpened(mCameraProxy); 477 } catch (CameraAccessException ex) { 478 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 479 generateHistoryString(mCameraIndex)); 480 } 481 } 482 } 483 484 @Override 485 public void onDisconnected(CameraDevice camera) { 486 Log.w(TAG, "Camera device '" + mCameraIndex + "' was disconnected"); 487 } 488 489 @Override 490 public void onError(CameraDevice camera, int error) { 491 Log.e(TAG, "Camera device '" + mCameraIndex + "' encountered error code '" + 492 error + '\''); 493 if (mOpenCallback != null) { 494 mOpenCallback.onDeviceOpenFailure(mCameraIndex, 495 generateHistoryString(mCameraIndex)); 496 } 497 }}; 498 499 private CameraCaptureSession.StateListener mCameraPreviewStateListener = 500 new CameraCaptureSession.StateListener() { 501 @Override 502 public void onConfigured(CameraCaptureSession session) { 503 mPreviewSession = session; 504 mCameraState.setState(AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); 505 } 506 507 @Override 508 public void onConfigureFailed(CameraCaptureSession session) { 509 // TODO: Invoke a callback 510 Log.e(TAG, "Failed to configure the camera for capture"); 511 }}; 512 } 513 514 private class AndroidCamera2ProxyImpl extends CameraAgent.CameraProxy { 515 private final int mCameraIndex; 516 private final CameraDevice mCamera; 517 private final CameraDeviceInfo.Characteristics mCharacteristics; 518 private final AndroidCamera2Capabilities mCapabilities; 519 520 public AndroidCamera2ProxyImpl(int cameraIndex, CameraDevice camera, 521 CameraDeviceInfo.Characteristics characteristics, 522 CameraCharacteristics properties) { 523 mCameraIndex = cameraIndex; 524 mCamera = camera; 525 mCharacteristics = characteristics; 526 mCapabilities = new AndroidCamera2Capabilities(properties); 527 } 528 529 // TODO: Implement 530 @Override 531 public android.hardware.Camera getCamera() { return null; } 532 533 @Override 534 public int getCameraId() { 535 return mCameraIndex; 536 } 537 538 @Override 539 public CameraDeviceInfo.Characteristics getCharacteristics() { 540 return mCharacteristics; 541 } 542 543 @Override 544 public CameraCapabilities getCapabilities() { 545 return mCapabilities; 546 } 547 548 private AndroidCamera2Capabilities getSpecializedCapabilities() { 549 return mCapabilities; 550 } 551 552 // TODO: Implement 553 @Override 554 public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb) {} 555 556 // TODO: Implement 557 @Override 558 public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb) {} 559 560 // TODO: Implement 561 @Override 562 public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb) 563 {} 564 565 // TODO: Implement 566 @Override 567 public void autoFocus(Handler handler, CameraAFCallback cb) {} 568 569 // TODO: Remove this method override once we handle this message 570 @Override 571 public void cancelAutoFocus() {} 572 573 // TODO: Implement 574 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 575 @Override 576 public void setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb) {} 577 578 // TODO: Implement 579 @Override 580 public void takePicture(Handler handler, 581 CameraShutterCallback shutter, 582 CameraPictureCallback raw, 583 CameraPictureCallback postview, 584 CameraPictureCallback jpeg) {} 585 586 // TODO: Remove this method override once we handle the message 587 @Override 588 public void setDisplayOrientation(int degrees) {} 589 590 // TODO: Implement 591 @Override 592 public void setZoomChangeListener(android.hardware.Camera.OnZoomChangeListener listener) {} 593 594 // TODO: Implement 595 @Override 596 public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback) 597 {} 598 599 // TODO: Remove this method override once we handle this message 600 @Override 601 public void startFaceDetection() {} 602 603 // TODO: Implement 604 @Override 605 public void setErrorCallback(Handler handler, CameraErrorCallback cb) {} 606 607 // TODO: Implement 608 @Override 609 public void setParameters(android.hardware.Camera.Parameters params) {} 610 611 // TODO: Implement 612 @Override 613 public android.hardware.Camera.Parameters getParameters() { return null; } 614 615 @Override 616 public CameraSettings getSettings() { 617 return mCameraHandler.buildSettings(mCapabilities); 618 } 619 620 @Override 621 public boolean applySettings(CameraSettings settings) { 622 return applySettingsHelper(settings, AndroidCamera2StateHolder.CAMERA_UNCONFIGURED | 623 AndroidCamera2StateHolder.CAMERA_CONFIGURED | 624 AndroidCamera2StateHolder.CAMERA_PREVIEW_READY); 625 } 626 627 // TODO: Implement 628 @Override 629 public String dumpDeviceSettings() { return null; } 630 631 @Override 632 public Handler getCameraHandler() { 633 return AndroidCamera2AgentImpl.this.getCameraHandler(); 634 } 635 636 @Override 637 public DispatchThread getDispatchThread() { 638 return AndroidCamera2AgentImpl.this.getDispatchThread(); 639 } 640 641 @Override 642 public CameraStateHolder getCameraState() { 643 return mCameraState; 644 } 645 } 646 647 private static class AndroidCamera2StateHolder extends CameraStateHolder { 648 // Usage flow: openCamera() -> applySettings() -> setPreviewTexture() -> startPreview() 649 /* Camera states */ 650 /** No camera device is opened. */ 651 public static final int CAMERA_UNOPENED = 1; 652 /** A camera is opened, but no settings have been provided. */ 653 public static final int CAMERA_UNCONFIGURED = 2; 654 /** The open camera has been configured by providing it with settings. */ 655 public static final int CAMERA_CONFIGURED = 3; 656 /** A capture session is ready to stream a preview, but still has no repeating request. */ 657 public static final int CAMERA_PREVIEW_READY = 4; 658 /** A preview is currently being streamed. */ 659 public static final int CAMERA_PREVIEW_ACTIVE = 5; 660 661 public AndroidCamera2StateHolder() { 662 this(CAMERA_UNOPENED); 663 } 664 665 public AndroidCamera2StateHolder(int state) { 666 super(state); 667 } 668 } 669 670 private static class AndroidCamera2DeviceInfo implements CameraDeviceInfo { 671 private final CameraManager mCameraManager; 672 private final String[] mCameraIds; 673 private final int mNumberOfCameras; 674 private final int mFirstBackCameraId; 675 private final int mFirstFrontCameraId; 676 677 public AndroidCamera2DeviceInfo(CameraManager cameraManager, 678 String[] cameraIds, int numberOfCameras) { 679 mCameraManager = cameraManager; 680 mCameraIds = cameraIds; 681 mNumberOfCameras = numberOfCameras; 682 683 int firstBackId = NO_DEVICE; 684 int firstFrontId = NO_DEVICE; 685 for (int id = 0; id < cameraIds.length; ++id) { 686 try { 687 int lensDirection = cameraManager.getCameraCharacteristics(cameraIds[id]) 688 .get(CameraCharacteristics.LENS_FACING); 689 if (firstBackId == NO_DEVICE && 690 lensDirection == CameraCharacteristics.LENS_FACING_BACK) { 691 firstBackId = id; 692 } 693 if (firstFrontId == NO_DEVICE && 694 lensDirection == CameraCharacteristics.LENS_FACING_FRONT) { 695 firstFrontId = id; 696 } 697 } catch (CameraAccessException ex) { 698 Log.w(TAG, "Couldn't get characteristics of camera '" + id + "'", ex); 699 } 700 } 701 mFirstBackCameraId = firstBackId; 702 mFirstFrontCameraId = firstFrontId; 703 } 704 705 @Override 706 public Characteristics getCharacteristics(int cameraId) { 707 String actualId = mCameraIds[cameraId]; 708 try { 709 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(actualId); 710 return new AndroidCharacteristics2(info); 711 } catch (CameraAccessException ex) { 712 return null; 713 } 714 } 715 716 @Override 717 public int getNumberOfCameras() { 718 return mNumberOfCameras; 719 } 720 721 @Override 722 public int getFirstBackCameraId() { 723 return mFirstBackCameraId; 724 } 725 726 @Override 727 public int getFirstFrontCameraId() { 728 return mFirstFrontCameraId; 729 } 730 731 private static class AndroidCharacteristics2 implements Characteristics { 732 private CameraCharacteristics mCameraInfo; 733 734 AndroidCharacteristics2(CameraCharacteristics cameraInfo) { 735 mCameraInfo = cameraInfo; 736 } 737 738 @Override 739 public boolean isFacingBack() { 740 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 741 .equals(CameraCharacteristics.LENS_FACING_BACK); 742 } 743 744 @Override 745 public boolean isFacingFront() { 746 return mCameraInfo.get(CameraCharacteristics.LENS_FACING) 747 .equals(CameraCharacteristics.LENS_FACING_FRONT); 748 } 749 750 @Override 751 public int getSensorOrientation() { 752 return mCameraInfo.get(CameraCharacteristics.SENSOR_ORIENTATION); 753 } 754 755 @Override 756 public boolean canDisableShutterSound() { 757 // The new API doesn't support this operation, so don't encourage people to try it. 758 // TODO: What kind of assumptions have callers made about this result's meaning? 759 return false; 760 } 761 } 762 } 763 764 private static final CameraExceptionCallback sCameraExceptionCallback = 765 new CameraExceptionCallback() { 766 @Override 767 public synchronized void onCameraException(RuntimeException e) { 768 throw e; 769 } 770 }; 771} 772