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