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.SystemService; 24 import android.content.Context; 25 import android.hardware.CameraInfo; 26 import android.hardware.CameraStatus; 27 import android.hardware.ICameraService; 28 import android.hardware.ICameraServiceListener; 29 import android.hardware.camera2.impl.CameraDeviceImpl; 30 import android.hardware.camera2.impl.CameraMetadataNative; 31 import android.hardware.camera2.legacy.CameraDeviceUserShim; 32 import android.hardware.camera2.legacy.LegacyMetadataMapper; 33 import android.os.Binder; 34 import android.os.DeadObjectException; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.Looper; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.os.ServiceSpecificException; 41 import android.os.SystemProperties; 42 import android.util.ArrayMap; 43 import android.util.Log; 44 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Comparator; 48 49 import java.util.concurrent.Executor; 50 import java.util.concurrent.Executors; 51 import java.util.concurrent.RejectedExecutionException; 52 import java.util.concurrent.ScheduledExecutorService; 53 import java.util.concurrent.TimeUnit; 54 55 /** 56 * <p>A system service manager for detecting, characterizing, and connecting to 57 * {@link CameraDevice CameraDevices}.</p> 58 * 59 * <p>For more details about communicating with camera devices, read the Camera 60 * developer guide or the {@link android.hardware.camera2 camera2} 61 * package documentation.</p> 62 */ 63 @SystemService(Context.CAMERA_SERVICE) 64 public final class CameraManager { 65 66 private static final String TAG = "CameraManager"; 67 private final boolean DEBUG = false; 68 69 private static final int USE_CALLING_UID = -1; 70 71 @SuppressWarnings("unused") 72 private static final int API_VERSION_1 = 1; 73 private static final int API_VERSION_2 = 2; 74 75 private static final int CAMERA_TYPE_BACKWARD_COMPATIBLE = 0; 76 private static final int CAMERA_TYPE_ALL = 1; 77 78 private ArrayList<String> mDeviceIdList; 79 80 private final Context mContext; 81 private final Object mLock = new Object(); 82 83 /** 84 * @hide 85 */ CameraManager(Context context)86 public CameraManager(Context context) { 87 synchronized(mLock) { 88 mContext = context; 89 } 90 } 91 92 /** 93 * Return the list of currently connected camera devices by identifier, including 94 * cameras that may be in use by other camera API clients. 95 * 96 * <p>Non-removable cameras use integers starting at 0 for their 97 * identifiers, while removable cameras have a unique identifier for each 98 * individual device, even if they are the same model.</p> 99 * 100 * @return The list of currently connected camera devices. 101 */ 102 @NonNull getCameraIdList()103 public String[] getCameraIdList() throws CameraAccessException { 104 return CameraManagerGlobal.get().getCameraIdList(); 105 } 106 107 /** 108 * Register a callback to be notified about camera device availability. 109 * 110 * <p>Registering the same callback again will replace the handler with the 111 * new one provided.</p> 112 * 113 * <p>The first time a callback is registered, it is immediately called 114 * with the availability status of all currently known camera devices.</p> 115 * 116 * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera 117 * device is opened by any camera API client. As of API level 23, other camera API clients may 118 * still be able to open such a camera device, evicting the existing client if they have higher 119 * priority than the existing client of a camera device. See open() for more details.</p> 120 * 121 * <p>Since this callback will be registered with the camera service, remember to unregister it 122 * once it is no longer needed; otherwise the callback will continue to receive events 123 * indefinitely and it may prevent other resources from being released. Specifically, the 124 * callbacks will be invoked independently of the general activity lifecycle and independently 125 * of the state of individual CameraManager instances.</p> 126 * 127 * @param callback the new callback to send camera availability notices to 128 * @param handler The handler on which the callback should be invoked, or {@code null} to use 129 * the current thread's {@link android.os.Looper looper}. 130 * 131 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has 132 * no looper. 133 */ registerAvailabilityCallback(@onNull AvailabilityCallback callback, @Nullable Handler handler)134 public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback, 135 @Nullable Handler handler) { 136 CameraManagerGlobal.get().registerAvailabilityCallback(callback, 137 CameraDeviceImpl.checkAndWrapHandler(handler)); 138 } 139 140 /** 141 * Register a callback to be notified about camera device availability. 142 * 143 * <p>The behavior of this method matches that of 144 * {@link #registerAvailabilityCallback(AvailabilityCallback, Handler)}, 145 * except that it uses {@link java.util.concurrent.Executor} as an argument 146 * instead of {@link android.os.Handler}.</p> 147 * 148 * @param executor The executor which will be used to invoke the callback. 149 * @param callback the new callback to send camera availability notices to 150 * 151 * @throws IllegalArgumentException if the executor is {@code null}. 152 */ registerAvailabilityCallback(@onNull @allbackExecutor Executor executor, @NonNull AvailabilityCallback callback)153 public void registerAvailabilityCallback(@NonNull @CallbackExecutor Executor executor, 154 @NonNull AvailabilityCallback callback) { 155 if (executor == null) { 156 throw new IllegalArgumentException("executor was null"); 157 } 158 CameraManagerGlobal.get().registerAvailabilityCallback(callback, executor); 159 } 160 161 /** 162 * Remove a previously-added callback; the callback will no longer receive connection and 163 * disconnection callbacks. 164 * 165 * <p>Removing a callback that isn't registered has no effect.</p> 166 * 167 * @param callback The callback to remove from the notification list 168 */ unregisterAvailabilityCallback(@onNull AvailabilityCallback callback)169 public void unregisterAvailabilityCallback(@NonNull AvailabilityCallback callback) { 170 CameraManagerGlobal.get().unregisterAvailabilityCallback(callback); 171 } 172 173 /** 174 * Register a callback to be notified about torch mode status. 175 * 176 * <p>Registering the same callback again will replace the handler with the 177 * new one provided.</p> 178 * 179 * <p>The first time a callback is registered, it is immediately called 180 * with the torch mode status of all currently known camera devices with a flash unit.</p> 181 * 182 * <p>Since this callback will be registered with the camera service, remember to unregister it 183 * once it is no longer needed; otherwise the callback will continue to receive events 184 * indefinitely and it may prevent other resources from being released. Specifically, the 185 * callbacks will be invoked independently of the general activity lifecycle and independently 186 * of the state of individual CameraManager instances.</p> 187 * 188 * @param callback The new callback to send torch mode status to 189 * @param handler The handler on which the callback should be invoked, or {@code null} to use 190 * the current thread's {@link android.os.Looper looper}. 191 * 192 * @throws IllegalArgumentException if the handler is {@code null} but the current thread has 193 * no looper. 194 */ registerTorchCallback(@onNull TorchCallback callback, @Nullable Handler handler)195 public void registerTorchCallback(@NonNull TorchCallback callback, @Nullable Handler handler) { 196 CameraManagerGlobal.get().registerTorchCallback(callback, 197 CameraDeviceImpl.checkAndWrapHandler(handler)); 198 } 199 200 /** 201 * Register a callback to be notified about torch mode status. 202 * 203 * <p>The behavior of this method matches that of 204 * {@link #registerTorchCallback(TorchCallback, Handler)}, 205 * except that it uses {@link java.util.concurrent.Executor} as an argument 206 * instead of {@link android.os.Handler}.</p> 207 * 208 * @param executor The executor which will be used to invoke the callback 209 * @param callback The new callback to send torch mode status to 210 * 211 * @throws IllegalArgumentException if the executor is {@code null}. 212 */ registerTorchCallback(@onNull @allbackExecutor Executor executor, @NonNull TorchCallback callback)213 public void registerTorchCallback(@NonNull @CallbackExecutor Executor executor, 214 @NonNull TorchCallback callback) { 215 if (executor == null) { 216 throw new IllegalArgumentException("executor was null"); 217 } 218 CameraManagerGlobal.get().registerTorchCallback(callback, executor); 219 } 220 221 /** 222 * Remove a previously-added callback; the callback will no longer receive torch mode status 223 * callbacks. 224 * 225 * <p>Removing a callback that isn't registered has no effect.</p> 226 * 227 * @param callback The callback to remove from the notification list 228 */ unregisterTorchCallback(@onNull TorchCallback callback)229 public void unregisterTorchCallback(@NonNull TorchCallback callback) { 230 CameraManagerGlobal.get().unregisterTorchCallback(callback); 231 } 232 233 /** 234 * <p>Query the capabilities of a camera device. These capabilities are 235 * immutable for a given camera.</p> 236 * 237 * @param cameraId The id of the camera device to query 238 * @return The properties of the given camera 239 * 240 * @throws IllegalArgumentException if the cameraId does not match any 241 * known camera device. 242 * @throws CameraAccessException if the camera device has been disconnected. 243 * 244 * @see #getCameraIdList 245 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 246 */ 247 @NonNull getCameraCharacteristics(@onNull String cameraId)248 public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId) 249 throws CameraAccessException { 250 CameraCharacteristics characteristics = null; 251 if (CameraManagerGlobal.sCameraServiceDisabled) { 252 throw new IllegalArgumentException("No cameras available on device"); 253 } 254 synchronized (mLock) { 255 /* 256 * Get the camera characteristics from the camera service directly if it supports it, 257 * otherwise get them from the legacy shim instead. 258 */ 259 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 260 if (cameraService == null) { 261 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 262 "Camera service is currently unavailable"); 263 } 264 try { 265 if (!supportsCamera2ApiLocked(cameraId)) { 266 // Legacy backwards compatibility path; build static info from the camera 267 // parameters 268 int id = Integer.parseInt(cameraId); 269 270 String parameters = cameraService.getLegacyParameters(id); 271 272 CameraInfo info = cameraService.getCameraInfo(id); 273 274 characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info); 275 } else { 276 // Normal path: Get the camera characteristics directly from the camera service 277 CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId); 278 279 characteristics = new CameraCharacteristics(info); 280 } 281 } catch (ServiceSpecificException e) { 282 throwAsPublicException(e); 283 } catch (RemoteException e) { 284 // Camera service died - act as if the camera was disconnected 285 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 286 "Camera service is currently unavailable", e); 287 } 288 } 289 return characteristics; 290 } 291 292 /** 293 * Helper for opening a connection to a camera with the given ID. 294 * 295 * @param cameraId The unique identifier of the camera device to open 296 * @param callback The callback for the camera. Must not be null. 297 * @param executor The executor to invoke the callback with. Must not be null. 298 * @param uid The UID of the application actually opening the camera. 299 * Must be USE_CALLING_UID unless the caller is a service 300 * that is trusted to open the device on behalf of an 301 * application and to forward the real UID. 302 * 303 * @throws CameraAccessException if the camera is disabled by device policy, 304 * too many camera devices are already open, or the cameraId does not match 305 * any currently available camera device. 306 * 307 * @throws SecurityException if the application does not have permission to 308 * access the camera 309 * @throws IllegalArgumentException if callback or handler is null. 310 * @return A handle to the newly-created camera device. 311 * 312 * @see #getCameraIdList 313 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 314 */ openCameraDeviceUserAsync(String cameraId, CameraDevice.StateCallback callback, Executor executor, final int uid)315 private CameraDevice openCameraDeviceUserAsync(String cameraId, 316 CameraDevice.StateCallback callback, Executor executor, final int uid) 317 throws CameraAccessException { 318 CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); 319 CameraDevice device = null; 320 321 synchronized (mLock) { 322 323 ICameraDeviceUser cameraUser = null; 324 325 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl = 326 new android.hardware.camera2.impl.CameraDeviceImpl( 327 cameraId, 328 callback, 329 executor, 330 characteristics, 331 mContext.getApplicationInfo().targetSdkVersion); 332 333 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks(); 334 335 try { 336 if (supportsCamera2ApiLocked(cameraId)) { 337 // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices 338 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 339 if (cameraService == null) { 340 throw new ServiceSpecificException( 341 ICameraService.ERROR_DISCONNECTED, 342 "Camera service is currently unavailable"); 343 } 344 cameraUser = cameraService.connectDevice(callbacks, cameraId, 345 mContext.getOpPackageName(), uid); 346 } else { 347 // Use legacy camera implementation for HAL1 devices 348 int id; 349 try { 350 id = Integer.parseInt(cameraId); 351 } catch (NumberFormatException e) { 352 throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: " 353 + cameraId); 354 } 355 356 Log.i(TAG, "Using legacy camera HAL."); 357 cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id); 358 } 359 } catch (ServiceSpecificException e) { 360 if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) { 361 throw new AssertionError("Should've gone down the shim path"); 362 } else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE || 363 e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE || 364 e.errorCode == ICameraService.ERROR_DISABLED || 365 e.errorCode == ICameraService.ERROR_DISCONNECTED || 366 e.errorCode == ICameraService.ERROR_INVALID_OPERATION) { 367 // Received one of the known connection errors 368 // The remote camera device cannot be connected to, so 369 // set the local camera to the startup error state 370 deviceImpl.setRemoteFailure(e); 371 372 if (e.errorCode == ICameraService.ERROR_DISABLED || 373 e.errorCode == ICameraService.ERROR_DISCONNECTED || 374 e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) { 375 // Per API docs, these failures call onError and throw 376 throwAsPublicException(e); 377 } 378 } else { 379 // Unexpected failure - rethrow 380 throwAsPublicException(e); 381 } 382 } catch (RemoteException e) { 383 // Camera service died - act as if it's a CAMERA_DISCONNECTED case 384 ServiceSpecificException sse = new ServiceSpecificException( 385 ICameraService.ERROR_DISCONNECTED, 386 "Camera service is currently unavailable"); 387 deviceImpl.setRemoteFailure(sse); 388 throwAsPublicException(sse); 389 } 390 391 // TODO: factor out callback to be non-nested, then move setter to constructor 392 // For now, calling setRemoteDevice will fire initial 393 // onOpened/onUnconfigured callbacks. 394 // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if 395 // cameraUser dies during setup. 396 deviceImpl.setRemoteDevice(cameraUser); 397 device = deviceImpl; 398 } 399 400 return device; 401 } 402 403 /** 404 * Open a connection to a camera with the given ID. 405 * 406 * <p>Use {@link #getCameraIdList} to get the list of available camera 407 * devices. Note that even if an id is listed, open may fail if the device 408 * is disconnected between the calls to {@link #getCameraIdList} and 409 * {@link #openCamera}, or if a higher-priority camera API client begins using the 410 * camera device.</p> 411 * 412 * <p>As of API level 23, devices for which the 413 * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the 414 * device being in use by a lower-priority, background camera API client can still potentially 415 * be opened by calling this method when the calling camera API client has a higher priority 416 * than the current camera API client using this device. In general, if the top, foreground 417 * activity is running within your application process, your process will be given the highest 418 * priority when accessing the camera, and this method will succeed even if the camera device is 419 * in use by another camera API client. Any lower-priority application that loses control of the 420 * camera in this way will receive an 421 * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.</p> 422 * 423 * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will 424 * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up 425 * for operation by calling {@link CameraDevice#createCaptureSession} and 426 * {@link CameraDevice#createCaptureRequest}</p> 427 * 428 * <!-- 429 * <p>Since the camera device will be opened asynchronously, any asynchronous operations done 430 * on the returned CameraDevice instance will be queued up until the device startup has 431 * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is 432 * called. The pending operations are then processed in order.</p> 433 * --> 434 * <p>If the camera becomes disconnected during initialization 435 * after this function call returns, 436 * {@link CameraDevice.StateCallback#onDisconnected} with a 437 * {@link CameraDevice} in the disconnected state (and 438 * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p> 439 * 440 * <p>If opening the camera device fails, then the device callback's 441 * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent 442 * calls on the camera device will throw a {@link CameraAccessException}.</p> 443 * 444 * @param cameraId 445 * The unique identifier of the camera device to open 446 * @param callback 447 * The callback which is invoked once the camera is opened 448 * @param handler 449 * The handler on which the callback should be invoked, or 450 * {@code null} to use the current thread's {@link android.os.Looper looper}. 451 * 452 * @throws CameraAccessException if the camera is disabled by device policy, 453 * has been disconnected, or is being used by a higher-priority camera API client. 454 * 455 * @throws IllegalArgumentException if cameraId or the callback was null, 456 * or the cameraId does not match any currently or previously available 457 * camera device. 458 * 459 * @throws SecurityException if the application does not have permission to 460 * access the camera 461 * 462 * @see #getCameraIdList 463 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 464 */ 465 @RequiresPermission(android.Manifest.permission.CAMERA) openCamera(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)466 public void openCamera(@NonNull String cameraId, 467 @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler) 468 throws CameraAccessException { 469 470 openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler), 471 USE_CALLING_UID); 472 } 473 474 /** 475 * Open a connection to a camera with the given ID. 476 * 477 * <p>The behavior of this method matches that of 478 * {@link #openCamera(String, StateCallback, Handler)}, except that it uses 479 * {@link java.util.concurrent.Executor} as an argument instead of 480 * {@link android.os.Handler}.</p> 481 * 482 * @param cameraId 483 * The unique identifier of the camera device to open 484 * @param executor 485 * The executor which will be used when invoking the callback. 486 * @param callback 487 * The callback which is invoked once the camera is opened 488 * 489 * @throws CameraAccessException if the camera is disabled by device policy, 490 * has been disconnected, or is being used by a higher-priority camera API client. 491 * 492 * @throws IllegalArgumentException if cameraId, the callback or the executor was null, 493 * or the cameraId does not match any currently or previously available 494 * camera device. 495 * 496 * @throws SecurityException if the application does not have permission to 497 * access the camera 498 * 499 * @see #getCameraIdList 500 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 501 */ 502 @RequiresPermission(android.Manifest.permission.CAMERA) openCamera(@onNull String cameraId, @NonNull @CallbackExecutor Executor executor, @NonNull final CameraDevice.StateCallback callback)503 public void openCamera(@NonNull String cameraId, 504 @NonNull @CallbackExecutor Executor executor, 505 @NonNull final CameraDevice.StateCallback callback) 506 throws CameraAccessException { 507 if (executor == null) { 508 throw new IllegalArgumentException("executor was null"); 509 } 510 openCameraForUid(cameraId, callback, executor, USE_CALLING_UID); 511 } 512 513 /** 514 * Open a connection to a camera with the given ID, on behalf of another application 515 * specified by clientUid. 516 * 517 * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows 518 * the caller to specify the UID to use for permission/etc verification. This can only be 519 * done by services trusted by the camera subsystem to act on behalf of applications and 520 * to forward the real UID.</p> 521 * 522 * @param clientUid 523 * The UID of the application on whose behalf the camera is being opened. 524 * Must be USE_CALLING_UID unless the caller is a trusted service. 525 * 526 * @hide 527 */ openCameraForUid(@onNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int clientUid)528 public void openCameraForUid(@NonNull String cameraId, 529 @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, 530 int clientUid) 531 throws CameraAccessException { 532 533 if (cameraId == null) { 534 throw new IllegalArgumentException("cameraId was null"); 535 } else if (callback == null) { 536 throw new IllegalArgumentException("callback was null"); 537 } 538 if (CameraManagerGlobal.sCameraServiceDisabled) { 539 throw new IllegalArgumentException("No cameras available on device"); 540 } 541 542 openCameraDeviceUserAsync(cameraId, callback, executor, clientUid); 543 } 544 545 /** 546 * Set the flash unit's torch mode of the camera of the given ID without opening the camera 547 * device. 548 * 549 * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use 550 * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit. 551 * Note that even if a camera device has a flash unit, turning on the torch mode may fail 552 * if the camera device or other camera resources needed to turn on the torch mode are in use. 553 * </p> 554 * 555 * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully, 556 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked. 557 * However, even if turning on the torch mode is successful, the application does not have the 558 * exclusive ownership of the flash unit or the camera device. The torch mode will be turned 559 * off and becomes unavailable when the camera device that the flash unit belongs to becomes 560 * unavailable or when other camera resources to keep the torch on become unavailable ( 561 * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also, 562 * other applications are free to call {@link #setTorchMode} to turn off the torch mode ( 563 * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest 564 * application that turned on the torch mode exits, the torch mode will be turned off. 565 * 566 * @param cameraId 567 * The unique identifier of the camera device that the flash unit belongs to. 568 * @param enabled 569 * The desired state of the torch mode for the target camera device. Set to 570 * {@code true} to turn on the torch mode. Set to {@code false} to turn off the 571 * torch mode. 572 * 573 * @throws CameraAccessException if it failed to access the flash unit. 574 * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device 575 * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if 576 * other camera resources needed to turn on the torch mode are in use. 577 * {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera 578 * service is not available. 579 * 580 * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently 581 * or previously available camera device, or the camera device doesn't have a 582 * flash unit. 583 */ setTorchMode(@onNull String cameraId, boolean enabled)584 public void setTorchMode(@NonNull String cameraId, boolean enabled) 585 throws CameraAccessException { 586 if (CameraManagerGlobal.sCameraServiceDisabled) { 587 throw new IllegalArgumentException("No cameras available on device"); 588 } 589 CameraManagerGlobal.get().setTorchMode(cameraId, enabled); 590 } 591 592 /** 593 * A callback for camera devices becoming available or unavailable to open. 594 * 595 * <p>Cameras become available when they are no longer in use, or when a new 596 * removable camera is connected. They become unavailable when some 597 * application or service starts using a camera, or when a removable camera 598 * is disconnected.</p> 599 * 600 * <p>Extend this callback and pass an instance of the subclass to 601 * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability 602 * changes.</p> 603 * 604 * @see #registerAvailabilityCallback 605 */ 606 public static abstract class AvailabilityCallback { 607 608 /** 609 * A new camera has become available to use. 610 * 611 * <p>The default implementation of this method does nothing.</p> 612 * 613 * @param cameraId The unique identifier of the new camera. 614 */ onCameraAvailable(@onNull String cameraId)615 public void onCameraAvailable(@NonNull String cameraId) { 616 // default empty implementation 617 } 618 619 /** 620 * A previously-available camera has become unavailable for use. 621 * 622 * <p>If an application had an active CameraDevice instance for the 623 * now-disconnected camera, that application will receive a 624 * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p> 625 * 626 * <p>The default implementation of this method does nothing.</p> 627 * 628 * @param cameraId The unique identifier of the disconnected camera. 629 */ onCameraUnavailable(@onNull String cameraId)630 public void onCameraUnavailable(@NonNull String cameraId) { 631 // default empty implementation 632 } 633 } 634 635 /** 636 * A callback for camera flash torch modes becoming unavailable, disabled, or enabled. 637 * 638 * <p>The torch mode becomes unavailable when the camera device it belongs to becomes 639 * unavailable or other camera resources it needs become busy due to other higher priority 640 * camera activities. The torch mode becomes disabled when it was turned off or when the camera 641 * device it belongs to is no longer in use and other camera resources it needs are no longer 642 * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to 643 * turn off the camera's torch mode, or when an application turns on another camera's torch mode 644 * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes 645 * enabled when it is turned on via {@link #setTorchMode}.</p> 646 * 647 * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled 648 * or enabled state.</p> 649 * 650 * <p>Extend this callback and pass an instance of the subclass to 651 * {@link CameraManager#registerTorchCallback} to be notified of such status changes. 652 * </p> 653 * 654 * @see #registerTorchCallback 655 */ 656 public static abstract class TorchCallback { 657 /** 658 * A camera's torch mode has become unavailable to set via {@link #setTorchMode}. 659 * 660 * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be 661 * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is 662 * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or 663 * enabled state again.</p> 664 * 665 * <p>The default implementation of this method does nothing.</p> 666 * 667 * @param cameraId The unique identifier of the camera whose torch mode has become 668 * unavailable. 669 */ onTorchModeUnavailable(@onNull String cameraId)670 public void onTorchModeUnavailable(@NonNull String cameraId) { 671 // default empty implementation 672 } 673 674 /** 675 * A camera's torch mode has become enabled or disabled and can be changed via 676 * {@link #setTorchMode}. 677 * 678 * <p>The default implementation of this method does nothing.</p> 679 * 680 * @param cameraId The unique identifier of the camera whose torch mode has been changed. 681 * 682 * @param enabled The state that the torch mode of the camera has been changed to. 683 * {@code true} when the torch mode has become on and available to be turned 684 * off. {@code false} when the torch mode has becomes off and available to 685 * be turned on. 686 */ onTorchModeChanged(@onNull String cameraId, boolean enabled)687 public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) { 688 // default empty implementation 689 } 690 } 691 692 /** 693 * Convert ServiceSpecificExceptions and Binder RemoteExceptions from camera binder interfaces 694 * into the correct public exceptions. 695 * 696 * @hide 697 */ throwAsPublicException(Throwable t)698 public static void throwAsPublicException(Throwable t) throws CameraAccessException { 699 if (t instanceof ServiceSpecificException) { 700 ServiceSpecificException e = (ServiceSpecificException) t; 701 int reason = CameraAccessException.CAMERA_ERROR; 702 switch(e.errorCode) { 703 case ICameraService.ERROR_DISCONNECTED: 704 reason = CameraAccessException.CAMERA_DISCONNECTED; 705 break; 706 case ICameraService.ERROR_DISABLED: 707 reason = CameraAccessException.CAMERA_DISABLED; 708 break; 709 case ICameraService.ERROR_CAMERA_IN_USE: 710 reason = CameraAccessException.CAMERA_IN_USE; 711 break; 712 case ICameraService.ERROR_MAX_CAMERAS_IN_USE: 713 reason = CameraAccessException.MAX_CAMERAS_IN_USE; 714 break; 715 case ICameraService.ERROR_DEPRECATED_HAL: 716 reason = CameraAccessException.CAMERA_DEPRECATED_HAL; 717 break; 718 case ICameraService.ERROR_ILLEGAL_ARGUMENT: 719 case ICameraService.ERROR_ALREADY_EXISTS: 720 throw new IllegalArgumentException(e.getMessage(), e); 721 case ICameraService.ERROR_PERMISSION_DENIED: 722 throw new SecurityException(e.getMessage(), e); 723 case ICameraService.ERROR_TIMED_OUT: 724 case ICameraService.ERROR_INVALID_OPERATION: 725 default: 726 reason = CameraAccessException.CAMERA_ERROR; 727 } 728 throw new CameraAccessException(reason, e.getMessage(), e); 729 } else if (t instanceof DeadObjectException) { 730 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 731 "Camera service has died unexpectedly", 732 t); 733 } else if (t instanceof RemoteException) { 734 throw new UnsupportedOperationException("An unknown RemoteException was thrown" + 735 " which should never happen.", t); 736 } else if (t instanceof RuntimeException) { 737 RuntimeException e = (RuntimeException) t; 738 throw e; 739 } 740 } 741 742 /** 743 * Queries the camera service if it supports the camera2 api directly, or needs a shim. 744 * 745 * @param cameraId a non-{@code null} camera identifier 746 * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise. 747 */ supportsCamera2ApiLocked(String cameraId)748 private boolean supportsCamera2ApiLocked(String cameraId) { 749 return supportsCameraApiLocked(cameraId, API_VERSION_2); 750 } 751 752 /** 753 * Queries the camera service if it supports a camera api directly, or needs a shim. 754 * 755 * @param cameraId a non-{@code null} camera identifier 756 * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2} 757 * @return {@code true} if connecting will work for that device version. 758 */ supportsCameraApiLocked(String cameraId, int apiVersion)759 private boolean supportsCameraApiLocked(String cameraId, int apiVersion) { 760 /* 761 * Possible return values: 762 * - NO_ERROR => CameraX API is supported 763 * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception) 764 * - Remote exception => If the camera service died 765 * 766 * Anything else is an unexpected error we don't want to recover from. 767 */ 768 try { 769 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 770 // If no camera service, no support 771 if (cameraService == null) return false; 772 773 return cameraService.supportsCameraApi(cameraId, apiVersion); 774 } catch (RemoteException e) { 775 // Camera service is now down, no support for any API level 776 } 777 return false; 778 } 779 780 /** 781 * A per-process global camera manager instance, to retain a connection to the camera service, 782 * and to distribute camera availability notices to API-registered callbacks 783 */ 784 private static final class CameraManagerGlobal extends ICameraServiceListener.Stub 785 implements IBinder.DeathRecipient { 786 787 private static final String TAG = "CameraManagerGlobal"; 788 private final boolean DEBUG = false; 789 790 private final int CAMERA_SERVICE_RECONNECT_DELAY_MS = 1000; 791 792 // Singleton instance 793 private static final CameraManagerGlobal gCameraManager = 794 new CameraManagerGlobal(); 795 796 /** 797 * This must match the ICameraService definition 798 */ 799 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; 800 801 private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1); 802 // Camera ID -> Status map 803 private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); 804 805 // Registered availablility callbacks and their executors 806 private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap = 807 new ArrayMap<AvailabilityCallback, Executor>(); 808 809 // torch client binder to set the torch mode with. 810 private Binder mTorchClientBinder = new Binder(); 811 812 // Camera ID -> Torch status map 813 private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>(); 814 815 // Registered torch callbacks and their executors 816 private final ArrayMap<TorchCallback, Executor> mTorchCallbackMap = 817 new ArrayMap<TorchCallback, Executor>(); 818 819 private final Object mLock = new Object(); 820 821 // Access only through getCameraService to deal with binder death 822 private ICameraService mCameraService; 823 824 // Singleton, don't allow construction CameraManagerGlobal()825 private CameraManagerGlobal() { 826 } 827 828 public static final boolean sCameraServiceDisabled = 829 SystemProperties.getBoolean("config.disable_cameraservice", false); 830 get()831 public static CameraManagerGlobal get() { 832 return gCameraManager; 833 } 834 835 @Override asBinder()836 public IBinder asBinder() { 837 return this; 838 } 839 840 /** 841 * Return a best-effort ICameraService. 842 * 843 * <p>This will be null if the camera service is not currently available. If the camera 844 * service has died since the last use of the camera service, will try to reconnect to the 845 * service.</p> 846 */ getCameraService()847 public ICameraService getCameraService() { 848 synchronized(mLock) { 849 connectCameraServiceLocked(); 850 if (mCameraService == null && !sCameraServiceDisabled) { 851 Log.e(TAG, "Camera service is unavailable"); 852 } 853 return mCameraService; 854 } 855 } 856 857 /** 858 * Connect to the camera service if it's available, and set up listeners. 859 * If the service is already connected, do nothing. 860 * 861 * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p> 862 */ connectCameraServiceLocked()863 private void connectCameraServiceLocked() { 864 // Only reconnect if necessary 865 if (mCameraService != null || sCameraServiceDisabled) return; 866 867 Log.i(TAG, "Connecting to camera service"); 868 869 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME); 870 if (cameraServiceBinder == null) { 871 // Camera service is now down, leave mCameraService as null 872 return; 873 } 874 try { 875 cameraServiceBinder.linkToDeath(this, /*flags*/ 0); 876 } catch (RemoteException e) { 877 // Camera service is now down, leave mCameraService as null 878 return; 879 } 880 881 ICameraService cameraService = ICameraService.Stub.asInterface(cameraServiceBinder); 882 883 try { 884 CameraMetadataNative.setupGlobalVendorTagDescriptor(); 885 } catch (ServiceSpecificException e) { 886 handleRecoverableSetupErrors(e); 887 } 888 889 try { 890 CameraStatus[] cameraStatuses = cameraService.addListener(this); 891 for (CameraStatus c : cameraStatuses) { 892 onStatusChangedLocked(c.status, c.cameraId); 893 } 894 mCameraService = cameraService; 895 } catch(ServiceSpecificException e) { 896 // Unexpected failure 897 throw new IllegalStateException("Failed to register a camera service listener", e); 898 } catch (RemoteException e) { 899 // Camera service is now down, leave mCameraService as null 900 } 901 } 902 903 /** 904 * Get a list of all camera IDs that are at least PRESENT; ignore devices that are 905 * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone. 906 */ getCameraIdList()907 public String[] getCameraIdList() { 908 String[] cameraIds = null; 909 synchronized(mLock) { 910 // Try to make sure we have an up-to-date list of camera devices. 911 connectCameraServiceLocked(); 912 913 int idCount = 0; 914 for (int i = 0; i < mDeviceStatus.size(); i++) { 915 int status = mDeviceStatus.valueAt(i); 916 if (status == ICameraServiceListener.STATUS_NOT_PRESENT || 917 status == ICameraServiceListener.STATUS_ENUMERATING) continue; 918 idCount++; 919 } 920 cameraIds = new String[idCount]; 921 idCount = 0; 922 for (int i = 0; i < mDeviceStatus.size(); i++) { 923 int status = mDeviceStatus.valueAt(i); 924 if (status == ICameraServiceListener.STATUS_NOT_PRESENT || 925 status == ICameraServiceListener.STATUS_ENUMERATING) continue; 926 cameraIds[idCount] = mDeviceStatus.keyAt(i); 927 idCount++; 928 } 929 } 930 931 // The sort logic must match the logic in 932 // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds 933 Arrays.sort(cameraIds, new Comparator<String>() { 934 @Override 935 public int compare(String s1, String s2) { 936 int s1Int = 0, s2Int = 0; 937 try { 938 s1Int = Integer.parseInt(s1); 939 } catch (NumberFormatException e) { 940 s1Int = -1; 941 } 942 943 try { 944 s2Int = Integer.parseInt(s2); 945 } catch (NumberFormatException e) { 946 s2Int = -1; 947 } 948 949 // Uint device IDs first 950 if (s1Int >= 0 && s2Int >= 0) { 951 return s1Int - s2Int; 952 } else if (s1Int >= 0) { 953 return -1; 954 } else if (s2Int >= 0) { 955 return 1; 956 } else { 957 // Simple string compare if both id are not uint 958 return s1.compareTo(s2); 959 } 960 }}); 961 return cameraIds; 962 } 963 setTorchMode(String cameraId, boolean enabled)964 public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException { 965 synchronized(mLock) { 966 967 if (cameraId == null) { 968 throw new IllegalArgumentException("cameraId was null"); 969 } 970 971 ICameraService cameraService = getCameraService(); 972 if (cameraService == null) { 973 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 974 "Camera service is currently unavailable"); 975 } 976 977 try { 978 cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder); 979 } catch(ServiceSpecificException e) { 980 throwAsPublicException(e); 981 } catch (RemoteException e) { 982 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 983 "Camera service is currently unavailable"); 984 } 985 } 986 } 987 handleRecoverableSetupErrors(ServiceSpecificException e)988 private void handleRecoverableSetupErrors(ServiceSpecificException e) { 989 switch (e.errorCode) { 990 case ICameraService.ERROR_DISCONNECTED: 991 Log.w(TAG, e.getMessage()); 992 break; 993 default: 994 throw new IllegalStateException(e); 995 } 996 } 997 isAvailable(int status)998 private boolean isAvailable(int status) { 999 switch (status) { 1000 case ICameraServiceListener.STATUS_PRESENT: 1001 return true; 1002 default: 1003 return false; 1004 } 1005 } 1006 validStatus(int status)1007 private boolean validStatus(int status) { 1008 switch (status) { 1009 case ICameraServiceListener.STATUS_NOT_PRESENT: 1010 case ICameraServiceListener.STATUS_PRESENT: 1011 case ICameraServiceListener.STATUS_ENUMERATING: 1012 case ICameraServiceListener.STATUS_NOT_AVAILABLE: 1013 return true; 1014 default: 1015 return false; 1016 } 1017 } 1018 validTorchStatus(int status)1019 private boolean validTorchStatus(int status) { 1020 switch (status) { 1021 case ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE: 1022 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 1023 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: 1024 return true; 1025 default: 1026 return false; 1027 } 1028 } 1029 postSingleUpdate(final AvailabilityCallback callback, final Executor executor, final String id, final int status)1030 private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor, 1031 final String id, final int status) { 1032 if (isAvailable(status)) { 1033 final long ident = Binder.clearCallingIdentity(); 1034 try { 1035 executor.execute( 1036 new Runnable() { 1037 @Override 1038 public void run() { 1039 callback.onCameraAvailable(id); 1040 } 1041 }); 1042 } finally { 1043 Binder.restoreCallingIdentity(ident); 1044 } 1045 } else { 1046 final long ident = Binder.clearCallingIdentity(); 1047 try { 1048 executor.execute( 1049 new Runnable() { 1050 @Override 1051 public void run() { 1052 callback.onCameraUnavailable(id); 1053 } 1054 }); 1055 } finally { 1056 Binder.restoreCallingIdentity(ident); 1057 } 1058 } 1059 } 1060 postSingleTorchUpdate(final TorchCallback callback, final Executor executor, final String id, final int status)1061 private void postSingleTorchUpdate(final TorchCallback callback, final Executor executor, 1062 final String id, final int status) { 1063 switch(status) { 1064 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON: 1065 case ICameraServiceListener.TORCH_STATUS_AVAILABLE_OFF: { 1066 final long ident = Binder.clearCallingIdentity(); 1067 try { 1068 executor.execute(() -> { 1069 callback.onTorchModeChanged(id, status == 1070 ICameraServiceListener.TORCH_STATUS_AVAILABLE_ON); 1071 }); 1072 } finally { 1073 Binder.restoreCallingIdentity(ident); 1074 } 1075 } 1076 break; 1077 default: { 1078 final long ident = Binder.clearCallingIdentity(); 1079 try { 1080 executor.execute(() -> { 1081 callback.onTorchModeUnavailable(id); 1082 }); 1083 } finally { 1084 Binder.restoreCallingIdentity(ident); 1085 } 1086 } 1087 break; 1088 } 1089 } 1090 1091 /** 1092 * Send the state of all known cameras to the provided listener, to initialize 1093 * the listener's knowledge of camera state. 1094 */ updateCallbackLocked(AvailabilityCallback callback, Executor executor)1095 private void updateCallbackLocked(AvailabilityCallback callback, Executor executor) { 1096 for (int i = 0; i < mDeviceStatus.size(); i++) { 1097 String id = mDeviceStatus.keyAt(i); 1098 Integer status = mDeviceStatus.valueAt(i); 1099 postSingleUpdate(callback, executor, id, status); 1100 } 1101 } 1102 onStatusChangedLocked(int status, String id)1103 private void onStatusChangedLocked(int status, String id) { 1104 if (DEBUG) { 1105 Log.v(TAG, 1106 String.format("Camera id %s has status changed to 0x%x", id, status)); 1107 } 1108 1109 if (!validStatus(status)) { 1110 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id, 1111 status)); 1112 return; 1113 } 1114 1115 Integer oldStatus; 1116 if (status == ICameraServiceListener.STATUS_NOT_PRESENT) { 1117 oldStatus = mDeviceStatus.remove(id); 1118 } else { 1119 oldStatus = mDeviceStatus.put(id, status); 1120 } 1121 1122 if (oldStatus != null && oldStatus == status) { 1123 if (DEBUG) { 1124 Log.v(TAG, String.format( 1125 "Device status changed to 0x%x, which is what it already was", 1126 status)); 1127 } 1128 return; 1129 } 1130 1131 // TODO: consider abstracting out this state minimization + transition 1132 // into a separate 1133 // more easily testable class 1134 // i.e. (new State()).addState(STATE_AVAILABLE) 1135 // .addState(STATE_NOT_AVAILABLE) 1136 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE), 1137 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE) 1138 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE); 1139 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE); 1140 1141 // Translate all the statuses to either 'available' or 'not available' 1142 // available -> available => no new update 1143 // not available -> not available => no new update 1144 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) { 1145 if (DEBUG) { 1146 Log.v(TAG, 1147 String.format( 1148 "Device status was previously available (%b), " + 1149 " and is now again available (%b)" + 1150 "so no new client visible update will be sent", 1151 isAvailable(oldStatus), isAvailable(status))); 1152 } 1153 return; 1154 } 1155 1156 final int callbackCount = mCallbackMap.size(); 1157 for (int i = 0; i < callbackCount; i++) { 1158 Executor executor = mCallbackMap.valueAt(i); 1159 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 1160 1161 postSingleUpdate(callback, executor, id, status); 1162 } 1163 } // onStatusChangedLocked 1164 updateTorchCallbackLocked(TorchCallback callback, Executor executor)1165 private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) { 1166 for (int i = 0; i < mTorchStatus.size(); i++) { 1167 String id = mTorchStatus.keyAt(i); 1168 Integer status = mTorchStatus.valueAt(i); 1169 postSingleTorchUpdate(callback, executor, id, status); 1170 } 1171 } 1172 onTorchStatusChangedLocked(int status, String id)1173 private void onTorchStatusChangedLocked(int status, String id) { 1174 if (DEBUG) { 1175 Log.v(TAG, 1176 String.format("Camera id %s has torch status changed to 0x%x", id, status)); 1177 } 1178 1179 if (!validTorchStatus(status)) { 1180 Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id, 1181 status)); 1182 return; 1183 } 1184 1185 Integer oldStatus = mTorchStatus.put(id, status); 1186 if (oldStatus != null && oldStatus == status) { 1187 if (DEBUG) { 1188 Log.v(TAG, String.format( 1189 "Torch status changed to 0x%x, which is what it already was", 1190 status)); 1191 } 1192 return; 1193 } 1194 1195 final int callbackCount = mTorchCallbackMap.size(); 1196 for (int i = 0; i < callbackCount; i++) { 1197 final Executor executor = mTorchCallbackMap.valueAt(i); 1198 final TorchCallback callback = mTorchCallbackMap.keyAt(i); 1199 postSingleTorchUpdate(callback, executor, id, status); 1200 } 1201 } // onTorchStatusChangedLocked 1202 1203 /** 1204 * Register a callback to be notified about camera device availability with the 1205 * global listener singleton. 1206 * 1207 * @param callback the new callback to send camera availability notices to 1208 * @param executor The executor which should invoke the callback. May not be null. 1209 */ registerAvailabilityCallback(AvailabilityCallback callback, Executor executor)1210 public void registerAvailabilityCallback(AvailabilityCallback callback, Executor executor) { 1211 synchronized (mLock) { 1212 connectCameraServiceLocked(); 1213 1214 Executor oldExecutor = mCallbackMap.put(callback, executor); 1215 // For new callbacks, provide initial availability information 1216 if (oldExecutor == null) { 1217 updateCallbackLocked(callback, executor); 1218 } 1219 1220 // If not connected to camera service, schedule a reconnect to camera service. 1221 if (mCameraService == null) { 1222 scheduleCameraServiceReconnectionLocked(); 1223 } 1224 } 1225 } 1226 1227 /** 1228 * Remove a previously-added callback; the callback will no longer receive connection and 1229 * disconnection callbacks, and is no longer referenced by the global listener singleton. 1230 * 1231 * @param callback The callback to remove from the notification list 1232 */ unregisterAvailabilityCallback(AvailabilityCallback callback)1233 public void unregisterAvailabilityCallback(AvailabilityCallback callback) { 1234 synchronized (mLock) { 1235 mCallbackMap.remove(callback); 1236 } 1237 } 1238 registerTorchCallback(TorchCallback callback, Executor executor)1239 public void registerTorchCallback(TorchCallback callback, Executor executor) { 1240 synchronized(mLock) { 1241 connectCameraServiceLocked(); 1242 1243 Executor oldExecutor = mTorchCallbackMap.put(callback, executor); 1244 // For new callbacks, provide initial torch information 1245 if (oldExecutor == null) { 1246 updateTorchCallbackLocked(callback, executor); 1247 } 1248 1249 // If not connected to camera service, schedule a reconnect to camera service. 1250 if (mCameraService == null) { 1251 scheduleCameraServiceReconnectionLocked(); 1252 } 1253 } 1254 } 1255 unregisterTorchCallback(TorchCallback callback)1256 public void unregisterTorchCallback(TorchCallback callback) { 1257 synchronized(mLock) { 1258 mTorchCallbackMap.remove(callback); 1259 } 1260 } 1261 1262 /** 1263 * Callback from camera service notifying the process about camera availability changes 1264 */ 1265 @Override onStatusChanged(int status, String cameraId)1266 public void onStatusChanged(int status, String cameraId) throws RemoteException { 1267 synchronized(mLock) { 1268 onStatusChangedLocked(status, cameraId); 1269 } 1270 } 1271 1272 @Override onTorchStatusChanged(int status, String cameraId)1273 public void onTorchStatusChanged(int status, String cameraId) throws RemoteException { 1274 synchronized (mLock) { 1275 onTorchStatusChangedLocked(status, cameraId); 1276 } 1277 } 1278 1279 /** 1280 * Try to connect to camera service after some delay if any client registered camera 1281 * availability callback or torch status callback. 1282 */ scheduleCameraServiceReconnectionLocked()1283 private void scheduleCameraServiceReconnectionLocked() { 1284 if (mCallbackMap.isEmpty() && mTorchCallbackMap.isEmpty()) { 1285 // Not necessary to reconnect camera service if no client registers a callback. 1286 return; 1287 } 1288 1289 if (DEBUG) { 1290 Log.v(TAG, "Reconnecting Camera Service in " + CAMERA_SERVICE_RECONNECT_DELAY_MS + 1291 " ms"); 1292 } 1293 1294 try { 1295 mScheduler.schedule(() -> { 1296 ICameraService cameraService = getCameraService(); 1297 if (cameraService == null) { 1298 synchronized(mLock) { 1299 if (DEBUG) { 1300 Log.v(TAG, "Reconnecting Camera Service failed."); 1301 } 1302 scheduleCameraServiceReconnectionLocked(); 1303 } 1304 } 1305 }, CAMERA_SERVICE_RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS); 1306 } catch (RejectedExecutionException e) { 1307 Log.e(TAG, "Failed to schedule camera service re-connect: " + e); 1308 } 1309 } 1310 1311 /** 1312 * Listener for camera service death. 1313 * 1314 * <p>The camera service isn't supposed to die under any normal circumstances, but can be 1315 * turned off during debug, or crash due to bugs. So detect that and null out the interface 1316 * object, so that the next calls to the manager can try to reconnect.</p> 1317 */ binderDied()1318 public void binderDied() { 1319 synchronized(mLock) { 1320 // Only do this once per service death 1321 if (mCameraService == null) return; 1322 1323 mCameraService = null; 1324 1325 // Tell listeners that the cameras and torch modes are unavailable and schedule a 1326 // reconnection to camera service. When camera service is reconnected, the camera 1327 // and torch statuses will be updated. 1328 for (int i = 0; i < mDeviceStatus.size(); i++) { 1329 String cameraId = mDeviceStatus.keyAt(i); 1330 onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, cameraId); 1331 } 1332 for (int i = 0; i < mTorchStatus.size(); i++) { 1333 String cameraId = mTorchStatus.keyAt(i); 1334 onTorchStatusChangedLocked(ICameraServiceListener.TORCH_STATUS_NOT_AVAILABLE, 1335 cameraId); 1336 } 1337 1338 scheduleCameraServiceReconnectionLocked(); 1339 } 1340 } 1341 1342 } // CameraManagerGlobal 1343 1344 } // CameraManager 1345