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