CameraManager.java revision 5d2d7788f1759b0f3d2c057af0b3ea61b0354fee
1/* 2 * Copyright (C) 2013 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 android.hardware.camera2; 18 19import android.annotation.RequiresPermission; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.content.Context; 23import android.hardware.ICameraService; 24import android.hardware.ICameraServiceListener; 25import android.hardware.CameraInfo; 26import android.hardware.camera2.impl.CameraMetadataNative; 27import android.hardware.camera2.legacy.CameraDeviceUserShim; 28import android.hardware.camera2.legacy.LegacyMetadataMapper; 29import android.os.IBinder; 30import android.os.Binder; 31import android.os.DeadObjectException; 32import android.os.Handler; 33import android.os.Looper; 34import android.os.RemoteException; 35import android.os.ServiceManager; 36import android.os.ServiceSpecificException; 37import android.util.Log; 38import android.util.ArrayMap; 39 40import java.util.ArrayList; 41 42/** 43 * <p>A system service manager for detecting, characterizing, and connecting to 44 * {@link CameraDevice CameraDevices}.</p> 45 * 46 * <p>You can get an instance of this class by calling 47 * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p> 48 * 49 * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre> 50 * 51 * <p>For more details about communicating with camera devices, read the Camera 52 * developer guide or the {@link android.hardware.camera2 camera2} 53 * package documentation.</p> 54 */ 55public final class CameraManager { 56 57 private static final String TAG = "CameraManager"; 58 private final boolean DEBUG = false; 59 60 private static final int USE_CALLING_UID = -1; 61 62 @SuppressWarnings("unused") 63 private static final int API_VERSION_1 = 1; 64 private static final int API_VERSION_2 = 2; 65 66 private static final int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0; 67 private static final int CAMERA_TYPE_ALL = 1; 68 69 private ArrayList<String> mDeviceIdList; 70 71 private final Context mContext; 72 private final Object mLock = new Object(); 73 74 /** 75 * @hide 76 */ 77 public CameraManager(Context context) { 78 synchronized(mLock) { 79 mContext = context; 80 } 81 } 82 83 /** 84 * Return the list of currently connected camera devices by identifier, including 85 * cameras that may be in use by other camera API clients. 86 * 87 * <p>Non-removable cameras use integers starting at 0 for their 88 * identifiers, while removable cameras have a unique identifier for each 89 * individual device, even if they are the same model.</p> 90 * 91 * @return The list of currently connected camera devices. 92 */ 93 @NonNull 94 public String[] getCameraIdList() throws CameraAccessException { 95 synchronized (mLock) { 96 // ID list creation handles various known failures in device enumeration, so only 97 // exceptions it'll throw are unexpected, and should be propagated upward. 98 return getOrCreateDeviceIdListLocked().toArray(new String[0]); 99 } 100 } 101 102 /** 103 * Register a callback to be notified about camera device availability. 104 * 105 * <p>Registering the same callback again will replace the handler with the 106 * new one provided.</p> 107 * 108 * <p>The first time a callback is registered, it is immediately called 109 * with the availability status of all currently known camera devices.</p> 110 * 111 * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera 112 * device is opened by any camera API client. As of API level 23, other camera API clients may 113 * still be able to open such a camera device, evicting the existing client if they have higher 114 * priority than the existing client of a camera device. See open() for more details.</p> 115 * 116 * <p>Since this callback will be registered with the camera service, remember to unregister it 117 * once it is no longer needed; otherwise the callback will continue to receive events 118 * indefinitely and it may prevent other resources from being released. Specifically, the 119 * callbacks will be invoked independently of the general activity lifecycle and independently 120 * of the state of individual CameraManager instances.</p> 121 * 122 * @param callback the new callback to send camera availability notices to 123 * @param handler The handler on which the callback should be invoked, or {@code null} to use 124 * the current thread's {@link android.os.Looper looper}. 125 * 126 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has 127 * no looper. 128 */ 129 public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback, 130 @Nullable Handler handler) { 131 if (handler == null) { 132 Looper looper = Looper.myLooper(); 133 if (looper == null) { 134 throw new IllegalArgumentException( 135 "No handler given, and current thread has no looper!"); 136 } 137 handler = new Handler(looper); 138 } 139 140 CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler); 141 } 142 143 /** 144 * Remove a previously-added callback; the callback will no longer receive connection and 145 * disconnection callbacks. 146 * 147 * <p>Removing a callback that isn't registered has no effect.</p> 148 * 149 * @param callback The callback to remove from the notification list 150 */ 151 public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) { 152 CameraManagerGlobal.get().unregisterAvailabilityCallback(callback); 153 } 154 155 /** 156 * Register a callback to be notified about torch mode status. 157 * 158 * <p>Registering the same callback again will replace the handler with the 159 * new one provided.</p> 160 * 161 * <p>The first time a callback is registered, it is immediately called 162 * with the torch mode status of all currently known camera devices with a flash unit.</p> 163 * 164 * <p>Since this callback will be registered with the camera service, remember to unregister it 165 * once it is no longer needed; otherwise the callback will continue to receive events 166 * indefinitely and it may prevent other resources from being released. Specifically, the 167 * callbacks will be invoked independently of the general activity lifecycle and independently 168 * of the state of individual CameraManager instances.</p> 169 * 170 * @param callback The new callback to send torch mode status to 171 * @param handler The handler on which the callback should be invoked, or {@code null} to use 172 * the current thread's {@link android.os.Looper looper}. 173 * 174 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has 175 * no looper. 176 */ 177 public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) { 178 if (handler == null) { 179 Looper looper = Looper.myLooper(); 180 if (looper == null) { 181 throw new IllegalArgumentException( 182 "No handler given, and current thread has no looper!"); 183 } 184 handler = new Handler(looper); 185 } 186 CameraManagerGlobal.get().registerTorchCallback(callback, handler); 187 } 188 189 /** 190 * Remove a previously-added callback; the callback will no longer receive torch mode status 191 * callbacks. 192 * 193 * <p>Removing a callback that isn't registered has no effect.</p> 194 * 195 * @param callback The callback to remove from the notification list 196 */ 197 public void unregisterTorchCallback(@NonNull TorchCallback callback) { 198 CameraManagerGlobal.get().unregisterTorchCallback(callback); 199 } 200 201 /** 202 * <p>Query the capabilities of a camera device. These capabilities are 203 * immutable for a given camera.</p> 204 * 205 * @param cameraId The id of the camera device to query 206 * @return The properties of the given camera 207 * 208 * @throws IllegalArgumentException if the cameraId does not match any 209 * known camera device. 210 * @throws CameraAccessException if the camera device has been disconnected. 211 * 212 * @see #getCameraIdList 213 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 214 */ 215 @NonNull 216 public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId) 217 throws CameraAccessException { 218 CameraCharacteristics characteristics = null; 219 220 synchronized (mLock) { 221 if (!getOrCreateDeviceIdListLocked().contains(cameraId)) { 222 throw new IllegalArgumentException(String.format("Camera id %s does not match any" + 223 " currently connected camera device", cameraId)); 224 } 225 226 int id = Integer.valueOf(cameraId); 227 228 /* 229 * Get the camera characteristics from the camera service directly if it supports it, 230 * otherwise get them from the legacy shim instead. 231 */ 232 233 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 234 if (cameraService == null) { 235 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 236 "Camera service is currently unavailable"); 237 } 238 try { 239 if (!supportsCamera2ApiLocked(cameraId)) { 240 // Legacy backwards compatibility path; build static info from the camera 241 // parameters 242 String parameters = cameraService.getLegacyParameters(id); 243 244 CameraInfo info = cameraService.getCameraInfo(id); 245 246 characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info); 247 } else { 248 // Normal path: Get the camera characteristics directly from the camera service 249 CameraMetadataNative info = cameraService.getCameraCharacteristics(id); 250 251 characteristics = new CameraCharacteristics(info); 252 } 253 } catch (ServiceSpecificException e) { 254 throwAsPublicException(e); 255 } catch (RemoteException e) { 256 // Camera service died - act as if the camera was disconnected 257 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 258 "Camera service is currently unavailable", e); 259 } 260 } 261 return characteristics; 262 } 263 264 /** 265 * Helper for opening a connection to a camera with the given ID. 266 * 267 * @param cameraId The unique identifier of the camera device to open 268 * @param callback The callback for the camera. Must not be null. 269 * @param handler The handler to invoke the callback on. Must not be null. 270 * 271 * @throws CameraAccessException if the camera is disabled by device policy, 272 * too many camera devices are already open, or the cameraId does not match 273 * any currently available camera device. 274 * 275 * @throws SecurityException if the application does not have permission to 276 * access the camera 277 * @throws IllegalArgumentException if callback or handler is null. 278 * @return A handle to the newly-created camera device. 279 * 280 * @see #getCameraIdList 281 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 282 */ 283 private CameraDevice openCameraDeviceUserAsync(String cameraId, 284 CameraDevice.StateCallback callback, Handler handler) 285 throws CameraAccessException { 286 CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); 287 CameraDevice device = null; 288 289 synchronized (mLock) { 290 291 ICameraDeviceUser cameraUser = null; 292 293 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl = 294 new android.hardware.camera2.impl.CameraDeviceImpl( 295 cameraId, 296 callback, 297 handler, 298 characteristics); 299 300 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks(); 301 302 int id; 303 try { 304 id = Integer.parseInt(cameraId); 305 } catch (NumberFormatException e) { 306 throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: " 307 + cameraId); 308 } 309 310 try { 311 if (supportsCamera2ApiLocked(cameraId)) { 312 // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices 313 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 314 if (cameraService == null) { 315 throw new ServiceSpecificException( 316 ICameraService.ERROR_DISCONNECTED, 317 "Camera service is currently unavailable"); 318 } 319 cameraUser = cameraService.connectDevice(callbacks, id, 320 mContext.getOpPackageName(), USE_CALLING_UID); 321 } else { 322 // Use legacy camera implementation for HAL1 devices 323 Log.i(TAG, "Using legacy camera HAL."); 324 cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id); 325 } 326 } catch (ServiceSpecificException e) { 327 if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) { 328 throw new AssertionError("Should've gone down the shim path"); 329 } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE || 330 e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE || 331 e.errorCode == ICameraService.ERROR_DISABLED || 332 e.errorCode == ICameraService.ERROR_DISCONNECTED || 333 e.errorCode == ICameraService.ERROR_INVALID_OPERATION) { 334 // Received one of the known connection errors 335 // The remote camera device cannot be connected to, so 336 // set the local camera to the startup error state 337 deviceImpl.setRemoteFailure(e); 338 339 if (e.errorCode == ICameraService.ERROR_DISABLED || 340 e.errorCode == ICameraService.ERROR_DISCONNECTED || 341 e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) { 342 // Per API docs, these failures call onError and throw 343 throwAsPublicException(e); 344 } 345 } else { 346 // Unexpected failure - rethrow 347 throwAsPublicException(e); 348 } 349 } catch (RemoteException e) { 350 // Camera service died - act as if it's a CAMERA_DISCONNECTED case 351 ServiceSpecificException sse = new ServiceSpecificException( 352 ICameraService.ERROR_DISCONNECTED, 353 "Camera service is currently unavailable"); 354 deviceImpl.setRemoteFailure(sse); 355 throwAsPublicException(sse); 356 } 357 358 // TODO: factor out callback to be non-nested, then move setter to constructor 359 // For now, calling setRemoteDevice will fire initial 360 // onOpened/onUnconfigured callbacks. 361 deviceImpl.setRemoteDevice(cameraUser); 362 device = deviceImpl; 363 } 364 365 return device; 366 } 367 368 /** 369 * Open a connection to a camera with the given ID. 370 * 371 * <p>Use {@link #getCameraIdList} to get the list of available camera 372 * devices. Note that even if an id is listed, open may fail if the device 373 * is disconnected between the calls to {@link #getCameraIdList} and 374 * {@link #openCamera}, or if a higher-priority camera API client begins using the 375 * camera device.</p> 376 * 377 * <p>As of API level 23, devices for which the 378 * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the 379 * device being in use by a lower-priority, background camera API client can still potentially 380 * be opened by calling this method when the calling camera API client has a higher priority 381 * than the current camera API client using this device. In general, if the top, foreground 382 * activity is running within your application process, your process will be given the highest 383 * priority when accessing the camera, and this method will succeed even if the camera device is 384 * in use by another camera API client. Any lower-priority application that loses control of the 385 * camera in this way will receive an 386 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.</p> 387 * 388 * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will 389 * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up 390 * for operation by calling {@link CameraDevice#createCaptureSession} and 391 * {@link CameraDevice#createCaptureRequest}</p> 392 * 393 * <!-- 394 * <p>Since the camera device will be opened asynchronously, any asynchronous operations done 395 * on the returned CameraDevice instance will be queued up until the device startup has 396 * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is 397 * called. The pending operations are then processed in order.</p> 398 * --> 399 * <p>If the camera becomes disconnected during initialization 400 * after this function call returns, 401 * {@link CameraDevice.StateCallback#onDisconnected} with a 402 * {@link CameraDevice} in the disconnected state (and 403 * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p> 404 * 405 * <p>If opening the camera device fails, then the device callback's 406 * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent 407 * calls on the camera device will throw a {@link CameraAccessException}.</p> 408 * 409 * @param cameraId 410 * The unique identifier of the camera device to open 411 * @param callback 412 * The callback which is invoked once the camera is opened 413 * @param handler 414 * The handler on which the callback should be invoked, or 415 * {@code null} to use the current thread's {@link android.os.Looper looper}. 416 * 417 * @throws CameraAccessException if the camera is disabled by device policy, 418 * has been disconnected, or is being used by a higher-priority camera API client. 419 * 420 * @throws IllegalArgumentException if cameraId or the callback was null, 421 * or the cameraId does not match any currently or previously available 422 * camera device. 423 * 424 * @throws SecurityException if the application does not have permission to 425 * access the camera 426 * 427 * @see #getCameraIdList 428 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 429 */ 430 @RequiresPermission(android.Manifest.permission.CAMERA) 431 public void openCamera(@NonNull String cameraId, 432 @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler) 433 throws CameraAccessException { 434 435 if (cameraId == null) { 436 throw new IllegalArgumentException("cameraId was null"); 437 } else if (callback == null) { 438 throw new IllegalArgumentException("callback was null"); 439 } else if (handler == null) { 440 if (Looper.myLooper() != null) { 441 handler = new Handler(); 442 } else { 443 throw new IllegalArgumentException( 444 "Handler argument is null, but no looper exists in the calling thread"); 445 } 446 } 447 448 openCameraDeviceUserAsync(cameraId, callback, handler); 449 } 450 451 /** 452 * Set the flash unit's torch mode of the camera of the given ID without opening the camera 453 * device. 454 * 455 * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use 456 * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit. 457 * Note that even if a camera device has a flash unit, turning on the torch mode may fail 458 * if the camera device or other camera resources needed to turn on the torch mode are in use. 459 * </p> 460 * 461 * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully, 462 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked. 463 * However, even if turning on the torch mode is successful, the application does not have the 464 * exclusive ownership of the flash unit or the camera device. The torch mode will be turned 465 * off and becomes unavailable when the camera device that the flash unit belongs to becomes 466 * unavailable or when other camera resources to keep the torch on become unavailable ( 467 * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also, 468 * other applications are free to call {@link #setTorchMode} to turn off the torch mode ( 469 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest 470 * application that turned on the torch mode exits, the torch mode will be turned off. 471 * 472 * @param cameraId 473 * The unique identifier of the camera device that the flash unit belongs to. 474 * @param enabled 475 * The desired state of the torch mode for the target camera device. Set to 476 * {@code true} to turn on the torch mode. Set to {@code false} to turn off the 477 * torch mode. 478 * 479 * @throws CameraAccessException if it failed to access the flash unit. 480 * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device 481 * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if 482 * other camera resources needed to turn on the torch mode are in use. 483 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera 484 * service is not available. 485 * 486 * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently 487 * or previously available camera device, or the camera device doesn't have a 488 * flash unit. 489 */ 490 public void setTorchMode(@NonNull String cameraId, boolean enabled) 491 throws CameraAccessException { 492 CameraManagerGlobal.get().setTorchMode(cameraId, enabled); 493 } 494 495 /** 496 * A callback for camera devices becoming available or unavailable to open. 497 * 498 * <p>Cameras become available when they are no longer in use, or when a new 499 * removable camera is connected. They become unavailable when some 500 * application or service starts using a camera, or when a removable camera 501 * is disconnected.</p> 502 * 503 * <p>Extend this callback and pass an instance of the subclass to 504 * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability 505 * changes.</p> 506 * 507 * @see #registerAvailabilityCallback 508 */ 509 public static abstract class AvailabilityCallback { 510 511 /** 512 * A new camera has become available to use. 513 * 514 * <p>The default implementation of this method does nothing.</p> 515 * 516 * @param cameraId The unique identifier of the new camera. 517 */ 518 public void onCameraAvailable(@NonNull String cameraId) { 519 // default empty implementation 520 } 521 522 /** 523 * A previously-available camera has become unavailable for use. 524 * 525 * <p>If an application had an active CameraDevice instance for the 526 * now-disconnected camera, that application will receive a 527 * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p> 528 * 529 * <p>The default implementation of this method does nothing.</p> 530 * 531 * @param cameraId The unique identifier of the disconnected camera. 532 */ 533 public void onCameraUnavailable(@NonNull String cameraId) { 534 // default empty implementation 535 } 536 } 537 538 /** 539 * A callback for camera flash torch modes becoming unavailable, disabled, or enabled. 540 * 541 * <p>The torch mode becomes unavailable when the camera device it belongs to becomes 542 * unavailable or other camera resources it needs become busy due to other higher priority 543 * camera activities. The torch mode becomes disabled when it was turned off or when the camera 544 * device it belongs to is no longer in use and other camera resources it needs are no longer 545 * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to 546 * turn off the camera's torch mode, or when an application turns on another camera's torch mode 547 * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes 548 * enabled when it is turned on via {@link #setTorchMode}.</p> 549 * 550 * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled 551 * or enabled state.</p> 552 * 553 * <p>Extend this callback and pass an instance of the subclass to 554 * {@link CameraManager#registerTorchCallback} to be notified of such status changes. 555 * </p> 556 * 557 * @see #registerTorchCallback 558 */ 559 public static abstract class TorchCallback { 560 /** 561 * A camera's torch mode has become unavailable to set via {@link #setTorchMode}. 562 * 563 * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be 564 * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is 565 * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or 566 * enabled state again.</p> 567 * 568 * <p>The default implementation of this method does nothing.</p> 569 * 570 * @param cameraId The unique identifier of the camera whose torch mode has become 571 * unavailable. 572 */ 573 public void onTorchModeUnavailable(@NonNull String cameraId) { 574 // default empty implementation 575 } 576 577 /** 578 * A camera's torch mode has become enabled or disabled and can be changed via 579 * {@link #setTorchMode}. 580 * 581 * <p>The default implementation of this method does nothing.</p> 582 * 583 * @param cameraId The unique identifier of the camera whose torch mode has been changed. 584 * 585 * @param enabled The state that the torch mode of the camera has been changed to. 586 * {@code true} when the torch mode has become on and available to be turned 587 * off. {@code false} when the torch mode has becomes off and available to 588 * be turned on. 589 */ 590 public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) { 591 // default empty implementation 592 } 593 } 594 595 /** 596 * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces 597 * into the correct public exceptions. 598 * 599 * @hide 600 */ 601 public static void throwAsPublicException(Throwable t) throws CameraAccessException { 602 if (t instanceof ServiceSpecificException) { 603 ServiceSpecificException e = (ServiceSpecificException) t; 604 int reason = CameraAccessException.CAMERA_ERROR; 605 switch(e.errorCode) { 606 case ICameraService.ERROR_DISCONNECTED: 607 reason = CameraAccessException.CAMERA_DISCONNECTED; 608 break; 609 case ICameraService.ERROR_DISABLED: 610 reason = CameraAccessException.CAMERA_DISABLED; 611 break; 612 case ICameraService.ERROR_CAMERA_IN_USE: 613 reason = CameraAccessException.CAMERA_IN_USE; 614 break; 615 case ICameraService.ERROR_MAX_CAMERAS_IN_USE: 616 reason = CameraAccessException.MAX_CAMERAS_IN_USE; 617 break; 618 case ICameraService.ERROR_DEPRECATED_HAL: 619 reason = CameraAccessException.CAMERA_DEPRECATED_HAL; 620 break; 621 case ICameraService.ERROR_ILLEGAL_ARGUMENT: 622 case ICameraService.ERROR_ALREADY_EXISTS: 623 throw new IllegalArgumentException(e.getMessage(), e); 624 case ICameraService.ERROR_PERMISSION_DENIED: 625 throw new SecurityException(e.getMessage(), e); 626 case ICameraService.ERROR_TIMED_OUT: 627 case ICameraService.ERROR_INVALID_OPERATION: 628 default: 629 reason = CameraAccessException.CAMERA_ERROR; 630 } 631 throw new CameraAccessException(reason, e.getMessage(), e); 632 } else if (t instanceof DeadObjectException) { 633 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 634 "Camera service has died unexpectedly", 635 t); 636 } else if (t instanceof RemoteException) { 637 throw new UnsupportedOperationException("An unknown RemoteException was thrown" + 638 " which should never happen.", t); 639 } else if (t instanceof RuntimeException) { 640 RuntimeException e = (RuntimeException) t; 641 throw e; 642 } 643 } 644 645 /** 646 * Return or create the list of currently connected camera devices. 647 * 648 * <p>In case of errors connecting to the camera service, will return an empty list.</p> 649 */ 650 private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException { 651 if (mDeviceIdList == null) { 652 int numCameras = 0; 653 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 654 ArrayList<String> deviceIdList = new ArrayList<>(); 655 656 // If no camera service, then no devices 657 if (cameraService == null) { 658 return deviceIdList; 659 } 660 661 try { 662 numCameras = cameraService.getNumberOfCameras(CAMERA_TYPE_ALL); 663 } catch(ServiceSpecificException e) { 664 throwAsPublicException(e); 665 } catch (RemoteException e) { 666 // camera service just died - if no camera service, then no devices 667 return deviceIdList; 668 } 669 670 for (int i = 0; i < numCameras; ++i) { 671 // Non-removable cameras use integers starting at 0 for their 672 // identifiers 673 boolean isDeviceSupported = false; 674 try { 675 CameraMetadataNative info = cameraService.getCameraCharacteristics(i); 676 if (!info.isEmpty()) { 677 isDeviceSupported = true; 678 } else { 679 throw new AssertionError("Expected to get non-empty characteristics"); 680 } 681 } catch(ServiceSpecificException e) { 682 // DISCONNECTED means that the HAL reported an low-level error getting the 683 // device info; ILLEGAL_ARGUMENT means that this devices is not supported. 684 // Skip listing the device. Other errors, 685 // propagate exception onward 686 if (e.errorCode != ICameraService.ERROR_DISCONNECTED || 687 e.errorCode != ICameraService.ERROR_ILLEGAL_ARGUMENT) { 688 throwAsPublicException(e); 689 } 690 } catch(RemoteException e) { 691 // Camera service died - no devices to list 692 deviceIdList.clear(); 693 return deviceIdList; 694 } 695 696 if (isDeviceSupported) { 697 deviceIdList.add(String.valueOf(i)); 698 } else { 699 Log.w(TAG, "Error querying camera device " + i + " for listing."); 700 } 701 702 } 703 mDeviceIdList = deviceIdList; 704 } 705 return mDeviceIdList; 706 } 707 708 /** 709 * Queries the camera service if it supports the camera2 api directly, or needs a shim. 710 * 711 * @param cameraId a non-{@code null} camera identifier 712 * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise. 713 */ 714 private boolean supportsCamera2ApiLocked(String cameraId) { 715 return supportsCameraApiLocked(cameraId, API_VERSION_2); 716 } 717 718 /** 719 * Queries the camera service if it supports a camera api directly, or needs a shim. 720 * 721 * @param cameraId a non-{@code null} camera identifier 722 * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2} 723 * @return {@code true} if connecting will work for that device version. 724 */ 725 private boolean supportsCameraApiLocked(String cameraId, int apiVersion) { 726 int id = Integer.parseInt(cameraId); 727 728 /* 729 * Possible return values: 730 * - NO_ERROR => CameraX API is supported 731 * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception) 732 * - Remote exception => If the camera service died 733 * 734 * Anything else is an unexpected error we don't want to recover from. 735 */ 736 try { 737 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 738 // If no camera service, no support 739 if (cameraService == null) return false; 740 741 return cameraService.supportsCameraApi(id, apiVersion); 742 } catch (RemoteException e) { 743 // Camera service is now down, no support for any API level 744 } 745 return false; 746 } 747 748 /** 749 * A per-process global camera manager instance, to retain a connection to the camera service, 750 * and to distribute camera availability notices to API-registered callbacks 751 */ 752 private static final class CameraManagerGlobal extends ICameraServiceListener.Stub 753 implements IBinder.DeathRecipient { 754 755 private static final String TAG = "CameraManagerGlobal"; 756 private final boolean DEBUG = false; 757 758 private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000; 759 760 // Singleton instance 761 private static final CameraManagerGlobal gCameraManager = 762 new CameraManagerGlobal(); 763 764 /** 765 * This must match the ICameraService definition 766 */ 767 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; 768 769 // Camera ID -> Status map 770 private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); 771 772 // Registered availablility callbacks and their handlers 773 private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap = 774 new ArrayMap<AvailabilityCallback, Handler>(); 775 776 // torch client binder to set the torch mode with. 777 private Binder mTorchClientBinder = new Binder(); 778 779 // Camera ID -> Torch status map 780 private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>(); 781 782 // Registered torch callbacks and their handlers 783 private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap = 784 new ArrayMap<TorchCallback, Handler>(); 785 786 private final Object mLock = new Object(); 787 788 // Access only through getCameraService to deal with binder death 789 private ICameraService mCameraService; 790 791 // Singleton, don't allow construction 792 private CameraManagerGlobal() { 793 } 794 795 public static CameraManagerGlobal get() { 796 return gCameraManager; 797 } 798 799 @Override 800 public IBinder asBinder() { 801 return this; 802 } 803 804 /** 805 * Return a best-effort ICameraService. 806 * 807 * <p>This will be null if the camera service is not currently available. If the camera 808 * service has died since the last use of the camera service, will try to reconnect to the 809 * service.</p> 810 */ 811 public ICameraService getCameraService() { 812 synchronized(mLock) { 813 connectCameraServiceLocked(); 814 if (mCameraService == null) { 815 Log.e(TAG, "Camera service is unavailable"); 816 } 817 return mCameraService; 818 } 819 } 820 821 /** 822 * Connect to the camera service if it's available, and set up listeners. 823 * If the service is already connected, do nothing. 824 * 825 * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p> 826 */ 827 private void connectCameraServiceLocked() { 828 // Only reconnect if necessary 829 if (mCameraService != null) return; 830 831 Log.i(TAG, "Connecting to camera service"); 832 833 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME); 834 if (cameraServiceBinder == null) { 835 // Camera service is now down, leave mCameraService as null 836 return; 837 } 838 try { 839 cameraServiceBinder.linkToDeath(this, /*flags*/ 0); 840 } catch (RemoteException e) { 841 // Camera service is now down, leave mCameraService as null 842 return; 843 } 844 845 ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder); 846 847 try { 848 CameraMetadataNative.setupGlobalVendorTagDescriptor(); 849 } catch (ServiceSpecificException e) { 850 handleRecoverableSetupErrors(e); 851 } 852 853 try { 854 cameraService.addListener(this); 855 mCameraService = cameraService; 856 } catch(ServiceSpecificException e) { 857 // Unexpected failure 858 throw new IllegalStateException("Failed to register a camera service listener", e); 859 } catch (RemoteException e) { 860 // Camera service is now down, leave mCameraService as null 861 } 862 } 863 864 public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException { 865 synchronized(mLock) { 866 867 if (cameraId == null) { 868 throw new IllegalArgumentException("cameraId was null"); 869 } 870 871 ICameraService cameraService = getCameraService(); 872 if (cameraService == null) { 873 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 874 "Camera service is currently unavailable"); 875 } 876 877 try { 878 cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder); 879 } catch(ServiceSpecificException e) { 880 throwAsPublicException(e); 881 } catch (RemoteException e) { 882 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 883 "Camera service is currently unavailable"); 884 } 885 } 886 } 887 888 private void handleRecoverableSetupErrors(ServiceSpecificException e) { 889 switch (e.errorCode) { 890 case ICameraService.ERROR_DISCONNECTED: 891 Log.w(TAG, e.getMessage()); 892 break; 893 default: 894 throw new IllegalStateException(e); 895 } 896 } 897 898 private boolean isAvailable(int status) { 899 switch (status) { 900 case ICameraServiceListener.STATUS_PRESENT: 901 return true; 902 default: 903 return false; 904 } 905 } 906 907 private boolean validStatus(int status) { 908 switch (status) { 909 case ICameraServiceListener.STATUS_NOT_PRESENT: 910 case ICameraServiceListener.STATUS_PRESENT: 911 case ICameraServiceListener.STATUS_ENUMERATING: 912 case ICameraServiceListener.STATUS_NOT_AVAILABLE: 913 return true; 914 default: 915 return false; 916 } 917 } 918 919 private boolean validTorchStatus(int status) { 920 switch (status) { 921 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE: 922 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 923 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: 924 return true; 925 default: 926 return false; 927 } 928 } 929 930 private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler, 931 final String id, final int status) { 932 if (isAvailable(status)) { 933 handler.post( 934 new Runnable() { 935 @Override 936 public void run() { 937 callback.onCameraAvailable(id); 938 } 939 }); 940 } else { 941 handler.post( 942 new Runnable() { 943 @Override 944 public void run() { 945 callback.onCameraUnavailable(id); 946 } 947 }); 948 } 949 } 950 951 private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler, 952 final String id, final int status) { 953 switch(status) { 954 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 955 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: 956 handler.post( 957 new Runnable() { 958 @Override 959 public void run() { 960 callback.onTorchModeChanged(id, status == 961 ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON); 962 } 963 }); 964 break; 965 default: 966 handler.post( 967 new Runnable() { 968 @Override 969 public void run() { 970 callback.onTorchModeUnavailable(id); 971 } 972 }); 973 break; 974 } 975 } 976 977 /** 978 * Send the state of all known cameras to the provided listener, to initialize 979 * the listener's knowledge of camera state. 980 */ 981 private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) { 982 for (int i = 0; i < mDeviceStatus.size(); i++) { 983 String id = mDeviceStatus.keyAt(i); 984 Integer status = mDeviceStatus.valueAt(i); 985 postSingleUpdate(callback, handler, id, status); 986 } 987 } 988 989 private void onStatusChangedLocked(int status, String id) { 990 if (DEBUG) { 991 Log.v(TAG, 992 String.format("Camera id %s has status changed to 0x%x", id, status)); 993 } 994 995 if (!validStatus(status)) { 996 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id, 997 status)); 998 return; 999 } 1000 1001 Integer oldStatus = mDeviceStatus.put(id, status); 1002 1003 if (oldStatus != null && oldStatus == status) { 1004 if (DEBUG) { 1005 Log.v(TAG, String.format( 1006 "Device status changed to 0x%x, which is what it already was", 1007 status)); 1008 } 1009 return; 1010 } 1011 1012 // TODO: consider abstracting out this state minimization + transition 1013 // into a separate 1014 // more easily testable class 1015 // i.e. (new State()).addState(STATE_AVAILABLE) 1016 // .addState(STATE_NOT_AVAILABLE) 1017 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE), 1018 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE) 1019 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE); 1020 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE); 1021 1022 // Translate all the statuses to either 'available' or 'not available' 1023 // available -> available => no new update 1024 // not available -> not available => no new update 1025 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) { 1026 if (DEBUG) { 1027 Log.v(TAG, 1028 String.format( 1029 "Device status was previously available (%b), " + 1030 " and is now again available (%b)" + 1031 "so no new client visible update will be sent", 1032 isAvailable(oldStatus), isAvailable(status))); 1033 } 1034 return; 1035 } 1036 1037 final int callbackCount = mCallbackMap.size(); 1038 for (int i = 0; i < callbackCount; i++) { 1039 Handler handler = mCallbackMap.valueAt(i); 1040 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 1041 1042 postSingleUpdate(callback, handler, id, status); 1043 } 1044 } // onStatusChangedLocked 1045 1046 private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) { 1047 for (int i = 0; i < mTorchStatus.size(); i++) { 1048 String id = mTorchStatus.keyAt(i); 1049 Integer status = mTorchStatus.valueAt(i); 1050 postSingleTorchUpdate(callback, handler, id, status); 1051 } 1052 } 1053 1054 private void onTorchStatusChangedLocked(int status, String id) { 1055 if (DEBUG) { 1056 Log.v(TAG, 1057 String.format("Camera id %s has torch status changed to 0x%x", id, status)); 1058 } 1059 1060 if (!validTorchStatus(status)) { 1061 Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id, 1062 status)); 1063 return; 1064 } 1065 1066 Integer oldStatus = mTorchStatus.put(id, status); 1067 if (oldStatus != null && oldStatus == status) { 1068 if (DEBUG) { 1069 Log.v(TAG, String.format( 1070 "Torch status changed to 0x%x, which is what it already was", 1071 status)); 1072 } 1073 return; 1074 } 1075 1076 final int callbackCount = mTorchCallbackMap.size(); 1077 for (int i = 0; i < callbackCount; i++) { 1078 final Handler handler = mTorchCallbackMap.valueAt(i); 1079 final TorchCallback callback = mTorchCallbackMap.keyAt(i); 1080 postSingleTorchUpdate(callback, handler, id, status); 1081 } 1082 } // onTorchStatusChangedLocked 1083 1084 /** 1085 * Register a callback to be notified about camera device availability with the 1086 * global listener singleton. 1087 * 1088 * @param callback the new callback to send camera availability notices to 1089 * @param handler The handler on which the callback should be invoked. May not be null. 1090 */ 1091 public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) { 1092 synchronized (mLock) { 1093 connectCameraServiceLocked(); 1094 1095 Handler oldHandler = mCallbackMap.put(callback, handler); 1096 // For new callbacks, provide initial availability information 1097 if (oldHandler == null) { 1098 updateCallbackLocked(callback, handler); 1099 } 1100 } 1101 } 1102 1103 /** 1104 * Remove a previously-added callback; the callback will no longer receive connection and 1105 * disconnection callbacks, and is no longer referenced by the global listener singleton. 1106 * 1107 * @param callback The callback to remove from the notification list 1108 */ 1109 public void unregisterAvailabilityCallback(AvailabilityCallback callback) { 1110 synchronized (mLock) { 1111 mCallbackMap.remove(callback); 1112 } 1113 } 1114 1115 public void registerTorchCallback(TorchCallback callback, Handler handler) { 1116 synchronized(mLock) { 1117 connectCameraServiceLocked(); 1118 1119 Handler oldHandler = mTorchCallbackMap.put(callback, handler); 1120 // For new callbacks, provide initial torch information 1121 if (oldHandler == null) { 1122 updateTorchCallbackLocked(callback, handler); 1123 } 1124 } 1125 } 1126 1127 public void unregisterTorchCallback(TorchCallback callback) { 1128 synchronized(mLock) { 1129 mTorchCallbackMap.remove(callback); 1130 } 1131 } 1132 1133 /** 1134 * Callback from camera service notifying the process about camera availability changes 1135 */ 1136 @Override 1137 public void onStatusChanged(int status, int cameraId) throws RemoteException { 1138 synchronized(mLock) { 1139 onStatusChangedLocked(status, String.valueOf(cameraId)); 1140 } 1141 } 1142 1143 @Override 1144 public void onTorchStatusChanged(int status, String cameraId) throws RemoteException { 1145 synchronized (mLock) { 1146 onTorchStatusChangedLocked(status, cameraId); 1147 } 1148 } 1149 1150 /** 1151 * Try to connect to camera service after some delay if any client registered camera 1152 * availability callback or torch status callback. 1153 */ 1154 private void scheduleCameraServiceReconnectionLocked() { 1155 final Handler handler; 1156 1157 if (mCallbackMap.size() > 0) { 1158 handler = mCallbackMap.valueAt(0); 1159 } else if (mTorchCallbackMap.size() > 0) { 1160 handler = mTorchCallbackMap.valueAt(0); 1161 } else { 1162 // Not necessary to reconnect camera service if no client registers a callback. 1163 return; 1164 } 1165 1166 if (DEBUG) { 1167 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS + 1168 " ms"); 1169 } 1170 1171 handler.postDelayed( 1172 new Runnable() { 1173 @Override 1174 public void run() { 1175 ICameraService cameraService = getCameraService(); 1176 if (cameraService == null) { 1177 synchronized(mLock) { 1178 if (DEBUG) { 1179 Log.v(TAG, "Reconnecting Camera Service failed."); 1180 } 1181 scheduleCameraServiceReconnectionLocked(); 1182 } 1183 } 1184 } 1185 }, 1186 CAMERA_SERVICE_RECONNECT_DELAY_MS); 1187 } 1188 1189 /** 1190 * Listener for camera service death. 1191 * 1192 * <p>The camera service isn't supposed to die under any normal circumstances, but can be 1193 * turned off during debug, or crash due to bugs. So detect that and null out the interface 1194 * object, so that the next calls to the manager can try to reconnect.</p> 1195 */ 1196 public void binderDied() { 1197 synchronized(mLock) { 1198 // Only do this once per service death 1199 if (mCameraService == null) return; 1200 1201 mCameraService = null; 1202 1203 // Tell listeners that the cameras and torch modes are unavailable and schedule a 1204 // reconnection to camera service. When camera service is reconnected, the camera 1205 // and torch statuses will be updated. 1206 for (int i = 0; i < mDeviceStatus.size(); i++) { 1207 String cameraId = mDeviceStatus.keyAt(i); 1208 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId); 1209 } 1210 for (int i = 0; i < mTorchStatus.size(); i++) { 1211 String cameraId = mTorchStatus.keyAt(i); 1212 onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE, 1213 cameraId); 1214 } 1215 1216 scheduleCameraServiceReconnectionLocked(); 1217 } 1218 } 1219 1220 } // CameraManagerGlobal 1221 1222} // CameraManager 1223