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