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