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