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