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.SystemApi; 24 import android.annotation.SystemService; 25 import android.annotation.TestApi; 26 import android.content.Context; 27 import android.hardware.CameraStatus; 28 import android.hardware.ICameraService; 29 import android.hardware.ICameraServiceListener; 30 import android.hardware.camera2.impl.CameraDeviceImpl; 31 import android.hardware.camera2.impl.CameraInjectionSessionImpl; 32 import android.hardware.camera2.impl.CameraMetadataNative; 33 import android.hardware.camera2.params.ExtensionSessionConfiguration; 34 import android.hardware.camera2.params.SessionConfiguration; 35 import android.hardware.camera2.params.StreamConfiguration; 36 import android.hardware.camera2.utils.CameraIdAndSessionConfiguration; 37 import android.hardware.camera2.utils.ConcurrentCameraIdCombination; 38 import android.hardware.display.DisplayManager; 39 import android.os.Binder; 40 import android.os.DeadObjectException; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.RemoteException; 44 import android.os.ServiceManager; 45 import android.os.ServiceSpecificException; 46 import android.os.SystemProperties; 47 import android.util.ArrayMap; 48 import android.util.ArraySet; 49 import android.util.Log; 50 import android.util.Size; 51 import android.view.Display; 52 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.Comparator; 56 import java.util.HashMap; 57 import java.util.Map; 58 import java.util.Set; 59 import java.util.concurrent.Executor; 60 import java.util.concurrent.Executors; 61 import java.util.concurrent.RejectedExecutionException; 62 import java.util.concurrent.ScheduledExecutorService; 63 import java.util.concurrent.TimeUnit; 64 65 /** 66 * <p>A system service manager for detecting, characterizing, and connecting to 67 * {@link CameraDevice CameraDevices}.</p> 68 * 69 * <p>For more details about communicating with camera devices, read the Camera 70 * developer guide or the {@link android.hardware.camera2 camera2} 71 * package documentation.</p> 72 */ 73 @SystemService(Context.CAMERA_SERVICE) 74 public final class CameraManager { 75 76 private static final String TAG = "CameraManager"; 77 private final boolean DEBUG = false; 78 79 private static final int USE_CALLING_UID = -1; 80 81 @SuppressWarnings("unused") 82 private static final int API_VERSION_1 = 1; 83 private static final int API_VERSION_2 = 2; 84 85 private static final int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0; 86 private static final int CAMERA_TYPE_ALL = 1; 87 88 private ArrayList<String> mDeviceIdList; 89 90 private final Context mContext; 91 private final Object mLock = new Object(); 92 93 /** 94 * @hide 95 */ CameraManager(Context context)96 public CameraManager(Context context) { 97 synchronized(mLock) { 98 mContext = context; 99 } 100 } 101 102 /** 103 * Return the list of currently connected camera devices by identifier, including 104 * cameras that may be in use by other camera API clients. 105 * 106 * <p>Non-removable cameras use integers starting at 0 for their 107 * identifiers, while removable cameras have a unique identifier for each 108 * individual device, even if they are the same model.</p> 109 * 110 * <p>This list doesn't contain physical cameras that can only be used as part of a logical 111 * multi-camera device.</p> 112 * 113 * @return The list of currently connected camera devices. 114 */ 115 @NonNull getCameraIdList()116 public String[] getCameraIdList() throws CameraAccessException { 117 return CameraManagerGlobal.get().getCameraIdList(); 118 } 119 120 /** 121 * Similar to getCameraIdList(). However, getCamerIdListNoLazy() necessarily communicates with 122 * cameraserver in order to get the list of camera ids. This is to faciliate testing since some 123 * camera ids may go 'offline' without callbacks from cameraserver because of changes in 124 * SYSTEM_CAMERA permissions (though this is not a changeable permission, tests may call 125 * adopt(drop)ShellPermissionIdentity() and effectively change their permissions). This call 126 * affects the camera ids returned by getCameraIdList() as well. Tests which do adopt shell 127 * permission identity should not mix getCameraIdList() and getCameraListNoLazyCalls(). 128 */ 129 /** @hide */ 130 @TestApi getCameraIdListNoLazy()131 public String[] getCameraIdListNoLazy() throws CameraAccessException { 132 return CameraManagerGlobal.get().getCameraIdListNoLazy(); 133 } 134 135 /** 136 * Return the set of combinations of currently connected camera device identifiers, which 137 * support configuring camera device sessions concurrently. 138 * 139 * <p>The devices in these combinations can be concurrently configured by the same 140 * client camera application. Using these camera devices concurrently by two different 141 * applications is not guaranteed to be supported, however.</p> 142 * 143 * <p>For concurrent operation, in chronological order : 144 * - Applications must first close any open cameras that have sessions configured, using 145 * {@link CameraDevice#close}. 146 * - All camera devices intended to be operated concurrently, must be opened using 147 * {@link #openCamera}, before configuring sessions on any of the camera devices.</p> 148 * 149 * <p>Each device in a combination, is guaranteed to support stream combinations which may be 150 * obtained by querying {@link #getCameraCharacteristics} for the key 151 * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}.</p> 152 * 153 * <p>For concurrent operation, if a camera device has a non null zoom ratio range as specified 154 * by 155 * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}, 156 * its complete zoom ratio range may not apply. Applications can use 157 * {@link android.hardware.camera2.CaptureRequest#CONTROL_ZOOM_RATIO} >=1 and <= 158 * {@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM} 159 * during concurrent operation. 160 * <p> 161 * 162 * <p>The set of combinations may include camera devices that may be in use by other camera API 163 * clients.</p> 164 * 165 * <p>Concurrent camera extension sessions {@link CameraExtensionSession} are not currently 166 * supported.</p> 167 * 168 * <p>The set of combinations doesn't contain physical cameras that can only be used as 169 * part of a logical multi-camera device.</p> 170 * 171 * <p> If a new camera id becomes available through 172 * {@link AvailabilityCallback#onCameraUnavailable(String)}, clients can call 173 * this method to check if new combinations of camera ids which can stream concurrently are 174 * available. 175 * 176 * @return The set of combinations of currently connected camera devices, that may have 177 * sessions configured concurrently. The set of combinations will be empty if no such 178 * combinations are supported by the camera subsystem. 179 * 180 * @throws CameraAccessException if the camera device has been disconnected. 181 */ 182 @NonNull getConcurrentCameraIds()183 public Set<Set<String>> getConcurrentCameraIds() throws CameraAccessException { 184 return CameraManagerGlobal.get().getConcurrentCameraIds(); 185 } 186 187 /** 188 * Checks whether the provided set of camera devices and their corresponding 189 * {@link SessionConfiguration} can be configured concurrently. 190 * 191 * <p>This method performs a runtime check of the given {@link SessionConfiguration} and camera 192 * id combinations. The result confirms whether or not the passed session configurations can be 193 * successfully used to create camera capture sessions concurrently, on the given camera 194 * devices using {@link CameraDevice#createCaptureSession(SessionConfiguration)}. 195 * </p> 196 * 197 * <p>The method can be called at any point before, during and after active capture sessions. 198 * It will not impact normal camera behavior in any way and must complete significantly 199 * faster than creating a regular or constrained capture session.</p> 200 * 201 * <p>Although this method is faster than creating a new capture session, it is not intended 202 * to be used for exploring the entire space of supported concurrent stream combinations. The 203 * available mandatory concurrent stream combinations may be obtained by querying 204 * {@link #getCameraCharacteristics} for the key 205 * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}. </p> 206 * 207 * <p>Note that session parameters will be ignored and calls to 208 * {@link SessionConfiguration#setSessionParameters} are not required.</p> 209 * 210 * @return {@code true} if the given combination of session configurations and corresponding 211 * camera ids are concurrently supported by the camera sub-system, 212 * {@code false} otherwise OR if the set of camera devices provided is not a subset of 213 * those returned by {@link #getConcurrentCameraIds}. 214 * 215 * @throws CameraAccessException if one of the camera devices queried is no longer connected. 216 * 217 */ 218 @RequiresPermission(android.Manifest.permission.CAMERA) isConcurrentSessionConfigurationSupported( @onNull Map<String, SessionConfiguration> cameraIdAndSessionConfig)219 public boolean isConcurrentSessionConfigurationSupported( 220 @NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig) 221 throws CameraAccessException { 222 return CameraManagerGlobal.get().isConcurrentSessionConfigurationSupported( 223 cameraIdAndSessionConfig, mContext.getApplicationInfo().targetSdkVersion); 224 } 225 226 /** 227 * Register a callback to be notified about camera device availability. 228 * 229 * <p>Registering the same callback again will replace the handler with the 230 * new one provided.</p> 231 * 232 * <p>The first time a callback is registered, it is immediately called 233 * with the availability status of all currently known camera devices.</p> 234 * 235 * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera 236 * device is opened by any camera API client. As of API level 23, other camera API clients may 237 * still be able to open such a camera device, evicting the existing client if they have higher 238 * priority than the existing client of a camera device. See open() for more details.</p> 239 * 240 * <p>Since this callback will be registered with the camera service, remember to unregister it 241 * once it is no longer needed; otherwise the callback will continue to receive events 242 * indefinitely and it may prevent other resources from being released. Specifically, the 243 * callbacks will be invoked independently of the general activity lifecycle and independently 244 * of the state of individual CameraManager instances.</p> 245 * 246 * @param callback the new callback to send camera availability notices to 247 * @param handler The handler on which the callback should be invoked, or {@code null} to use 248 * the current thread's {@link android.os.Looper looper}. 249 * 250 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has 251 * no looper. 252 */ registerAvailabilityCallback(@onNull AvailabilityCallback callback, @Nullable Handler handler)253 public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback, 254 @Nullable Handler handler) { 255 CameraManagerGlobal.get().registerAvailabilityCallback(callback, 256 CameraDeviceImpl.checkAndWrapHandler(handler)); 257 } 258 259 /** 260 * Register a callback to be notified about camera device availability. 261 * 262 * <p>The behavior of this method matches that of 263 * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)}, 264 * except that it uses {@link java.util.concurrent.Executor} as an argument 265 * instead of {@link android.os.Handler}.</p> 266 * 267 * @param executor The executor which will be used to invoke the callback. 268 * @param callback the new callback to send camera availability notices to 269 * 270 * @throws IllegalArgumentException if the executor is {@code null}. 271 */ registerAvailabilityCallback(@onNull @allbackExecutor Executor executor, @NonNull AvailabilityCallback callback)272 public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor, 273 @NonNull AvailabilityCallback callback) { 274 if (executor == null) { 275 throw new IllegalArgumentException("executor was null"); 276 } 277 CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor); 278 } 279 280 /** 281 * Remove a previously-added callback; the callback will no longer receive connection and 282 * disconnection callbacks. 283 * 284 * <p>Removing a callback that isn't registered has no effect.</p> 285 * 286 * @param callback The callback to remove from the notification list 287 */ unregisterAvailabilityCallback(@onNull AvailabilityCallback callback)288 public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) { 289 CameraManagerGlobal.get().unregisterAvailabilityCallback(callback); 290 } 291 292 /** 293 * Register a callback to be notified about torch mode status. 294 * 295 * <p>Registering the same callback again will replace the handler with the 296 * new one provided.</p> 297 * 298 * <p>The first time a callback is registered, it is immediately called 299 * with the torch mode status of all currently known camera devices with a flash unit.</p> 300 * 301 * <p>Since this callback will be registered with the camera service, remember to unregister it 302 * once it is no longer needed; otherwise the callback will continue to receive events 303 * indefinitely and it may prevent other resources from being released. Specifically, the 304 * callbacks will be invoked independently of the general activity lifecycle and independently 305 * of the state of individual CameraManager instances.</p> 306 * 307 * @param callback The new callback to send torch mode status to 308 * @param handler The handler on which the callback should be invoked, or {@code null} to use 309 * the current thread's {@link android.os.Looper looper}. 310 * 311 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has 312 * no looper. 313 */ registerTorchCallback(@onNull TorchCallback callback, @Nullable Handler handler)314 public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) { 315 CameraManagerGlobal.get().registerTorchCallback(callback, 316 CameraDeviceImpl.checkAndWrapHandler(handler)); 317 } 318 319 /** 320 * Register a callback to be notified about torch mode status. 321 * 322 * <p>The behavior of this method matches that of 323 * {@link #registerTorchCallback(TorchCallback, Handler)}, 324 * except that it uses {@link java.util.concurrent.Executor} as an argument 325 * instead of {@link android.os.Handler}.</p> 326 * 327 * @param executor The executor which will be used to invoke the callback 328 * @param callback The new callback to send torch mode status to 329 * 330 * @throws IllegalArgumentException if the executor is {@code null}. 331 */ registerTorchCallback(@onNull @allbackExecutor Executor executor, @NonNull TorchCallback callback)332 public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor, 333 @NonNull TorchCallback callback) { 334 if (executor == null) { 335 throw new IllegalArgumentException("executor was null"); 336 } 337 CameraManagerGlobal.get().registerTorchCallback(callback, executor); 338 } 339 340 /** 341 * Remove a previously-added callback; the callback will no longer receive torch mode status 342 * callbacks. 343 * 344 * <p>Removing a callback that isn't registered has no effect.</p> 345 * 346 * @param callback The callback to remove from the notification list 347 */ unregisterTorchCallback(@onNull TorchCallback callback)348 public void unregisterTorchCallback(@NonNull TorchCallback callback) { 349 CameraManagerGlobal.get().unregisterTorchCallback(callback); 350 } 351 352 // TODO(b/147726300): Investigate how to support foldables/multi-display devices. getDisplaySize()353 private Size getDisplaySize() { 354 Size ret = new Size(0, 0); 355 356 try { 357 DisplayManager displayManager = 358 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); 359 Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); 360 if (display != null) { 361 int width = display.getWidth(); 362 int height = display.getHeight(); 363 364 if (height > width) { 365 height = width; 366 width = display.getHeight(); 367 } 368 369 ret = new Size(width, height); 370 } else { 371 Log.e(TAG, "Invalid default display!"); 372 } 373 } catch (Exception e) { 374 Log.e(TAG, "getDisplaySize Failed. " + e.toString()); 375 } 376 377 return ret; 378 } 379 380 /** 381 * Get all physical cameras' multi-resolution stream configuration map 382 * 383 * <p>For a logical multi-camera, query the map between physical camera id and 384 * the physical camera's multi-resolution stream configuration. This map is in turn 385 * combined to form the logical camera's multi-resolution stream configuration map.</p> 386 * 387 * <p>For an ultra high resolution camera, directly use 388 * android.scaler.physicalCameraMultiResolutionStreamConfigurations as the camera device's 389 * multi-resolution stream configuration map.</p> 390 */ getPhysicalCameraMultiResolutionConfigs( String cameraId, CameraMetadataNative info, ICameraService cameraService)391 private Map<String, StreamConfiguration[]> getPhysicalCameraMultiResolutionConfigs( 392 String cameraId, CameraMetadataNative info, ICameraService cameraService) 393 throws CameraAccessException { 394 HashMap<String, StreamConfiguration[]> multiResolutionStreamConfigurations = 395 new HashMap<String, StreamConfiguration[]>(); 396 397 Boolean multiResolutionStreamSupported = info.get( 398 CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED); 399 if (multiResolutionStreamSupported == null || !multiResolutionStreamSupported) { 400 return multiResolutionStreamConfigurations; 401 } 402 403 // Query the characteristics of all physical sub-cameras, and combine the multi-resolution 404 // stream configurations. Alternatively, for ultra-high resolution camera, direclty use 405 // its multi-resolution stream configurations. Note that framework derived formats such as 406 // HEIC and DEPTH_JPEG aren't supported as multi-resolution input or output formats. 407 Set<String> physicalCameraIds = info.getPhysicalCameraIds(); 408 if (physicalCameraIds.size() == 0 && info.isUltraHighResolutionSensor()) { 409 StreamConfiguration[] configs = info.get(CameraCharacteristics. 410 SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS); 411 if (configs != null) { 412 multiResolutionStreamConfigurations.put(cameraId, configs); 413 } 414 return multiResolutionStreamConfigurations; 415 } 416 try { 417 for (String physicalCameraId : physicalCameraIds) { 418 CameraMetadataNative physicalCameraInfo = 419 cameraService.getCameraCharacteristics(physicalCameraId, 420 mContext.getApplicationInfo().targetSdkVersion); 421 StreamConfiguration[] configs = physicalCameraInfo.get( 422 CameraCharacteristics. 423 SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS); 424 if (configs != null) { 425 multiResolutionStreamConfigurations.put(physicalCameraId, configs); 426 } 427 } 428 } catch (RemoteException e) { 429 ServiceSpecificException sse = new ServiceSpecificException( 430 ICameraService.ERROR_DISCONNECTED, 431 "Camera service is currently unavailable"); 432 throwAsPublicException(sse); 433 } 434 435 return multiResolutionStreamConfigurations; 436 } 437 438 /** 439 * <p>Query the capabilities of a camera device. These capabilities are 440 * immutable for a given camera.</p> 441 * 442 * <p>From API level 29, this function can also be used to query the capabilities of physical 443 * cameras that can only be used as part of logical multi-camera. These cameras cannot be 444 * opened directly via {@link #openCamera}</p> 445 * 446 * <p>Also starting with API level 29, while most basic camera information is still available 447 * even without the CAMERA permission, some values are not available to apps that do not hold 448 * that permission. The keys not available are listed by 449 * {@link CameraCharacteristics#getKeysNeedingPermission}.</p> 450 * 451 * @param cameraId The id of the camera device to query. This could be either a standalone 452 * camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that 453 * can only used as part of a logical multi-camera. 454 * @return The properties of the given camera 455 * 456 * @throws IllegalArgumentException if the cameraId does not match any 457 * known camera device. 458 * @throws CameraAccessException if the camera device has been disconnected. 459 * 460 * @see #getCameraIdList 461 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 462 */ 463 @NonNull getCameraCharacteristics(@onNull String cameraId)464 public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId) 465 throws CameraAccessException { 466 CameraCharacteristics characteristics = null; 467 if (CameraManagerGlobal.sCameraServiceDisabled) { 468 throw new IllegalArgumentException("No cameras available on device"); 469 } 470 synchronized (mLock) { 471 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 472 if (cameraService == null) { 473 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 474 "Camera service is currently unavailable"); 475 } 476 try { 477 Size displaySize = getDisplaySize(); 478 479 CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId, 480 mContext.getApplicationInfo().targetSdkVersion); 481 try { 482 info.setCameraId(Integer.parseInt(cameraId)); 483 } catch (NumberFormatException e) { 484 Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer"); 485 } 486 487 boolean hasConcurrentStreams = 488 CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId); 489 info.setHasMandatoryConcurrentStreams(hasConcurrentStreams); 490 info.setDisplaySize(displaySize); 491 492 Map<String, StreamConfiguration[]> multiResolutionSizeMap = 493 getPhysicalCameraMultiResolutionConfigs(cameraId, info, cameraService); 494 if (multiResolutionSizeMap.size() > 0) { 495 info.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap); 496 } 497 498 characteristics = new CameraCharacteristics(info); 499 } catch (ServiceSpecificException e) { 500 throwAsPublicException(e); 501 } catch (RemoteException e) { 502 // Camera service died - act as if the camera was disconnected 503 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 504 "Camera service is currently unavailable", e); 505 } 506 } 507 return characteristics; 508 } 509 510 /** 511 * <p>Query the camera extension capabilities of a camera device.</p> 512 * 513 * @param cameraId The id of the camera device to query. This must be a standalone 514 * camera ID which can be directly opened by {@link #openCamera}. 515 * @return The properties of the given camera 516 * 517 * @throws IllegalArgumentException if the cameraId does not match any 518 * known camera device. 519 * @throws CameraAccessException if the camera device has been disconnected. 520 * 521 * @see CameraExtensionCharacteristics 522 * @see CameraDevice#createExtensionSession(ExtensionSessionConfiguration) 523 * @see CameraExtensionSession 524 */ 525 @NonNull getCameraExtensionCharacteristics( @onNull String cameraId)526 public CameraExtensionCharacteristics getCameraExtensionCharacteristics( 527 @NonNull String cameraId) throws CameraAccessException { 528 CameraCharacteristics chars = getCameraCharacteristics(cameraId); 529 return new CameraExtensionCharacteristics(mContext, cameraId, chars); 530 } 531 getPhysicalIdToCharsMap( CameraCharacteristics chars)532 private Map<String, CameraCharacteristics> getPhysicalIdToCharsMap( 533 CameraCharacteristics chars) throws CameraAccessException { 534 HashMap<String, CameraCharacteristics> physicalIdsToChars = 535 new HashMap<String, CameraCharacteristics>(); 536 Set<String> physicalCameraIds = chars.getPhysicalCameraIds(); 537 for (String physicalCameraId : physicalCameraIds) { 538 CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId); 539 physicalIdsToChars.put(physicalCameraId, physicalChars); 540 } 541 return physicalIdsToChars; 542 } 543 544 /** 545 * Helper for opening a connection to a camera with the given ID. 546 * 547 * @param cameraId The unique identifier of the camera device to open 548 * @param callback The callback for the camera. Must not be null. 549 * @param executor The executor to invoke the callback with. Must not be null. 550 * @param uid The UID of the application actually opening the camera. 551 * Must be USE_CALLING_UID unless the caller is a service 552 * that is trusted to open the device on behalf of an 553 * application and to forward the real UID. 554 * 555 * @throws CameraAccessException if the camera is disabled by device policy, 556 * too many camera devices are already open, or the cameraId does not match 557 * any currently available camera device. 558 * 559 * @throws SecurityException if the application does not have permission to 560 * access the camera 561 * @throws IllegalArgumentException if callback or handler is null. 562 * @return A handle to the newly-created camera device. 563 * 564 * @see #getCameraIdList 565 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 566 */ openCameraDeviceUserAsync(String cameraId, CameraDevice.StateCallback callback, Executor executor, final int uid, final int oomScoreOffset)567 private CameraDevice openCameraDeviceUserAsync(String cameraId, 568 CameraDevice.StateCallback callback, Executor executor, final int uid, 569 final int oomScoreOffset) throws CameraAccessException { 570 CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); 571 CameraDevice device = null; 572 Map<String, CameraCharacteristics> physicalIdsToChars = 573 getPhysicalIdToCharsMap(characteristics); 574 synchronized (mLock) { 575 576 ICameraDeviceUser cameraUser = null; 577 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl = 578 new android.hardware.camera2.impl.CameraDeviceImpl( 579 cameraId, 580 callback, 581 executor, 582 characteristics, 583 physicalIdsToChars, 584 mContext.getApplicationInfo().targetSdkVersion, 585 mContext); 586 587 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks(); 588 589 try { 590 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 591 if (cameraService == null) { 592 throw new ServiceSpecificException( 593 ICameraService.ERROR_DISCONNECTED, 594 "Camera service is currently unavailable"); 595 } 596 cameraUser = cameraService.connectDevice(callbacks, cameraId, 597 mContext.getOpPackageName(), mContext.getAttributionTag(), uid, 598 oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion); 599 } catch (ServiceSpecificException e) { 600 if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) { 601 throw new AssertionError("Should've gone down the shim path"); 602 } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE || 603 e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE || 604 e.errorCode == ICameraService.ERROR_DISABLED || 605 e.errorCode == ICameraService.ERROR_DISCONNECTED || 606 e.errorCode == ICameraService.ERROR_INVALID_OPERATION) { 607 // Received one of the known connection errors 608 // The remote camera device cannot be connected to, so 609 // set the local camera to the startup error state 610 deviceImpl.setRemoteFailure(e); 611 612 if (e.errorCode == ICameraService.ERROR_DISABLED || 613 e.errorCode == ICameraService.ERROR_DISCONNECTED || 614 e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) { 615 // Per API docs, these failures call onError and throw 616 throwAsPublicException(e); 617 } 618 } else { 619 // Unexpected failure - rethrow 620 throwAsPublicException(e); 621 } 622 } catch (RemoteException e) { 623 // Camera service died - act as if it's a CAMERA_DISCONNECTED case 624 ServiceSpecificException sse = new ServiceSpecificException( 625 ICameraService.ERROR_DISCONNECTED, 626 "Camera service is currently unavailable"); 627 deviceImpl.setRemoteFailure(sse); 628 throwAsPublicException(sse); 629 } 630 631 // TODO: factor out callback to be non-nested, then move setter to constructor 632 // For now, calling setRemoteDevice will fire initial 633 // onOpened/onUnconfigured callbacks. 634 // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if 635 // cameraUser dies during setup. 636 deviceImpl.setRemoteDevice(cameraUser); 637 device = deviceImpl; 638 } 639 640 return device; 641 } 642 643 /** 644 * Open a connection to a camera with the given ID. 645 * 646 * <p>Use {@link #getCameraIdList} to get the list of available camera 647 * devices. Note that even if an id is listed, open may fail if the device 648 * is disconnected between the calls to {@link #getCameraIdList} and 649 * {@link #openCamera}, or if a higher-priority camera API client begins using the 650 * camera device.</p> 651 * 652 * <p>As of API level 23, devices for which the 653 * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the 654 * device being in use by a lower-priority, background camera API client can still potentially 655 * be opened by calling this method when the calling camera API client has a higher priority 656 * than the current camera API client using this device. In general, if the top, foreground 657 * activity is running within your application process, your process will be given the highest 658 * priority when accessing the camera, and this method will succeed even if the camera device is 659 * in use by another camera API client. Any lower-priority application that loses control of the 660 * camera in this way will receive an 661 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback. 662 * Opening the same camera ID twice in the same application will similarly cause the 663 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback 664 * being fired for the {@link CameraDevice} from the first open call and all ongoing tasks 665 * being droppped.</p> 666 * 667 * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will 668 * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up 669 * for operation by calling {@link CameraDevice#createCaptureSession} and 670 * {@link CameraDevice#createCaptureRequest}</p> 671 * 672 * <p>Before API level 30, when the application tries to open multiple {@link CameraDevice} of 673 * different IDs and the device does not support opening such combination, either the 674 * {@link #openCamera} will fail and throw a {@link CameraAccessException} or one or more of 675 * already opened {@link CameraDevice} will be disconnected and receive 676 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback. Which 677 * behavior will happen depends on the device implementation and can vary on different devices. 678 * Starting in API level 30, if the device does not support the combination of cameras being 679 * opened, it is guaranteed the {@link #openCamera} call will fail and none of existing 680 * {@link CameraDevice} will be disconnected.</p> 681 * 682 * <!-- 683 * <p>Since the camera device will be opened asynchronously, any asynchronous operations done 684 * on the returned CameraDevice instance will be queued up until the device startup has 685 * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is 686 * called. The pending operations are then processed in order.</p> 687 * --> 688 * <p>If the camera becomes disconnected during initialization 689 * after this function call returns, 690 * {@link CameraDevice.StateCallback#onDisconnected} with a 691 * {@link CameraDevice} in the disconnected state (and 692 * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p> 693 * 694 * <p>If opening the camera device fails, then the device callback's 695 * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent 696 * calls on the camera device will throw a {@link CameraAccessException}.</p> 697 * 698 * @param cameraId 699 * The unique identifier of the camera device to open 700 * @param callback 701 * The callback which is invoked once the camera is opened 702 * @param handler 703 * The handler on which the callback should be invoked, or 704 * {@code null} to use the current thread's {@link android.os.Looper looper}. 705 * 706 * @throws CameraAccessException if the camera is disabled by device policy, 707 * has been disconnected, is being used by a higher-priority camera API client, or the device 708 * has reached its maximal resource and cannot open this camera device. 709 * 710 * @throws IllegalArgumentException if cameraId or the callback was null, 711 * or the cameraId does not match any currently or previously available 712 * camera device returned by {@link #getCameraIdList}. 713 * 714 * @throws SecurityException if the application does not have permission to 715 * access the camera 716 * 717 * @see #getCameraIdList 718 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 719 */ 720 @RequiresPermission(android.Manifest.permission.CAMERA) openCamera(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)721 public void openCamera(@NonNull String cameraId, 722 @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler) 723 throws CameraAccessException { 724 725 openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler), 726 USE_CALLING_UID); 727 } 728 729 /** 730 * Open a connection to a camera with the given ID. 731 * 732 * <p>The behavior of this method matches that of 733 * {@link #openCamera(String, StateCallback, Handler)}, except that it uses 734 * {@link java.util.concurrent.Executor} as an argument instead of 735 * {@link android.os.Handler}.</p> 736 * 737 * @param cameraId 738 * The unique identifier of the camera device to open 739 * @param executor 740 * The executor which will be used when invoking the callback. 741 * @param callback 742 * The callback which is invoked once the camera is opened 743 * 744 * @throws CameraAccessException if the camera is disabled by device policy, 745 * has been disconnected, or is being used by a higher-priority camera API client. 746 * 747 * @throws IllegalArgumentException if cameraId, the callback or the executor was null, 748 * or the cameraId does not match any currently or previously available 749 * camera device. 750 * 751 * @throws SecurityException if the application does not have permission to 752 * access the camera 753 * 754 * @see #getCameraIdList 755 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 756 */ 757 @RequiresPermission(android.Manifest.permission.CAMERA) openCamera(@onNull String cameraId, @NonNull @CallbackExecutor Executor executor, @NonNull final CameraDevice.StateCallback callback)758 public void openCamera(@NonNull String cameraId, 759 @NonNull @CallbackExecutor Executor executor, 760 @NonNull final CameraDevice.StateCallback callback) 761 throws CameraAccessException { 762 if (executor == null) { 763 throw new IllegalArgumentException("executor was null"); 764 } 765 openCameraForUid(cameraId, callback, executor, USE_CALLING_UID); 766 } 767 768 /** 769 * Open a connection to a camera with the given ID. Also specify what oom score must be offset 770 * by cameraserver for this client. This api can be useful for system 771 * components which want to assume a lower priority (for camera arbitration) than other clients 772 * which it might contend for camera devices with. Increasing the oom score of a client reduces 773 * its priority when the camera framework manages camera arbitration. 774 * Considering typical use cases: 775 * 776 * 1) oom score(apps hosting activities visible to the user) - oom score(of a foreground app) 777 * is approximately 100. 778 * 779 * 2) The oom score (process which hosts components which that are perceptible to the user / 780 * native vendor camera clients) - oom (foreground app) is approximately 200. 781 * 782 * 3) The oom score (process which is cached hosting activities not visible) - oom (foreground 783 * app) is approximately 999. 784 * 785 * <p>The behavior of this method matches that of 786 * {@link #openCamera(String, StateCallback, Handler)}, except that it uses 787 * {@link java.util.concurrent.Executor} as an argument instead of 788 * {@link android.os.Handler}.</p> 789 * 790 * @param cameraId 791 * The unique identifier of the camera device to open 792 * @param executor 793 * The executor which will be used when invoking the callback. 794 * @param callback 795 * The callback which is invoked once the camera is opened 796 * @param oomScoreOffset 797 * The value by which the oom score of this client must be offset by the camera 798 * framework in order to assist it with camera arbitration. This value must be > 0. 799 * A positive value lowers the priority of this camera client compared to what the 800 * camera framework would have originally seen. 801 * 802 * @throws CameraAccessException if the camera is disabled by device policy, 803 * has been disconnected, or is being used by a higher-priority camera API client. 804 * 805 * @throws IllegalArgumentException if cameraId, the callback or the executor was null, 806 * or the cameraId does not match any currently or previously available 807 * camera device. 808 * 809 * @throws SecurityException if the application does not have permission to 810 * access the camera 811 * 812 * @see #getCameraIdList 813 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 814 * 815 * @hide 816 */ 817 @SystemApi 818 @TestApi 819 @RequiresPermission(allOf = { 820 android.Manifest.permission.SYSTEM_CAMERA, 821 android.Manifest.permission.CAMERA, 822 }) openCamera(@onNull String cameraId, int oomScoreOffset, @NonNull @CallbackExecutor Executor executor, @NonNull final CameraDevice.StateCallback callback)823 public void openCamera(@NonNull String cameraId, int oomScoreOffset, 824 @NonNull @CallbackExecutor Executor executor, 825 @NonNull final CameraDevice.StateCallback callback) throws CameraAccessException { 826 if (executor == null) { 827 throw new IllegalArgumentException("executor was null"); 828 } 829 if (oomScoreOffset < 0) { 830 throw new IllegalArgumentException( 831 "oomScoreOffset < 0, cannot increase priority of camera client"); 832 } 833 openCameraForUid(cameraId, callback, executor, USE_CALLING_UID, oomScoreOffset); 834 } 835 836 /** 837 * Open a connection to a camera with the given ID, on behalf of another application 838 * specified by clientUid. Also specify the minimum oom score and process state the application 839 * should have, as seen by the cameraserver. 840 * 841 * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows 842 * the caller to specify the UID to use for permission/etc verification. This can only be 843 * done by services trusted by the camera subsystem to act on behalf of applications and 844 * to forward the real UID.</p> 845 * 846 * @param clientUid 847 * The UID of the application on whose behalf the camera is being opened. 848 * Must be USE_CALLING_UID unless the caller is a trusted service. 849 * @param oomScoreOffset 850 * The minimum oom score that cameraservice must see for this client. 851 * @hide 852 */ openCameraForUid(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int clientUid, int oomScoreOffset)853 public void openCameraForUid(@NonNull String cameraId, 854 @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, 855 int clientUid, int oomScoreOffset) throws CameraAccessException { 856 857 if (cameraId == null) { 858 throw new IllegalArgumentException("cameraId was null"); 859 } else if (callback == null) { 860 throw new IllegalArgumentException("callback was null"); 861 } 862 if (CameraManagerGlobal.sCameraServiceDisabled) { 863 throw new IllegalArgumentException("No cameras available on device"); 864 } 865 866 openCameraDeviceUserAsync(cameraId, callback, executor, clientUid, oomScoreOffset); 867 } 868 869 /** 870 * Open a connection to a camera with the given ID, on behalf of another application 871 * specified by clientUid. 872 * 873 * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows 874 * the caller to specify the UID to use for permission/etc verification. This can only be 875 * done by services trusted by the camera subsystem to act on behalf of applications and 876 * to forward the real UID.</p> 877 * 878 * @param clientUid 879 * The UID of the application on whose behalf the camera is being opened. 880 * Must be USE_CALLING_UID unless the caller is a trusted service. 881 * 882 * @hide 883 */ openCameraForUid(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int clientUid)884 public void openCameraForUid(@NonNull String cameraId, 885 @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, 886 int clientUid) throws CameraAccessException { 887 openCameraForUid(cameraId, callback, executor, clientUid, /*oomScoreOffset*/0); 888 } 889 890 /** 891 * Set the flash unit's torch mode of the camera of the given ID without opening the camera 892 * device. 893 * 894 * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use 895 * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit. 896 * Note that even if a camera device has a flash unit, turning on the torch mode may fail 897 * if the camera device or other camera resources needed to turn on the torch mode are in use. 898 * </p> 899 * 900 * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully, 901 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked. 902 * However, even if turning on the torch mode is successful, the application does not have the 903 * exclusive ownership of the flash unit or the camera device. The torch mode will be turned 904 * off and becomes unavailable when the camera device that the flash unit belongs to becomes 905 * unavailable or when other camera resources to keep the torch on become unavailable ( 906 * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also, 907 * other applications are free to call {@link #setTorchMode} to turn off the torch mode ( 908 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest 909 * application that turned on the torch mode exits, the torch mode will be turned off. 910 * 911 * @param cameraId 912 * The unique identifier of the camera device that the flash unit belongs to. 913 * @param enabled 914 * The desired state of the torch mode for the target camera device. Set to 915 * {@code true} to turn on the torch mode. Set to {@code false} to turn off the 916 * torch mode. 917 * 918 * @throws CameraAccessException if it failed to access the flash unit. 919 * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device 920 * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if 921 * other camera resources needed to turn on the torch mode are in use. 922 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera 923 * service is not available. 924 * 925 * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently 926 * or previously available camera device, or the camera device doesn't have a 927 * flash unit. 928 */ setTorchMode(@onNull String cameraId, boolean enabled)929 public void setTorchMode(@NonNull String cameraId, boolean enabled) 930 throws CameraAccessException { 931 if (CameraManagerGlobal.sCameraServiceDisabled) { 932 throw new IllegalArgumentException("No cameras available on device"); 933 } 934 CameraManagerGlobal.get().setTorchMode(cameraId, enabled); 935 } 936 937 /** 938 * A callback for camera devices becoming available or unavailable to open. 939 * 940 * <p>Cameras become available when they are no longer in use, or when a new 941 * removable camera is connected. They become unavailable when some 942 * application or service starts using a camera, or when a removable camera 943 * is disconnected.</p> 944 * 945 * <p>Extend this callback and pass an instance of the subclass to 946 * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability 947 * changes.</p> 948 * 949 * @see #registerAvailabilityCallback 950 */ 951 public static abstract class AvailabilityCallback { 952 953 /** 954 * A new camera has become available to use. 955 * 956 * <p>The default implementation of this method does nothing.</p> 957 * 958 * @param cameraId The unique identifier of the new camera. 959 */ onCameraAvailable(@onNull String cameraId)960 public void onCameraAvailable(@NonNull String cameraId) { 961 // default empty implementation 962 } 963 964 /** 965 * A previously-available camera has become unavailable for use. 966 * 967 * <p>If an application had an active CameraDevice instance for the 968 * now-disconnected camera, that application will receive a 969 * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p> 970 * 971 * <p>The default implementation of this method does nothing.</p> 972 * 973 * @param cameraId The unique identifier of the disconnected camera. 974 */ onCameraUnavailable(@onNull String cameraId)975 public void onCameraUnavailable(@NonNull String cameraId) { 976 // default empty implementation 977 } 978 979 /** 980 * Called whenever camera access priorities change. 981 * 982 * <p>Notification that camera access priorities have changed and the camera may 983 * now be openable. An application that was previously denied camera access due to 984 * a higher-priority user already using the camera, or that was disconnected from an 985 * active camera session due to a higher-priority user trying to open the camera, 986 * should try to open the camera again if it still wants to use it. Note that 987 * multiple applications may receive this callback at the same time, and only one of 988 * them will succeed in opening the camera in practice, depending on exact access 989 * priority levels and timing. This method is useful in cases where multiple 990 * applications may be in the resumed state at the same time, and the user switches 991 * focus between them, or if the current camera-using application moves between 992 * full-screen and Picture-in-Picture (PiP) states. In such cases, the camera 993 * available/unavailable callbacks will not be invoked, but another application may 994 * now have higher priority for camera access than the current camera-using 995 * application.</p> 996 * 997 * <p>The default implementation of this method does nothing.</p> 998 * 999 */ onCameraAccessPrioritiesChanged()1000 public void onCameraAccessPrioritiesChanged() { 1001 // default empty implementation 1002 } 1003 1004 /** 1005 * A physical camera has become available for use again. 1006 * 1007 * <p>By default, all of the physical cameras of a logical multi-camera are 1008 * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical 1009 * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical 1010 * multi-camera is invoked. However, if some specific physical cameras are unavailable 1011 * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after 1012 * {@link #onCameraAvailable}.</p> 1013 * 1014 * <p>The default implementation of this method does nothing.</p> 1015 * 1016 * @param cameraId The unique identifier of the logical multi-camera. 1017 * @param physicalCameraId The unique identifier of the physical camera. 1018 * 1019 * @see #onCameraAvailable 1020 * @see #onPhysicalCameraUnavailable 1021 */ onPhysicalCameraAvailable(@onNull String cameraId, @NonNull String physicalCameraId)1022 public void onPhysicalCameraAvailable(@NonNull String cameraId, 1023 @NonNull String physicalCameraId) { 1024 // default empty implementation 1025 } 1026 1027 /** 1028 * A previously-available physical camera has become unavailable for use. 1029 * 1030 * <p>By default, all of the physical cameras of a logical multi-camera are 1031 * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical 1032 * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical 1033 * multi-camera is invoked. If some specific physical cameras are unavailable 1034 * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after 1035 * {@link #onCameraAvailable}.</p> 1036 * 1037 * <p>The default implementation of this method does nothing.</p> 1038 * 1039 * @param cameraId The unique identifier of the logical multi-camera. 1040 * @param physicalCameraId The unique identifier of the physical camera. 1041 * 1042 * @see #onCameraAvailable 1043 * @see #onPhysicalCameraAvailable 1044 */ onPhysicalCameraUnavailable(@onNull String cameraId, @NonNull String physicalCameraId)1045 public void onPhysicalCameraUnavailable(@NonNull String cameraId, 1046 @NonNull String physicalCameraId) { 1047 // default empty implementation 1048 } 1049 1050 /** 1051 * A camera device has been opened by an application. 1052 * 1053 * <p>The default implementation of this method does nothing.</p> 1054 * android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER is required to receive this 1055 * callback 1056 * @param cameraId The unique identifier of the camera opened. 1057 * @param packageId The package Id of the application opening the camera. 1058 * 1059 * @see #onCameraClosed 1060 * @hide 1061 */ 1062 @SystemApi 1063 @TestApi 1064 @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER) onCameraOpened(@onNull String cameraId, @NonNull String packageId)1065 public void onCameraOpened(@NonNull String cameraId, @NonNull String packageId) { 1066 // default empty implementation 1067 } 1068 1069 /** 1070 * A previously-opened camera has been closed. 1071 * 1072 * <p>The default implementation of this method does nothing.</p> 1073 * android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER is required to receive this 1074 * callback. 1075 * @param cameraId The unique identifier of the closed camera. 1076 * @hide 1077 */ 1078 @SystemApi 1079 @TestApi 1080 @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER) onCameraClosed(@onNull String cameraId)1081 public void onCameraClosed(@NonNull String cameraId) { 1082 // default empty implementation 1083 } 1084 } 1085 1086 /** 1087 * A callback for camera flash torch modes becoming unavailable, disabled, or enabled. 1088 * 1089 * <p>The torch mode becomes unavailable when the camera device it belongs to becomes 1090 * unavailable or other camera resources it needs become busy due to other higher priority 1091 * camera activities. The torch mode becomes disabled when it was turned off or when the camera 1092 * device it belongs to is no longer in use and other camera resources it needs are no longer 1093 * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to 1094 * turn off the camera's torch mode, or when an application turns on another camera's torch mode 1095 * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes 1096 * enabled when it is turned on via {@link #setTorchMode}.</p> 1097 * 1098 * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled 1099 * or enabled state.</p> 1100 * 1101 * <p>Extend this callback and pass an instance of the subclass to 1102 * {@link CameraManager#registerTorchCallback} to be notified of such status changes. 1103 * </p> 1104 * 1105 * @see #registerTorchCallback 1106 */ 1107 public static abstract class TorchCallback { 1108 /** 1109 * A camera's torch mode has become unavailable to set via {@link #setTorchMode}. 1110 * 1111 * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be 1112 * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is 1113 * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or 1114 * enabled state again.</p> 1115 * 1116 * <p>The default implementation of this method does nothing.</p> 1117 * 1118 * @param cameraId The unique identifier of the camera whose torch mode has become 1119 * unavailable. 1120 */ onTorchModeUnavailable(@onNull String cameraId)1121 public void onTorchModeUnavailable(@NonNull String cameraId) { 1122 // default empty implementation 1123 } 1124 1125 /** 1126 * A camera's torch mode has become enabled or disabled and can be changed via 1127 * {@link #setTorchMode}. 1128 * 1129 * <p>The default implementation of this method does nothing.</p> 1130 * 1131 * @param cameraId The unique identifier of the camera whose torch mode has been changed. 1132 * 1133 * @param enabled The state that the torch mode of the camera has been changed to. 1134 * {@code true} when the torch mode has become on and available to be turned 1135 * off. {@code false} when the torch mode has becomes off and available to 1136 * be turned on. 1137 */ onTorchModeChanged(@onNull String cameraId, boolean enabled)1138 public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) { 1139 // default empty implementation 1140 } 1141 } 1142 1143 /** 1144 * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces 1145 * into the correct public exceptions. 1146 * 1147 * @hide 1148 */ throwAsPublicException(Throwable t)1149 public static void throwAsPublicException(Throwable t) throws CameraAccessException { 1150 if (t instanceof ServiceSpecificException) { 1151 ServiceSpecificException e = (ServiceSpecificException) t; 1152 int reason = CameraAccessException.CAMERA_ERROR; 1153 switch(e.errorCode) { 1154 case ICameraService.ERROR_DISCONNECTED: 1155 reason = CameraAccessException.CAMERA_DISCONNECTED; 1156 break; 1157 case ICameraService.ERROR_DISABLED: 1158 reason = CameraAccessException.CAMERA_DISABLED; 1159 break; 1160 case ICameraService.ERROR_CAMERA_IN_USE: 1161 reason = CameraAccessException.CAMERA_IN_USE; 1162 break; 1163 case ICameraService.ERROR_MAX_CAMERAS_IN_USE: 1164 reason = CameraAccessException.MAX_CAMERAS_IN_USE; 1165 break; 1166 case ICameraService.ERROR_DEPRECATED_HAL: 1167 reason = CameraAccessException.CAMERA_DEPRECATED_HAL; 1168 break; 1169 case ICameraService.ERROR_ILLEGAL_ARGUMENT: 1170 case ICameraService.ERROR_ALREADY_EXISTS: 1171 throw new IllegalArgumentException(e.getMessage(), e); 1172 case ICameraService.ERROR_PERMISSION_DENIED: 1173 throw new SecurityException(e.getMessage(), e); 1174 case ICameraService.ERROR_TIMED_OUT: 1175 case ICameraService.ERROR_INVALID_OPERATION: 1176 default: 1177 reason = CameraAccessException.CAMERA_ERROR; 1178 } 1179 throw new CameraAccessException(reason, e.getMessage(), e); 1180 } else if (t instanceof DeadObjectException) { 1181 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 1182 "Camera service has died unexpectedly", 1183 t); 1184 } else if (t instanceof RemoteException) { 1185 throw new UnsupportedOperationException("An unknown RemoteException was thrown" + 1186 " which should never happen.", t); 1187 } else if (t instanceof RuntimeException) { 1188 RuntimeException e = (RuntimeException) t; 1189 throw e; 1190 } 1191 } 1192 1193 /** 1194 * Queries the camera service if a cameraId is a hidden physical camera that belongs to a 1195 * logical camera device. 1196 * 1197 * A hidden physical camera is a camera that cannot be opened by the application. But it 1198 * can be used as part of a logical camera. 1199 * 1200 * @param cameraId a non-{@code null} camera identifier 1201 * @return {@code true} if cameraId is a hidden physical camera device 1202 * 1203 * @hide 1204 */ isHiddenPhysicalCamera(String cameraId)1205 public static boolean isHiddenPhysicalCamera(String cameraId) { 1206 try { 1207 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 1208 // If no camera service, no support 1209 if (cameraService == null) return false; 1210 1211 return cameraService.isHiddenPhysicalCamera(cameraId); 1212 } catch (RemoteException e) { 1213 // Camera service is now down, no support for any API level 1214 } 1215 return false; 1216 } 1217 1218 /** 1219 * Inject the external camera to replace the internal camera session. 1220 * 1221 * <p>If injecting the external camera device fails, then the injection callback's 1222 * {@link CameraInjectionSession.InjectionStatusCallback#onInjectionError 1223 * onInjectionError} method will be called.</p> 1224 * 1225 * @param packageName It scopes the injection to a particular app. 1226 * @param internalCamId The id of one of the physical or logical cameras on the phone. 1227 * @param externalCamId The id of one of the remote cameras that are provided by the dynamic 1228 * camera HAL. 1229 * @param executor The executor which will be used when invoking the callback. 1230 * @param callback The callback which is invoked once the external camera is injected. 1231 * 1232 * @throws CameraAccessException If the camera device has been disconnected. 1233 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be 1234 * thrown if camera service is not available. 1235 * @throws SecurityException If the specific application that can cast to external 1236 * devices does not have permission to inject the external 1237 * camera. 1238 * @throws IllegalArgumentException If cameraId doesn't match any currently or previously 1239 * available camera device or some camera functions might not 1240 * work properly or the injection camera runs into a fatal 1241 * error. 1242 * @hide 1243 */ 1244 @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA) injectCamera(@onNull String packageName, @NonNull String internalCamId, @NonNull String externalCamId, @NonNull @CallbackExecutor Executor executor, @NonNull CameraInjectionSession.InjectionStatusCallback callback)1245 public void injectCamera(@NonNull String packageName, @NonNull String internalCamId, 1246 @NonNull String externalCamId, @NonNull @CallbackExecutor Executor executor, 1247 @NonNull CameraInjectionSession.InjectionStatusCallback callback) 1248 throws CameraAccessException, SecurityException, 1249 IllegalArgumentException { 1250 if (CameraManagerGlobal.sCameraServiceDisabled) { 1251 throw new IllegalArgumentException("No cameras available on device"); 1252 } 1253 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 1254 if (cameraService == null) { 1255 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 1256 "Camera service is currently unavailable"); 1257 } 1258 synchronized (mLock) { 1259 try { 1260 CameraInjectionSessionImpl injectionSessionImpl = 1261 new CameraInjectionSessionImpl(callback, executor); 1262 ICameraInjectionCallback cameraInjectionCallback = 1263 injectionSessionImpl.getCallback(); 1264 ICameraInjectionSession injectionSession = cameraService.injectCamera(packageName, 1265 internalCamId, externalCamId, cameraInjectionCallback); 1266 injectionSessionImpl.setRemoteInjectionSession(injectionSession); 1267 } catch (ServiceSpecificException e) { 1268 throwAsPublicException(e); 1269 } catch (RemoteException e) { 1270 // Camera service died - act as if it's a CAMERA_DISCONNECTED case 1271 ServiceSpecificException sse = new ServiceSpecificException( 1272 ICameraService.ERROR_DISCONNECTED, 1273 "Camera service is currently unavailable"); 1274 throwAsPublicException(sse); 1275 } 1276 } 1277 } 1278 1279 /** 1280 * A per-process global camera manager instance, to retain a connection to the camera service, 1281 * and to distribute camera availability notices to API-registered callbacks 1282 */ 1283 private static final class CameraManagerGlobal extends ICameraServiceListener.Stub 1284 implements IBinder.DeathRecipient { 1285 1286 private static final String TAG = "CameraManagerGlobal"; 1287 private final boolean DEBUG = false; 1288 1289 private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000; 1290 1291 // Singleton instance 1292 private static final CameraManagerGlobal gCameraManager = 1293 new CameraManagerGlobal(); 1294 1295 /** 1296 * This must match the ICameraService definition 1297 */ 1298 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; 1299 1300 private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1); 1301 // Camera ID -> Status map 1302 private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); 1303 // Camera ID -> (physical camera ID -> Status map) 1304 private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices = 1305 new ArrayMap<String, ArrayList<String>>(); 1306 1307 private final Set<Set<String>> mConcurrentCameraIdCombinations = 1308 new ArraySet<Set<String>>(); 1309 1310 // Registered availablility callbacks and their executors 1311 private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap = 1312 new ArrayMap<AvailabilityCallback, Executor>(); 1313 1314 // torch client binder to set the torch mode with. 1315 private Binder mTorchClientBinder = new Binder(); 1316 1317 // Camera ID -> Torch status map 1318 private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>(); 1319 1320 // Registered torch callbacks and their executors 1321 private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap = 1322 new ArrayMap<TorchCallback, Executor>(); 1323 1324 private final Object mLock = new Object(); 1325 1326 // Access only through getCameraService to deal with binder death 1327 private ICameraService mCameraService; 1328 1329 // Singleton, don't allow construction CameraManagerGlobal()1330 private CameraManagerGlobal() { 1331 } 1332 1333 public static final boolean sCameraServiceDisabled = 1334 SystemProperties.getBoolean("config.disable_cameraservice", false); 1335 get()1336 public static CameraManagerGlobal get() { 1337 return gCameraManager; 1338 } 1339 1340 @Override asBinder()1341 public IBinder asBinder() { 1342 return this; 1343 } 1344 1345 /** 1346 * Return a best-effort ICameraService. 1347 * 1348 * <p>This will be null if the camera service is not currently available. If the camera 1349 * service has died since the last use of the camera service, will try to reconnect to the 1350 * service.</p> 1351 */ getCameraService()1352 public ICameraService getCameraService() { 1353 synchronized(mLock) { 1354 connectCameraServiceLocked(); 1355 if (mCameraService == null && !sCameraServiceDisabled) { 1356 Log.e(TAG, "Camera service is unavailable"); 1357 } 1358 return mCameraService; 1359 } 1360 } 1361 1362 /** 1363 * Connect to the camera service if it's available, and set up listeners. 1364 * If the service is already connected, do nothing. 1365 * 1366 * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p> 1367 */ connectCameraServiceLocked()1368 private void connectCameraServiceLocked() { 1369 // Only reconnect if necessary 1370 if (mCameraService != null || sCameraServiceDisabled) return; 1371 1372 Log.i(TAG, "Connecting to camera service"); 1373 1374 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME); 1375 if (cameraServiceBinder == null) { 1376 // Camera service is now down, leave mCameraService as null 1377 return; 1378 } 1379 try { 1380 cameraServiceBinder.linkToDeath(this, /*flags*/ 0); 1381 } catch (RemoteException e) { 1382 // Camera service is now down, leave mCameraService as null 1383 return; 1384 } 1385 1386 ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder); 1387 1388 try { 1389 CameraMetadataNative.setupGlobalVendorTagDescriptor(); 1390 } catch (ServiceSpecificException e) { 1391 handleRecoverableSetupErrors(e); 1392 } 1393 1394 try { 1395 CameraStatus[] cameraStatuses = cameraService.addListener(this); 1396 for (CameraStatus c : cameraStatuses) { 1397 onStatusChangedLocked(c.status, c.cameraId); 1398 1399 if (c.unavailablePhysicalCameras != null) { 1400 for (String unavailPhysicalCamera : c.unavailablePhysicalCameras) { 1401 onPhysicalCameraStatusChangedLocked( 1402 ICameraServiceListener.STATUS_NOT_PRESENT, 1403 c.cameraId, unavailPhysicalCamera); 1404 } 1405 } 1406 } 1407 mCameraService = cameraService; 1408 } catch(ServiceSpecificException e) { 1409 // Unexpected failure 1410 throw new IllegalStateException("Failed to register a camera service listener", e); 1411 } catch (RemoteException e) { 1412 // Camera service is now down, leave mCameraService as null 1413 } 1414 1415 try { 1416 ConcurrentCameraIdCombination[] cameraIdCombinations = 1417 cameraService.getConcurrentCameraIds(); 1418 for (ConcurrentCameraIdCombination comb : cameraIdCombinations) { 1419 mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination()); 1420 } 1421 } catch (ServiceSpecificException e) { 1422 // Unexpected failure 1423 throw new IllegalStateException("Failed to get concurrent camera id combinations", 1424 e); 1425 } catch (RemoteException e) { 1426 // Camera service died in all probability 1427 } 1428 } 1429 extractCameraIdListLocked()1430 private String[] extractCameraIdListLocked() { 1431 String[] cameraIds = null; 1432 int idCount = 0; 1433 for (int i = 0; i < mDeviceStatus.size(); i++) { 1434 int status = mDeviceStatus.valueAt(i); 1435 if (status == ICameraServiceListener.STATUS_NOT_PRESENT 1436 || status == ICameraServiceListener.STATUS_ENUMERATING) continue; 1437 idCount++; 1438 } 1439 cameraIds = new String[idCount]; 1440 idCount = 0; 1441 for (int i = 0; i < mDeviceStatus.size(); i++) { 1442 int status = mDeviceStatus.valueAt(i); 1443 if (status == ICameraServiceListener.STATUS_NOT_PRESENT 1444 || status == ICameraServiceListener.STATUS_ENUMERATING) continue; 1445 cameraIds[idCount] = mDeviceStatus.keyAt(i); 1446 idCount++; 1447 } 1448 return cameraIds; 1449 } 1450 extractConcurrentCameraIdListLocked()1451 private Set<Set<String>> extractConcurrentCameraIdListLocked() { 1452 Set<Set<String>> concurrentCameraIds = new ArraySet<Set<String>>(); 1453 for (Set<String> cameraIds : mConcurrentCameraIdCombinations) { 1454 Set<String> extractedCameraIds = new ArraySet<String>(); 1455 for (String cameraId : cameraIds) { 1456 // if the camera id status is NOT_PRESENT or ENUMERATING; skip the device. 1457 // TODO: Would a device status NOT_PRESENT ever be in the map ? it gets removed 1458 // in the callback anyway. 1459 Integer status = mDeviceStatus.get(cameraId); 1460 if (status == null) { 1461 // camera id not present 1462 continue; 1463 } 1464 if (status == ICameraServiceListener.STATUS_ENUMERATING 1465 || status == ICameraServiceListener.STATUS_NOT_PRESENT) { 1466 continue; 1467 } 1468 extractedCameraIds.add(cameraId); 1469 } 1470 concurrentCameraIds.add(extractedCameraIds); 1471 } 1472 return concurrentCameraIds; 1473 } 1474 sortCameraIds(String[] cameraIds)1475 private static void sortCameraIds(String[] cameraIds) { 1476 // The sort logic must match the logic in 1477 // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds 1478 Arrays.sort(cameraIds, new Comparator<String>() { 1479 @Override 1480 public int compare(String s1, String s2) { 1481 int s1Int = 0, s2Int = 0; 1482 try { 1483 s1Int = Integer.parseInt(s1); 1484 } catch (NumberFormatException e) { 1485 s1Int = -1; 1486 } 1487 1488 try { 1489 s2Int = Integer.parseInt(s2); 1490 } catch (NumberFormatException e) { 1491 s2Int = -1; 1492 } 1493 1494 // Uint device IDs first 1495 if (s1Int >= 0 && s2Int >= 0) { 1496 return s1Int - s2Int; 1497 } else if (s1Int >= 0) { 1498 return -1; 1499 } else if (s2Int >= 0) { 1500 return 1; 1501 } else { 1502 // Simple string compare if both id are not uint 1503 return s1.compareTo(s2); 1504 } 1505 }}); 1506 1507 } 1508 cameraStatusesContains(CameraStatus[] cameraStatuses, String id)1509 public static boolean cameraStatusesContains(CameraStatus[] cameraStatuses, String id) { 1510 for (CameraStatus c : cameraStatuses) { 1511 if (c.cameraId.equals(id)) { 1512 return true; 1513 } 1514 } 1515 return false; 1516 } 1517 getCameraIdListNoLazy()1518 public String[] getCameraIdListNoLazy() { 1519 if (sCameraServiceDisabled) { 1520 return new String[] {}; 1521 } 1522 1523 CameraStatus[] cameraStatuses; 1524 ICameraServiceListener.Stub testListener = new ICameraServiceListener.Stub() { 1525 @Override 1526 public void onStatusChanged(int status, String id) throws RemoteException { 1527 } 1528 @Override 1529 public void onPhysicalCameraStatusChanged(int status, 1530 String id, String physicalId) throws RemoteException { 1531 } 1532 @Override 1533 public void onTorchStatusChanged(int status, String id) throws RemoteException { 1534 } 1535 @Override 1536 public void onCameraAccessPrioritiesChanged() { 1537 } 1538 @Override 1539 public void onCameraOpened(String id, String clientPackageId) { 1540 } 1541 @Override 1542 public void onCameraClosed(String id) { 1543 }}; 1544 1545 String[] cameraIds = null; 1546 synchronized (mLock) { 1547 connectCameraServiceLocked(); 1548 try { 1549 // The purpose of the addListener, removeListener pair here is to get a fresh 1550 // list of camera ids from cameraserver. We do this since for in test processes, 1551 // changes can happen w.r.t non-changeable permissions (eg: SYSTEM_CAMERA 1552 // permissions can be effectively changed by calling 1553 // adopt(drop)ShellPermissionIdentity()). 1554 // Camera devices, which have their discovery affected by these permission 1555 // changes, will not have clients get callbacks informing them about these 1556 // devices going offline (in real world scenarios, these permissions aren't 1557 // changeable). Future calls to getCameraIdList() will reflect the changes in 1558 // the camera id list after getCameraIdListNoLazy() is called. 1559 // We need to remove the torch ids which may have been associated with the 1560 // devices removed as well. This is the same situation. 1561 cameraStatuses = mCameraService.addListener(testListener); 1562 mCameraService.removeListener(testListener); 1563 for (CameraStatus c : cameraStatuses) { 1564 onStatusChangedLocked(c.status, c.cameraId); 1565 } 1566 Set<String> deviceCameraIds = mDeviceStatus.keySet(); 1567 ArrayList<String> deviceIdsToRemove = new ArrayList<String>(); 1568 for (String deviceCameraId : deviceCameraIds) { 1569 // Its possible that a device id was removed without a callback notifying 1570 // us. This may happen in case a process 'drops' system camera permissions 1571 // (even though the permission isn't a changeable one, tests may call 1572 // adoptShellPermissionIdentity() and then dropShellPermissionIdentity(). 1573 if (!cameraStatusesContains(cameraStatuses, deviceCameraId)) { 1574 deviceIdsToRemove.add(deviceCameraId); 1575 } 1576 } 1577 for (String id : deviceIdsToRemove) { 1578 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, id); 1579 mTorchStatus.remove(id); 1580 } 1581 } catch (ServiceSpecificException e) { 1582 // Unexpected failure 1583 throw new IllegalStateException("Failed to register a camera service listener", 1584 e); 1585 } catch (RemoteException e) { 1586 // Camera service is now down, leave mCameraService as null 1587 } 1588 cameraIds = extractCameraIdListLocked(); 1589 } 1590 sortCameraIds(cameraIds); 1591 return cameraIds; 1592 } 1593 1594 /** 1595 * Get a list of all camera IDs that are at least PRESENT; ignore devices that are 1596 * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone. 1597 */ getCameraIdList()1598 public String[] getCameraIdList() { 1599 String[] cameraIds = null; 1600 synchronized (mLock) { 1601 // Try to make sure we have an up-to-date list of camera devices. 1602 connectCameraServiceLocked(); 1603 cameraIds = extractCameraIdListLocked(); 1604 } 1605 sortCameraIds(cameraIds); 1606 return cameraIds; 1607 } 1608 getConcurrentCameraIds()1609 public @NonNull Set<Set<String>> getConcurrentCameraIds() { 1610 Set<Set<String>> concurrentStreamingCameraIds = null; 1611 synchronized (mLock) { 1612 // Try to make sure we have an up-to-date list of concurrent camera devices. 1613 connectCameraServiceLocked(); 1614 concurrentStreamingCameraIds = extractConcurrentCameraIdListLocked(); 1615 } 1616 // TODO: Some sort of sorting ? 1617 return concurrentStreamingCameraIds; 1618 } 1619 isConcurrentSessionConfigurationSupported( @onNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations, int targetSdkVersion)1620 public boolean isConcurrentSessionConfigurationSupported( 1621 @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations, 1622 int targetSdkVersion) throws CameraAccessException { 1623 1624 if (cameraIdsAndSessionConfigurations == null) { 1625 throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null"); 1626 } 1627 1628 int size = cameraIdsAndSessionConfigurations.size(); 1629 if (size == 0) { 1630 throw new IllegalArgumentException("camera id and session combination is empty"); 1631 } 1632 1633 synchronized (mLock) { 1634 // Go through all the elements and check if the camera ids are valid at least / 1635 // belong to one of the combinations returned by getConcurrentCameraIds() 1636 boolean subsetFound = false; 1637 for (Set<String> combination : mConcurrentCameraIdCombinations) { 1638 if (combination.containsAll(cameraIdsAndSessionConfigurations.keySet())) { 1639 subsetFound = true; 1640 } 1641 } 1642 if (!subsetFound) { 1643 Log.v(TAG, "isConcurrentSessionConfigurationSupported called with a subset of" 1644 + "camera ids not returned by getConcurrentCameraIds"); 1645 return false; 1646 } 1647 CameraIdAndSessionConfiguration [] cameraIdsAndConfigs = 1648 new CameraIdAndSessionConfiguration[size]; 1649 int i = 0; 1650 for (Map.Entry<String, SessionConfiguration> pair : 1651 cameraIdsAndSessionConfigurations.entrySet()) { 1652 cameraIdsAndConfigs[i] = 1653 new CameraIdAndSessionConfiguration(pair.getKey(), pair.getValue()); 1654 i++; 1655 } 1656 try { 1657 return mCameraService.isConcurrentSessionConfigurationSupported( 1658 cameraIdsAndConfigs, targetSdkVersion); 1659 } catch (ServiceSpecificException e) { 1660 throwAsPublicException(e); 1661 } catch (RemoteException e) { 1662 // Camera service died - act as if the camera was disconnected 1663 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 1664 "Camera service is currently unavailable", e); 1665 } 1666 } 1667 1668 return false; 1669 } 1670 1671 /** 1672 * Helper function to find out if a camera id is in the set of combinations returned by 1673 * getConcurrentCameraIds() 1674 * @param cameraId the unique identifier of the camera device to query 1675 * @return Whether the camera device was found in the set of combinations returned by 1676 * getConcurrentCameraIds 1677 */ cameraIdHasConcurrentStreamsLocked(String cameraId)1678 public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) { 1679 if (!mDeviceStatus.containsKey(cameraId)) { 1680 // physical camera ids aren't advertised in concurrent camera id combinations. 1681 if (DEBUG) { 1682 Log.v(TAG, " physical camera id " + cameraId + " is hidden." + 1683 " Available logical camera ids : " + mDeviceStatus.toString()); 1684 } 1685 return false; 1686 } 1687 for (Set<String> comb : mConcurrentCameraIdCombinations) { 1688 if (comb.contains(cameraId)) { 1689 return true; 1690 } 1691 } 1692 return false; 1693 } 1694 setTorchMode(String cameraId, boolean enabled)1695 public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException { 1696 synchronized(mLock) { 1697 1698 if (cameraId == null) { 1699 throw new IllegalArgumentException("cameraId was null"); 1700 } 1701 1702 ICameraService cameraService = getCameraService(); 1703 if (cameraService == null) { 1704 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 1705 "Camera service is currently unavailable"); 1706 } 1707 1708 try { 1709 cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder); 1710 } catch(ServiceSpecificException e) { 1711 throwAsPublicException(e); 1712 } catch (RemoteException e) { 1713 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 1714 "Camera service is currently unavailable"); 1715 } 1716 } 1717 } 1718 handleRecoverableSetupErrors(ServiceSpecificException e)1719 private void handleRecoverableSetupErrors(ServiceSpecificException e) { 1720 switch (e.errorCode) { 1721 case ICameraService.ERROR_DISCONNECTED: 1722 Log.w(TAG, e.getMessage()); 1723 break; 1724 default: 1725 throw new IllegalStateException(e); 1726 } 1727 } 1728 isAvailable(int status)1729 private boolean isAvailable(int status) { 1730 switch (status) { 1731 case ICameraServiceListener.STATUS_PRESENT: 1732 return true; 1733 default: 1734 return false; 1735 } 1736 } 1737 validStatus(int status)1738 private boolean validStatus(int status) { 1739 switch (status) { 1740 case ICameraServiceListener.STATUS_NOT_PRESENT: 1741 case ICameraServiceListener.STATUS_PRESENT: 1742 case ICameraServiceListener.STATUS_ENUMERATING: 1743 case ICameraServiceListener.STATUS_NOT_AVAILABLE: 1744 return true; 1745 default: 1746 return false; 1747 } 1748 } 1749 validTorchStatus(int status)1750 private boolean validTorchStatus(int status) { 1751 switch (status) { 1752 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE: 1753 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 1754 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: 1755 return true; 1756 default: 1757 return false; 1758 } 1759 } 1760 postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback, final Executor executor)1761 private void postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback, 1762 final Executor executor) { 1763 final long ident = Binder.clearCallingIdentity(); 1764 try { 1765 executor.execute( 1766 new Runnable() { 1767 @Override 1768 public void run() { 1769 callback.onCameraAccessPrioritiesChanged(); 1770 } 1771 }); 1772 } finally { 1773 Binder.restoreCallingIdentity(ident); 1774 } 1775 } 1776 postSingleCameraOpenedUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final String packageId)1777 private void postSingleCameraOpenedUpdate(final AvailabilityCallback callback, 1778 final Executor executor, final String id, final String packageId) { 1779 final long ident = Binder.clearCallingIdentity(); 1780 try { 1781 executor.execute( 1782 new Runnable() { 1783 @Override 1784 public void run() { 1785 callback.onCameraOpened(id, packageId); 1786 } 1787 }); 1788 } finally { 1789 Binder.restoreCallingIdentity(ident); 1790 } 1791 } 1792 postSingleCameraClosedUpdate(final AvailabilityCallback callback, final Executor executor, final String id)1793 private void postSingleCameraClosedUpdate(final AvailabilityCallback callback, 1794 final Executor executor, final String id) { 1795 final long ident = Binder.clearCallingIdentity(); 1796 try { 1797 executor.execute( 1798 new Runnable() { 1799 @Override 1800 public void run() { 1801 callback.onCameraClosed(id); 1802 } 1803 }); 1804 } finally { 1805 Binder.restoreCallingIdentity(ident); 1806 } 1807 } 1808 postSingleUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final String physicalId, final int status)1809 private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor, 1810 final String id, final String physicalId, final int status) { 1811 if (isAvailable(status)) { 1812 final long ident = Binder.clearCallingIdentity(); 1813 try { 1814 executor.execute( 1815 new Runnable() { 1816 @Override 1817 public void run() { 1818 if (physicalId == null) { 1819 callback.onCameraAvailable(id); 1820 } else { 1821 callback.onPhysicalCameraAvailable(id, physicalId); 1822 } 1823 } 1824 }); 1825 } finally { 1826 Binder.restoreCallingIdentity(ident); 1827 } 1828 } else { 1829 final long ident = Binder.clearCallingIdentity(); 1830 try { 1831 executor.execute( 1832 new Runnable() { 1833 @Override 1834 public void run() { 1835 if (physicalId == null) { 1836 callback.onCameraUnavailable(id); 1837 } else { 1838 callback.onPhysicalCameraUnavailable(id, physicalId); 1839 } 1840 } 1841 }); 1842 } finally { 1843 Binder.restoreCallingIdentity(ident); 1844 } 1845 } 1846 } 1847 postSingleTorchUpdate(final TorchCallback callback, final Executor executor, final String id, final int status)1848 private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor, 1849 final String id, final int status) { 1850 switch(status) { 1851 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 1852 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: { 1853 final long ident = Binder.clearCallingIdentity(); 1854 try { 1855 executor.execute(() -> { 1856 callback.onTorchModeChanged(id, status == 1857 ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON); 1858 }); 1859 } finally { 1860 Binder.restoreCallingIdentity(ident); 1861 } 1862 } 1863 break; 1864 default: { 1865 final long ident = Binder.clearCallingIdentity(); 1866 try { 1867 executor.execute(() -> { 1868 callback.onTorchModeUnavailable(id); 1869 }); 1870 } finally { 1871 Binder.restoreCallingIdentity(ident); 1872 } 1873 } 1874 break; 1875 } 1876 } 1877 1878 /** 1879 * Send the state of all known cameras to the provided listener, to initialize 1880 * the listener's knowledge of camera state. 1881 */ updateCallbackLocked(AvailabilityCallback callback, Executor executor)1882 private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) { 1883 for (int i = 0; i < mDeviceStatus.size(); i++) { 1884 String id = mDeviceStatus.keyAt(i); 1885 Integer status = mDeviceStatus.valueAt(i); 1886 postSingleUpdate(callback, executor, id, null /*physicalId*/, status); 1887 1888 // Send the NOT_PRESENT state for unavailable physical cameras 1889 if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) { 1890 ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id); 1891 for (String unavailableId : unavailableIds) { 1892 postSingleUpdate(callback, executor, id, unavailableId, 1893 ICameraServiceListener.STATUS_NOT_PRESENT); 1894 } 1895 } 1896 } 1897 } 1898 onStatusChangedLocked(int status, String id)1899 private void onStatusChangedLocked(int status, String id) { 1900 if (DEBUG) { 1901 Log.v(TAG, 1902 String.format("Camera id %s has status changed to 0x%x", id, status)); 1903 } 1904 1905 if (!validStatus(status)) { 1906 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id, 1907 status)); 1908 return; 1909 } 1910 1911 Integer oldStatus; 1912 if (status == ICameraServiceListener.STATUS_NOT_PRESENT) { 1913 oldStatus = mDeviceStatus.remove(id); 1914 mUnavailablePhysicalDevices.remove(id); 1915 } else { 1916 oldStatus = mDeviceStatus.put(id, status); 1917 if (oldStatus == null) { 1918 mUnavailablePhysicalDevices.put(id, new ArrayList<String>()); 1919 } 1920 } 1921 1922 if (oldStatus != null && oldStatus == status) { 1923 if (DEBUG) { 1924 Log.v(TAG, String.format( 1925 "Device status changed to 0x%x, which is what it already was", 1926 status)); 1927 } 1928 return; 1929 } 1930 1931 // TODO: consider abstracting out this state minimization + transition 1932 // into a separate 1933 // more easily testable class 1934 // i.e. (new State()).addState(STATE_AVAILABLE) 1935 // .addState(STATE_NOT_AVAILABLE) 1936 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE), 1937 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE) 1938 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE); 1939 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE); 1940 1941 // Translate all the statuses to either 'available' or 'not available' 1942 // available -> available => no new update 1943 // not available -> not available => no new update 1944 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) { 1945 if (DEBUG) { 1946 Log.v(TAG, 1947 String.format( 1948 "Device status was previously available (%b), " + 1949 " and is now again available (%b)" + 1950 "so no new client visible update will be sent", 1951 isAvailable(oldStatus), isAvailable(status))); 1952 } 1953 return; 1954 } 1955 1956 final int callbackCount = mCallbackMap.size(); 1957 for (int i = 0; i < callbackCount; i++) { 1958 Executor executor = mCallbackMap.valueAt(i); 1959 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 1960 1961 postSingleUpdate(callback, executor, id, null /*physicalId*/, status); 1962 } 1963 } // onStatusChangedLocked 1964 onPhysicalCameraStatusChangedLocked(int status, String id, String physicalId)1965 private void onPhysicalCameraStatusChangedLocked(int status, 1966 String id, String physicalId) { 1967 if (DEBUG) { 1968 Log.v(TAG, 1969 String.format("Camera id %s physical camera id %s has status " 1970 + "changed to 0x%x", id, physicalId, status)); 1971 } 1972 1973 if (!validStatus(status)) { 1974 Log.e(TAG, String.format( 1975 "Ignoring invalid device %s physical device %s status 0x%x", id, 1976 physicalId, status)); 1977 return; 1978 } 1979 1980 //TODO: Do we need to treat this as error? 1981 if (!mDeviceStatus.containsKey(id) || !isAvailable(mDeviceStatus.get(id)) 1982 || !mUnavailablePhysicalDevices.containsKey(id)) { 1983 Log.e(TAG, String.format("Camera %s is not available. Ignore physical camera " 1984 + "status change", id)); 1985 return; 1986 } 1987 1988 ArrayList<String> unavailablePhysicalDevices = mUnavailablePhysicalDevices.get(id); 1989 if (!isAvailable(status) 1990 && !unavailablePhysicalDevices.contains(physicalId)) { 1991 unavailablePhysicalDevices.add(physicalId); 1992 } else if (isAvailable(status) 1993 && unavailablePhysicalDevices.contains(physicalId)) { 1994 unavailablePhysicalDevices.remove(physicalId); 1995 } else { 1996 if (DEBUG) { 1997 Log.v(TAG, 1998 String.format( 1999 "Physical camera device status was previously available (%b), " 2000 + " and is now again available (%b)" 2001 + "so no new client visible update will be sent", 2002 !unavailablePhysicalDevices.contains(physicalId), 2003 isAvailable(status))); 2004 } 2005 return; 2006 } 2007 2008 final int callbackCount = mCallbackMap.size(); 2009 for (int i = 0; i < callbackCount; i++) { 2010 Executor executor = mCallbackMap.valueAt(i); 2011 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 2012 2013 postSingleUpdate(callback, executor, id, physicalId, status); 2014 } 2015 } // onPhysicalCameraStatusChangedLocked 2016 updateTorchCallbackLocked(TorchCallback callback, Executor executor)2017 private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) { 2018 for (int i = 0; i < mTorchStatus.size(); i++) { 2019 String id = mTorchStatus.keyAt(i); 2020 Integer status = mTorchStatus.valueAt(i); 2021 postSingleTorchUpdate(callback, executor, id, status); 2022 } 2023 } 2024 onTorchStatusChangedLocked(int status, String id)2025 private void onTorchStatusChangedLocked(int status, String id) { 2026 if (DEBUG) { 2027 Log.v(TAG, 2028 String.format("Camera id %s has torch status changed to 0x%x", id, status)); 2029 } 2030 2031 if (!validTorchStatus(status)) { 2032 Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id, 2033 status)); 2034 return; 2035 } 2036 2037 Integer oldStatus = mTorchStatus.put(id, status); 2038 if (oldStatus != null && oldStatus == status) { 2039 if (DEBUG) { 2040 Log.v(TAG, String.format( 2041 "Torch status changed to 0x%x, which is what it already was", 2042 status)); 2043 } 2044 return; 2045 } 2046 2047 final int callbackCount = mTorchCallbackMap.size(); 2048 for (int i = 0; i < callbackCount; i++) { 2049 final Executor executor = mTorchCallbackMap.valueAt(i); 2050 final TorchCallback callback = mTorchCallbackMap.keyAt(i); 2051 postSingleTorchUpdate(callback, executor, id, status); 2052 } 2053 } // onTorchStatusChangedLocked 2054 2055 /** 2056 * Register a callback to be notified about camera device availability with the 2057 * global listener singleton. 2058 * 2059 * @param callback the new callback to send camera availability notices to 2060 * @param executor The executor which should invoke the callback. May not be null. 2061 */ registerAvailabilityCallback(AvailabilityCallback callback, Executor executor)2062 public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) { 2063 synchronized (mLock) { 2064 connectCameraServiceLocked(); 2065 2066 Executor oldExecutor = mCallbackMap.put(callback, executor); 2067 // For new callbacks, provide initial availability information 2068 if (oldExecutor == null) { 2069 updateCallbackLocked(callback, executor); 2070 } 2071 2072 // If not connected to camera service, schedule a reconnect to camera service. 2073 if (mCameraService == null) { 2074 scheduleCameraServiceReconnectionLocked(); 2075 } 2076 } 2077 } 2078 2079 /** 2080 * Remove a previously-added callback; the callback will no longer receive connection and 2081 * disconnection callbacks, and is no longer referenced by the global listener singleton. 2082 * 2083 * @param callback The callback to remove from the notification list 2084 */ unregisterAvailabilityCallback(AvailabilityCallback callback)2085 public void unregisterAvailabilityCallback(AvailabilityCallback callback) { 2086 synchronized (mLock) { 2087 mCallbackMap.remove(callback); 2088 } 2089 } 2090 registerTorchCallback(TorchCallback callback, Executor executor)2091 public void registerTorchCallback(TorchCallback callback, Executor executor) { 2092 synchronized(mLock) { 2093 connectCameraServiceLocked(); 2094 2095 Executor oldExecutor = mTorchCallbackMap.put(callback, executor); 2096 // For new callbacks, provide initial torch information 2097 if (oldExecutor == null) { 2098 updateTorchCallbackLocked(callback, executor); 2099 } 2100 2101 // If not connected to camera service, schedule a reconnect to camera service. 2102 if (mCameraService == null) { 2103 scheduleCameraServiceReconnectionLocked(); 2104 } 2105 } 2106 } 2107 unregisterTorchCallback(TorchCallback callback)2108 public void unregisterTorchCallback(TorchCallback callback) { 2109 synchronized(mLock) { 2110 mTorchCallbackMap.remove(callback); 2111 } 2112 } 2113 2114 /** 2115 * Callback from camera service notifying the process about camera availability changes 2116 */ 2117 @Override onStatusChanged(int status, String cameraId)2118 public void onStatusChanged(int status, String cameraId) throws RemoteException { 2119 synchronized(mLock) { 2120 onStatusChangedLocked(status, cameraId); 2121 } 2122 } 2123 2124 @Override onPhysicalCameraStatusChanged(int status, String cameraId, String physicalCameraId)2125 public void onPhysicalCameraStatusChanged(int status, String cameraId, 2126 String physicalCameraId) throws RemoteException { 2127 synchronized (mLock) { 2128 onPhysicalCameraStatusChangedLocked(status, cameraId, physicalCameraId); 2129 } 2130 } 2131 2132 @Override onTorchStatusChanged(int status, String cameraId)2133 public void onTorchStatusChanged(int status, String cameraId) throws RemoteException { 2134 synchronized (mLock) { 2135 onTorchStatusChangedLocked(status, cameraId); 2136 } 2137 } 2138 2139 @Override onCameraAccessPrioritiesChanged()2140 public void onCameraAccessPrioritiesChanged() { 2141 synchronized (mLock) { 2142 final int callbackCount = mCallbackMap.size(); 2143 for (int i = 0; i < callbackCount; i++) { 2144 Executor executor = mCallbackMap.valueAt(i); 2145 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 2146 2147 postSingleAccessPriorityChangeUpdate(callback, executor); 2148 } 2149 } 2150 } 2151 2152 @Override onCameraOpened(String cameraId, String clientPackageId)2153 public void onCameraOpened(String cameraId, String clientPackageId) { 2154 synchronized (mLock) { 2155 final int callbackCount = mCallbackMap.size(); 2156 for (int i = 0; i < callbackCount; i++) { 2157 Executor executor = mCallbackMap.valueAt(i); 2158 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 2159 2160 postSingleCameraOpenedUpdate(callback, executor, cameraId, clientPackageId); 2161 } 2162 } 2163 } 2164 2165 @Override onCameraClosed(String cameraId)2166 public void onCameraClosed(String cameraId) { 2167 synchronized (mLock) { 2168 final int callbackCount = mCallbackMap.size(); 2169 for (int i = 0; i < callbackCount; i++) { 2170 Executor executor = mCallbackMap.valueAt(i); 2171 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 2172 2173 postSingleCameraClosedUpdate(callback, executor, cameraId); 2174 } 2175 } 2176 } 2177 2178 /** 2179 * Try to connect to camera service after some delay if any client registered camera 2180 * availability callback or torch status callback. 2181 */ scheduleCameraServiceReconnectionLocked()2182 private void scheduleCameraServiceReconnectionLocked() { 2183 if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) { 2184 // Not necessary to reconnect camera service if no client registers a callback. 2185 return; 2186 } 2187 2188 if (DEBUG) { 2189 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS + 2190 " ms"); 2191 } 2192 2193 try { 2194 mScheduler.schedule(() -> { 2195 ICameraService cameraService = getCameraService(); 2196 if (cameraService == null) { 2197 synchronized(mLock) { 2198 if (DEBUG) { 2199 Log.v(TAG, "Reconnecting Camera Service failed."); 2200 } 2201 scheduleCameraServiceReconnectionLocked(); 2202 } 2203 } 2204 }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS); 2205 } catch (RejectedExecutionException e) { 2206 Log.e(TAG, "Failed to schedule camera service re-connect: " + e); 2207 } 2208 } 2209 2210 /** 2211 * Listener for camera service death. 2212 * 2213 * <p>The camera service isn't supposed to die under any normal circumstances, but can be 2214 * turned off during debug, or crash due to bugs. So detect that and null out the interface 2215 * object, so that the next calls to the manager can try to reconnect.</p> 2216 */ binderDied()2217 public void binderDied() { 2218 synchronized(mLock) { 2219 // Only do this once per service death 2220 if (mCameraService == null) return; 2221 2222 mCameraService = null; 2223 2224 // Tell listeners that the cameras and torch modes are unavailable and schedule a 2225 // reconnection to camera service. When camera service is reconnected, the camera 2226 // and torch statuses will be updated. 2227 // Iterate from the end to the beginning befcause onStatusChangedLocked removes 2228 // entries from the ArrayMap. 2229 for (int i = mDeviceStatus.size() - 1; i >= 0; i--) { 2230 String cameraId = mDeviceStatus.keyAt(i); 2231 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId); 2232 } 2233 for (int i = 0; i < mTorchStatus.size(); i++) { 2234 String cameraId = mTorchStatus.keyAt(i); 2235 onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE, 2236 cameraId); 2237 } 2238 2239 mConcurrentCameraIdCombinations.clear(); 2240 2241 scheduleCameraServiceReconnectionLocked(); 2242 } 2243 } 2244 2245 } // CameraManagerGlobal 2246 2247 } // CameraManager 2248