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 * A camera device has been opened by an application. 707 * 708 * <p>The default implementation of this method does nothing.</p> 709 * 710 * @param cameraId The unique identifier of the new camera. 711 * @param packageId The package Id of the application opening the camera. 712 * 713 * @see #onCameraClosed 714 */ 715 /** @hide */ onCameraOpened(@onNull String cameraId, @NonNull String packageId)716 public void onCameraOpened(@NonNull String cameraId, @NonNull String packageId) { 717 // default empty implementation 718 } 719 720 /** 721 * A previously-opened camera has been closed. 722 * 723 * <p>The default implementation of this method does nothing.</p> 724 * 725 * @param cameraId The unique identifier of the closed camera. 726 */ 727 /** @hide */ onCameraClosed(@onNull String cameraId)728 public void onCameraClosed(@NonNull String cameraId) { 729 // default empty implementation 730 } 731 } 732 733 /** 734 * A callback for camera flash torch modes becoming unavailable, disabled, or enabled. 735 * 736 * <p>The torch mode becomes unavailable when the camera device it belongs to becomes 737 * unavailable or other camera resources it needs become busy due to other higher priority 738 * camera activities. The torch mode becomes disabled when it was turned off or when the camera 739 * device it belongs to is no longer in use and other camera resources it needs are no longer 740 * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to 741 * turn off the camera's torch mode, or when an application turns on another camera's torch mode 742 * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes 743 * enabled when it is turned on via {@link #setTorchMode}.</p> 744 * 745 * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled 746 * or enabled state.</p> 747 * 748 * <p>Extend this callback and pass an instance of the subclass to 749 * {@link CameraManager#registerTorchCallback} to be notified of such status changes. 750 * </p> 751 * 752 * @see #registerTorchCallback 753 */ 754 public static abstract class TorchCallback { 755 /** 756 * A camera's torch mode has become unavailable to set via {@link #setTorchMode}. 757 * 758 * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be 759 * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is 760 * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or 761 * enabled state again.</p> 762 * 763 * <p>The default implementation of this method does nothing.</p> 764 * 765 * @param cameraId The unique identifier of the camera whose torch mode has become 766 * unavailable. 767 */ onTorchModeUnavailable(@onNull String cameraId)768 public void onTorchModeUnavailable(@NonNull String cameraId) { 769 // default empty implementation 770 } 771 772 /** 773 * A camera's torch mode has become enabled or disabled and can be changed via 774 * {@link #setTorchMode}. 775 * 776 * <p>The default implementation of this method does nothing.</p> 777 * 778 * @param cameraId The unique identifier of the camera whose torch mode has been changed. 779 * 780 * @param enabled The state that the torch mode of the camera has been changed to. 781 * {@code true} when the torch mode has become on and available to be turned 782 * off. {@code false} when the torch mode has becomes off and available to 783 * be turned on. 784 */ onTorchModeChanged(@onNull String cameraId, boolean enabled)785 public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) { 786 // default empty implementation 787 } 788 } 789 790 /** 791 * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces 792 * into the correct public exceptions. 793 * 794 * @hide 795 */ throwAsPublicException(Throwable t)796 public static void throwAsPublicException(Throwable t) throws CameraAccessException { 797 if (t instanceof ServiceSpecificException) { 798 ServiceSpecificException e = (ServiceSpecificException) t; 799 int reason = CameraAccessException.CAMERA_ERROR; 800 switch(e.errorCode) { 801 case ICameraService.ERROR_DISCONNECTED: 802 reason = CameraAccessException.CAMERA_DISCONNECTED; 803 break; 804 case ICameraService.ERROR_DISABLED: 805 reason = CameraAccessException.CAMERA_DISABLED; 806 break; 807 case ICameraService.ERROR_CAMERA_IN_USE: 808 reason = CameraAccessException.CAMERA_IN_USE; 809 break; 810 case ICameraService.ERROR_MAX_CAMERAS_IN_USE: 811 reason = CameraAccessException.MAX_CAMERAS_IN_USE; 812 break; 813 case ICameraService.ERROR_DEPRECATED_HAL: 814 reason = CameraAccessException.CAMERA_DEPRECATED_HAL; 815 break; 816 case ICameraService.ERROR_ILLEGAL_ARGUMENT: 817 case ICameraService.ERROR_ALREADY_EXISTS: 818 throw new IllegalArgumentException(e.getMessage(), e); 819 case ICameraService.ERROR_PERMISSION_DENIED: 820 throw new SecurityException(e.getMessage(), e); 821 case ICameraService.ERROR_TIMED_OUT: 822 case ICameraService.ERROR_INVALID_OPERATION: 823 default: 824 reason = CameraAccessException.CAMERA_ERROR; 825 } 826 throw new CameraAccessException(reason, e.getMessage(), e); 827 } else if (t instanceof DeadObjectException) { 828 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 829 "Camera service has died unexpectedly", 830 t); 831 } else if (t instanceof RemoteException) { 832 throw new UnsupportedOperationException("An unknown RemoteException was thrown" + 833 " which should never happen.", t); 834 } else if (t instanceof RuntimeException) { 835 RuntimeException e = (RuntimeException) t; 836 throw e; 837 } 838 } 839 840 /** 841 * Queries the camera service if it supports the camera2 api directly, or needs a shim. 842 * 843 * @param cameraId a non-{@code null} camera identifier 844 * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise. 845 */ supportsCamera2ApiLocked(String cameraId)846 private boolean supportsCamera2ApiLocked(String cameraId) { 847 return supportsCameraApiLocked(cameraId, API_VERSION_2); 848 } 849 850 /** 851 * Queries the camera service if it supports a camera api directly, or needs a shim. 852 * 853 * @param cameraId a non-{@code null} camera identifier 854 * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2} 855 * @return {@code true} if connecting will work for that device version. 856 */ supportsCameraApiLocked(String cameraId, int apiVersion)857 private boolean supportsCameraApiLocked(String cameraId, int apiVersion) { 858 /* 859 * Possible return values: 860 * - NO_ERROR => CameraX API is supported 861 * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception) 862 * - Remote exception => If the camera service died 863 * 864 * Anything else is an unexpected error we don't want to recover from. 865 */ 866 try { 867 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 868 // If no camera service, no support 869 if (cameraService == null) return false; 870 871 return cameraService.supportsCameraApi(cameraId, apiVersion); 872 } catch (RemoteException e) { 873 // Camera service is now down, no support for any API level 874 } 875 return false; 876 } 877 878 /** 879 * Queries the camera service if a cameraId is a hidden physical camera that belongs to a 880 * logical camera device. 881 * 882 * A hidden physical camera is a camera that cannot be opened by the application. But it 883 * can be used as part of a logical camera. 884 * 885 * @param cameraId a non-{@code null} camera identifier 886 * @return {@code true} if cameraId is a hidden physical camera device 887 * 888 * @hide 889 */ isHiddenPhysicalCamera(String cameraId)890 public static boolean isHiddenPhysicalCamera(String cameraId) { 891 try { 892 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 893 // If no camera service, no support 894 if (cameraService == null) return false; 895 896 return cameraService.isHiddenPhysicalCamera(cameraId); 897 } catch (RemoteException e) { 898 // Camera service is now down, no support for any API level 899 } 900 return false; 901 } 902 903 /** 904 * A per-process global camera manager instance, to retain a connection to the camera service, 905 * and to distribute camera availability notices to API-registered callbacks 906 */ 907 private static final class CameraManagerGlobal extends ICameraServiceListener.Stub 908 implements IBinder.DeathRecipient { 909 910 private static final String TAG = "CameraManagerGlobal"; 911 private final boolean DEBUG = false; 912 913 private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000; 914 915 // Singleton instance 916 private static final CameraManagerGlobal gCameraManager = 917 new CameraManagerGlobal(); 918 919 /** 920 * This must match the ICameraService definition 921 */ 922 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; 923 924 private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1); 925 // Camera ID -> Status map 926 private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); 927 928 // Registered availablility callbacks and their executors 929 private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap = 930 new ArrayMap<AvailabilityCallback, Executor>(); 931 932 // torch client binder to set the torch mode with. 933 private Binder mTorchClientBinder = new Binder(); 934 935 // Camera ID -> Torch status map 936 private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>(); 937 938 // Registered torch callbacks and their executors 939 private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap = 940 new ArrayMap<TorchCallback, Executor>(); 941 942 private final Object mLock = new Object(); 943 944 // Access only through getCameraService to deal with binder death 945 private ICameraService mCameraService; 946 947 // Singleton, don't allow construction CameraManagerGlobal()948 private CameraManagerGlobal() { 949 } 950 951 public static final boolean sCameraServiceDisabled = 952 SystemProperties.getBoolean("config.disable_cameraservice", false); 953 get()954 public static CameraManagerGlobal get() { 955 return gCameraManager; 956 } 957 958 @Override asBinder()959 public IBinder asBinder() { 960 return this; 961 } 962 963 /** 964 * Return a best-effort ICameraService. 965 * 966 * <p>This will be null if the camera service is not currently available. If the camera 967 * service has died since the last use of the camera service, will try to reconnect to the 968 * service.</p> 969 */ getCameraService()970 public ICameraService getCameraService() { 971 synchronized(mLock) { 972 connectCameraServiceLocked(); 973 if (mCameraService == null && !sCameraServiceDisabled) { 974 Log.e(TAG, "Camera service is unavailable"); 975 } 976 return mCameraService; 977 } 978 } 979 980 /** 981 * Connect to the camera service if it's available, and set up listeners. 982 * If the service is already connected, do nothing. 983 * 984 * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p> 985 */ connectCameraServiceLocked()986 private void connectCameraServiceLocked() { 987 // Only reconnect if necessary 988 if (mCameraService != null || sCameraServiceDisabled) return; 989 990 Log.i(TAG, "Connecting to camera service"); 991 992 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME); 993 if (cameraServiceBinder == null) { 994 // Camera service is now down, leave mCameraService as null 995 return; 996 } 997 try { 998 cameraServiceBinder.linkToDeath(this, /*flags*/ 0); 999 } catch (RemoteException e) { 1000 // Camera service is now down, leave mCameraService as null 1001 return; 1002 } 1003 1004 ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder); 1005 1006 try { 1007 CameraMetadataNative.setupGlobalVendorTagDescriptor(); 1008 } catch (ServiceSpecificException e) { 1009 handleRecoverableSetupErrors(e); 1010 } 1011 1012 try { 1013 CameraStatus[] cameraStatuses = cameraService.addListener(this); 1014 for (CameraStatus c : cameraStatuses) { 1015 onStatusChangedLocked(c.status, c.cameraId); 1016 } 1017 mCameraService = cameraService; 1018 } catch(ServiceSpecificException e) { 1019 // Unexpected failure 1020 throw new IllegalStateException("Failed to register a camera service listener", e); 1021 } catch (RemoteException e) { 1022 // Camera service is now down, leave mCameraService as null 1023 } 1024 } 1025 1026 /** 1027 * Get a list of all camera IDs that are at least PRESENT; ignore devices that are 1028 * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone. 1029 */ getCameraIdList()1030 public String[] getCameraIdList() { 1031 String[] cameraIds = null; 1032 synchronized(mLock) { 1033 // Try to make sure we have an up-to-date list of camera devices. 1034 connectCameraServiceLocked(); 1035 1036 int idCount = 0; 1037 for (int i = 0; i < mDeviceStatus.size(); i++) { 1038 int status = mDeviceStatus.valueAt(i); 1039 if (status == ICameraServiceListener.STATUS_NOT_PRESENT || 1040 status == ICameraServiceListener.STATUS_ENUMERATING) continue; 1041 idCount++; 1042 } 1043 cameraIds = new String[idCount]; 1044 idCount = 0; 1045 for (int i = 0; i < mDeviceStatus.size(); i++) { 1046 int status = mDeviceStatus.valueAt(i); 1047 if (status == ICameraServiceListener.STATUS_NOT_PRESENT || 1048 status == ICameraServiceListener.STATUS_ENUMERATING) continue; 1049 cameraIds[idCount] = mDeviceStatus.keyAt(i); 1050 idCount++; 1051 } 1052 } 1053 1054 // The sort logic must match the logic in 1055 // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds 1056 Arrays.sort(cameraIds, new Comparator<String>() { 1057 @Override 1058 public int compare(String s1, String s2) { 1059 int s1Int = 0, s2Int = 0; 1060 try { 1061 s1Int = Integer.parseInt(s1); 1062 } catch (NumberFormatException e) { 1063 s1Int = -1; 1064 } 1065 1066 try { 1067 s2Int = Integer.parseInt(s2); 1068 } catch (NumberFormatException e) { 1069 s2Int = -1; 1070 } 1071 1072 // Uint device IDs first 1073 if (s1Int >= 0 && s2Int >= 0) { 1074 return s1Int - s2Int; 1075 } else if (s1Int >= 0) { 1076 return -1; 1077 } else if (s2Int >= 0) { 1078 return 1; 1079 } else { 1080 // Simple string compare if both id are not uint 1081 return s1.compareTo(s2); 1082 } 1083 }}); 1084 return cameraIds; 1085 } 1086 setTorchMode(String cameraId, boolean enabled)1087 public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException { 1088 synchronized(mLock) { 1089 1090 if (cameraId == null) { 1091 throw new IllegalArgumentException("cameraId was null"); 1092 } 1093 1094 ICameraService cameraService = getCameraService(); 1095 if (cameraService == null) { 1096 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 1097 "Camera service is currently unavailable"); 1098 } 1099 1100 try { 1101 cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder); 1102 } catch(ServiceSpecificException e) { 1103 throwAsPublicException(e); 1104 } catch (RemoteException e) { 1105 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 1106 "Camera service is currently unavailable"); 1107 } 1108 } 1109 } 1110 handleRecoverableSetupErrors(ServiceSpecificException e)1111 private void handleRecoverableSetupErrors(ServiceSpecificException e) { 1112 switch (e.errorCode) { 1113 case ICameraService.ERROR_DISCONNECTED: 1114 Log.w(TAG, e.getMessage()); 1115 break; 1116 default: 1117 throw new IllegalStateException(e); 1118 } 1119 } 1120 isAvailable(int status)1121 private boolean isAvailable(int status) { 1122 switch (status) { 1123 case ICameraServiceListener.STATUS_PRESENT: 1124 return true; 1125 default: 1126 return false; 1127 } 1128 } 1129 validStatus(int status)1130 private boolean validStatus(int status) { 1131 switch (status) { 1132 case ICameraServiceListener.STATUS_NOT_PRESENT: 1133 case ICameraServiceListener.STATUS_PRESENT: 1134 case ICameraServiceListener.STATUS_ENUMERATING: 1135 case ICameraServiceListener.STATUS_NOT_AVAILABLE: 1136 return true; 1137 default: 1138 return false; 1139 } 1140 } 1141 validTorchStatus(int status)1142 private boolean validTorchStatus(int status) { 1143 switch (status) { 1144 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE: 1145 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 1146 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: 1147 return true; 1148 default: 1149 return false; 1150 } 1151 } 1152 postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback, final Executor executor)1153 private void postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback, 1154 final Executor executor) { 1155 final long ident = Binder.clearCallingIdentity(); 1156 try { 1157 executor.execute( 1158 new Runnable() { 1159 @Override 1160 public void run() { 1161 callback.onCameraAccessPrioritiesChanged(); 1162 } 1163 }); 1164 } finally { 1165 Binder.restoreCallingIdentity(ident); 1166 } 1167 } 1168 postSingleCameraOpenedUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final String packageId)1169 private void postSingleCameraOpenedUpdate(final AvailabilityCallback callback, 1170 final Executor executor, final String id, final String packageId) { 1171 final long ident = Binder.clearCallingIdentity(); 1172 try { 1173 executor.execute( 1174 new Runnable() { 1175 @Override 1176 public void run() { 1177 callback.onCameraOpened(id, packageId); 1178 } 1179 }); 1180 } finally { 1181 Binder.restoreCallingIdentity(ident); 1182 } 1183 } 1184 postSingleCameraClosedUpdate(final AvailabilityCallback callback, final Executor executor, final String id)1185 private void postSingleCameraClosedUpdate(final AvailabilityCallback callback, 1186 final Executor executor, final String id) { 1187 final long ident = Binder.clearCallingIdentity(); 1188 try { 1189 executor.execute( 1190 new Runnable() { 1191 @Override 1192 public void run() { 1193 callback.onCameraClosed(id); 1194 } 1195 }); 1196 } finally { 1197 Binder.restoreCallingIdentity(ident); 1198 } 1199 } 1200 postSingleUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final int status)1201 private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor, 1202 final String id, final int status) { 1203 if (isAvailable(status)) { 1204 final long ident = Binder.clearCallingIdentity(); 1205 try { 1206 executor.execute( 1207 new Runnable() { 1208 @Override 1209 public void run() { 1210 callback.onCameraAvailable(id); 1211 } 1212 }); 1213 } finally { 1214 Binder.restoreCallingIdentity(ident); 1215 } 1216 } else { 1217 final long ident = Binder.clearCallingIdentity(); 1218 try { 1219 executor.execute( 1220 new Runnable() { 1221 @Override 1222 public void run() { 1223 callback.onCameraUnavailable(id); 1224 } 1225 }); 1226 } finally { 1227 Binder.restoreCallingIdentity(ident); 1228 } 1229 } 1230 } 1231 postSingleTorchUpdate(final TorchCallback callback, final Executor executor, final String id, final int status)1232 private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor, 1233 final String id, final int status) { 1234 switch(status) { 1235 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 1236 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: { 1237 final long ident = Binder.clearCallingIdentity(); 1238 try { 1239 executor.execute(() -> { 1240 callback.onTorchModeChanged(id, status == 1241 ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON); 1242 }); 1243 } finally { 1244 Binder.restoreCallingIdentity(ident); 1245 } 1246 } 1247 break; 1248 default: { 1249 final long ident = Binder.clearCallingIdentity(); 1250 try { 1251 executor.execute(() -> { 1252 callback.onTorchModeUnavailable(id); 1253 }); 1254 } finally { 1255 Binder.restoreCallingIdentity(ident); 1256 } 1257 } 1258 break; 1259 } 1260 } 1261 1262 /** 1263 * Send the state of all known cameras to the provided listener, to initialize 1264 * the listener's knowledge of camera state. 1265 */ updateCallbackLocked(AvailabilityCallback callback, Executor executor)1266 private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) { 1267 for (int i = 0; i < mDeviceStatus.size(); i++) { 1268 String id = mDeviceStatus.keyAt(i); 1269 Integer status = mDeviceStatus.valueAt(i); 1270 postSingleUpdate(callback, executor, id, status); 1271 } 1272 } 1273 onStatusChangedLocked(int status, String id)1274 private void onStatusChangedLocked(int status, String id) { 1275 if (DEBUG) { 1276 Log.v(TAG, 1277 String.format("Camera id %s has status changed to 0x%x", id, status)); 1278 } 1279 1280 if (!validStatus(status)) { 1281 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id, 1282 status)); 1283 return; 1284 } 1285 1286 Integer oldStatus; 1287 if (status == ICameraServiceListener.STATUS_NOT_PRESENT) { 1288 oldStatus = mDeviceStatus.remove(id); 1289 } else { 1290 oldStatus = mDeviceStatus.put(id, status); 1291 } 1292 1293 if (oldStatus != null && oldStatus == status) { 1294 if (DEBUG) { 1295 Log.v(TAG, String.format( 1296 "Device status changed to 0x%x, which is what it already was", 1297 status)); 1298 } 1299 return; 1300 } 1301 1302 // TODO: consider abstracting out this state minimization + transition 1303 // into a separate 1304 // more easily testable class 1305 // i.e. (new State()).addState(STATE_AVAILABLE) 1306 // .addState(STATE_NOT_AVAILABLE) 1307 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE), 1308 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE) 1309 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE); 1310 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE); 1311 1312 // Translate all the statuses to either 'available' or 'not available' 1313 // available -> available => no new update 1314 // not available -> not available => no new update 1315 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) { 1316 if (DEBUG) { 1317 Log.v(TAG, 1318 String.format( 1319 "Device status was previously available (%b), " + 1320 " and is now again available (%b)" + 1321 "so no new client visible update will be sent", 1322 isAvailable(oldStatus), isAvailable(status))); 1323 } 1324 return; 1325 } 1326 1327 final int callbackCount = mCallbackMap.size(); 1328 for (int i = 0; i < callbackCount; i++) { 1329 Executor executor = mCallbackMap.valueAt(i); 1330 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 1331 1332 postSingleUpdate(callback, executor, id, status); 1333 } 1334 } // onStatusChangedLocked 1335 updateTorchCallbackLocked(TorchCallback callback, Executor executor)1336 private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) { 1337 for (int i = 0; i < mTorchStatus.size(); i++) { 1338 String id = mTorchStatus.keyAt(i); 1339 Integer status = mTorchStatus.valueAt(i); 1340 postSingleTorchUpdate(callback, executor, id, status); 1341 } 1342 } 1343 onTorchStatusChangedLocked(int status, String id)1344 private void onTorchStatusChangedLocked(int status, String id) { 1345 if (DEBUG) { 1346 Log.v(TAG, 1347 String.format("Camera id %s has torch status changed to 0x%x", id, status)); 1348 } 1349 1350 if (!validTorchStatus(status)) { 1351 Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id, 1352 status)); 1353 return; 1354 } 1355 1356 Integer oldStatus = mTorchStatus.put(id, status); 1357 if (oldStatus != null && oldStatus == status) { 1358 if (DEBUG) { 1359 Log.v(TAG, String.format( 1360 "Torch status changed to 0x%x, which is what it already was", 1361 status)); 1362 } 1363 return; 1364 } 1365 1366 final int callbackCount = mTorchCallbackMap.size(); 1367 for (int i = 0; i < callbackCount; i++) { 1368 final Executor executor = mTorchCallbackMap.valueAt(i); 1369 final TorchCallback callback = mTorchCallbackMap.keyAt(i); 1370 postSingleTorchUpdate(callback, executor, id, status); 1371 } 1372 } // onTorchStatusChangedLocked 1373 1374 /** 1375 * Register a callback to be notified about camera device availability with the 1376 * global listener singleton. 1377 * 1378 * @param callback the new callback to send camera availability notices to 1379 * @param executor The executor which should invoke the callback. May not be null. 1380 */ registerAvailabilityCallback(AvailabilityCallback callback, Executor executor)1381 public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) { 1382 synchronized (mLock) { 1383 connectCameraServiceLocked(); 1384 1385 Executor oldExecutor = mCallbackMap.put(callback, executor); 1386 // For new callbacks, provide initial availability information 1387 if (oldExecutor == null) { 1388 updateCallbackLocked(callback, executor); 1389 } 1390 1391 // If not connected to camera service, schedule a reconnect to camera service. 1392 if (mCameraService == null) { 1393 scheduleCameraServiceReconnectionLocked(); 1394 } 1395 } 1396 } 1397 1398 /** 1399 * Remove a previously-added callback; the callback will no longer receive connection and 1400 * disconnection callbacks, and is no longer referenced by the global listener singleton. 1401 * 1402 * @param callback The callback to remove from the notification list 1403 */ unregisterAvailabilityCallback(AvailabilityCallback callback)1404 public void unregisterAvailabilityCallback(AvailabilityCallback callback) { 1405 synchronized (mLock) { 1406 mCallbackMap.remove(callback); 1407 } 1408 } 1409 registerTorchCallback(TorchCallback callback, Executor executor)1410 public void registerTorchCallback(TorchCallback callback, Executor executor) { 1411 synchronized(mLock) { 1412 connectCameraServiceLocked(); 1413 1414 Executor oldExecutor = mTorchCallbackMap.put(callback, executor); 1415 // For new callbacks, provide initial torch information 1416 if (oldExecutor == null) { 1417 updateTorchCallbackLocked(callback, executor); 1418 } 1419 1420 // If not connected to camera service, schedule a reconnect to camera service. 1421 if (mCameraService == null) { 1422 scheduleCameraServiceReconnectionLocked(); 1423 } 1424 } 1425 } 1426 unregisterTorchCallback(TorchCallback callback)1427 public void unregisterTorchCallback(TorchCallback callback) { 1428 synchronized(mLock) { 1429 mTorchCallbackMap.remove(callback); 1430 } 1431 } 1432 1433 /** 1434 * Callback from camera service notifying the process about camera availability changes 1435 */ 1436 @Override onStatusChanged(int status, String cameraId)1437 public void onStatusChanged(int status, String cameraId) throws RemoteException { 1438 synchronized(mLock) { 1439 onStatusChangedLocked(status, cameraId); 1440 } 1441 } 1442 1443 @Override onTorchStatusChanged(int status, String cameraId)1444 public void onTorchStatusChanged(int status, String cameraId) throws RemoteException { 1445 synchronized (mLock) { 1446 onTorchStatusChangedLocked(status, cameraId); 1447 } 1448 } 1449 1450 @Override onCameraAccessPrioritiesChanged()1451 public void onCameraAccessPrioritiesChanged() { 1452 synchronized (mLock) { 1453 final int callbackCount = mCallbackMap.size(); 1454 for (int i = 0; i < callbackCount; i++) { 1455 Executor executor = mCallbackMap.valueAt(i); 1456 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 1457 1458 postSingleAccessPriorityChangeUpdate(callback, executor); 1459 } 1460 } 1461 } 1462 1463 @Override onCameraOpened(String cameraId, String clientPackageId)1464 public void onCameraOpened(String cameraId, String clientPackageId) { 1465 synchronized (mLock) { 1466 final int callbackCount = mCallbackMap.size(); 1467 for (int i = 0; i < callbackCount; i++) { 1468 Executor executor = mCallbackMap.valueAt(i); 1469 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 1470 1471 postSingleCameraOpenedUpdate(callback, executor, cameraId, clientPackageId); 1472 } 1473 } 1474 } 1475 1476 @Override onCameraClosed(String cameraId)1477 public void onCameraClosed(String cameraId) { 1478 synchronized (mLock) { 1479 final int callbackCount = mCallbackMap.size(); 1480 for (int i = 0; i < callbackCount; i++) { 1481 Executor executor = mCallbackMap.valueAt(i); 1482 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 1483 1484 postSingleCameraClosedUpdate(callback, executor, cameraId); 1485 } 1486 } 1487 } 1488 1489 /** 1490 * Try to connect to camera service after some delay if any client registered camera 1491 * availability callback or torch status callback. 1492 */ scheduleCameraServiceReconnectionLocked()1493 private void scheduleCameraServiceReconnectionLocked() { 1494 if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) { 1495 // Not necessary to reconnect camera service if no client registers a callback. 1496 return; 1497 } 1498 1499 if (DEBUG) { 1500 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS + 1501 " ms"); 1502 } 1503 1504 try { 1505 mScheduler.schedule(() -> { 1506 ICameraService cameraService = getCameraService(); 1507 if (cameraService == null) { 1508 synchronized(mLock) { 1509 if (DEBUG) { 1510 Log.v(TAG, "Reconnecting Camera Service failed."); 1511 } 1512 scheduleCameraServiceReconnectionLocked(); 1513 } 1514 } 1515 }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS); 1516 } catch (RejectedExecutionException e) { 1517 Log.e(TAG, "Failed to schedule camera service re-connect: " + e); 1518 } 1519 } 1520 1521 /** 1522 * Listener for camera service death. 1523 * 1524 * <p>The camera service isn't supposed to die under any normal circumstances, but can be 1525 * turned off during debug, or crash due to bugs. So detect that and null out the interface 1526 * object, so that the next calls to the manager can try to reconnect.</p> 1527 */ binderDied()1528 public void binderDied() { 1529 synchronized(mLock) { 1530 // Only do this once per service death 1531 if (mCameraService == null) return; 1532 1533 mCameraService = null; 1534 1535 // Tell listeners that the cameras and torch modes are unavailable and schedule a 1536 // reconnection to camera service. When camera service is reconnected, the camera 1537 // and torch statuses will be updated. 1538 for (int i = 0; i < mDeviceStatus.size(); i++) { 1539 String cameraId = mDeviceStatus.keyAt(i); 1540 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId); 1541 } 1542 for (int i = 0; i < mTorchStatus.size(); i++) { 1543 String cameraId = mTorchStatus.keyAt(i); 1544 onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE, 1545 cameraId); 1546 } 1547 1548 scheduleCameraServiceReconnectionLocked(); 1549 } 1550 } 1551 1552 } // CameraManagerGlobal 1553 1554 } // CameraManager 1555