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 static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT; 20 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA; 21 import static android.content.Context.DEVICE_ID_DEFAULT; 22 import static android.content.Context.DEVICE_ID_INVALID; 23 24 import android.annotation.CallbackExecutor; 25 import android.annotation.FlaggedApi; 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.annotation.RequiresPermission; 29 import android.annotation.SystemApi; 30 import android.annotation.SystemService; 31 import android.annotation.TestApi; 32 import android.app.ActivityManager; 33 import android.app.CameraCompatTaskInfo; 34 import android.app.TaskInfo; 35 import android.app.compat.CompatChanges; 36 import android.companion.virtual.VirtualDeviceManager; 37 import android.compat.annotation.ChangeId; 38 import android.compat.annotation.EnabledSince; 39 import android.compat.annotation.Overridable; 40 import android.content.AttributionSource; 41 import android.content.AttributionSourceState; 42 import android.content.Context; 43 import android.content.pm.PackageManager; 44 import android.graphics.Point; 45 import android.hardware.CameraExtensionSessionStats; 46 import android.hardware.CameraStatus; 47 import android.hardware.ICameraService; 48 import android.hardware.ICameraServiceListener; 49 import android.hardware.camera2.CameraDevice.StateCallback; 50 import android.hardware.camera2.impl.CameraDeviceImpl; 51 import android.hardware.camera2.impl.CameraDeviceSetupImpl; 52 import android.hardware.camera2.impl.CameraInjectionSessionImpl; 53 import android.hardware.camera2.impl.CameraMetadataNative; 54 import android.hardware.camera2.params.ExtensionSessionConfiguration; 55 import android.hardware.camera2.params.SessionConfiguration; 56 import android.hardware.camera2.params.StreamConfiguration; 57 import android.hardware.camera2.utils.CameraIdAndSessionConfiguration; 58 import android.hardware.camera2.utils.ConcurrentCameraIdCombination; 59 import android.hardware.camera2.utils.ExceptionUtils; 60 import android.hardware.devicestate.DeviceState; 61 import android.hardware.devicestate.DeviceStateManager; 62 import android.hardware.devicestate.feature.flags.FeatureFlags; 63 import android.hardware.devicestate.feature.flags.FeatureFlagsImpl; 64 import android.hardware.display.DisplayManager; 65 import android.os.Binder; 66 import android.os.Handler; 67 import android.os.HandlerExecutor; 68 import android.os.HandlerThread; 69 import android.os.IBinder; 70 import android.os.Process; 71 import android.os.RemoteException; 72 import android.os.ServiceManager; 73 import android.os.ServiceSpecificException; 74 import android.os.SystemProperties; 75 import android.text.TextUtils; 76 import android.util.ArrayMap; 77 import android.util.ArraySet; 78 import android.util.Log; 79 import android.util.Pair; 80 import android.util.Size; 81 import android.view.Display; 82 import android.window.DesktopModeFlags; 83 84 import com.android.internal.camera.flags.Flags; 85 import com.android.internal.util.ArrayUtils; 86 87 import java.lang.ref.WeakReference; 88 import java.util.ArrayDeque; 89 import java.util.ArrayList; 90 import java.util.Arrays; 91 import java.util.Comparator; 92 import java.util.HashMap; 93 import java.util.Iterator; 94 import java.util.List; 95 import java.util.Map; 96 import java.util.Objects; 97 import java.util.Set; 98 import java.util.concurrent.Executor; 99 import java.util.concurrent.Executors; 100 import java.util.concurrent.RejectedExecutionException; 101 import java.util.concurrent.ScheduledExecutorService; 102 import java.util.concurrent.TimeUnit; 103 104 /** 105 * <p>A system service manager for detecting, characterizing, and connecting to 106 * {@link CameraDevice CameraDevices}.</p> 107 * 108 * <p>For more details about communicating with camera devices, read the Camera 109 * developer guide or the {@link android.hardware.camera2 camera2} 110 * package documentation.</p> 111 */ 112 @SystemService(Context.CAMERA_SERVICE) 113 public final class CameraManager { 114 115 private static final String TAG = "CameraManager"; 116 private final boolean DEBUG = false; 117 118 private static final int USE_CALLING_UID = -1; 119 private static final int USE_CALLING_PID = -1; 120 121 @SuppressWarnings("unused") 122 private static final int API_VERSION_1 = 1; 123 private static final int API_VERSION_2 = 2; 124 125 private static final int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0; 126 private static final int CAMERA_TYPE_ALL = 1; 127 128 /** 129 * Caches the mapping between a logical camera ID and 'MultiResolutionStreamConfigurationMap' 130 * that is calculated by {@link #getPhysicalCameraMultiResolutionConfigs} as the calculation 131 * might take many binder calls. 132 * <p> 133 * Note, this is a map of maps. The structure is: 134 * <pre> 135 * { 136 * logicalCameraId_1 -> { 137 * physicalCameraId_1 -> [ 138 * streamConfiguration_1, 139 * streamConfiguration_2, 140 * ... 141 * ], 142 * physicalCameraId_2 -> [...], 143 * ... 144 * }, 145 * logicalCameraId_2 -> { 146 * ... 147 * }, 148 * ... 149 * } 150 * </pre> 151 * </p> 152 */ 153 private final Map<String, Map<String, StreamConfiguration[]>> 154 mCameraIdToMultiResolutionStreamConfigurationMap = new HashMap<>(); 155 156 private final Context mContext; 157 private final Object mLock = new Object(); 158 159 private static final String CAMERA_OPEN_CLOSE_LISTENER_PERMISSION = 160 "android.permission.CAMERA_OPEN_CLOSE_LISTENER"; 161 private final boolean mHasOpenCloseListenerPermission; 162 163 private VirtualDeviceManager mVirtualDeviceManager; 164 165 /** 166 * Force camera output to be rotated to portrait orientation on landscape cameras. 167 * Many apps do not handle this situation and display stretched images otherwise. 168 * @hide 169 */ 170 @ChangeId 171 @Overridable 172 @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE) 173 @TestApi 174 public static final long OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT = 250678880L; 175 176 /** 177 * System property for allowing the above 178 * @hide 179 */ 180 @TestApi 181 public static final String LANDSCAPE_TO_PORTRAIT_PROP = 182 "camera.enable_landscape_to_portrait"; 183 184 /** 185 * Does not override landscape feed to portrait. 186 * 187 * @hide 188 */ 189 @TestApi 190 @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) 191 public static final int ROTATION_OVERRIDE_NONE = ICameraService.ROTATION_OVERRIDE_NONE; 192 193 /** 194 * Crops and rotates landscape camera feed to portrait, and changes sensor orientation to 195 * portrait. 196 * 197 * @hide 198 */ 199 @TestApi 200 @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) 201 public static final int ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT = 202 ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT; 203 204 /** 205 * Crops and rotates landscape camera feed to portrait, but doesn't change sensor orientation. 206 * 207 * @hide 208 */ 209 @TestApi 210 @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) 211 public static final int ROTATION_OVERRIDE_ROTATION_ONLY = 212 ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY; 213 214 /** 215 * Enable physical camera availability callbacks when the logical camera is unavailable 216 * 217 * <p>Previously once a logical camera becomes unavailable, no 218 * {@link AvailabilityCallback#onPhysicalCameraAvailable} or 219 * {@link AvailabilityCallback#onPhysicalCameraUnavailable} will 220 * be called until the logical camera becomes available again. The 221 * results in the app opening the logical camera not able to 222 * receive physical camera availability change.</p> 223 * 224 * <p>With this change, the {@link 225 * AvailabilityCallback#onPhysicalCameraAvailable} and {@link 226 * AvailabilityCallback#onPhysicalCameraUnavailable} can still be 227 * called while the logical camera is unavailable. </p> 228 */ 229 @ChangeId 230 @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 231 private static final long ENABLE_PHYSICAL_CAMERA_CALLBACK_FOR_UNAVAILABLE_LOGICAL_CAMERA = 232 244358506L; 233 234 /** 235 * @hide 236 */ CameraManager(Context context)237 public CameraManager(Context context) { 238 synchronized(mLock) { 239 mContext = context; 240 mHasOpenCloseListenerPermission = 241 mContext.checkSelfPermission(CAMERA_OPEN_CLOSE_LISTENER_PERMISSION) == 242 PackageManager.PERMISSION_GRANTED; 243 } 244 } 245 246 /** 247 * @hide 248 */ 249 public interface DeviceStateListener { onDeviceStateChanged(boolean folded)250 void onDeviceStateChanged(boolean folded); 251 } 252 253 private static final class FoldStateListener implements DeviceStateManager.DeviceStateCallback { 254 private final int[] mFoldedDeviceStates; 255 256 private ArrayList<WeakReference<DeviceStateListener>> mDeviceStateListeners = 257 new ArrayList<>(); 258 private boolean mFoldedDeviceState; 259 private final FeatureFlags mDeviceStateManagerFlags; 260 FoldStateListener(Context context)261 public FoldStateListener(Context context) { 262 mFoldedDeviceStates = context.getResources().getIntArray( 263 com.android.internal.R.array.config_foldedDeviceStates); 264 mDeviceStateManagerFlags = new FeatureFlagsImpl(); 265 } 266 handleStateChange(DeviceState state)267 private synchronized void handleStateChange(DeviceState state) { 268 final boolean folded; 269 if (mDeviceStateManagerFlags.deviceStatePropertyMigration()) { 270 folded = state.hasProperty( 271 DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY); 272 } else { 273 folded = ArrayUtils.contains(mFoldedDeviceStates, state.getIdentifier()); 274 } 275 276 mFoldedDeviceState = folded; 277 Iterator<WeakReference<DeviceStateListener>> it = mDeviceStateListeners.iterator(); 278 while(it.hasNext()) { 279 DeviceStateListener callback = it.next().get(); 280 if (callback != null) { 281 callback.onDeviceStateChanged(folded); 282 } else { 283 it.remove(); 284 } 285 } 286 } 287 addDeviceStateListener(DeviceStateListener listener)288 public synchronized void addDeviceStateListener(DeviceStateListener listener) { 289 listener.onDeviceStateChanged(mFoldedDeviceState); 290 mDeviceStateListeners.removeIf(l -> l.get() == null); 291 mDeviceStateListeners.add(new WeakReference<>(listener)); 292 } 293 294 @SuppressWarnings("FlaggedApi") 295 @Override onDeviceStateChanged(@onNull DeviceState state)296 public void onDeviceStateChanged(@NonNull DeviceState state) { 297 handleStateChange(state); 298 } 299 } 300 301 /** 302 * Register a {@link CameraCharacteristics} device state listener 303 * 304 * @param chars Camera characteristics that need to receive device state updates 305 * 306 * @hide 307 */ registerDeviceStateListener(@onNull CameraCharacteristics chars)308 public void registerDeviceStateListener(@NonNull CameraCharacteristics chars) { 309 CameraManagerGlobal.get().registerDeviceStateListener(chars, mContext); 310 } 311 312 /** 313 * Return the list of currently connected camera devices by identifier, including 314 * cameras that may be in use by other camera API clients. 315 * 316 * <p>Non-removable cameras use integers starting at 0 for their 317 * identifiers, while removable cameras have a unique identifier for each 318 * individual device, even if they are the same model.</p> 319 * 320 * <p>This list doesn't contain physical cameras that can only be used as part of a logical 321 * multi-camera device.</p> 322 * 323 * @return The list of currently connected camera devices. 324 */ 325 @NonNull getCameraIdList()326 public String[] getCameraIdList() throws CameraAccessException { 327 return CameraManagerGlobal.get().getCameraIdList(mContext.getDeviceId(), 328 getDevicePolicyFromContext(mContext)); 329 } 330 331 /** 332 * Similar to getCameraIdList(). However, getCamerIdListNoLazy() necessarily communicates with 333 * cameraserver in order to get the list of camera ids. This is to facilitate testing since some 334 * camera ids may go 'offline' without callbacks from cameraserver because of changes in 335 * SYSTEM_CAMERA permissions (though this is not a changeable permission, tests may call 336 * adopt(drop)ShellPermissionIdentity() and effectively change their permissions). This call 337 * affects the camera ids returned by getCameraIdList() as well. Tests which do adopt shell 338 * permission identity should not mix getCameraIdList() and getCameraListNoLazyCalls(). 339 * 340 * @hide 341 */ 342 @TestApi getCameraIdListNoLazy()343 public String[] getCameraIdListNoLazy() throws CameraAccessException { 344 return CameraManagerGlobal.get().getCameraIdListNoLazy(mContext.getDeviceId(), 345 getDevicePolicyFromContext(mContext)); 346 } 347 348 /** 349 * Return the set of combinations of currently connected camera device identifiers, which 350 * support configuring camera device sessions concurrently. 351 * 352 * <p>The devices in these combinations can be concurrently configured by the same 353 * client camera application. Using these camera devices concurrently by two different 354 * applications is not guaranteed to be supported, however.</p> 355 * 356 * <p>For concurrent operation, in chronological order : 357 * <ul> 358 * <li> Applications must first close any open cameras that have sessions configured, using 359 * {@link CameraDevice#close}. </li> 360 * <li> All camera devices intended to be operated concurrently, must be opened using 361 * {@link #openCamera}, before configuring sessions on any of the camera devices.</li> 362 *</ul> 363 *</p> 364 * <p>Each device in a combination, is guaranteed to support stream combinations which may be 365 * obtained by querying {@link #getCameraCharacteristics} for the key 366 * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}.</p> 367 * 368 * <p>For concurrent operation, if a camera device has a non null zoom ratio range as specified 369 * by 370 * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}, 371 * its complete zoom ratio range may not apply. Applications can use 372 * {@link android.hardware.camera2.CaptureRequest#CONTROL_ZOOM_RATIO} >=1 and <= 373 * {@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM} 374 * during concurrent operation. 375 * <p> 376 * 377 * <p>The set of combinations may include camera devices that may be in use by other camera API 378 * clients.</p> 379 * 380 * <p>Concurrent camera extension sessions {@link CameraExtensionSession} are not currently 381 * supported.</p> 382 * 383 * <p>The set of combinations doesn't contain physical cameras that can only be used as 384 * part of a logical multi-camera device.</p> 385 * 386 * <p> If a new camera id becomes available through 387 * {@link AvailabilityCallback#onCameraUnavailable(String)}, clients can call 388 * this method to check if new combinations of camera ids which can stream concurrently are 389 * available. 390 * 391 * @return The set of combinations of currently connected camera devices, that may have 392 * sessions configured concurrently. The set of combinations will be empty if no such 393 * combinations are supported by the camera subsystem. 394 * 395 * @throws CameraAccessException if the camera device has been disconnected. 396 */ 397 @NonNull getConcurrentCameraIds()398 public Set<Set<String>> getConcurrentCameraIds() throws CameraAccessException { 399 return CameraManagerGlobal.get().getConcurrentCameraIds(mContext.getDeviceId(), 400 getDevicePolicyFromContext(mContext)); 401 } 402 403 /** 404 * Checks whether the provided set of camera devices and their corresponding 405 * {@link SessionConfiguration} can be configured concurrently. 406 * 407 * <p>This method performs a runtime check of the given {@link SessionConfiguration} and camera 408 * id combinations. The result confirms whether or not the passed session configurations can be 409 * successfully used to create camera capture sessions concurrently, on the given camera 410 * devices using {@link CameraDevice#createCaptureSession(SessionConfiguration)}. 411 * </p> 412 * 413 * <p>The method can be called at any point before, during and after active capture sessions. 414 * It will not impact normal camera behavior in any way and must complete significantly 415 * faster than creating a regular or constrained capture session.</p> 416 * 417 * <p>Although this method is faster than creating a new capture session, it is not intended 418 * to be used for exploring the entire space of supported concurrent stream combinations. The 419 * available mandatory concurrent stream combinations may be obtained by querying 420 * {@link #getCameraCharacteristics} for the key 421 * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}. </p> 422 * 423 * <p>Note that session parameters will be ignored and calls to 424 * {@link SessionConfiguration#setSessionParameters} are not required.</p> 425 * 426 * @return {@code true} if the given combination of session configurations and corresponding 427 * camera ids are concurrently supported by the camera sub-system, 428 * {@code false} otherwise OR if the set of camera devices provided is not a subset of 429 * those returned by {@link #getConcurrentCameraIds}. 430 * 431 * @throws CameraAccessException if one of the camera devices queried is no longer connected. 432 * 433 */ 434 @RequiresPermission(android.Manifest.permission.CAMERA) isConcurrentSessionConfigurationSupported( @onNull Map<String, SessionConfiguration> cameraIdAndSessionConfig)435 public boolean isConcurrentSessionConfigurationSupported( 436 @NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig) 437 throws CameraAccessException { 438 return CameraManagerGlobal.get() 439 .isConcurrentSessionConfigurationSupported( 440 cameraIdAndSessionConfig, 441 mContext.getApplicationInfo().targetSdkVersion, 442 getClientAttribution(), 443 getDevicePolicyFromContext(mContext)); 444 } 445 446 /** 447 * Register a callback to be notified about camera device availability. 448 * 449 * <p>Registering the same callback again will replace the handler with the 450 * new one provided.</p> 451 * 452 * <p>The first time a callback is registered, it is immediately called 453 * with the availability status of all currently known camera devices.</p> 454 * 455 * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera 456 * device is opened by any camera API client. As of API level 23, other camera API clients may 457 * still be able to open such a camera device, evicting the existing client if they have higher 458 * priority than the existing client of a camera device. See open() for more details.</p> 459 * 460 * <p>Since this callback will be registered with the camera service, remember to unregister it 461 * once it is no longer needed; otherwise the callback will continue to receive events 462 * indefinitely and it may prevent other resources from being released. Specifically, the 463 * callbacks will be invoked independently of the general activity lifecycle and independently 464 * of the state of individual CameraManager instances.</p> 465 * 466 * @param callback the new callback to send camera availability notices to 467 * @param handler The handler on which the callback should be invoked, or {@code null} to use 468 * the current thread's {@link android.os.Looper looper}. 469 * 470 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has 471 * no looper. 472 */ registerAvailabilityCallback(@onNull AvailabilityCallback callback, @Nullable Handler handler)473 public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback, 474 @Nullable Handler handler) { 475 CameraManagerGlobal.get().registerAvailabilityCallback(callback, 476 CameraDeviceImpl.checkAndWrapHandler(handler), mHasOpenCloseListenerPermission, 477 mContext.getDeviceId(), getDevicePolicyFromContext(mContext)); 478 } 479 480 /** 481 * Register a callback to be notified about camera device availability. 482 * 483 * <p>The behavior of this method matches that of 484 * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)}, 485 * except that it uses {@link java.util.concurrent.Executor} as an argument 486 * instead of {@link android.os.Handler}.</p> 487 * 488 * <p>Note: If the order between some availability callbacks matters, the implementation of the 489 * executor should handle those callbacks in the same thread to maintain the callbacks' order. 490 * Some examples are:</p> 491 * 492 * <ul> 493 * 494 * <li>{@link AvailabilityCallback#onCameraAvailable} and 495 * {@link AvailabilityCallback#onCameraUnavailable} of the same camera ID.</li> 496 * 497 * <li>{@link AvailabilityCallback#onCameraAvailable} or 498 * {@link AvailabilityCallback#onCameraUnavailable} of a logical multi-camera, and {@link 499 * AvailabilityCallback#onPhysicalCameraUnavailable} or 500 * {@link AvailabilityCallback#onPhysicalCameraAvailable} of its physical 501 * cameras.</li> 502 * 503 * </ul> 504 * 505 * @param executor The executor which will be used to invoke the callback. 506 * @param callback the new callback to send camera availability notices to 507 * 508 * @throws IllegalArgumentException if the executor is {@code null}. 509 */ registerAvailabilityCallback(@onNull @allbackExecutor Executor executor, @NonNull AvailabilityCallback callback)510 public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor, 511 @NonNull AvailabilityCallback callback) { 512 if (executor == null) { 513 throw new IllegalArgumentException("executor was null"); 514 } 515 CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor, 516 mHasOpenCloseListenerPermission, mContext.getDeviceId(), 517 getDevicePolicyFromContext(mContext)); 518 } 519 520 /** 521 * Remove a previously-added callback; the callback will no longer receive connection and 522 * disconnection callbacks. 523 * 524 * <p>Removing a callback that isn't registered has no effect.</p> 525 * 526 * @param callback The callback to remove from the notification list 527 */ unregisterAvailabilityCallback(@onNull AvailabilityCallback callback)528 public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) { 529 CameraManagerGlobal.get().unregisterAvailabilityCallback(callback); 530 } 531 532 /** 533 * Register a callback to be notified about torch mode status. 534 * 535 * <p>Registering the same callback again will replace the handler with the 536 * new one provided.</p> 537 * 538 * <p>The first time a callback is registered, it is immediately called 539 * with the torch mode status of all currently known camera devices with a flash unit.</p> 540 * 541 * <p>Since this callback will be registered with the camera service, remember to unregister it 542 * once it is no longer needed; otherwise the callback will continue to receive events 543 * indefinitely and it may prevent other resources from being released. Specifically, the 544 * callbacks will be invoked independently of the general activity lifecycle and independently 545 * of the state of individual CameraManager instances.</p> 546 * 547 * @param callback The new callback to send torch mode status to 548 * @param handler The handler on which the callback should be invoked, or {@code null} to use 549 * the current thread's {@link android.os.Looper looper}. 550 * 551 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has 552 * no looper. 553 */ registerTorchCallback(@onNull TorchCallback callback, @Nullable Handler handler)554 public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) { 555 CameraManagerGlobal.get().registerTorchCallback(callback, 556 CameraDeviceImpl.checkAndWrapHandler(handler), mContext.getDeviceId(), 557 getDevicePolicyFromContext(mContext)); 558 } 559 560 /** 561 * Register a callback to be notified about torch mode status. 562 * 563 * <p>The behavior of this method matches that of 564 * {@link #registerTorchCallback(TorchCallback, Handler)}, 565 * except that it uses {@link java.util.concurrent.Executor} as an argument 566 * instead of {@link android.os.Handler}.</p> 567 * 568 * @param executor The executor which will be used to invoke the callback 569 * @param callback The new callback to send torch mode status to 570 * 571 * @throws IllegalArgumentException if the executor is {@code null}. 572 */ registerTorchCallback(@onNull @allbackExecutor Executor executor, @NonNull TorchCallback callback)573 public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor, 574 @NonNull TorchCallback callback) { 575 if (executor == null) { 576 throw new IllegalArgumentException("executor was null"); 577 } 578 CameraManagerGlobal.get().registerTorchCallback(callback, executor, mContext.getDeviceId(), 579 getDevicePolicyFromContext(mContext)); 580 } 581 582 /** 583 * Remove a previously-added callback; the callback will no longer receive torch mode status 584 * callbacks. 585 * 586 * <p>Removing a callback that isn't registered has no effect.</p> 587 * 588 * @param callback The callback to remove from the notification list 589 */ unregisterTorchCallback(@onNull TorchCallback callback)590 public void unregisterTorchCallback(@NonNull TorchCallback callback) { 591 CameraManagerGlobal.get().unregisterTorchCallback(callback); 592 } 593 594 /** @hide */ getDevicePolicyFromContext(@onNull Context context)595 public int getDevicePolicyFromContext(@NonNull Context context) { 596 if (context.getDeviceId() == DEVICE_ID_DEFAULT) { 597 return DEVICE_POLICY_DEFAULT; 598 } 599 600 if (mVirtualDeviceManager == null) { 601 mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class); 602 } 603 return mVirtualDeviceManager == null 604 ? DEVICE_POLICY_DEFAULT 605 : mVirtualDeviceManager.getDevicePolicy(context.getDeviceId(), POLICY_TYPE_CAMERA); 606 } 607 608 // TODO(b/147726300): Investigate how to support foldables/multi-display devices. getDisplaySize()609 private Size getDisplaySize() { 610 Size ret = new Size(0, 0); 611 612 try { 613 DisplayManager displayManager = 614 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); 615 Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); 616 if (display != null) { 617 Point sz = new Point(); 618 display.getRealSize(sz); 619 int width = sz.x; 620 int height = sz.y; 621 622 if (height > width) { 623 height = width; 624 width = sz.y; 625 } 626 627 ret = new Size(width, height); 628 } else { 629 Log.e(TAG, "Invalid default display!"); 630 } 631 } catch (Exception e) { 632 Log.e(TAG, "getDisplaySize Failed. " + e); 633 } 634 635 return ret; 636 } 637 638 /** 639 * Get all physical cameras' multi-resolution stream configuration map 640 * 641 * <p>For a logical multi-camera, query the map between physical camera id and 642 * the physical camera's multi-resolution stream configuration. This map is in turn 643 * combined to form the logical camera's multi-resolution stream configuration map.</p> 644 * 645 * <p>For an ultra high resolution camera, directly use 646 * android.scaler.physicalCameraMultiResolutionStreamConfigurations as the camera device's 647 * multi-resolution stream configuration map.</p> 648 */ getPhysicalCameraMultiResolutionConfigs( String cameraId, CameraMetadataNative info, ICameraService cameraService)649 private Map<String, StreamConfiguration[]> getPhysicalCameraMultiResolutionConfigs( 650 String cameraId, CameraMetadataNative info, ICameraService cameraService) 651 throws CameraAccessException { 652 if (mCameraIdToMultiResolutionStreamConfigurationMap.containsKey(cameraId)) { 653 return mCameraIdToMultiResolutionStreamConfigurationMap.get(cameraId); 654 } 655 656 HashMap<String, StreamConfiguration[]> multiResolutionStreamConfigurations = 657 new HashMap<>(); 658 mCameraIdToMultiResolutionStreamConfigurationMap.put(cameraId, 659 multiResolutionStreamConfigurations); 660 661 Boolean multiResolutionStreamSupported = info.get( 662 CameraCharacteristics.SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED); 663 if (multiResolutionStreamSupported == null || !multiResolutionStreamSupported) { 664 return multiResolutionStreamConfigurations; 665 } 666 667 // Query the characteristics of all physical sub-cameras, and combine the multi-resolution 668 // stream configurations. Alternatively, for ultra-high resolution camera, directly use 669 // its multi-resolution stream configurations. Note that framework derived formats such as 670 // HEIC and DEPTH_JPEG aren't supported as multi-resolution input or output formats. 671 Set<String> physicalCameraIds = info.getPhysicalCameraIds(); 672 if (physicalCameraIds.size() == 0 && info.isUltraHighResolutionSensor()) { 673 StreamConfiguration[] configs = info.get(CameraCharacteristics. 674 SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS); 675 if (configs != null) { 676 multiResolutionStreamConfigurations.put(cameraId, configs); 677 } 678 return multiResolutionStreamConfigurations; 679 } 680 try { 681 for (String physicalCameraId : physicalCameraIds) { 682 AttributionSourceState clientAttribution = getClientAttribution(DEVICE_ID_DEFAULT, 683 /* useContextAttributionSource= */ false); 684 CameraMetadataNative physicalCameraInfo = 685 cameraService.getCameraCharacteristics( 686 physicalCameraId, 687 mContext.getApplicationInfo().targetSdkVersion, 688 /*rotationOverride*/ ICameraService.ROTATION_OVERRIDE_NONE, 689 clientAttribution, 690 DEVICE_POLICY_DEFAULT); 691 StreamConfiguration[] configs = physicalCameraInfo.get( 692 CameraCharacteristics. 693 SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS); 694 if (configs != null) { 695 multiResolutionStreamConfigurations.put(physicalCameraId, configs); 696 } 697 } 698 } catch (RemoteException e) { 699 ServiceSpecificException sse = new ServiceSpecificException( 700 ICameraService.ERROR_DISCONNECTED, 701 "Camera service is currently unavailable"); 702 throw ExceptionUtils.throwAsPublicException(sse); 703 } 704 705 return multiResolutionStreamConfigurations; 706 } 707 708 /** 709 * <p>Query the capabilities of a camera device. These capabilities are 710 * immutable for a given camera.</p> 711 * 712 * <p>From API level 29, this function can also be used to query the capabilities of physical 713 * cameras that can only be used as part of logical multi-camera. These cameras cannot be 714 * opened directly via {@link #openCamera}</p> 715 * 716 * <p>Also starting with API level 29, while most basic camera information is still available 717 * even without the CAMERA permission, some values are not available to apps that do not hold 718 * that permission. The keys not available are listed by 719 * {@link CameraCharacteristics#getKeysNeedingPermission}.</p> 720 * 721 * @param cameraId The id of the camera device to query. This could be either a standalone 722 * camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that 723 * can only used as part of a logical multi-camera. 724 * @return The properties of the given camera 725 * 726 * @throws IllegalArgumentException if the cameraId does not match any 727 * known camera device. 728 * @throws CameraAccessException if the camera device has been disconnected. 729 * 730 * @see #getCameraIdList 731 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 732 */ 733 @NonNull getCameraCharacteristics(@onNull String cameraId)734 public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId) 735 throws CameraAccessException { 736 return getCameraCharacteristics(cameraId, getRotationOverride(mContext)); 737 } 738 739 /** 740 * <p>Query the capabilities of a camera device. These capabilities are 741 * immutable for a given camera.</p> 742 * 743 * <p>The value of {@link CameraCharacteristics.SENSOR_ORIENTATION} will change for landscape 744 * cameras depending on whether overrideToPortrait is enabled. If enabled, these cameras will 745 * appear to be portrait orientation instead, provided that the override is supported by the 746 * camera device. Only devices that can be opened by {@link #openCamera} will report a changed 747 * {@link CameraCharacteristics.SENSOR_ORIENTATION}.</p> 748 * 749 * @param cameraId The id of the camera device to query. This could be either a standalone 750 * camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that 751 * can only used as part of a logical multi-camera. 752 * @param overrideToPortrait Whether to apply the landscape to portrait override. 753 * @return The properties of the given camera 754 * 755 * @hide 756 */ 757 @TestApi 758 @NonNull getCameraCharacteristics(@onNull String cameraId, boolean overrideToPortrait)759 public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId, 760 boolean overrideToPortrait) throws CameraAccessException { 761 return getCameraCharacteristics(cameraId, 762 overrideToPortrait 763 ? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT 764 : ICameraService.ROTATION_OVERRIDE_NONE); 765 } 766 767 @NonNull getCameraCharacteristics(@onNull String cameraId, int rotationOverride)768 private CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId, 769 int rotationOverride) throws CameraAccessException { 770 CameraCharacteristics characteristics = null; 771 if (CameraManagerGlobal.sCameraServiceDisabled) { 772 throw new IllegalArgumentException("No cameras available on device"); 773 } 774 synchronized (mLock) { 775 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 776 if (cameraService == null) { 777 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 778 "Camera service is currently unavailable"); 779 } 780 try { 781 CameraMetadataNative info = 782 cameraService.getCameraCharacteristics( 783 cameraId, 784 mContext.getApplicationInfo().targetSdkVersion, 785 rotationOverride, 786 getClientAttribution(), 787 getDevicePolicyFromContext(mContext)); 788 characteristics = prepareCameraCharacteristics(cameraId, info, cameraService); 789 } catch (ServiceSpecificException e) { 790 throw ExceptionUtils.throwAsPublicException(e); 791 } catch (RemoteException e) { 792 // Camera service died - act as if the camera was disconnected 793 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 794 "Camera service is currently unavailable", e); 795 } 796 } 797 registerDeviceStateListener(characteristics); 798 return characteristics; 799 } 800 801 802 /** 803 * Utility method to take a {@link CameraMetadataNative} object and wrap it into a 804 * {@link CameraCharacteristics} object that has all required fields and keys set and is fit 805 * for apps to consume. 806 * 807 * @param cameraId camera Id that the CameraMetadataNative was fetched for. 808 * @param metadata base CameraMetadataNative to be wrapped 809 * @param cameraService remote cameraservice instance to be used if binder calls need 810 * to be made. 811 * @return A CameraCharacteristics object that can be used by the apps. 812 * @hide 813 */ 814 @NonNull prepareCameraCharacteristics( @onNull String cameraId, CameraMetadataNative metadata, ICameraService cameraService)815 public CameraCharacteristics prepareCameraCharacteristics( 816 @NonNull String cameraId, CameraMetadataNative metadata, ICameraService cameraService) 817 throws CameraAccessException { 818 synchronized (mLock) { 819 try { 820 metadata.setCameraId(Integer.parseInt(cameraId)); 821 } catch (NumberFormatException e) { 822 Log.v(TAG, "Failed to parse camera Id " + cameraId + " to integer"); 823 } 824 825 boolean hasConcurrentStreams = 826 CameraManagerGlobal.get().cameraIdHasConcurrentStreams(cameraId, 827 mContext.getDeviceId(), getDevicePolicyFromContext(mContext)); 828 metadata.setHasMandatoryConcurrentStreams(hasConcurrentStreams); 829 830 Size displaySize = getDisplaySize(); 831 metadata.setDisplaySize(displaySize); 832 833 Map<String, StreamConfiguration[]> multiResolutionSizeMap = 834 getPhysicalCameraMultiResolutionConfigs(cameraId, metadata, cameraService); 835 if (!multiResolutionSizeMap.isEmpty()) { 836 metadata.setMultiResolutionStreamConfigurationMap(multiResolutionSizeMap); 837 } 838 839 return new CameraCharacteristics(metadata); 840 } 841 } 842 843 /** 844 * <p>Query the camera extension capabilities of a camera device.</p> 845 * 846 * @param cameraId The id of the camera device to query. This must be a standalone 847 * camera ID which can be directly opened by {@link #openCamera}. 848 * @return The properties of the given camera 849 * 850 * @throws IllegalArgumentException if the cameraId does not match any 851 * known camera device. 852 * @throws CameraAccessException if the camera device has been disconnected. 853 * 854 * @see CameraExtensionCharacteristics 855 * @see CameraDevice#createExtensionSession(ExtensionSessionConfiguration) 856 * @see CameraExtensionSession 857 */ 858 @NonNull getCameraExtensionCharacteristics( @onNull String cameraId)859 public CameraExtensionCharacteristics getCameraExtensionCharacteristics( 860 @NonNull String cameraId) throws CameraAccessException { 861 CameraCharacteristics chars = getCameraCharacteristics(cameraId); 862 Map<String, CameraCharacteristics> characteristicsMap = getPhysicalIdToCharsMap(chars); 863 characteristicsMap.put(cameraId, chars); 864 865 return new CameraExtensionCharacteristics(mContext, cameraId, characteristicsMap); 866 } 867 868 /** 869 * @hide 870 */ getPhysicalIdToCharsMap( CameraCharacteristics chars)871 public Map<String, CameraCharacteristics> getPhysicalIdToCharsMap( 872 CameraCharacteristics chars) throws CameraAccessException { 873 HashMap<String, CameraCharacteristics> physicalIdsToChars = 874 new HashMap<String, CameraCharacteristics>(); 875 Set<String> physicalCameraIds = chars.getPhysicalCameraIds(); 876 for (String physicalCameraId : physicalCameraIds) { 877 CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId); 878 physicalIdsToChars.put(physicalCameraId, physicalChars); 879 } 880 return physicalIdsToChars; 881 } 882 883 /** 884 * Returns a {@link CameraDevice.CameraDeviceSetup} object for the given {@code cameraId}, 885 * which provides limited access to CameraDevice setup and query functionality without 886 * requiring an {@link #openCamera} call. The {@link CameraDevice} can later be obtained either 887 * by calling {@link #openCamera}, or {@link CameraDevice.CameraDeviceSetup#openCamera}. 888 * 889 * <p>Support for {@link CameraDevice.CameraDeviceSetup} for a given {@code cameraId} must be 890 * checked with {@link #isCameraDeviceSetupSupported}. If {@code isCameraDeviceSetupSupported} 891 * returns {@code false} for a {@code cameraId}, this method will throw an 892 * {@link UnsupportedOperationException}</p> 893 * 894 * @param cameraId The unique identifier of the camera device for which 895 * {@link CameraDevice.CameraDeviceSetup} object must be constructed. This 896 * identifier must be present in {@link #getCameraIdList()} 897 * 898 * @return {@link CameraDevice.CameraDeviceSetup} object corresponding to the provided 899 * {@code cameraId} 900 * 901 * @throws IllegalArgumentException If {@code cameraId} is null, or if {@code cameraId} does not 902 * match any device in {@link #getCameraIdList()}. 903 * @throws CameraAccessException if the camera device is not accessible 904 * @throws UnsupportedOperationException if {@link CameraDevice.CameraDeviceSetup} instance 905 * cannot be constructed for the given {@code cameraId}, i.e. 906 * {@link #isCameraDeviceSetupSupported} returns false. 907 * 908 * @see CameraDevice.CameraDeviceSetup 909 * @see #getCameraIdList() 910 * @see #openCamera 911 */ 912 @NonNull 913 @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP) getCameraDeviceSetup(@onNull String cameraId)914 public CameraDevice.CameraDeviceSetup getCameraDeviceSetup(@NonNull String cameraId) 915 throws CameraAccessException { 916 // isCameraDeviceSetup does all the error checking we need. 917 if (!isCameraDeviceSetupSupported(cameraId)) { 918 throw new UnsupportedOperationException( 919 "CameraDeviceSetup is not supported for Camera ID: " + cameraId); 920 } 921 922 return getCameraDeviceSetupUnsafe(cameraId); 923 } 924 925 /** 926 * Creates and returns a {@link CameraDeviceSetup} instance without any error checking. To 927 * be used (carefully) by callers who are sure that CameraDeviceSetup instance can be legally 928 * created and don't want to pay the latency cost of calling {@link #getCameraDeviceSetup}. 929 */ getCameraDeviceSetupUnsafe(@onNull String cameraId)930 private CameraDevice.CameraDeviceSetup getCameraDeviceSetupUnsafe(@NonNull String cameraId) { 931 return new CameraDeviceSetupImpl(cameraId, /*cameraManager=*/ this, mContext); 932 } 933 934 /** 935 * Checks a Camera Device's characteristics to ensure that a 936 * {@link CameraDevice.CameraDeviceSetup} instance can be constructed for a given 937 * {@code cameraId}. If this method returns false for a {@code cameraId}, calling 938 * {@link #getCameraDeviceSetup} for that {@code cameraId} will throw an 939 * {@link UnsupportedOperationException}. 940 * 941 * <p>{@link CameraDevice.CameraDeviceSetup} is supported for all devices that report 942 * {@link CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION} > 943 * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}</p> 944 * 945 * @param cameraId The unique identifier of the camera device for which 946 * {@link CameraDevice.CameraDeviceSetup} support is being queried. This 947 * identifier must be present in {@link #getCameraIdList()}. 948 * 949 * @return {@code true} if {@link CameraDevice.CameraDeviceSetup} object can be constructed 950 * for the provided {@code cameraId}; {@code false} otherwise. 951 * 952 * @throws IllegalArgumentException If {@code cameraId} is null, or if {@code cameraId} does not 953 * match any device in {@link #getCameraIdList()}. 954 * @throws CameraAccessException if the camera device is not accessible 955 * 956 * @see CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION 957 * @see CameraDevice.CameraDeviceSetup 958 * @see #getCameraDeviceSetup(String) 959 * @see #getCameraIdList() 960 */ 961 @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP) isCameraDeviceSetupSupported(@onNull String cameraId)962 public boolean isCameraDeviceSetupSupported(@NonNull String cameraId) 963 throws CameraAccessException { 964 if (cameraId == null) { 965 throw new IllegalArgumentException("Camera ID was null"); 966 } 967 968 if (CameraManagerGlobal.sCameraServiceDisabled 969 || !Arrays.asList(CameraManagerGlobal.get().getCameraIdList(mContext.getDeviceId(), 970 getDevicePolicyFromContext(mContext))).contains(cameraId)) { 971 throw new IllegalArgumentException( 972 "Camera ID '" + cameraId + "' not available on device."); 973 } 974 975 CameraCharacteristics chars = getCameraCharacteristics(cameraId); 976 return CameraDeviceSetupImpl.isCameraDeviceSetupSupported(chars); 977 } 978 979 /** 980 * Checks if a camera device can be opened in a shared mode for a given {@code cameraId}. 981 * If this method returns false for a {@code cameraId}, calling {@link #openSharedCamera} 982 * for that {@code cameraId} will throw an {@link UnsupportedOperationException}. 983 * 984 * @param cameraId The unique identifier of the camera device for which sharing support is 985 * being queried. This identifier must be present in 986 * {@link #getCameraIdList()}. 987 * 988 * @return {@code true} if camera can be opened in shared mode 989 * for the provided {@code cameraId}; {@code false} otherwise. 990 * 991 * @throws IllegalArgumentException If {@code cameraId} is null, or if {@code cameraId} does not 992 * match any device in {@link #getCameraIdList()}. 993 * @throws CameraAccessException if the camera device has been disconnected. 994 * 995 * @see #getCameraIdList() 996 * @hide 997 */ 998 @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) 999 @SystemApi isCameraDeviceSharingSupported(@onNull String cameraId)1000 public boolean isCameraDeviceSharingSupported(@NonNull String cameraId) 1001 throws CameraAccessException { 1002 if (cameraId == null) { 1003 throw new IllegalArgumentException("Camera ID was null"); 1004 } 1005 1006 if (CameraManagerGlobal.sCameraServiceDisabled 1007 || !Arrays.asList(CameraManagerGlobal.get().getCameraIdList(mContext.getDeviceId(), 1008 getDevicePolicyFromContext(mContext))).contains(cameraId)) { 1009 throw new IllegalArgumentException( 1010 "Camera ID '" + cameraId + "' not available on device."); 1011 } 1012 1013 CameraCharacteristics chars = getCameraCharacteristics(cameraId); 1014 long[] sharedOutputConfiguration = 1015 chars.get(CameraCharacteristics.SHARED_SESSION_OUTPUT_CONFIGURATIONS); 1016 return (sharedOutputConfiguration != null); 1017 } 1018 1019 /** 1020 * Retrieves the AttributionSourceState to pass to the CameraService. 1021 * 1022 * @param deviceIdOverride An override of the AttributionSource's deviceId, if not equal to 1023 * DEVICE_ID_INVALID 1024 * @param useContextAttributionSource Whether to return the full attribution source provided by 1025 * the Context. 1026 * 1027 * @hide 1028 */ getClientAttribution(int deviceIdOverride, boolean useContextAttributionSource)1029 public AttributionSourceState getClientAttribution(int deviceIdOverride, 1030 boolean useContextAttributionSource) { 1031 AttributionSource contextAttributionSource = mContext.getAttributionSource(); 1032 if (deviceIdOverride != DEVICE_ID_INVALID) { 1033 contextAttributionSource = contextAttributionSource.withDeviceId(deviceIdOverride); 1034 } 1035 AttributionSourceState contextAttributionSourceState = 1036 contextAttributionSource.asState(); 1037 1038 if (Flags.dataDeliveryPermissionChecks() && useContextAttributionSource) { 1039 return contextAttributionSourceState; 1040 } else { 1041 AttributionSourceState clientAttribution = 1042 new AttributionSourceState(); 1043 clientAttribution.uid = USE_CALLING_UID; 1044 clientAttribution.pid = USE_CALLING_PID; 1045 clientAttribution.deviceId = contextAttributionSourceState.deviceId; 1046 clientAttribution.packageName = mContext.getOpPackageName(); 1047 clientAttribution.attributionTag = mContext.getAttributionTag(); 1048 clientAttribution.next = new AttributionSourceState[0]; 1049 return clientAttribution; 1050 } 1051 } 1052 1053 /** 1054 * Retrieves the AttributionSourceState to pass to the CameraService. 1055 * 1056 * @param useContextAttributionSource Whether to return the full attribution source provided by 1057 * the Context. 1058 * 1059 * @hide 1060 */ getClientAttribution(boolean useContextAttributionSource)1061 public AttributionSourceState getClientAttribution(boolean useContextAttributionSource) { 1062 return getClientAttribution(DEVICE_ID_INVALID, useContextAttributionSource); 1063 } 1064 1065 /** 1066 * Retrieves the AttributionSourceState to pass to the CameraService. 1067 * 1068 * @hide 1069 */ getClientAttribution()1070 public AttributionSourceState getClientAttribution() { 1071 return getClientAttribution(DEVICE_ID_INVALID, /* useContextAttributionSource= */ false); 1072 } 1073 1074 /** 1075 * Helper for opening a connection to a camera with the given ID. 1076 * 1077 * @param cameraId The unique identifier of the camera device to open 1078 * @param callback The callback for the camera. Must not be null. 1079 * @param executor The executor to invoke the callback with. Must not be null. 1080 * @param oomScoreOffset The minimum oom score that cameraservice must see for this client. 1081 * @param rotationOverride The type of rotation override. 1082 * @param sharedMode Parameter specifying if the camera should be opened in shared mode. 1083 * 1084 * @throws CameraAccessException if the camera is disabled by device policy, 1085 * too many camera devices are already open, or the cameraId does not match 1086 * any currently available camera device. 1087 * 1088 * @throws SecurityException if the application does not have permission to 1089 * access the camera 1090 * @throws IllegalArgumentException if callback or handler is null. 1091 * @return A handle to the newly-created camera device. 1092 * 1093 * @see #getCameraIdList 1094 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 1095 */ openCameraDeviceUserAsync(String cameraId, CameraDevice.StateCallback callback, Executor executor, final int oomScoreOffset, int rotationOverride, boolean sharedMode)1096 private CameraDevice openCameraDeviceUserAsync(String cameraId, 1097 CameraDevice.StateCallback callback, Executor executor, 1098 final int oomScoreOffset, int rotationOverride, boolean sharedMode) 1099 throws CameraAccessException { 1100 CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); 1101 CameraDevice device = null; 1102 synchronized (mLock) { 1103 ICameraDeviceUser cameraUser = null; 1104 CameraDevice.CameraDeviceSetup cameraDeviceSetup = null; 1105 if (Flags.cameraDeviceSetup() 1106 && CameraDeviceSetupImpl.isCameraDeviceSetupSupported(characteristics)) { 1107 cameraDeviceSetup = getCameraDeviceSetupUnsafe(cameraId); 1108 } 1109 1110 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl = 1111 new CameraDeviceImpl( 1112 cameraId, 1113 callback, 1114 executor, 1115 characteristics, 1116 this, 1117 mContext.getApplicationInfo().targetSdkVersion, 1118 mContext, cameraDeviceSetup, sharedMode); 1119 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks(); 1120 1121 try { 1122 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 1123 if (cameraService == null) { 1124 throw new ServiceSpecificException( 1125 ICameraService.ERROR_DISCONNECTED, 1126 "Camera service is currently unavailable"); 1127 } 1128 1129 AttributionSourceState clientAttribution = 1130 getClientAttribution(/* useContextAttributionSource= */ true); 1131 cameraUser = 1132 cameraService.connectDevice( 1133 callbacks, 1134 cameraId, 1135 oomScoreOffset, 1136 mContext.getApplicationInfo().targetSdkVersion, 1137 rotationOverride, 1138 clientAttribution, 1139 getDevicePolicyFromContext(mContext), sharedMode); 1140 } catch (ServiceSpecificException e) { 1141 if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) { 1142 throw new AssertionError("Should've gone down the shim path"); 1143 } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE || 1144 e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE || 1145 e.errorCode == ICameraService.ERROR_DISABLED || 1146 e.errorCode == ICameraService.ERROR_DISCONNECTED || 1147 e.errorCode == ICameraService.ERROR_INVALID_OPERATION) { 1148 // Received one of the known connection errors 1149 // The remote camera device cannot be connected to, so 1150 // set the local camera to the startup error state 1151 deviceImpl.setRemoteFailure(e); 1152 1153 if (e.errorCode == ICameraService.ERROR_DISABLED || 1154 e.errorCode == ICameraService.ERROR_DISCONNECTED || 1155 e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) { 1156 // Per API docs, these failures call onError and throw 1157 throw ExceptionUtils.throwAsPublicException(e); 1158 } 1159 } else { 1160 // Unexpected failure - rethrow 1161 throw ExceptionUtils.throwAsPublicException(e); 1162 } 1163 } catch (RemoteException e) { 1164 // Camera service died - act as if it's a CAMERA_DISCONNECTED case 1165 ServiceSpecificException sse = new ServiceSpecificException( 1166 ICameraService.ERROR_DISCONNECTED, 1167 "Camera service is currently unavailable"); 1168 deviceImpl.setRemoteFailure(sse); 1169 throw ExceptionUtils.throwAsPublicException(sse); 1170 } 1171 1172 // TODO: factor out callback to be non-nested, then move setter to constructor 1173 // For now, calling setRemoteDevice will fire initial 1174 // onOpened/onUnconfigured callbacks. 1175 // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if 1176 // cameraUser dies during setup. 1177 deviceImpl.setRemoteDevice(cameraUser); 1178 device = deviceImpl; 1179 } 1180 1181 return device; 1182 } 1183 1184 /** 1185 * Open a connection to a camera with the given ID. 1186 * 1187 * <p>Use {@link #getCameraIdList} to get the list of available camera 1188 * devices. Note that even if an id is listed, open may fail if the device 1189 * is disconnected between the calls to {@link #getCameraIdList} and 1190 * {@link #openCamera}, or if a higher-priority camera API client begins using the 1191 * camera device.</p> 1192 * 1193 * <p>As of API level 23, devices for which the 1194 * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the 1195 * device being in use by a lower-priority, background camera API client can still potentially 1196 * be opened by calling this method when the calling camera API client has a higher priority 1197 * than the current camera API client using this device. In general, if the top, foreground 1198 * activity is running within your application process, your process will be given the highest 1199 * priority when accessing the camera, and this method will succeed even if the camera device is 1200 * in use by another camera API client. Any lower-priority application that loses control of the 1201 * camera in this way will receive an 1202 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback. 1203 * Opening the same camera ID twice in the same application will similarly cause the 1204 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback 1205 * being fired for the {@link CameraDevice} from the first open call and all ongoing tasks 1206 * being dropped.</p> 1207 * 1208 * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will 1209 * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up 1210 * for operation by calling {@link CameraDevice#createCaptureSession} and 1211 * {@link CameraDevice#createCaptureRequest}</p> 1212 * 1213 * <p>Before API level 30, when the application tries to open multiple {@link CameraDevice} of 1214 * different IDs and the device does not support opening such combination, either the 1215 * {@link #openCamera} will fail and throw a {@link CameraAccessException} or one or more of 1216 * already opened {@link CameraDevice} will be disconnected and receive 1217 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback. Which 1218 * behavior will happen depends on the device implementation and can vary on different devices. 1219 * Starting in API level 30, if the device does not support the combination of cameras being 1220 * opened, it is guaranteed the {@link #openCamera} call will fail and none of existing 1221 * {@link CameraDevice} will be disconnected.</p> 1222 * 1223 * <!-- 1224 * <p>Since the camera device will be opened asynchronously, any asynchronous operations done 1225 * on the returned CameraDevice instance will be queued up until the device startup has 1226 * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is 1227 * called. The pending operations are then processed in order.</p> 1228 * --> 1229 * <p>If the camera becomes disconnected during initialization 1230 * after this function call returns, 1231 * {@link CameraDevice.StateCallback#onDisconnected} with a 1232 * {@link CameraDevice} in the disconnected state (and 1233 * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p> 1234 * 1235 * <p>If opening the camera device fails, then the device callback's 1236 * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent 1237 * calls on the camera device will throw a {@link CameraAccessException}.</p> 1238 * 1239 * @param cameraId 1240 * The unique identifier of the camera device to open 1241 * @param callback 1242 * The callback which is invoked once the camera is opened 1243 * @param handler 1244 * The handler on which the callback should be invoked, or 1245 * {@code null} to use the current thread's {@link android.os.Looper looper}. 1246 * 1247 * @throws CameraAccessException if the camera is disabled by device policy, 1248 * has been disconnected, is being used by a higher-priority camera API client, or the device 1249 * has reached its maximal resource and cannot open this camera device. 1250 * 1251 * @throws IllegalArgumentException if cameraId or the callback was null, 1252 * or the cameraId does not match any currently or previously available 1253 * camera device returned by {@link #getCameraIdList}. 1254 * 1255 * @throws SecurityException if the application does not have permission to 1256 * access the camera 1257 * 1258 * @see #getCameraIdList 1259 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 1260 */ 1261 @RequiresPermission(android.Manifest.permission.CAMERA) openCamera(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)1262 public void openCamera(@NonNull String cameraId, 1263 @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler) 1264 throws CameraAccessException { 1265 1266 openCameraImpl(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler), 1267 /*oomScoreOffset*/0, getRotationOverride(mContext), /*sharedMode*/false); 1268 } 1269 1270 /** 1271 * Open a connection to a camera with the given ID. Also specify overrideToPortrait for testing. 1272 * 1273 * @param cameraId 1274 * The unique identifier of the camera device to open 1275 * @param handler 1276 * The handler on which the callback should be invoked, or 1277 * {@code null} to use the current thread's {@link android.os.Looper looper}. 1278 * @param callback 1279 * The callback which is invoked once the camera is opened 1280 * @param overrideToPortrait 1281 * Whether to apply the landscape to portrait override, using rotate and crop. 1282 * 1283 * @throws CameraAccessException if the camera is disabled by device policy, 1284 * has been disconnected, or is being used by a higher-priority camera API client. 1285 * 1286 * @throws IllegalArgumentException if cameraId, the callback or the executor was null, 1287 * or the cameraId does not match any currently or previously available 1288 * camera device. 1289 * 1290 * @throws SecurityException if the application does not have permission to 1291 * access the camera 1292 * 1293 * @see #getCameraIdList 1294 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 1295 * 1296 * @hide 1297 */ 1298 @TestApi 1299 @RequiresPermission(android.Manifest.permission.CAMERA) openCamera(@onNull String cameraId, boolean overrideToPortrait, @Nullable Handler handler, @NonNull final CameraDevice.StateCallback callback)1300 public void openCamera(@NonNull String cameraId, boolean overrideToPortrait, 1301 @Nullable Handler handler, 1302 @NonNull final CameraDevice.StateCallback callback) throws CameraAccessException { 1303 openCameraImpl(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler), 1304 /*oomScoreOffset*/0, 1305 overrideToPortrait 1306 ? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT 1307 : ICameraService.ROTATION_OVERRIDE_NONE, /*sharedMode*/false); 1308 } 1309 1310 /** 1311 * Open a connection to a camera with the given ID. 1312 * 1313 * <p>The behavior of this method matches that of 1314 * {@link #openCamera(String, StateCallback, Handler)}, except that it uses 1315 * {@link java.util.concurrent.Executor} as an argument instead of 1316 * {@link android.os.Handler}.</p> 1317 * 1318 * <p>Do note that typically callbacks are expected to be dispatched 1319 * by the executor in a single thread. If the executor uses two or 1320 * more threads to dispatch callbacks, then clients must ensure correct 1321 * synchronization and must also be able to handle potentially different 1322 * ordering of the incoming callbacks.</p> 1323 * 1324 * @param cameraId 1325 * The unique identifier of the camera device to open 1326 * @param executor 1327 * The executor which will be used when invoking the callback. 1328 * @param callback 1329 * The callback which is invoked once the camera is opened 1330 * 1331 * @throws CameraAccessException if the camera is disabled by device policy, 1332 * has been disconnected, or is being used by a higher-priority camera API client. 1333 * 1334 * @throws IllegalArgumentException if cameraId, the callback or the executor was null, 1335 * or the cameraId does not match any currently or previously available 1336 * camera device. 1337 * 1338 * @throws SecurityException if the application does not have permission to 1339 * access the camera 1340 * 1341 * @see #getCameraIdList 1342 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 1343 */ 1344 @RequiresPermission(android.Manifest.permission.CAMERA) openCamera(@onNull String cameraId, @NonNull @CallbackExecutor Executor executor, @NonNull final CameraDevice.StateCallback callback)1345 public void openCamera(@NonNull String cameraId, 1346 @NonNull @CallbackExecutor Executor executor, 1347 @NonNull final CameraDevice.StateCallback callback) 1348 throws CameraAccessException { 1349 if (executor == null) { 1350 throw new IllegalArgumentException("executor was null"); 1351 } 1352 openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0, 1353 getRotationOverride(mContext), /*sharedMode*/false); 1354 } 1355 1356 /** 1357 * Opens a shared connection to a camera with the given ID. 1358 * 1359 * <p>The behavior of this method matches that of 1360 * {@link #openCamera(String, Executor, StateCallback)}, except that it opens the camera in 1361 * shared mode where more than one client can access the camera at the same time.</p> 1362 * 1363 * @param cameraId The unique identifier of the camera device to open. 1364 * @param executor The executor which will be used when invoking the callback. 1365 * @param callback The callback which is invoked once the camera is opened 1366 * 1367 * @throws CameraAccessException if the camera is disabled by device policy, or is being used 1368 * by a higher-priority client in non-shared mode or the device 1369 * has reached its maximal resource and cannot open this camera 1370 * device. 1371 * 1372 * @throws IllegalArgumentException if cameraId, the callback or the executor was null, 1373 * or the cameraId does not match any currently or previously 1374 * available camera device. 1375 * 1376 * @throws SecurityException if the application does not have permission to 1377 * access the camera 1378 * 1379 * @throws UnsupportedOperationException if {@link #isCameraDeviceSharingSupported} returns 1380 * false for the given {@code cameraId}. 1381 * 1382 * @see #getCameraIdList 1383 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 1384 * 1385 * @hide 1386 */ 1387 @SystemApi 1388 @RequiresPermission(allOf = { 1389 android.Manifest.permission.SYSTEM_CAMERA, 1390 android.Manifest.permission.CAMERA, 1391 }) 1392 @FlaggedApi(Flags.FLAG_CAMERA_MULTI_CLIENT) openSharedCamera(@onNull String cameraId, @NonNull @CallbackExecutor Executor executor, @NonNull final CameraDevice.StateCallback callback)1393 public void openSharedCamera(@NonNull String cameraId, 1394 @NonNull @CallbackExecutor Executor executor, 1395 @NonNull final CameraDevice.StateCallback callback) 1396 throws CameraAccessException { 1397 if (executor == null) { 1398 throw new IllegalArgumentException("executor was null"); 1399 } 1400 if (!isCameraDeviceSharingSupported(cameraId)) { 1401 throw new UnsupportedOperationException( 1402 "CameraDevice sharing is not supported for Camera ID: " + cameraId); 1403 } 1404 openCameraImpl(cameraId, callback, executor, /*oomScoreOffset*/0, 1405 getRotationOverride(mContext), /*sharedMode*/true); 1406 } 1407 1408 1409 /** 1410 * Open a connection to a camera with the given ID. Also specify what oom score must be offset 1411 * by cameraserver for this client. This api can be useful for system 1412 * components which want to assume a lower priority (for camera arbitration) than other clients 1413 * which it might contend for camera devices with. Increasing the oom score of a client reduces 1414 * its priority when the camera framework manages camera arbitration. 1415 * Considering typical use cases: 1416 * 1417 * 1) oom score(apps hosting activities visible to the user) - oom score(of a foreground app) 1418 * is approximately 100. 1419 * 1420 * 2) The oom score (process which hosts components which that are perceptible to the user / 1421 * native vendor camera clients) - oom (foreground app) is approximately 200. 1422 * 1423 * 3) The oom score (process which is cached hosting activities not visible) - oom (foreground 1424 * app) is approximately 999. 1425 * 1426 * <p>The behavior of this method matches that of 1427 * {@link #openCamera(String, StateCallback, Handler)}, except that it uses 1428 * {@link java.util.concurrent.Executor} as an argument instead of 1429 * {@link android.os.Handler}.</p> 1430 * 1431 * @param cameraId 1432 * The unique identifier of the camera device to open 1433 * @param executor 1434 * The executor which will be used when invoking the callback. 1435 * @param callback 1436 * The callback which is invoked once the camera is opened 1437 * @param oomScoreOffset 1438 * The value by which the oom score of this client must be offset by the camera 1439 * framework in order to assist it with camera arbitration. This value must be > 0. 1440 * A positive value lowers the priority of this camera client compared to what the 1441 * camera framework would have originally seen. 1442 * 1443 * @throws CameraAccessException if the camera is disabled by device policy, 1444 * has been disconnected, or is being used by a higher-priority camera API client. 1445 * 1446 * @throws IllegalArgumentException if cameraId, the callback or the executor was null, 1447 * or the cameraId does not match any currently or previously available 1448 * camera device. 1449 * 1450 * @throws SecurityException if the application does not have permission to 1451 * access the camera 1452 * 1453 * @see #getCameraIdList 1454 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 1455 * 1456 * @hide 1457 */ 1458 @SystemApi 1459 @TestApi 1460 @RequiresPermission(allOf = { 1461 android.Manifest.permission.SYSTEM_CAMERA, 1462 android.Manifest.permission.CAMERA, 1463 }) openCamera(@onNull String cameraId, int oomScoreOffset, @NonNull @CallbackExecutor Executor executor, @NonNull final CameraDevice.StateCallback callback)1464 public void openCamera(@NonNull String cameraId, int oomScoreOffset, 1465 @NonNull @CallbackExecutor Executor executor, 1466 @NonNull final CameraDevice.StateCallback callback) throws CameraAccessException { 1467 if (executor == null) { 1468 throw new IllegalArgumentException("executor was null"); 1469 } 1470 if (oomScoreOffset < 0) { 1471 throw new IllegalArgumentException( 1472 "oomScoreOffset < 0, cannot increase priority of camera client"); 1473 } 1474 openCameraImpl(cameraId, callback, executor, oomScoreOffset, 1475 getRotationOverride(mContext), /*sharedMode*/false); 1476 } 1477 1478 /** 1479 * Open a connection to a camera with the given ID, on behalf of another application. 1480 * 1481 * @param cameraId 1482 * The unique identifier of the camera device to open 1483 * @param callback 1484 * The callback which is invoked once the camera is opened 1485 * @param executor 1486 * The executor which will be used when invoking the callback. 1487 * @param oomScoreOffset 1488 * The minimum oom score that cameraservice must see for this client. 1489 * @param rotationOverride 1490 * The type of rotation override (none, override_to_portrait, rotation_only) 1491 * that should be followed for this camera id connection 1492 * @param sharedMode 1493 * Parameter specifying if the camera should be opened in shared mode. 1494 * 1495 * @throws CameraAccessException if the camera is disabled by device policy, 1496 * has been disconnected, or is being used by a higher-priority camera API client in 1497 * non shared mode. 1498 * 1499 * @hide 1500 */ openCameraImpl(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int oomScoreOffset, int rotationOverride, boolean sharedMode)1501 public void openCameraImpl(@NonNull String cameraId, 1502 @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, 1503 int oomScoreOffset, int rotationOverride, boolean sharedMode) 1504 throws CameraAccessException { 1505 1506 if (cameraId == null) { 1507 throw new IllegalArgumentException("cameraId was null"); 1508 } else if (callback == null) { 1509 throw new IllegalArgumentException("callback was null"); 1510 } 1511 if (CameraManagerGlobal.sCameraServiceDisabled) { 1512 throw new IllegalArgumentException("No cameras available on device"); 1513 } 1514 1515 openCameraDeviceUserAsync(cameraId, callback, executor, oomScoreOffset, 1516 rotationOverride, sharedMode); 1517 } 1518 1519 /** 1520 * Set the flash unit's torch mode of the camera of the given ID without opening the camera 1521 * device. 1522 * 1523 * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use 1524 * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit. 1525 * Note that even if a camera device has a flash unit, turning on the torch mode may fail 1526 * if the camera device or other camera resources needed to turn on the torch mode are in use. 1527 * </p> 1528 * 1529 * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully, 1530 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked. 1531 * However, even if turning on the torch mode is successful, the application does not have the 1532 * exclusive ownership of the flash unit or the camera device. The torch mode will be turned 1533 * off and becomes unavailable when the camera device that the flash unit belongs to becomes 1534 * unavailable or when other camera resources to keep the torch on become unavailable ( 1535 * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also, 1536 * other applications are free to call {@link #setTorchMode} to turn off the torch mode ( 1537 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest 1538 * application that turned on the torch mode exits, the torch mode will be turned off. 1539 * 1540 * @param cameraId 1541 * The unique identifier of the camera device that the flash unit belongs to. 1542 * @param enabled 1543 * The desired state of the torch mode for the target camera device. Set to 1544 * {@code true} to turn on the torch mode. Set to {@code false} to turn off the 1545 * torch mode. 1546 * 1547 * @throws CameraAccessException if it failed to access the flash unit. 1548 * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device 1549 * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if 1550 * other camera resources needed to turn on the torch mode are in use. 1551 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera 1552 * service is not available. 1553 * 1554 * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently 1555 * or previously available camera device, or the camera device doesn't have a 1556 * flash unit. 1557 */ setTorchMode(@onNull String cameraId, boolean enabled)1558 public void setTorchMode(@NonNull String cameraId, boolean enabled) 1559 throws CameraAccessException { 1560 if (CameraManagerGlobal.sCameraServiceDisabled) { 1561 throw new IllegalArgumentException("No cameras available on device"); 1562 } 1563 CameraManagerGlobal.get() 1564 .setTorchMode( 1565 cameraId, 1566 enabled, 1567 getClientAttribution(), 1568 getDevicePolicyFromContext(mContext)); 1569 } 1570 1571 /** 1572 * Set the brightness level of the flashlight associated with the given cameraId in torch 1573 * mode. If the torch is OFF and torchStrength is >= 1, torch will turn ON with the 1574 * strength level specified in torchStrength. 1575 * 1576 * <p>Use 1577 * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL} 1578 * to check whether the camera device supports flash unit strength control or not. If this value 1579 * is greater than 1, applications can call this API to control the flashlight brightness level. 1580 * </p> 1581 * 1582 * <p>If {@link #turnOnTorchWithStrengthLevel} is called to change the brightness level of the 1583 * flash unit {@link CameraManager.TorchCallback#onTorchStrengthLevelChanged} will be invoked. 1584 * If the new desired strength level is same as previously set level, then this callback will 1585 * not be invoked. 1586 * If the torch is OFF and {@link #turnOnTorchWithStrengthLevel} is called with level >= 1, 1587 * the torch will be turned ON with that brightness level. In this case 1588 * {@link CameraManager.TorchCallback#onTorchModeChanged} will also be invoked. 1589 * </p> 1590 * 1591 * <p>When the torch is turned OFF via {@link #setTorchMode}, the flashlight brightness level 1592 * will reset to default value 1593 * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL} 1594 * In this case the {@link CameraManager.TorchCallback#onTorchStrengthLevelChanged} will not be 1595 * invoked. 1596 * </p> 1597 * 1598 * <p>If torch is enabled via {@link #setTorchMode} after calling 1599 * {@link #turnOnTorchWithStrengthLevel} with level N then the flash unit will have the 1600 * brightness level N. 1601 * Since multiple applications are free to call {@link #setTorchMode}, when the latest 1602 * application that turned ON the torch mode exits, the torch mode will be turned OFF 1603 * and in this case the brightness level will reset to default level. 1604 * </p> 1605 * 1606 * @param cameraId 1607 * The unique identifier of the camera device that the flash unit belongs to. 1608 * @param torchStrength 1609 * The desired brightness level to be set for the flash unit in the range 1 to 1610 * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL}. 1611 * 1612 * @throws CameraAccessException if it failed to access the flash unit. 1613 * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device 1614 * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if 1615 * other camera resources needed to turn on the torch mode are in use. 1616 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera 1617 * service is not available. 1618 * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently 1619 * or previously available camera device, the camera device doesn't have a 1620 * flash unit or if torchStrength is not within the range i.e. is greater than 1621 * the maximum level 1622 * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL} 1623 * or <= 0. 1624 * 1625 */ turnOnTorchWithStrengthLevel(@onNull String cameraId, int torchStrength)1626 public void turnOnTorchWithStrengthLevel(@NonNull String cameraId, int torchStrength) 1627 throws CameraAccessException { 1628 if (CameraManagerGlobal.sCameraServiceDisabled) { 1629 throw new IllegalArgumentException("No camera available on device"); 1630 } 1631 CameraManagerGlobal.get() 1632 .turnOnTorchWithStrengthLevel( 1633 cameraId, 1634 torchStrength, 1635 getClientAttribution(), 1636 getDevicePolicyFromContext(mContext)); 1637 } 1638 1639 /** 1640 * Returns the brightness level of the flash unit associated with the cameraId. 1641 * 1642 * @param cameraId 1643 * The unique identifier of the camera device that the flash unit belongs to. 1644 * @return The brightness level of the flash unit associated with cameraId. 1645 * When the torch is turned OFF, the strength level will reset to a default level 1646 * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL}. 1647 * In this case the return value will be 1648 * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL} 1649 * rather than 0. 1650 * 1651 * @throws CameraAccessException if it failed to access the flash unit. 1652 * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently 1653 * or previously available camera device, or the camera device doesn't have a 1654 * flash unit. 1655 * 1656 */ getTorchStrengthLevel(@onNull String cameraId)1657 public int getTorchStrengthLevel(@NonNull String cameraId) 1658 throws CameraAccessException { 1659 if (CameraManagerGlobal.sCameraServiceDisabled) { 1660 throw new IllegalArgumentException("No camera available on device."); 1661 } 1662 return CameraManagerGlobal.get() 1663 .getTorchStrengthLevel( 1664 cameraId, 1665 getClientAttribution(), 1666 getDevicePolicyFromContext(mContext)); 1667 } 1668 1669 /** 1670 * @hide 1671 */ getRotationOverride(@ullable Context context)1672 public static int getRotationOverride(@Nullable Context context) { 1673 PackageManager packageManager = null; 1674 String packageName = null; 1675 1676 if (context != null) { 1677 packageManager = context.getPackageManager(); 1678 packageName = context.getOpPackageName(); 1679 } 1680 1681 return getRotationOverride(context, packageManager, packageName); 1682 } 1683 1684 /** 1685 * @hide 1686 */ getRotationOverride(@ullable Context context, @Nullable PackageManager packageManager, @Nullable String packageName)1687 public static int getRotationOverride(@Nullable Context context, 1688 @Nullable PackageManager packageManager, @Nullable String packageName) { 1689 // Isolated process does not have access to the ContentProvider which 1690 // `DesktopModeFlags` uses. `DesktopModeFlags` combines developer options and Aconfig flags. 1691 if (!Process.isIsolated() && DesktopModeFlags 1692 .ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()) { 1693 return getRotationOverrideInternal(context, packageManager, packageName); 1694 } else { 1695 return shouldOverrideToPortrait(packageManager, packageName) 1696 ? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT 1697 : ICameraService.ROTATION_OVERRIDE_NONE; 1698 } 1699 } 1700 1701 /** 1702 * @hide 1703 */ 1704 @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) 1705 @TestApi getRotationOverrideInternal(@ullable Context context, @Nullable PackageManager packageManager, @Nullable String packageName)1706 public static int getRotationOverrideInternal(@Nullable Context context, 1707 @Nullable PackageManager packageManager, @Nullable String packageName) { 1708 if (!CameraManagerGlobal.sLandscapeToPortrait) { 1709 return ICameraService.ROTATION_OVERRIDE_NONE; 1710 } 1711 1712 // Isolated process does not have access to ActivityTaskManager service, which is used 1713 // indirectly in `ActivityManager.getAppTasks()`. 1714 if (context != null && !Process.isIsolated()) { 1715 final ActivityManager activityManager = context.getSystemService(ActivityManager.class); 1716 if (activityManager != null) { 1717 for (ActivityManager.AppTask appTask : activityManager.getAppTasks()) { 1718 final TaskInfo taskInfo = appTask.getTaskInfo(); 1719 final int freeformCameraCompatMode = taskInfo.appCompatTaskInfo 1720 .cameraCompatTaskInfo.freeformCameraCompatMode; 1721 if (isInCameraCompatMode(freeformCameraCompatMode) 1722 && taskInfo.topActivity != null 1723 && taskInfo.topActivity.getPackageName().equals(packageName)) { 1724 // WindowManager has requested rotation override. 1725 return getRotationOverrideForCompatFreeform(freeformCameraCompatMode); 1726 } 1727 } 1728 } 1729 } 1730 1731 if (packageManager != null && packageName != null) { 1732 try { 1733 return packageManager.getProperty( 1734 PackageManager.PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT, 1735 packageName).getBoolean() 1736 ? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT 1737 : ICameraService.ROTATION_OVERRIDE_NONE; 1738 } catch (PackageManager.NameNotFoundException e) { 1739 // No such property 1740 } 1741 } 1742 1743 return CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT) 1744 ? ICameraService.ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT 1745 : ICameraService.ROTATION_OVERRIDE_NONE; 1746 } 1747 isInCameraCompatMode(@ameraCompatTaskInfo.FreeformCameraCompatMode int freeformCameraCompatMode)1748 private static boolean isInCameraCompatMode(@CameraCompatTaskInfo.FreeformCameraCompatMode int 1749 freeformCameraCompatMode) { 1750 return (freeformCameraCompatMode != CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_UNSPECIFIED) 1751 && (freeformCameraCompatMode != CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE); 1752 } 1753 getRotationOverrideForCompatFreeform( @ameraCompatTaskInfo.FreeformCameraCompatMode int freeformCameraCompatMode)1754 private static int getRotationOverrideForCompatFreeform( 1755 @CameraCompatTaskInfo.FreeformCameraCompatMode int freeformCameraCompatMode) { 1756 // Only rotate-and-crop if the app and device orientations do not match. 1757 if (freeformCameraCompatMode 1758 == CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT 1759 || freeformCameraCompatMode 1760 == CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE) { 1761 return ICameraService.ROTATION_OVERRIDE_ROTATION_ONLY; 1762 } else { 1763 return ICameraService.ROTATION_OVERRIDE_NONE; 1764 } 1765 } 1766 1767 /** 1768 * @hide 1769 */ 1770 @TestApi shouldOverrideToPortrait(@ullable PackageManager packageManager, @Nullable String packageName)1771 public static boolean shouldOverrideToPortrait(@Nullable PackageManager packageManager, 1772 @Nullable String packageName) { 1773 if (!CameraManagerGlobal.sLandscapeToPortrait) { 1774 return false; 1775 } 1776 1777 if (packageManager != null && packageName != null) { 1778 try { 1779 return packageManager.getProperty( 1780 PackageManager.PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT, 1781 packageName).getBoolean(); 1782 } catch (PackageManager.NameNotFoundException e) { 1783 // No such property 1784 } 1785 } 1786 1787 return CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_LANDSCAPE_TO_PORTRAIT); 1788 } 1789 1790 1791 /** 1792 * @hide 1793 */ physicalCallbacksAreEnabledForUnavailableCamera()1794 public static boolean physicalCallbacksAreEnabledForUnavailableCamera() { 1795 return CompatChanges.isChangeEnabled( 1796 ENABLE_PHYSICAL_CAMERA_CALLBACK_FOR_UNAVAILABLE_LOGICAL_CAMERA); 1797 } 1798 1799 /** 1800 * A callback for camera devices becoming available or unavailable to open. 1801 * 1802 * <p>Cameras become available when they are no longer in use, or when a new 1803 * removable camera is connected. They become unavailable when some 1804 * application or service starts using a camera, or when a removable camera 1805 * is disconnected.</p> 1806 * 1807 * <p>Extend this callback and pass an instance of the subclass to 1808 * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability 1809 * changes.</p> 1810 * 1811 * @see #registerAvailabilityCallback 1812 */ 1813 public static abstract class AvailabilityCallback { 1814 1815 private int mDeviceId; 1816 private int mDevicePolicy; 1817 1818 /** 1819 * A new camera has become available to use. 1820 * 1821 * <p>The default implementation of this method does nothing.</p> 1822 * 1823 * @param cameraId The unique identifier of the new camera. 1824 */ onCameraAvailable(@onNull String cameraId)1825 public void onCameraAvailable(@NonNull String cameraId) { 1826 // default empty implementation 1827 } 1828 1829 /** 1830 * A previously-available camera has become unavailable for use. 1831 * 1832 * <p>If an application had an active CameraDevice instance for the 1833 * now-disconnected camera, that application will receive a 1834 * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p> 1835 * 1836 * <p>The default implementation of this method does nothing.</p> 1837 * 1838 * @param cameraId The unique identifier of the disconnected camera. 1839 */ onCameraUnavailable(@onNull String cameraId)1840 public void onCameraUnavailable(@NonNull String cameraId) { 1841 // default empty implementation 1842 } 1843 1844 /** 1845 * Called whenever camera access priorities change. 1846 * 1847 * <p>Notification that camera access priorities have changed and the camera may 1848 * now be openable. An application that was previously denied camera access due to 1849 * a higher-priority user already using the camera, or that was disconnected from an 1850 * active camera session due to a higher-priority user trying to open the camera, 1851 * should try to open the camera again if it still wants to use it. Note that 1852 * multiple applications may receive this callback at the same time, and only one of 1853 * them will succeed in opening the camera in practice, depending on exact access 1854 * priority levels and timing. This method is useful in cases where multiple 1855 * applications may be in the resumed state at the same time, and the user switches 1856 * focus between them, or if the current camera-using application moves between 1857 * full-screen and Picture-in-Picture (PiP) states. In such cases, the camera 1858 * available/unavailable callbacks will not be invoked, but another application may 1859 * now have higher priority for camera access than the current camera-using 1860 * application.</p> 1861 * 1862 * <p>The default implementation of this method does nothing.</p> 1863 * 1864 */ onCameraAccessPrioritiesChanged()1865 public void onCameraAccessPrioritiesChanged() { 1866 // default empty implementation 1867 } 1868 1869 /** 1870 * A physical camera has become available for use again. 1871 * 1872 * <p>By default, all of the physical cameras of a logical multi-camera are 1873 * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical 1874 * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical 1875 * multi-camera is invoked. However, if some specific physical cameras are unavailable 1876 * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after 1877 * {@link #onCameraAvailable}.</p> 1878 * 1879 * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} 1880 * < {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, opening a logical camera 1881 * disables the {@link #onPhysicalCameraAvailable} and {@link #onPhysicalCameraUnavailable} 1882 * callbacks for its physical cameras. For example, if app A opens the camera device:</p> 1883 * 1884 * <ul> 1885 * 1886 * <li>All apps subscribing to ActivityCallback get {@link #onCameraUnavailable}.</li> 1887 * 1888 * <li>No app (including app A) subscribing to ActivityCallback gets 1889 * {@link #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable}, because 1890 * the logical camera is unavailable (some app is using it).</li> 1891 * 1892 * </ul> 1893 * 1894 * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} 1895 * ≥ {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:</p> 1896 * 1897 * <ul> 1898 * 1899 * <li>A physical camera status change will trigger {@link #onPhysicalCameraAvailable} 1900 * or {@link #onPhysicalCameraUnavailable} even after the logical camera becomes 1901 * unavailable. A {@link #onCameraUnavailable} call for a logical camera doesn't reset the 1902 * physical cameras' availability status. This makes it possible for an application opening 1903 * the logical camera device to know which physical camera becomes unavailable or available 1904 * to use.</li> 1905 * 1906 * <li>Similar to {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13} and earlier, 1907 * the logical camera's {@link #onCameraAvailable} callback implies all of its physical 1908 * cameras' status become available. {@link #onPhysicalCameraUnavailable} will be called 1909 * for any unavailable physical cameras upon the logical camera becoming available.</li> 1910 * 1911 * </ul> 1912 * 1913 * <p>Given the pipeline nature of the camera capture through {@link 1914 * android.hardware.camera2.CaptureRequest}, there may be frame drops if the application 1915 * requests images from a physical camera of a logical multi-camera and that physical camera 1916 * becomes unavailable. The application should stop requesting directly from an unavailable 1917 * physical camera as soon as {@link #onPhysicalCameraUnavailable} is received, and also be 1918 * ready to robustly handle frame drop errors for requests targeting physical cameras, 1919 * since those errors may arrive before the unavailability callback.</p> 1920 * 1921 * <p>The default implementation of this method does nothing.</p> 1922 * 1923 * @param cameraId The unique identifier of the logical multi-camera. 1924 * @param physicalCameraId The unique identifier of the physical camera. 1925 * 1926 * @see #onCameraAvailable 1927 * @see #onPhysicalCameraUnavailable 1928 */ onPhysicalCameraAvailable(@onNull String cameraId, @NonNull String physicalCameraId)1929 public void onPhysicalCameraAvailable(@NonNull String cameraId, 1930 @NonNull String physicalCameraId) { 1931 // default empty implementation 1932 } 1933 1934 /** 1935 * A previously-available physical camera has become unavailable for use. 1936 * 1937 * <p>By default, all of the physical cameras of a logical multi-camera are 1938 * unavailable if the logical camera itself is unavailable. 1939 * No availability callbacks will be called for any of the physical 1940 * cameras of its parent logical multi-camera, when {@link #onCameraUnavailable} for 1941 * the logical multi-camera is invoked.</p> 1942 * 1943 * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} 1944 * < {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, opening a logical camera 1945 * disables the {@link #onPhysicalCameraAvailable} and {@link #onPhysicalCameraUnavailable} 1946 * callbacks for its physical cameras. For example, if app A opens the camera device:</p> 1947 * 1948 * <ul> 1949 * 1950 * <li>All apps subscribing to ActivityCallback get {@link #onCameraUnavailable}.</li> 1951 * 1952 * <li>No app (including app A) subscribing to ActivityCallback gets 1953 * {@link #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable}, because 1954 * the logical camera is unavailable (some app is using it).</li> 1955 * 1956 * </ul> 1957 * 1958 * <p>If {@link android.content.pm.ApplicationInfo#targetSdkVersion targetSdkVersion} 1959 * ≥ {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}:</p> 1960 * 1961 * <ul> 1962 * 1963 * <li>A physical camera status change will trigger {@link #onPhysicalCameraAvailable} 1964 * or {@link #onPhysicalCameraUnavailable} even after the logical camera becomes 1965 * unavailable. A {@link #onCameraUnavailable} call for a logical camera doesn't reset the 1966 * physical cameras' availability status. This makes it possible for an application opening 1967 * the logical camera device to know which physical camera becomes unavailable or available 1968 * to use.</li> 1969 * 1970 * <li>Similar to {@link android.os.Build.VERSION_CODES#TIRAMISU Android 13} and earlier, 1971 * the logical camera's {@link #onCameraAvailable} callback implies all of its physical 1972 * cameras' status become available. {@link #onPhysicalCameraUnavailable} will be called 1973 * for any unavailable physical cameras upon the logical camera becoming available.</li> 1974 * 1975 * </ul> 1976 * 1977 * <p>Given the pipeline nature of the camera capture through {@link 1978 * android.hardware.camera2.CaptureRequest}, there may be frame drops if the application 1979 * requests images from a physical camera of a logical multi-camera and that physical camera 1980 * becomes unavailable. The application should stop requesting directly from an unavailable 1981 * physical camera as soon as {@link #onPhysicalCameraUnavailable} is received, and also be 1982 * ready to robustly handle frame drop errors for requests targeting physical cameras, 1983 * since those errors may arrive before the unavailability callback.</p> 1984 * 1985 * <p>The default implementation of this method does nothing.</p> 1986 * 1987 * @param cameraId The unique identifier of the logical multi-camera. 1988 * @param physicalCameraId The unique identifier of the physical camera. 1989 * 1990 * @see #onCameraAvailable 1991 * @see #onPhysicalCameraAvailable 1992 */ onPhysicalCameraUnavailable(@onNull String cameraId, @NonNull String physicalCameraId)1993 public void onPhysicalCameraUnavailable(@NonNull String cameraId, 1994 @NonNull String physicalCameraId) { 1995 // default empty implementation 1996 } 1997 1998 /** 1999 * A camera device has been opened by an application. 2000 * 2001 * <p>The default implementation of this method does nothing.</p> 2002 * android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER is required to receive this 2003 * callback 2004 * @param cameraId The unique identifier of the camera opened. 2005 * @param packageId The package Id of the application opening the camera. 2006 * 2007 * @see #onCameraClosed 2008 * @hide 2009 */ 2010 @SystemApi 2011 @TestApi 2012 @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER) onCameraOpened(@onNull String cameraId, @NonNull String packageId)2013 public void onCameraOpened(@NonNull String cameraId, @NonNull String packageId) { 2014 // default empty implementation 2015 } 2016 2017 /** 2018 * A previously-opened camera has been closed. 2019 * 2020 * <p>The default implementation of this method does nothing.</p> 2021 * android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER is required to receive this 2022 * callback. 2023 * @param cameraId The unique identifier of the closed camera. 2024 * @hide 2025 */ 2026 @SystemApi 2027 @TestApi 2028 @RequiresPermission(android.Manifest.permission.CAMERA_OPEN_CLOSE_LISTENER) onCameraClosed(@onNull String cameraId)2029 public void onCameraClosed(@NonNull String cameraId) { 2030 // default empty implementation 2031 } 2032 } 2033 2034 /** 2035 * A callback for camera flash torch modes becoming unavailable, disabled, or enabled. 2036 * 2037 * <p>The torch mode becomes unavailable when the camera device it belongs to becomes 2038 * unavailable or other camera resources it needs become busy due to other higher priority 2039 * camera activities. The torch mode becomes disabled when it was turned off or when the camera 2040 * device it belongs to is no longer in use and other camera resources it needs are no longer 2041 * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to 2042 * turn off the camera's torch mode, or when an application turns on another camera's torch mode 2043 * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes 2044 * enabled when it is turned on via {@link #setTorchMode}.</p> 2045 * 2046 * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled 2047 * or enabled state.</p> 2048 * 2049 * <p>Extend this callback and pass an instance of the subclass to 2050 * {@link CameraManager#registerTorchCallback} to be notified of such status changes. 2051 * </p> 2052 * 2053 * @see #registerTorchCallback 2054 */ 2055 public static abstract class TorchCallback { 2056 2057 private int mDeviceId; 2058 private int mDevicePolicy; 2059 2060 /** 2061 * A camera's torch mode has become unavailable to set via {@link #setTorchMode}. 2062 * 2063 * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be 2064 * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is 2065 * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or 2066 * enabled state again.</p> 2067 * 2068 * <p>The default implementation of this method does nothing.</p> 2069 * 2070 * @param cameraId The unique identifier of the camera whose torch mode has become 2071 * unavailable. 2072 */ onTorchModeUnavailable(@onNull String cameraId)2073 public void onTorchModeUnavailable(@NonNull String cameraId) { 2074 // default empty implementation 2075 } 2076 2077 /** 2078 * A camera's torch mode has become enabled or disabled and can be changed via 2079 * {@link #setTorchMode}. 2080 * 2081 * <p>The default implementation of this method does nothing.</p> 2082 * 2083 * @param cameraId The unique identifier of the camera whose torch mode has been changed. 2084 * 2085 * @param enabled The state that the torch mode of the camera has been changed to. 2086 * {@code true} when the torch mode has become on and available to be turned 2087 * off. {@code false} when the torch mode has becomes off and available to 2088 * be turned on. 2089 */ onTorchModeChanged(@onNull String cameraId, boolean enabled)2090 public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) { 2091 // default empty implementation 2092 } 2093 2094 /** 2095 * A camera's flash unit brightness level has been changed in torch mode via 2096 * {@link #turnOnTorchWithStrengthLevel}. When the torch is turned OFF, this 2097 * callback will not be triggered even though the torch strength level resets to 2098 * default value 2099 * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL} 2100 * 2101 * <p>The default implementation of this method does nothing.</p> 2102 * 2103 * @param cameraId The unique identifier of the camera whose flash unit brightness level has 2104 * been changed. 2105 * 2106 * @param newStrengthLevel The brightness level of the flash unit that has been changed to. 2107 */ onTorchStrengthLevelChanged(@onNull String cameraId, int newStrengthLevel)2108 public void onTorchStrengthLevelChanged(@NonNull String cameraId, int newStrengthLevel) { 2109 // default empty implementation 2110 } 2111 } 2112 2113 /** 2114 * Queries the camera service if a cameraId is a hidden physical camera that belongs to a 2115 * logical camera device. 2116 * 2117 * A hidden physical camera is a camera that cannot be opened by the application. But it 2118 * can be used as part of a logical camera. 2119 * 2120 * @param cameraId a non-{@code null} camera identifier 2121 * @return {@code true} if cameraId is a hidden physical camera device 2122 * 2123 * @hide 2124 */ isHiddenPhysicalCamera(String cameraId)2125 public static boolean isHiddenPhysicalCamera(String cameraId) { 2126 try { 2127 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 2128 // If no camera service, no support 2129 if (cameraService == null) return false; 2130 2131 return cameraService.isHiddenPhysicalCamera(cameraId); 2132 } catch (RemoteException e) { 2133 // Camera service is now down, no support for any API level 2134 } 2135 return false; 2136 } 2137 2138 /** 2139 * Inject the external camera to replace the internal camera session. 2140 * 2141 * <p>If injecting the external camera device fails, then the injection callback's 2142 * {@link CameraInjectionSession.InjectionStatusCallback#onInjectionError 2143 * onInjectionError} method will be called.</p> 2144 * 2145 * @param packageName It scopes the injection to a particular app. 2146 * @param internalCamId The id of one of the physical or logical cameras on the phone. 2147 * @param externalCamId The id of one of the remote cameras that are provided by the dynamic 2148 * camera HAL. 2149 * @param executor The executor which will be used when invoking the callback. 2150 * @param callback The callback which is invoked once the external camera is injected. 2151 * 2152 * @throws CameraAccessException If the camera device has been disconnected. 2153 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be 2154 * thrown if camera service is not available. 2155 * @throws SecurityException If the specific application that can cast to external 2156 * devices does not have permission to inject the external 2157 * camera. 2158 * @throws IllegalArgumentException If cameraId doesn't match any currently or previously 2159 * available camera device or some camera functions might not 2160 * work properly or the injection camera runs into a fatal 2161 * error. 2162 * @hide 2163 */ 2164 @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)2165 public void injectCamera(@NonNull String packageName, @NonNull String internalCamId, 2166 @NonNull String externalCamId, @NonNull @CallbackExecutor Executor executor, 2167 @NonNull CameraInjectionSession.InjectionStatusCallback callback) 2168 throws CameraAccessException, SecurityException, 2169 IllegalArgumentException { 2170 if (CameraManagerGlobal.sCameraServiceDisabled) { 2171 throw new IllegalArgumentException("No cameras available on device"); 2172 } 2173 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 2174 if (cameraService == null) { 2175 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 2176 "Camera service is currently unavailable"); 2177 } 2178 synchronized (mLock) { 2179 try { 2180 CameraInjectionSessionImpl injectionSessionImpl = 2181 new CameraInjectionSessionImpl(callback, executor); 2182 ICameraInjectionCallback cameraInjectionCallback = 2183 injectionSessionImpl.getCallback(); 2184 ICameraInjectionSession injectionSession = cameraService.injectCamera(packageName, 2185 internalCamId, externalCamId, cameraInjectionCallback); 2186 injectionSessionImpl.setRemoteInjectionSession(injectionSession); 2187 } catch (ServiceSpecificException e) { 2188 throw ExceptionUtils.throwAsPublicException(e); 2189 } catch (RemoteException e) { 2190 // Camera service died - act as if it's a CAMERA_DISCONNECTED case 2191 ServiceSpecificException sse = new ServiceSpecificException( 2192 ICameraService.ERROR_DISCONNECTED, 2193 "Camera service is currently unavailable"); 2194 throw ExceptionUtils.throwAsPublicException(sse); 2195 } 2196 } 2197 } 2198 2199 /** 2200 * Injects session params into existing clients in the CameraService. 2201 * 2202 * @param cameraId The camera id of client to inject session params into. 2203 * If no such client exists for cameraId, no injection will 2204 * take place. 2205 * @param sessionParams A {@link CaptureRequest} object containing the 2206 * the sessionParams to inject into the existing client. 2207 * 2208 * @throws CameraAccessException {@link CameraAccessException#CAMERA_DISCONNECTED} will be 2209 * thrown if camera service is not available. Further, if 2210 * if no such client exists for cameraId, 2211 * {@link CameraAccessException#CAMERA_ERROR} will be thrown. 2212 * @throws SecurityException If the caller does not have permission to inject session 2213 * params 2214 * @hide 2215 */ 2216 @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA) injectSessionParams(@onNull String cameraId, @NonNull CaptureRequest sessionParams)2217 public void injectSessionParams(@NonNull String cameraId, @NonNull CaptureRequest sessionParams) 2218 throws CameraAccessException, SecurityException { 2219 CameraManagerGlobal.get().injectSessionParams(cameraId, sessionParams); 2220 } 2221 2222 /** 2223 * Returns the current CameraService instance connected to Global 2224 * @hide 2225 */ getCameraService()2226 public ICameraService getCameraService() { 2227 return CameraManagerGlobal.get().getCameraService(); 2228 } 2229 2230 /** 2231 * Returns true if cameraservice is currently disabled. If true, {@link #getCameraService()} 2232 * will definitely return null. 2233 * @hide 2234 */ isCameraServiceDisabled()2235 public boolean isCameraServiceDisabled() { 2236 return CameraManagerGlobal.sCameraServiceDisabled; 2237 } 2238 2239 /** 2240 * Reports {@link CameraExtensionSessionStats} to the {@link ICameraService} to be logged for 2241 * currently active session. Validation is done downstream. 2242 * 2243 * @param extStats Extension Session stats to be logged by cameraservice 2244 * 2245 * @return the key to be used with the next call. 2246 * See {@link ICameraService#reportExtensionSessionStats}. 2247 * @hide 2248 */ reportExtensionSessionStats(CameraExtensionSessionStats extStats)2249 public static String reportExtensionSessionStats(CameraExtensionSessionStats extStats) { 2250 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 2251 if (cameraService == null) { 2252 Log.e(TAG, "CameraService not available. Not reporting extension stats."); 2253 return ""; 2254 } 2255 try { 2256 return cameraService.reportExtensionSessionStats(extStats); 2257 } catch (RemoteException e) { 2258 Log.e(TAG, "Failed to report extension session stats to cameraservice.", e); 2259 } 2260 return ""; 2261 } 2262 2263 /** 2264 * A per-process global camera manager instance, to retain a connection to the camera service, 2265 * and to distribute camera availability notices to API-registered callbacks 2266 */ 2267 private static final class CameraManagerGlobal extends ICameraServiceListener.Stub 2268 implements IBinder.DeathRecipient { 2269 2270 private static final String TAG = "CameraManagerGlobal"; 2271 2272 private final boolean DEBUG = false; 2273 2274 private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000; 2275 2276 // Singleton instance 2277 private static final CameraManagerGlobal gCameraManager = 2278 new CameraManagerGlobal(); 2279 2280 /** 2281 * This must match the ICameraService definition 2282 */ 2283 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; 2284 2285 private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1); 2286 // Camera ID -> Status map 2287 private final ArrayMap<DeviceCameraInfo, Integer> mDeviceStatus = new ArrayMap<>(); 2288 // Camera ID -> (physical camera ID -> Status map) 2289 private final ArrayMap<DeviceCameraInfo, ArrayList<String>> mUnavailablePhysicalDevices = 2290 new ArrayMap<>(); 2291 // Opened Camera ID -> apk name map 2292 private final ArrayMap<DeviceCameraInfo, String> mOpenedDevices = new ArrayMap<>(); 2293 2294 private final Set<Set<DeviceCameraInfo>> mConcurrentCameraIdCombinations = new ArraySet<>(); 2295 2296 // Diagnostic messages for ArrayIndexOutOfBoundsException in extractCameraIdListLocked 2297 // b/367649718 2298 private static final int DEVICE_STATUS_ARRAY_SIZE = 10; 2299 private final ArrayDeque<String> mDeviceStatusHistory = 2300 new ArrayDeque<>(DEVICE_STATUS_ARRAY_SIZE); 2301 2302 // Registered availability callbacks and their executors 2303 private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap = new ArrayMap<>(); 2304 2305 // torch client binder to set the torch mode with. 2306 private final Binder mTorchClientBinder = new Binder(); 2307 2308 // Camera ID -> Torch status map 2309 private final ArrayMap<DeviceCameraInfo, Integer> mTorchStatus = new ArrayMap<>(); 2310 2311 // Registered torch callbacks and their executors 2312 private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap = new ArrayMap<>(); 2313 2314 private final Object mLock = new Object(); 2315 2316 // Access only through getCameraService to deal with binder death 2317 private ICameraService mCameraService; 2318 private boolean mHasOpenCloseListenerPermission = false; 2319 2320 private HandlerThread mDeviceStateHandlerThread; 2321 private Handler mDeviceStateHandler; 2322 private FoldStateListener mFoldStateListener; 2323 2324 // Singleton, don't allow construction CameraManagerGlobal()2325 private CameraManagerGlobal() { } 2326 2327 public static final boolean sCameraServiceDisabled = 2328 SystemProperties.getBoolean("config.disable_cameraservice", false); 2329 2330 public static final boolean sLandscapeToPortrait = 2331 SystemProperties.getBoolean(LANDSCAPE_TO_PORTRAIT_PROP, false); 2332 get()2333 public static CameraManagerGlobal get() { 2334 return gCameraManager; 2335 } 2336 registerDeviceStateListener(@onNull CameraCharacteristics chars, @NonNull Context ctx)2337 public void registerDeviceStateListener(@NonNull CameraCharacteristics chars, 2338 @NonNull Context ctx) { 2339 synchronized(mLock) { 2340 if (mDeviceStateHandlerThread == null) { 2341 mDeviceStateHandlerThread = new HandlerThread(TAG); 2342 mDeviceStateHandlerThread.start(); 2343 mDeviceStateHandler = new Handler(mDeviceStateHandlerThread.getLooper()); 2344 } 2345 2346 if (mFoldStateListener == null) { 2347 mFoldStateListener = new FoldStateListener(ctx); 2348 try { 2349 ctx.getSystemService(DeviceStateManager.class).registerCallback( 2350 new HandlerExecutor(mDeviceStateHandler), mFoldStateListener); 2351 } catch (IllegalStateException e) { 2352 mFoldStateListener = null; 2353 Log.v(TAG, "Failed to register device state listener!"); 2354 Log.v(TAG, "Device state dependent characteristics updates will not be" + 2355 "functional!"); 2356 return; 2357 } 2358 } 2359 2360 mFoldStateListener.addDeviceStateListener(chars.getDeviceStateListener()); 2361 } 2362 } 2363 2364 @Override asBinder()2365 public IBinder asBinder() { 2366 return this; 2367 } 2368 2369 /** 2370 * Return a best-effort ICameraService. 2371 * 2372 * <p>This will be null if the camera service is not currently available. If the camera 2373 * service has died since the last use of the camera service, will try to reconnect to the 2374 * service.</p> 2375 */ getCameraService()2376 public ICameraService getCameraService() { 2377 synchronized(mLock) { 2378 connectCameraServiceLocked(); 2379 if (mCameraService == null && !sCameraServiceDisabled) { 2380 Log.e(TAG, "Camera service is unavailable"); 2381 } 2382 return mCameraService; 2383 } 2384 } 2385 2386 /** 2387 * Connect to the camera service if it's available, and set up listeners. 2388 * If the service is already connected, do nothing. 2389 * 2390 * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p> 2391 */ connectCameraServiceLocked()2392 private void connectCameraServiceLocked() { 2393 // Only reconnect if necessary 2394 if (mCameraService != null || sCameraServiceDisabled) return; 2395 2396 Log.i(TAG, "Connecting to camera service"); 2397 2398 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME); 2399 if (cameraServiceBinder == null) { 2400 // Camera service is now down, leave mCameraService as null 2401 return; 2402 } 2403 try { 2404 cameraServiceBinder.linkToDeath(this, /*flags*/ 0); 2405 } catch (RemoteException e) { 2406 // Camera service is now down, leave mCameraService as null 2407 return; 2408 } 2409 2410 ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder); 2411 2412 try { 2413 CameraMetadataNative.setupGlobalVendorTagDescriptor(); 2414 } catch (ServiceSpecificException e) { 2415 handleRecoverableSetupErrors(e); 2416 } 2417 2418 try { 2419 addDeviceStatusHistoryLocked(TextUtils.formatSimple( 2420 "connectCameraServiceLocked(E): tid(%d): mDeviceStatus size %d", 2421 Thread.currentThread().getId(), mDeviceStatus.size())); 2422 2423 CameraStatus[] cameraStatuses = cameraService.addListener(this); 2424 for (CameraStatus cameraStatus : cameraStatuses) { 2425 DeviceCameraInfo info = new DeviceCameraInfo(cameraStatus.cameraId, 2426 cameraStatus.deviceId); 2427 onStatusChangedLocked(cameraStatus.status, info); 2428 2429 if (cameraStatus.unavailablePhysicalCameras != null) { 2430 for (String unavailablePhysicalCamera : 2431 cameraStatus.unavailablePhysicalCameras) { 2432 onPhysicalCameraStatusChangedLocked( 2433 ICameraServiceListener.STATUS_NOT_PRESENT, 2434 info, unavailablePhysicalCamera); 2435 } 2436 } 2437 2438 if (mHasOpenCloseListenerPermission 2439 && cameraStatus.status == ICameraServiceListener.STATUS_NOT_AVAILABLE 2440 && !cameraStatus.clientPackage.isEmpty()) { 2441 onCameraOpenedLocked(info, cameraStatus.clientPackage); 2442 } 2443 } 2444 mCameraService = cameraService; 2445 2446 addDeviceStatusHistoryLocked(TextUtils.formatSimple( 2447 "connectCameraServiceLocked(X): tid(%d): mDeviceStatus size %d", 2448 Thread.currentThread().getId(), mDeviceStatus.size())); 2449 } catch (ServiceSpecificException e) { 2450 // Unexpected failure 2451 throw new IllegalStateException("Failed to register a camera service listener", e); 2452 } catch (RemoteException e) { 2453 // Camera service is now down, leave mCameraService as null 2454 } 2455 2456 try { 2457 ConcurrentCameraIdCombination[] cameraIdCombinations = 2458 cameraService.getConcurrentCameraIds(); 2459 for (ConcurrentCameraIdCombination comb : cameraIdCombinations) { 2460 Set<Pair<String, Integer>> combination = 2461 comb.getConcurrentCameraIdCombination(); 2462 Set<DeviceCameraInfo> deviceCameraInfoSet = new ArraySet<>(); 2463 for (Pair<String, Integer> entry : combination) { 2464 deviceCameraInfoSet.add(new DeviceCameraInfo(entry.first, entry.second)); 2465 } 2466 mConcurrentCameraIdCombinations.add(deviceCameraInfoSet); 2467 } 2468 } catch (ServiceSpecificException e) { 2469 // Unexpected failure 2470 throw new IllegalStateException("Failed to get concurrent camera id combinations", 2471 e); 2472 } catch (RemoteException e) { 2473 // Camera service died in all probability 2474 } 2475 } 2476 2477 /** Injects session params into an existing client for cameraid. */ injectSessionParams(@onNull String cameraId, @NonNull CaptureRequest sessionParams)2478 public void injectSessionParams(@NonNull String cameraId, 2479 @NonNull CaptureRequest sessionParams) 2480 throws CameraAccessException, SecurityException { 2481 synchronized (mLock) { 2482 ICameraService cameraService = getCameraService(); 2483 if (cameraService == null) { 2484 throw new CameraAccessException( 2485 CameraAccessException.CAMERA_DISCONNECTED, 2486 "Camera service is currently unavailable."); 2487 } 2488 2489 try { 2490 cameraService.injectSessionParams(cameraId, sessionParams.getNativeMetadata()); 2491 } catch (ServiceSpecificException e) { 2492 throw ExceptionUtils.throwAsPublicException(e); 2493 } catch (RemoteException e) { 2494 throw new CameraAccessException( 2495 CameraAccessException.CAMERA_DISCONNECTED, 2496 "Camera service is currently unavailable."); 2497 } 2498 } 2499 } 2500 extractCameraIdListLocked(int deviceId, int devicePolicy)2501 private String[] extractCameraIdListLocked(int deviceId, int devicePolicy) { 2502 addDeviceStatusHistoryLocked(TextUtils.formatSimple( 2503 "extractCameraIdListLocked(E): tid(%d): mDeviceStatus size %d", 2504 Thread.currentThread().getId(), mDeviceStatus.size())); 2505 try { 2506 List<String> cameraIds = new ArrayList<>(); 2507 for (int i = 0; i < mDeviceStatus.size(); i++) { 2508 int status = mDeviceStatus.valueAt(i); 2509 DeviceCameraInfo info = mDeviceStatus.keyAt(i); 2510 if (status == ICameraServiceListener.STATUS_NOT_PRESENT 2511 || status == ICameraServiceListener.STATUS_ENUMERATING 2512 || shouldHideCamera(deviceId, devicePolicy, info)) { 2513 continue; 2514 } 2515 cameraIds.add(info.mCameraId); 2516 } 2517 return cameraIds.toArray(new String[0]); 2518 } catch (ArrayIndexOutOfBoundsException e) { 2519 String message = e.getMessage(); 2520 String messageWithHistory = message + ": {" 2521 + String.join(" -> ", mDeviceStatusHistory) + "}"; 2522 throw new ArrayIndexOutOfBoundsException(messageWithHistory); 2523 } 2524 } 2525 extractConcurrentCameraIdListLocked(int deviceId, int devicePolicy)2526 private Set<Set<String>> extractConcurrentCameraIdListLocked(int deviceId, 2527 int devicePolicy) { 2528 Set<Set<String>> concurrentCameraIds = new ArraySet<>(); 2529 for (Set<DeviceCameraInfo> deviceCameraInfos : mConcurrentCameraIdCombinations) { 2530 Set<String> extractedCameraIds = new ArraySet<>(); 2531 for (DeviceCameraInfo info : deviceCameraInfos) { 2532 // if the camera id status is NOT_PRESENT or ENUMERATING; skip the device. 2533 // TODO: Would a device status NOT_PRESENT ever be in the map ? it gets removed 2534 // in the callback anyway. 2535 Integer status = mDeviceStatus.get(info); 2536 if (status == null) { 2537 // camera id not present 2538 continue; 2539 } 2540 if (status == ICameraServiceListener.STATUS_ENUMERATING 2541 || status == ICameraServiceListener.STATUS_NOT_PRESENT) { 2542 continue; 2543 } 2544 if (shouldHideCamera(deviceId, devicePolicy, info)) { 2545 continue; 2546 } 2547 extractedCameraIds.add(info.mCameraId); 2548 } 2549 if (!extractedCameraIds.isEmpty()) { 2550 concurrentCameraIds.add(extractedCameraIds); 2551 } 2552 } 2553 return concurrentCameraIds; 2554 } 2555 sortCameraIds(String[] cameraIds)2556 private static void sortCameraIds(String[] cameraIds) { 2557 // The sort logic must match the logic in 2558 // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds 2559 Arrays.sort(cameraIds, new Comparator<String>() { 2560 @Override 2561 public int compare(String s1, String s2) { 2562 int s1Int = 0, s2Int = 0; 2563 try { 2564 s1Int = Integer.parseInt(s1); 2565 } catch (NumberFormatException e) { 2566 s1Int = -1; 2567 } 2568 2569 try { 2570 s2Int = Integer.parseInt(s2); 2571 } catch (NumberFormatException e) { 2572 s2Int = -1; 2573 } 2574 2575 // Uint device IDs first 2576 if (s1Int >= 0 && s2Int >= 0) { 2577 return s1Int - s2Int; 2578 } else if (s1Int >= 0) { 2579 return -1; 2580 } else if (s2Int >= 0) { 2581 return 1; 2582 } else { 2583 // Simple string compare if both id are not uint 2584 return s1.compareTo(s2); 2585 } 2586 }}); 2587 } 2588 shouldHideCamera(int currentDeviceId, int devicePolicy, DeviceCameraInfo info)2589 private boolean shouldHideCamera(int currentDeviceId, int devicePolicy, 2590 DeviceCameraInfo info) { 2591 if (devicePolicy == DEVICE_POLICY_DEFAULT && info.mDeviceId == DEVICE_ID_DEFAULT) { 2592 // Don't hide default-device cameras for a default-policy virtual device. 2593 return false; 2594 } 2595 2596 return currentDeviceId != info.mDeviceId; 2597 } 2598 cameraStatusesContains(CameraStatus[] cameraStatuses, DeviceCameraInfo info)2599 private static boolean cameraStatusesContains(CameraStatus[] cameraStatuses, 2600 DeviceCameraInfo info) { 2601 for (CameraStatus c : cameraStatuses) { 2602 if (c.cameraId.equals(info.mCameraId) && c.deviceId == info.mDeviceId) { 2603 return true; 2604 } 2605 } 2606 return false; 2607 } 2608 getCameraIdListNoLazy(int deviceId, int devicePolicy)2609 public String[] getCameraIdListNoLazy(int deviceId, int devicePolicy) { 2610 if (sCameraServiceDisabled) { 2611 return new String[] {}; 2612 } 2613 2614 CameraStatus[] cameraStatuses; 2615 ICameraServiceListener.Stub testListener = new ICameraServiceListener.Stub() { 2616 @Override 2617 public void onStatusChanged(int status, String id, int deviceId) 2618 throws RemoteException { 2619 } 2620 @Override 2621 public void onPhysicalCameraStatusChanged(int status, 2622 String id, String physicalId, int deviceId) throws RemoteException { 2623 } 2624 @Override 2625 public void onTorchStatusChanged(int status, String id, int deviceId) 2626 throws RemoteException { 2627 } 2628 @Override 2629 public void onTorchStrengthLevelChanged(String id, int newStrengthLevel, 2630 int deviceId) throws RemoteException { 2631 } 2632 @Override 2633 public void onCameraAccessPrioritiesChanged() { 2634 } 2635 @Override 2636 public void onCameraOpened(String id, String clientPackageId, int deviceId) { 2637 } 2638 @Override 2639 public void onCameraClosed(String id, int deviceId) { 2640 } 2641 @Override 2642 public void onCameraOpenedInSharedMode(String id, String clientPackageId, 2643 int deviceId, boolean primaryClient) { 2644 }}; 2645 2646 String[] cameraIds; 2647 synchronized (mLock) { 2648 connectCameraServiceLocked(); 2649 try { 2650 addDeviceStatusHistoryLocked(TextUtils.formatSimple( 2651 "getCameraIdListNoLazy(E): tid(%d): mDeviceStatus size %d", 2652 Thread.currentThread().getId(), mDeviceStatus.size())); 2653 2654 // The purpose of the addListener, removeListener pair here is to get a fresh 2655 // list of camera ids from cameraserver. We do this since for in test processes, 2656 // changes can happen w.r.t non-changeable permissions (eg: SYSTEM_CAMERA 2657 // permissions can be effectively changed by calling 2658 // adopt(drop)ShellPermissionIdentity()). 2659 // Camera devices, which have their discovery affected by these permission 2660 // changes, will not have clients get callbacks informing them about these 2661 // devices going offline (in real world scenarios, these permissions aren't 2662 // changeable). Future calls to getCameraIdList() will reflect the changes in 2663 // the camera id list after getCameraIdListNoLazy() is called. 2664 // We need to remove the torch ids which may have been associated with the 2665 // devices removed as well. This is the same situation. 2666 cameraStatuses = mCameraService.addListener(testListener); 2667 mCameraService.removeListener(testListener); 2668 for (CameraStatus cameraStatus : cameraStatuses) { 2669 onStatusChangedLocked(cameraStatus.status, 2670 new DeviceCameraInfo(cameraStatus.cameraId, cameraStatus.deviceId)); 2671 } 2672 Set<DeviceCameraInfo> deviceCameraInfos = mDeviceStatus.keySet(); 2673 List<DeviceCameraInfo> deviceInfosToRemove = new ArrayList<>(); 2674 for (DeviceCameraInfo info : deviceCameraInfos) { 2675 // Its possible that a device id was removed without a callback notifying 2676 // us. This may happen in case a process 'drops' system camera permissions 2677 // (even though the permission isn't a changeable one, tests may call 2678 // adoptShellPermissionIdentity() and then dropShellPermissionIdentity(). 2679 if (!cameraStatusesContains(cameraStatuses, info)) { 2680 deviceInfosToRemove.add(info); 2681 } 2682 } 2683 for (DeviceCameraInfo info : deviceInfosToRemove) { 2684 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, info); 2685 mTorchStatus.remove(info); 2686 } 2687 addDeviceStatusHistoryLocked(TextUtils.formatSimple( 2688 "getCameraIdListNoLazy(X): tid(%d): mDeviceStatus size %d", 2689 Thread.currentThread().getId(), mDeviceStatus.size())); 2690 } catch (ServiceSpecificException e) { 2691 // Unexpected failure 2692 throw new IllegalStateException("Failed to register a camera service listener", 2693 e); 2694 } catch (RemoteException e) { 2695 // Camera service is now down, leave mCameraService as null 2696 } 2697 cameraIds = extractCameraIdListLocked(deviceId, devicePolicy); 2698 } 2699 sortCameraIds(cameraIds); 2700 return cameraIds; 2701 } 2702 2703 /** 2704 * Get a list of all camera IDs that are at least PRESENT; ignore devices that are 2705 * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone. 2706 */ getCameraIdList(int deviceId, int devicePolicy)2707 public String[] getCameraIdList(int deviceId, int devicePolicy) { 2708 String[] cameraIds; 2709 synchronized (mLock) { 2710 // Try to make sure we have an up-to-date list of camera devices. 2711 connectCameraServiceLocked(); 2712 cameraIds = extractCameraIdListLocked(deviceId, devicePolicy); 2713 } 2714 sortCameraIds(cameraIds); 2715 return cameraIds; 2716 } 2717 getConcurrentCameraIds(int deviceId, int devicePolicy)2718 public @NonNull Set<Set<String>> getConcurrentCameraIds(int deviceId, int devicePolicy) { 2719 Set<Set<String>> concurrentStreamingCameraIds; 2720 synchronized (mLock) { 2721 // Try to make sure we have an up-to-date list of concurrent camera devices. 2722 connectCameraServiceLocked(); 2723 concurrentStreamingCameraIds = extractConcurrentCameraIdListLocked(deviceId, 2724 devicePolicy); 2725 } 2726 // TODO: Some sort of sorting ? 2727 return concurrentStreamingCameraIds; 2728 } 2729 isConcurrentSessionConfigurationSupported( @onNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations, int targetSdkVersion, AttributionSourceState clientAttribution, int devicePolicy)2730 public boolean isConcurrentSessionConfigurationSupported( 2731 @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations, 2732 int targetSdkVersion, 2733 AttributionSourceState clientAttribution, 2734 int devicePolicy) 2735 throws CameraAccessException { 2736 if (cameraIdsAndSessionConfigurations == null) { 2737 throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null"); 2738 } 2739 2740 int size = cameraIdsAndSessionConfigurations.size(); 2741 if (size == 0) { 2742 throw new IllegalArgumentException("camera id and session combination is empty"); 2743 } 2744 2745 synchronized (mLock) { 2746 // Go through all the elements and check if the camera ids are valid at least / 2747 // belong to one of the combinations returned by getConcurrentCameraIds() 2748 boolean subsetFound = false; 2749 for (Set<DeviceCameraInfo> combination : mConcurrentCameraIdCombinations) { 2750 Set<DeviceCameraInfo> infos = new ArraySet<>(); 2751 for (String cameraId : cameraIdsAndSessionConfigurations.keySet()) { 2752 infos.add( 2753 new DeviceCameraInfo( 2754 cameraId, 2755 devicePolicy == DEVICE_POLICY_DEFAULT 2756 ? DEVICE_ID_DEFAULT 2757 : clientAttribution.deviceId)); 2758 } 2759 if (combination.containsAll(infos)) { 2760 subsetFound = true; 2761 } 2762 } 2763 if (!subsetFound) { 2764 Log.v(TAG, "isConcurrentSessionConfigurationSupported called with a subset of" 2765 + " camera ids not returned by getConcurrentCameraIds"); 2766 return false; 2767 } 2768 CameraIdAndSessionConfiguration [] cameraIdsAndConfigs = 2769 new CameraIdAndSessionConfiguration[size]; 2770 int i = 0; 2771 for (Map.Entry<String, SessionConfiguration> pair : 2772 cameraIdsAndSessionConfigurations.entrySet()) { 2773 cameraIdsAndConfigs[i] = 2774 new CameraIdAndSessionConfiguration(pair.getKey(), pair.getValue()); 2775 i++; 2776 } 2777 try { 2778 return mCameraService.isConcurrentSessionConfigurationSupported( 2779 cameraIdsAndConfigs, targetSdkVersion, clientAttribution, devicePolicy); 2780 } catch (ServiceSpecificException e) { 2781 throw ExceptionUtils.throwAsPublicException(e); 2782 } catch (RemoteException e) { 2783 // Camera service died - act as if the camera was disconnected 2784 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 2785 "Camera service is currently unavailable", e); 2786 } 2787 } 2788 } 2789 2790 /** 2791 * Helper function to find out if a camera id is in the set of combinations returned by 2792 * getConcurrentCameraIds() 2793 * @param cameraId the unique identifier of the camera device to query 2794 * @param deviceId the device id of the context 2795 * @return Whether the camera device was found in the set of combinations returned by 2796 * getConcurrentCameraIds 2797 */ cameraIdHasConcurrentStreams(String cameraId, int deviceId, int devicePolicy)2798 public boolean cameraIdHasConcurrentStreams(String cameraId, int deviceId, 2799 int devicePolicy) { 2800 synchronized (mLock) { 2801 DeviceCameraInfo info = new DeviceCameraInfo(cameraId, 2802 devicePolicy == DEVICE_POLICY_DEFAULT ? DEVICE_ID_DEFAULT : deviceId); 2803 if (!mDeviceStatus.containsKey(info)) { 2804 // physical camera ids aren't advertised in concurrent camera id combinations. 2805 if (DEBUG) { 2806 Log.v(TAG, " physical camera id " + cameraId + " is hidden." 2807 + " Available logical camera ids : " + mDeviceStatus); 2808 } 2809 return false; 2810 } 2811 for (Set<DeviceCameraInfo> comb : mConcurrentCameraIdCombinations) { 2812 if (comb.contains(info)) { 2813 return true; 2814 } 2815 } 2816 return false; 2817 } 2818 } 2819 setTorchMode( String cameraId, boolean enabled, AttributionSourceState clientAttribution, int devicePolicy)2820 public void setTorchMode( 2821 String cameraId, 2822 boolean enabled, 2823 AttributionSourceState clientAttribution, 2824 int devicePolicy) 2825 throws CameraAccessException { 2826 synchronized (mLock) { 2827 if (cameraId == null) { 2828 throw new IllegalArgumentException("cameraId was null"); 2829 } 2830 2831 ICameraService cameraService = getCameraService(); 2832 if (cameraService == null) { 2833 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 2834 "Camera service is currently unavailable"); 2835 } 2836 2837 try { 2838 cameraService.setTorchMode( 2839 cameraId, enabled, mTorchClientBinder, clientAttribution, devicePolicy); 2840 } catch(ServiceSpecificException e) { 2841 throw ExceptionUtils.throwAsPublicException(e); 2842 } catch (RemoteException e) { 2843 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 2844 "Camera service is currently unavailable"); 2845 } 2846 } 2847 } 2848 turnOnTorchWithStrengthLevel( String cameraId, int torchStrength, AttributionSourceState clientAttribution, int devicePolicy)2849 public void turnOnTorchWithStrengthLevel( 2850 String cameraId, 2851 int torchStrength, 2852 AttributionSourceState clientAttribution, 2853 int devicePolicy) 2854 throws CameraAccessException { 2855 synchronized (mLock) { 2856 if (cameraId == null) { 2857 throw new IllegalArgumentException("cameraId was null"); 2858 } 2859 2860 ICameraService cameraService = getCameraService(); 2861 if (cameraService == null) { 2862 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 2863 "Camera service is currently unavailable."); 2864 } 2865 2866 try { 2867 cameraService.turnOnTorchWithStrengthLevel( 2868 cameraId, 2869 torchStrength, 2870 mTorchClientBinder, 2871 clientAttribution, 2872 devicePolicy); 2873 } catch(ServiceSpecificException e) { 2874 throw ExceptionUtils.throwAsPublicException(e); 2875 } catch (RemoteException e) { 2876 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 2877 "Camera service is currently unavailable."); 2878 } 2879 } 2880 } 2881 getTorchStrengthLevel( String cameraId, AttributionSourceState clientAttribution, int devicePolicy)2882 public int getTorchStrengthLevel( 2883 String cameraId, AttributionSourceState clientAttribution, int devicePolicy) 2884 throws CameraAccessException { 2885 int torchStrength; 2886 synchronized (mLock) { 2887 if (cameraId == null) { 2888 throw new IllegalArgumentException("cameraId was null"); 2889 } 2890 2891 ICameraService cameraService = getCameraService(); 2892 if (cameraService == null) { 2893 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 2894 "Camera service is currently unavailable."); 2895 } 2896 2897 try { 2898 torchStrength = 2899 cameraService.getTorchStrengthLevel( 2900 cameraId, clientAttribution, devicePolicy); 2901 } catch(ServiceSpecificException e) { 2902 throw ExceptionUtils.throwAsPublicException(e); 2903 } catch (RemoteException e) { 2904 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 2905 "Camera service is currently unavailable."); 2906 } 2907 } 2908 return torchStrength; 2909 } 2910 handleRecoverableSetupErrors(ServiceSpecificException e)2911 private void handleRecoverableSetupErrors(ServiceSpecificException e) { 2912 switch (e.errorCode) { 2913 case ICameraService.ERROR_DISCONNECTED: 2914 Log.w(TAG, e.getMessage()); 2915 break; 2916 default: 2917 throw new IllegalStateException(e); 2918 } 2919 } 2920 isAvailable(int status)2921 private boolean isAvailable(int status) { 2922 switch (status) { 2923 case ICameraServiceListener.STATUS_PRESENT: 2924 return true; 2925 default: 2926 return false; 2927 } 2928 } 2929 validStatus(int status)2930 private boolean validStatus(int status) { 2931 switch (status) { 2932 case ICameraServiceListener.STATUS_NOT_PRESENT: 2933 case ICameraServiceListener.STATUS_PRESENT: 2934 case ICameraServiceListener.STATUS_ENUMERATING: 2935 case ICameraServiceListener.STATUS_NOT_AVAILABLE: 2936 return true; 2937 default: 2938 return false; 2939 } 2940 } 2941 validTorchStatus(int status)2942 private boolean validTorchStatus(int status) { 2943 switch (status) { 2944 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE: 2945 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 2946 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: 2947 return true; 2948 default: 2949 return false; 2950 } 2951 } 2952 postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback, final Executor executor)2953 private void postSingleAccessPriorityChangeUpdate(final AvailabilityCallback callback, 2954 final Executor executor) { 2955 final long ident = Binder.clearCallingIdentity(); 2956 try { 2957 executor.execute(callback::onCameraAccessPrioritiesChanged); 2958 } finally { 2959 Binder.restoreCallingIdentity(ident); 2960 } 2961 } 2962 postSingleCameraOpenedUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final String packageId)2963 private void postSingleCameraOpenedUpdate(final AvailabilityCallback callback, 2964 final Executor executor, final String id, final String packageId) { 2965 final long ident = Binder.clearCallingIdentity(); 2966 try { 2967 executor.execute(() -> callback.onCameraOpened(id, packageId)); 2968 } finally { 2969 Binder.restoreCallingIdentity(ident); 2970 } 2971 } 2972 postSingleCameraClosedUpdate(final AvailabilityCallback callback, final Executor executor, final String id)2973 private void postSingleCameraClosedUpdate(final AvailabilityCallback callback, 2974 final Executor executor, final String id) { 2975 final long ident = Binder.clearCallingIdentity(); 2976 try { 2977 executor.execute(() -> callback.onCameraClosed(id)); 2978 } finally { 2979 Binder.restoreCallingIdentity(ident); 2980 } 2981 } 2982 postSingleUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final String physicalId, final int status)2983 private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor, 2984 final String id, final String physicalId, final int status) { 2985 if (isAvailable(status)) { 2986 final long ident = Binder.clearCallingIdentity(); 2987 try { 2988 executor.execute( 2989 () -> { 2990 if (physicalId == null) { 2991 callback.onCameraAvailable(id); 2992 } else { 2993 callback.onPhysicalCameraAvailable(id, physicalId); 2994 } 2995 }); 2996 } finally { 2997 Binder.restoreCallingIdentity(ident); 2998 } 2999 } else { 3000 final long ident = Binder.clearCallingIdentity(); 3001 try { 3002 executor.execute( 3003 () -> { 3004 if (physicalId == null) { 3005 callback.onCameraUnavailable(id); 3006 } else { 3007 callback.onPhysicalCameraUnavailable(id, physicalId); 3008 } 3009 }); 3010 } finally { 3011 Binder.restoreCallingIdentity(ident); 3012 } 3013 } 3014 } 3015 postSingleTorchUpdate(final TorchCallback callback, final Executor executor, final String id, final int status)3016 private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor, 3017 final String id, final int status) { 3018 switch(status) { 3019 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 3020 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: { 3021 final long ident = Binder.clearCallingIdentity(); 3022 try { 3023 executor.execute(() -> callback.onTorchModeChanged(id, status 3024 == ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON)); 3025 } finally { 3026 Binder.restoreCallingIdentity(ident); 3027 } 3028 break; 3029 } 3030 default: { 3031 final long ident = Binder.clearCallingIdentity(); 3032 try { 3033 executor.execute(() -> callback.onTorchModeUnavailable(id)); 3034 } finally { 3035 Binder.restoreCallingIdentity(ident); 3036 } 3037 break; 3038 } 3039 } 3040 } 3041 postSingleTorchStrengthLevelUpdate(final TorchCallback callback, final Executor executor, final String id, final int newStrengthLevel)3042 private void postSingleTorchStrengthLevelUpdate(final TorchCallback callback, 3043 final Executor executor, final String id, final int newStrengthLevel) { 3044 final long ident = Binder.clearCallingIdentity(); 3045 try { 3046 executor.execute(() -> callback.onTorchStrengthLevelChanged(id, newStrengthLevel)); 3047 } finally { 3048 Binder.restoreCallingIdentity(ident); 3049 } 3050 } 3051 3052 /** 3053 * Send the state of all known cameras to the provided listener, to initialize 3054 * the listener's knowledge of camera state. 3055 */ updateCallbackLocked(AvailabilityCallback callback, Executor executor)3056 private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) { 3057 for (int i = 0; i < mDeviceStatus.size(); i++) { 3058 DeviceCameraInfo info = mDeviceStatus.keyAt(i); 3059 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3060 continue; 3061 } 3062 3063 Integer status = mDeviceStatus.valueAt(i); 3064 postSingleUpdate(callback, executor, info.mCameraId, null /* physicalId */, status); 3065 3066 // Send the NOT_PRESENT state for unavailable physical cameras 3067 if ((isAvailable(status) || physicalCallbacksAreEnabledForUnavailableCamera()) 3068 && mUnavailablePhysicalDevices.containsKey(info)) { 3069 List<String> unavailableIds = mUnavailablePhysicalDevices.get(info); 3070 for (String unavailableId : unavailableIds) { 3071 postSingleUpdate(callback, executor, info.mCameraId, unavailableId, 3072 ICameraServiceListener.STATUS_NOT_PRESENT); 3073 } 3074 } 3075 } 3076 3077 for (int i = 0; i < mOpenedDevices.size(); i++) { 3078 DeviceCameraInfo info = mOpenedDevices.keyAt(i); 3079 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3080 continue; 3081 } 3082 3083 String clientPackageId = mOpenedDevices.valueAt(i); 3084 postSingleCameraOpenedUpdate(callback, executor, info.mCameraId, clientPackageId); 3085 } 3086 } 3087 onStatusChangedLocked(int status, DeviceCameraInfo info)3088 private void onStatusChangedLocked(int status, DeviceCameraInfo info) { 3089 if (DEBUG) { 3090 Log.v(TAG, 3091 String.format("Camera id %s has status changed to 0x%x for device %d", 3092 info.mCameraId, status, info.mDeviceId)); 3093 } 3094 3095 if (!validStatus(status)) { 3096 Log.e(TAG, String.format("Ignoring invalid camera %s status 0x%x for device %d", 3097 info.mCameraId, status, info.mDeviceId)); 3098 return; 3099 } 3100 3101 Integer oldStatus; 3102 if (status == ICameraServiceListener.STATUS_NOT_PRESENT) { 3103 oldStatus = mDeviceStatus.remove(info); 3104 mUnavailablePhysicalDevices.remove(info); 3105 } else { 3106 oldStatus = mDeviceStatus.put(info, status); 3107 if (oldStatus == null) { 3108 mUnavailablePhysicalDevices.put(info, new ArrayList<>()); 3109 } 3110 } 3111 3112 if (oldStatus != null && oldStatus == status) { 3113 if (DEBUG) { 3114 Log.v(TAG, String.format( 3115 "Device status changed to 0x%x, which is what it already was", 3116 status)); 3117 } 3118 return; 3119 } 3120 3121 // TODO: consider abstracting out this state minimization + transition 3122 // into a separate 3123 // more easily testable class 3124 // i.e. (new State()).addState(STATE_AVAILABLE) 3125 // .addState(STATE_NOT_AVAILABLE) 3126 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE), 3127 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE) 3128 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE); 3129 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE); 3130 3131 // Translate all the statuses to either 'available' or 'not available' 3132 // available -> available => no new update 3133 // not available -> not available => no new update 3134 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) { 3135 if (DEBUG) { 3136 Log.v(TAG, 3137 String.format( 3138 "Device status was previously available (%b), " + 3139 " and is now again available (%b)" + 3140 "so no new client visible update will be sent", 3141 isAvailable(oldStatus), isAvailable(status))); 3142 } 3143 return; 3144 } 3145 3146 final int callbackCount = mCallbackMap.size(); 3147 for (int i = 0; i < callbackCount; i++) { 3148 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 3149 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3150 continue; 3151 } 3152 3153 final Executor executor = mCallbackMap.valueAt(i); 3154 postSingleUpdate(callback, executor, info.mCameraId, null /* physicalId */, status); 3155 3156 // Send the NOT_PRESENT state for unavailable physical cameras 3157 if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(info)) { 3158 List<String> unavailableIds = mUnavailablePhysicalDevices.get(info); 3159 for (String unavailableId : unavailableIds) { 3160 postSingleUpdate(callback, executor, info.mCameraId, unavailableId, 3161 ICameraServiceListener.STATUS_NOT_PRESENT); 3162 } 3163 } 3164 } 3165 } // onStatusChangedLocked 3166 onPhysicalCameraStatusChangedLocked(int status, DeviceCameraInfo info, String physicalId)3167 private void onPhysicalCameraStatusChangedLocked(int status, DeviceCameraInfo info, 3168 String physicalId) { 3169 if (DEBUG) { 3170 Log.v(TAG, 3171 String.format("Camera id %s physical camera id %s has status changed " 3172 + "to 0x%x for device %d", info.mCameraId, physicalId, status, 3173 info.mDeviceId)); 3174 } 3175 3176 if (!validStatus(status)) { 3177 Log.e(TAG, String.format( 3178 "Ignoring invalid device %s physical device %s status 0x%x for device %d", 3179 info.mCameraId, physicalId, status, info.mDeviceId)); 3180 return; 3181 } 3182 3183 //TODO: Do we need to treat this as error? 3184 if (!mDeviceStatus.containsKey(info) 3185 || !mUnavailablePhysicalDevices.containsKey(info)) { 3186 Log.e(TAG, String.format("Camera %s is not present. Ignore physical camera " 3187 + "status change", info.mCameraId)); 3188 return; 3189 } 3190 3191 List<String> unavailablePhysicalDevices = mUnavailablePhysicalDevices.get(info); 3192 if (!isAvailable(status) 3193 && !unavailablePhysicalDevices.contains(physicalId)) { 3194 unavailablePhysicalDevices.add(physicalId); 3195 } else if (isAvailable(status) 3196 && unavailablePhysicalDevices.contains(physicalId)) { 3197 unavailablePhysicalDevices.remove(physicalId); 3198 } else { 3199 if (DEBUG) { 3200 Log.v(TAG, 3201 String.format( 3202 "Physical camera device status was previously available (%b), " 3203 + " and is now again available (%b)" 3204 + "so no new client visible update will be sent", 3205 !unavailablePhysicalDevices.contains(physicalId), 3206 isAvailable(status))); 3207 } 3208 return; 3209 } 3210 3211 if (!physicalCallbacksAreEnabledForUnavailableCamera() 3212 && !isAvailable(mDeviceStatus.get(info))) { 3213 Log.i(TAG, String.format("Camera %s is not available. Ignore physical camera " 3214 + "status change callback(s)", info.mCameraId)); 3215 return; 3216 } 3217 3218 final int callbackCount = mCallbackMap.size(); 3219 for (int i = 0; i < callbackCount; i++) { 3220 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 3221 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3222 continue; 3223 } 3224 3225 final Executor executor = mCallbackMap.valueAt(i); 3226 postSingleUpdate(callback, executor, info.mCameraId, physicalId, status); 3227 } 3228 } // onPhysicalCameraStatusChangedLocked 3229 updateTorchCallbackLocked(TorchCallback callback, Executor executor)3230 private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) { 3231 for (int i = 0; i < mTorchStatus.size(); i++) { 3232 DeviceCameraInfo info = mTorchStatus.keyAt(i); 3233 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3234 continue; 3235 } 3236 3237 Integer status = mTorchStatus.valueAt(i); 3238 postSingleTorchUpdate(callback, executor, info.mCameraId, status); 3239 } 3240 } 3241 onTorchStatusChangedLocked(int status, DeviceCameraInfo info)3242 private void onTorchStatusChangedLocked(int status, DeviceCameraInfo info) { 3243 if (DEBUG) { 3244 Log.v(TAG, String.format( 3245 "Camera id %s has torch status changed to 0x%x for device %d", 3246 info.mCameraId, status, info.mDeviceId)); 3247 } 3248 3249 if (!validTorchStatus(status)) { 3250 Log.e(TAG, String.format( 3251 "Ignoring invalid camera %s torch status 0x%x for device %d", 3252 info.mCameraId, status, info.mDeviceId)); 3253 return; 3254 } 3255 3256 Integer oldStatus = mTorchStatus.put(info, status); 3257 if (oldStatus != null && oldStatus == status) { 3258 if (DEBUG) { 3259 Log.v(TAG, String.format( 3260 "Torch status changed to 0x%x, which is what it already was", 3261 status)); 3262 } 3263 return; 3264 } 3265 3266 final int callbackCount = mTorchCallbackMap.size(); 3267 for (int i = 0; i < callbackCount; i++) { 3268 final TorchCallback callback = mTorchCallbackMap.keyAt(i); 3269 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3270 continue; 3271 } 3272 3273 final Executor executor = mTorchCallbackMap.valueAt(i); 3274 postSingleTorchUpdate(callback, executor, info.mCameraId, status); 3275 } 3276 } // onTorchStatusChangedLocked 3277 onTorchStrengthLevelChangedLocked(DeviceCameraInfo info, int newStrengthLevel)3278 private void onTorchStrengthLevelChangedLocked(DeviceCameraInfo info, 3279 int newStrengthLevel) { 3280 if (DEBUG) { 3281 Log.v(TAG, String.format( 3282 "Camera id %s has torch strength level changed to %d for device %d", 3283 info.mCameraId, newStrengthLevel, info.mDeviceId)); 3284 } 3285 3286 final int callbackCount = mTorchCallbackMap.size(); 3287 for (int i = 0; i < callbackCount; i++) { 3288 final TorchCallback callback = mTorchCallbackMap.keyAt(i); 3289 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3290 continue; 3291 } 3292 3293 final Executor executor = mTorchCallbackMap.valueAt(i); 3294 postSingleTorchStrengthLevelUpdate(callback, executor, info.mCameraId, 3295 newStrengthLevel); 3296 } 3297 } // onTorchStrengthLevelChanged 3298 3299 /** 3300 * Register a callback to be notified about camera device availability with the 3301 * global listener singleton. 3302 * 3303 * @param callback the new callback to send camera availability notices to 3304 * @param executor The executor which should invoke the callback. May not be null. 3305 * @param hasOpenCloseListenerPermission whether the client has permission for 3306 * onCameraOpened/onCameraClosed callback 3307 */ registerAvailabilityCallback(AvailabilityCallback callback, Executor executor, boolean hasOpenCloseListenerPermission, int deviceId, int devicePolicy)3308 public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor, 3309 boolean hasOpenCloseListenerPermission, int deviceId, int devicePolicy) { 3310 synchronized (mLock) { 3311 // In practice, this permission doesn't change. So we don't need one flag for each 3312 // callback object. 3313 mHasOpenCloseListenerPermission = hasOpenCloseListenerPermission; 3314 connectCameraServiceLocked(); 3315 3316 callback.mDeviceId = deviceId; 3317 callback.mDevicePolicy = devicePolicy; 3318 3319 Executor oldExecutor = mCallbackMap.put(callback, executor); 3320 // For new callbacks, provide initial availability information 3321 if (oldExecutor == null) { 3322 updateCallbackLocked(callback, executor); 3323 } 3324 3325 // If not connected to camera service, schedule a reconnect to camera service. 3326 if (mCameraService == null) { 3327 scheduleCameraServiceReconnectionLocked(); 3328 } 3329 } 3330 } 3331 3332 /** 3333 * Remove a previously-added callback; the callback will no longer receive connection and 3334 * disconnection callbacks, and is no longer referenced by the global listener singleton. 3335 * 3336 * @param callback The callback to remove from the notification list 3337 */ unregisterAvailabilityCallback(AvailabilityCallback callback)3338 public void unregisterAvailabilityCallback(AvailabilityCallback callback) { 3339 synchronized (mLock) { 3340 mCallbackMap.remove(callback); 3341 } 3342 } 3343 registerTorchCallback(TorchCallback callback, Executor executor, int deviceId, int devicePolicy)3344 public void registerTorchCallback(TorchCallback callback, Executor executor, int deviceId, 3345 int devicePolicy) { 3346 synchronized(mLock) { 3347 connectCameraServiceLocked(); 3348 3349 callback.mDeviceId = deviceId; 3350 callback.mDevicePolicy = devicePolicy; 3351 3352 Executor oldExecutor = mTorchCallbackMap.put(callback, executor); 3353 // For new callbacks, provide initial torch information 3354 if (oldExecutor == null) { 3355 updateTorchCallbackLocked(callback, executor); 3356 } 3357 3358 // If not connected to camera service, schedule a reconnect to camera service. 3359 if (mCameraService == null) { 3360 scheduleCameraServiceReconnectionLocked(); 3361 } 3362 } 3363 } 3364 unregisterTorchCallback(TorchCallback callback)3365 public void unregisterTorchCallback(TorchCallback callback) { 3366 synchronized(mLock) { 3367 mTorchCallbackMap.remove(callback); 3368 } 3369 } 3370 3371 /** 3372 * Callback from camera service notifying the process about camera availability changes 3373 */ 3374 @Override onStatusChanged(int status, String cameraId, int deviceId)3375 public void onStatusChanged(int status, String cameraId, int deviceId) 3376 throws RemoteException { 3377 synchronized(mLock) { 3378 addDeviceStatusHistoryLocked( 3379 TextUtils.formatSimple("onStatusChanged(E): tid(%d): mDeviceStatus size %d", 3380 Thread.currentThread().getId(), mDeviceStatus.size())); 3381 onStatusChangedLocked(status, new DeviceCameraInfo(cameraId, deviceId)); 3382 addDeviceStatusHistoryLocked( 3383 TextUtils.formatSimple("onStatusChanged(X): tid(%d): mDeviceStatus size %d", 3384 Thread.currentThread().getId(), mDeviceStatus.size())); 3385 } 3386 } 3387 3388 @Override onPhysicalCameraStatusChanged(int status, String cameraId, String physicalCameraId, int deviceId)3389 public void onPhysicalCameraStatusChanged(int status, String cameraId, 3390 String physicalCameraId, int deviceId) throws RemoteException { 3391 synchronized (mLock) { 3392 onPhysicalCameraStatusChangedLocked(status, 3393 new DeviceCameraInfo(cameraId, deviceId), physicalCameraId); 3394 } 3395 } 3396 3397 @Override onTorchStatusChanged(int status, String cameraId, int deviceId)3398 public void onTorchStatusChanged(int status, String cameraId, int deviceId) 3399 throws RemoteException { 3400 synchronized (mLock) { 3401 onTorchStatusChangedLocked(status, new DeviceCameraInfo(cameraId, deviceId)); 3402 } 3403 } 3404 3405 @Override onTorchStrengthLevelChanged(String cameraId, int newStrengthLevel, int deviceId)3406 public void onTorchStrengthLevelChanged(String cameraId, int newStrengthLevel, int deviceId) 3407 throws RemoteException { 3408 synchronized (mLock) { 3409 onTorchStrengthLevelChangedLocked(new DeviceCameraInfo(cameraId, deviceId), 3410 newStrengthLevel); 3411 } 3412 } 3413 3414 @Override onCameraAccessPrioritiesChanged()3415 public void onCameraAccessPrioritiesChanged() { 3416 synchronized (mLock) { 3417 final int callbackCount = mCallbackMap.size(); 3418 for (int i = 0; i < callbackCount; i++) { 3419 Executor executor = mCallbackMap.valueAt(i); 3420 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 3421 3422 postSingleAccessPriorityChangeUpdate(callback, executor); 3423 } 3424 } 3425 } 3426 3427 @Override onCameraOpenedInSharedMode(String cameraId, String clientPackageId, int deviceId, boolean primaryClient)3428 public void onCameraOpenedInSharedMode(String cameraId, String clientPackageId, 3429 int deviceId, boolean primaryClient) { 3430 } 3431 3432 @Override onCameraOpened(String cameraId, String clientPackageId, int deviceId)3433 public void onCameraOpened(String cameraId, String clientPackageId, int deviceId) { 3434 synchronized (mLock) { 3435 onCameraOpenedLocked(new DeviceCameraInfo(cameraId, deviceId), clientPackageId); 3436 } 3437 } 3438 onCameraOpenedLocked(DeviceCameraInfo info, String clientPackageId)3439 private void onCameraOpenedLocked(DeviceCameraInfo info, String clientPackageId) { 3440 String oldApk = mOpenedDevices.put(info, clientPackageId); 3441 3442 if (oldApk != null) { 3443 if (oldApk.equals(clientPackageId)) { 3444 Log.w(TAG, 3445 "onCameraOpened was previously called for " + oldApk 3446 + " and is now again called for the same package name, " 3447 + "so no new client visible update will be sent"); 3448 return; 3449 } else { 3450 Log.w(TAG, 3451 "onCameraOpened was previously called for " + oldApk 3452 + " and is now called for " + clientPackageId 3453 + " without onCameraClosed being called first"); 3454 } 3455 } 3456 3457 final int callbackCount = mCallbackMap.size(); 3458 for (int i = 0; i < callbackCount; i++) { 3459 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 3460 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3461 continue; 3462 } 3463 3464 final Executor executor = mCallbackMap.valueAt(i); 3465 postSingleCameraOpenedUpdate(callback, executor, info.mCameraId, clientPackageId); 3466 } 3467 } 3468 3469 @Override onCameraClosed(String cameraId, int deviceId)3470 public void onCameraClosed(String cameraId, int deviceId) { 3471 synchronized (mLock) { 3472 onCameraClosedLocked(new DeviceCameraInfo(cameraId, deviceId)); 3473 } 3474 } 3475 onCameraClosedLocked(DeviceCameraInfo info)3476 private void onCameraClosedLocked(DeviceCameraInfo info) { 3477 mOpenedDevices.remove(info); 3478 3479 final int callbackCount = mCallbackMap.size(); 3480 for (int i = 0; i < callbackCount; i++) { 3481 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 3482 if (shouldHideCamera(callback.mDeviceId, callback.mDevicePolicy, info)) { 3483 continue; 3484 } 3485 3486 final Executor executor = mCallbackMap.valueAt(i); 3487 postSingleCameraClosedUpdate(callback, executor, info.mCameraId); 3488 } 3489 } 3490 3491 /** 3492 * Try to connect to camera service after some delay if any client registered camera 3493 * availability callback or torch status callback. 3494 */ scheduleCameraServiceReconnectionLocked()3495 private void scheduleCameraServiceReconnectionLocked() { 3496 if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) { 3497 // Not necessary to reconnect camera service if no client registers a callback. 3498 return; 3499 } 3500 3501 if (DEBUG) { 3502 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS + 3503 " ms"); 3504 } 3505 3506 try { 3507 mScheduler.schedule(() -> { 3508 ICameraService cameraService = getCameraService(); 3509 if (cameraService == null) { 3510 synchronized(mLock) { 3511 if (DEBUG) { 3512 Log.v(TAG, "Reconnecting Camera Service failed."); 3513 } 3514 scheduleCameraServiceReconnectionLocked(); 3515 } 3516 } 3517 }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS); 3518 } catch (RejectedExecutionException e) { 3519 Log.e(TAG, "Failed to schedule camera service re-connect: " + e); 3520 } 3521 } 3522 3523 /** 3524 * Listener for camera service death. 3525 * 3526 * <p>The camera service isn't supposed to die under any normal circumstances, but can be 3527 * turned off during debug, or crash due to bugs. So detect that and null out the interface 3528 * object, so that the next calls to the manager can try to reconnect.</p> 3529 */ binderDied()3530 public void binderDied() { 3531 synchronized(mLock) { 3532 addDeviceStatusHistoryLocked( 3533 TextUtils.formatSimple("binderDied(E): tid(%d): mDeviceStatus size %d", 3534 Thread.currentThread().getId(), mDeviceStatus.size())); 3535 3536 // Only do this once per service death 3537 if (mCameraService == null) return; 3538 3539 mCameraService = null; 3540 3541 // Tell listeners that the cameras and torch modes are unavailable and schedule a 3542 // reconnection to camera service. When camera service is reconnected, the camera 3543 // and torch statuses will be updated. 3544 // Iterate from the end to the beginning because onStatusChangedLocked removes 3545 // entries from the ArrayMap. 3546 for (int i = mDeviceStatus.size() - 1; i >= 0; i--) { 3547 DeviceCameraInfo info = mDeviceStatus.keyAt(i); 3548 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, info); 3549 3550 if (mHasOpenCloseListenerPermission) { 3551 onCameraClosedLocked(info); 3552 } 3553 } 3554 3555 for (int i = 0; i < mTorchStatus.size(); i++) { 3556 DeviceCameraInfo info = mTorchStatus.keyAt(i); 3557 onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE, 3558 info); 3559 } 3560 3561 mConcurrentCameraIdCombinations.clear(); 3562 3563 scheduleCameraServiceReconnectionLocked(); 3564 3565 addDeviceStatusHistoryLocked( 3566 TextUtils.formatSimple("binderDied(X): tid(%d): mDeviceStatus size %d", 3567 Thread.currentThread().getId(), mDeviceStatus.size())); 3568 } 3569 } 3570 3571 private static final class DeviceCameraInfo { 3572 private final String mCameraId; 3573 private final int mDeviceId; 3574 DeviceCameraInfo(String cameraId, int deviceId)3575 DeviceCameraInfo(String cameraId, int deviceId) { 3576 mCameraId = cameraId; 3577 mDeviceId = deviceId; 3578 } 3579 3580 @Override equals(Object o)3581 public boolean equals(Object o) { 3582 if (this == o) { 3583 return true; 3584 } 3585 if (o == null || getClass() != o.getClass()) { 3586 return false; 3587 } 3588 DeviceCameraInfo that = (DeviceCameraInfo) o; 3589 return mDeviceId == that.mDeviceId && Objects.equals(mCameraId, that.mCameraId); 3590 } 3591 3592 @Override hashCode()3593 public int hashCode() { 3594 return Objects.hash(mCameraId, mDeviceId); 3595 } 3596 } 3597 addDeviceStatusHistoryLocked(String log)3598 private void addDeviceStatusHistoryLocked(String log) { 3599 if (mDeviceStatusHistory.size() == DEVICE_STATUS_ARRAY_SIZE) { 3600 mDeviceStatusHistory.removeFirst(); 3601 } 3602 mDeviceStatusHistory.addLast(log); 3603 } 3604 3605 } // CameraManagerGlobal 3606 } // CameraManager 3607