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