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