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.content.Context; 20 import android.hardware.ICameraService; 21 import android.hardware.ICameraServiceListener; 22 import android.hardware.CameraInfo; 23 import android.hardware.camera2.impl.CameraMetadataNative; 24 import android.hardware.camera2.legacy.CameraDeviceUserShim; 25 import android.hardware.camera2.legacy.LegacyMetadataMapper; 26 import android.hardware.camera2.utils.CameraServiceBinderDecorator; 27 import android.hardware.camera2.utils.CameraRuntimeException; 28 import android.hardware.camera2.utils.BinderHolder; 29 import android.os.IBinder; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.RemoteException; 33 import android.os.ServiceManager; 34 import android.util.Log; 35 import android.util.ArrayMap; 36 37 import java.util.ArrayList; 38 39 /** 40 * <p>A system service manager for detecting, characterizing, and connecting to 41 * {@link CameraDevice CameraDevices}.</p> 42 * 43 * <p>You can get an instance of this class by calling 44 * {@link android.content.Context#getSystemService(String) Context.getSystemService()}.</p> 45 * 46 * <pre>CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);</pre> 47 * 48 * <p>For more details about communicating with camera devices, read the Camera 49 * developer guide or the {@link android.hardware.camera2 camera2} 50 * package documentation.</p> 51 */ 52 public final class CameraManager { 53 54 private static final String TAG = "CameraManager"; 55 private final boolean DEBUG; 56 57 private static final int USE_CALLING_UID = -1; 58 59 @SuppressWarnings("unused") 60 private static final int API_VERSION_1 = 1; 61 private static final int API_VERSION_2 = 2; 62 63 private ArrayList<String> mDeviceIdList; 64 65 private final Context mContext; 66 private final Object mLock = new Object(); 67 68 /** 69 * @hide 70 */ CameraManager(Context context)71 public CameraManager(Context context) { 72 DEBUG = Log.isLoggable(TAG, Log.DEBUG); 73 synchronized(mLock) { 74 mContext = context; 75 } 76 } 77 78 /** 79 * Return the list of currently connected camera devices by 80 * identifier. 81 * 82 * <p>Non-removable cameras use integers starting at 0 for their 83 * identifiers, while removable cameras have a unique identifier for each 84 * individual device, even if they are the same model.</p> 85 * 86 * @return The list of currently connected camera devices. 87 */ getCameraIdList()88 public String[] getCameraIdList() throws CameraAccessException { 89 synchronized (mLock) { 90 // ID list creation handles various known failures in device enumeration, so only 91 // exceptions it'll throw are unexpected, and should be propagated upward. 92 return getOrCreateDeviceIdListLocked().toArray(new String[0]); 93 } 94 } 95 96 /** 97 * Register a callback to be notified about camera device availability. 98 * 99 * <p>Registering the same callback again will replace the handler with the 100 * new one provided.</p> 101 * 102 * <p>The first time a callback is registered, it is immediately called 103 * with the availability status of all currently known camera devices.</p> 104 * 105 * <p>Since this callback will be registered with the camera service, remember to unregister it 106 * once it is no longer needed; otherwise the callback will continue to receive events 107 * indefinitely and it may prevent other resources from being released. Specifically, the 108 * callbacks will be invoked independently of the general activity lifecycle and independently 109 * of the state of individual CameraManager instances.</p> 110 * 111 * @param callback the new callback to send camera availability notices to 112 * @param handler The handler on which the callback should be invoked, or 113 * {@code null} to use the current thread's {@link android.os.Looper looper}. 114 */ registerAvailabilityCallback(AvailabilityCallback callback, Handler handler)115 public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) { 116 if (handler == null) { 117 Looper looper = Looper.myLooper(); 118 if (looper == null) { 119 throw new IllegalArgumentException( 120 "No handler given, and current thread has no looper!"); 121 } 122 handler = new Handler(looper); 123 } 124 125 CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler); 126 } 127 128 /** 129 * Remove a previously-added callback; the callback will no longer receive connection and 130 * disconnection callbacks. 131 * 132 * <p>Removing a callback that isn't registered has no effect.</p> 133 * 134 * @param callback The callback to remove from the notification list 135 */ unregisterAvailabilityCallback(AvailabilityCallback callback)136 public void unregisterAvailabilityCallback(AvailabilityCallback callback) { 137 CameraManagerGlobal.get().unregisterAvailabilityCallback(callback); 138 } 139 140 /** 141 * <p>Query the capabilities of a camera device. These capabilities are 142 * immutable for a given camera.</p> 143 * 144 * @param cameraId The id of the camera device to query 145 * @return The properties of the given camera 146 * 147 * @throws IllegalArgumentException if the cameraId does not match any 148 * known camera device. 149 * @throws CameraAccessException if the camera is disabled by device policy, or 150 * the camera device has been disconnected. 151 * @throws SecurityException if the application does not have permission to 152 * access the camera 153 * 154 * @see #getCameraIdList 155 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 156 */ getCameraCharacteristics(String cameraId)157 public CameraCharacteristics getCameraCharacteristics(String cameraId) 158 throws CameraAccessException { 159 CameraCharacteristics characteristics = null; 160 161 synchronized (mLock) { 162 if (!getOrCreateDeviceIdListLocked().contains(cameraId)) { 163 throw new IllegalArgumentException(String.format("Camera id %s does not match any" + 164 " currently connected camera device", cameraId)); 165 } 166 167 int id = Integer.valueOf(cameraId); 168 169 /* 170 * Get the camera characteristics from the camera service directly if it supports it, 171 * otherwise get them from the legacy shim instead. 172 */ 173 174 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 175 if (cameraService == null) { 176 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 177 "Camera service is currently unavailable"); 178 } 179 try { 180 if (!supportsCamera2ApiLocked(cameraId)) { 181 // Legacy backwards compatibility path; build static info from the camera 182 // parameters 183 String[] outParameters = new String[1]; 184 185 cameraService.getLegacyParameters(id, /*out*/outParameters); 186 String parameters = outParameters[0]; 187 188 CameraInfo info = new CameraInfo(); 189 cameraService.getCameraInfo(id, /*out*/info); 190 191 characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info); 192 } else { 193 // Normal path: Get the camera characteristics directly from the camera service 194 CameraMetadataNative info = new CameraMetadataNative(); 195 196 cameraService.getCameraCharacteristics(id, info); 197 198 characteristics = new CameraCharacteristics(info); 199 } 200 } catch (CameraRuntimeException e) { 201 throw e.asChecked(); 202 } catch (RemoteException e) { 203 // Camera service died - act as if the camera was disconnected 204 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, 205 "Camera service is currently unavailable", e); 206 } 207 } 208 return characteristics; 209 } 210 211 /** 212 * Helper for openning a connection to a camera with the given ID. 213 * 214 * @param cameraId The unique identifier of the camera device to open 215 * @param callback The callback for the camera. Must not be null. 216 * @param handler The handler to invoke the callback on. Must not be null. 217 * 218 * @throws CameraAccessException if the camera is disabled by device policy, 219 * or too many camera devices are already open, or the cameraId does not match 220 * any currently available camera device. 221 * 222 * @throws SecurityException if the application does not have permission to 223 * access the camera 224 * @throws IllegalArgumentException if callback or handler is null. 225 * @return A handle to the newly-created camera device. 226 * 227 * @see #getCameraIdList 228 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 229 */ openCameraDeviceUserAsync(String cameraId, CameraDevice.StateCallback callback, Handler handler)230 private CameraDevice openCameraDeviceUserAsync(String cameraId, 231 CameraDevice.StateCallback callback, Handler handler) 232 throws CameraAccessException { 233 CameraCharacteristics characteristics = getCameraCharacteristics(cameraId); 234 CameraDevice device = null; 235 try { 236 237 synchronized (mLock) { 238 239 ICameraDeviceUser cameraUser = null; 240 241 android.hardware.camera2.impl.CameraDeviceImpl deviceImpl = 242 new android.hardware.camera2.impl.CameraDeviceImpl( 243 cameraId, 244 callback, 245 handler, 246 characteristics); 247 248 BinderHolder holder = new BinderHolder(); 249 250 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks(); 251 int id = Integer.parseInt(cameraId); 252 try { 253 if (supportsCamera2ApiLocked(cameraId)) { 254 // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices 255 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 256 if (cameraService == null) { 257 throw new CameraRuntimeException( 258 CameraAccessException.CAMERA_DISCONNECTED, 259 "Camera service is currently unavailable"); 260 } 261 cameraService.connectDevice(callbacks, id, 262 mContext.getPackageName(), USE_CALLING_UID, holder); 263 cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder()); 264 } else { 265 // Use legacy camera implementation for HAL1 devices 266 Log.i(TAG, "Using legacy camera HAL."); 267 cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id); 268 } 269 } catch (CameraRuntimeException e) { 270 if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) { 271 throw new AssertionError("Should've gone down the shim path"); 272 } else if (e.getReason() == CameraAccessException.CAMERA_IN_USE || 273 e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE || 274 e.getReason() == CameraAccessException.CAMERA_DISABLED || 275 e.getReason() == CameraAccessException.CAMERA_DISCONNECTED || 276 e.getReason() == CameraAccessException.CAMERA_ERROR) { 277 // Received one of the known connection errors 278 // The remote camera device cannot be connected to, so 279 // set the local camera to the startup error state 280 deviceImpl.setRemoteFailure(e); 281 282 if (e.getReason() == CameraAccessException.CAMERA_DISABLED || 283 e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) { 284 // Per API docs, these failures call onError and throw 285 throw e.asChecked(); 286 } 287 } else { 288 // Unexpected failure - rethrow 289 throw e; 290 } 291 } catch (RemoteException e) { 292 // Camera service died - act as if it's a CAMERA_DISCONNECTED case 293 CameraRuntimeException ce = new CameraRuntimeException( 294 CameraAccessException.CAMERA_DISCONNECTED, 295 "Camera service is currently unavailable", e); 296 deviceImpl.setRemoteFailure(ce); 297 throw ce.asChecked(); 298 } 299 300 // TODO: factor out callback to be non-nested, then move setter to constructor 301 // For now, calling setRemoteDevice will fire initial 302 // onOpened/onUnconfigured callbacks. 303 deviceImpl.setRemoteDevice(cameraUser); 304 device = deviceImpl; 305 } 306 307 } catch (NumberFormatException e) { 308 throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: " 309 + cameraId); 310 } catch (CameraRuntimeException e) { 311 throw e.asChecked(); 312 } 313 return device; 314 } 315 316 /** 317 * Open a connection to a camera with the given ID. 318 * 319 * <p>Use {@link #getCameraIdList} to get the list of available camera 320 * devices. Note that even if an id is listed, open may fail if the device 321 * is disconnected between the calls to {@link #getCameraIdList} and 322 * {@link #openCamera}.</p> 323 * 324 * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will 325 * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up 326 * for operation by calling {@link CameraDevice#createCaptureSession} and 327 * {@link CameraDevice#createCaptureRequest}</p> 328 * 329 * <!-- 330 * <p>Since the camera device will be opened asynchronously, any asynchronous operations done 331 * on the returned CameraDevice instance will be queued up until the device startup has 332 * completed and the callback's {@link CameraDevice.StateCallback#onOpened onOpened} method is 333 * called. The pending operations are then processed in order.</p> 334 * --> 335 * <p>If the camera becomes disconnected during initialization 336 * after this function call returns, 337 * {@link CameraDevice.StateCallback#onDisconnected} with a 338 * {@link CameraDevice} in the disconnected state (and 339 * {@link CameraDevice.StateCallback#onOpened} will be skipped).</p> 340 * 341 * <p>If opening the camera device fails, then the device callback's 342 * {@link CameraDevice.StateCallback#onError onError} method will be called, and subsequent 343 * calls on the camera device will throw a {@link CameraAccessException}.</p> 344 * 345 * @param cameraId 346 * The unique identifier of the camera device to open 347 * @param callback 348 * The callback which is invoked once the camera is opened 349 * @param handler 350 * The handler on which the callback should be invoked, or 351 * {@code null} to use the current thread's {@link android.os.Looper looper}. 352 * 353 * @throws CameraAccessException if the camera is disabled by device policy, 354 * or the camera has become or was disconnected. 355 * 356 * @throws IllegalArgumentException if cameraId or the callback was null, 357 * or the cameraId does not match any currently or previously available 358 * camera device. 359 * 360 * @throws SecurityException if the application does not have permission to 361 * access the camera 362 * 363 * @see #getCameraIdList 364 * @see android.app.admin.DevicePolicyManager#setCameraDisabled 365 */ openCamera(String cameraId, final CameraDevice.StateCallback callback, Handler handler)366 public void openCamera(String cameraId, final CameraDevice.StateCallback callback, 367 Handler handler) 368 throws CameraAccessException { 369 370 if (cameraId == null) { 371 throw new IllegalArgumentException("cameraId was null"); 372 } else if (callback == null) { 373 throw new IllegalArgumentException("callback was null"); 374 } else if (handler == null) { 375 if (Looper.myLooper() != null) { 376 handler = new Handler(); 377 } else { 378 throw new IllegalArgumentException( 379 "Looper doesn't exist in the calling thread"); 380 } 381 } 382 383 openCameraDeviceUserAsync(cameraId, callback, handler); 384 } 385 386 /** 387 * A callback for camera devices becoming available or 388 * unavailable to open. 389 * 390 * <p>Cameras become available when they are no longer in use, or when a new 391 * removable camera is connected. They become unavailable when some 392 * application or service starts using a camera, or when a removable camera 393 * is disconnected.</p> 394 * 395 * <p>Extend this callback and pass an instance of the subclass to 396 * {@link CameraManager#registerAvailabilityCallback} to be notified of such availability 397 * changes.</p> 398 * 399 * @see registerAvailabilityCallback 400 */ 401 public static abstract class AvailabilityCallback { 402 403 /** 404 * A new camera has become available to use. 405 * 406 * <p>The default implementation of this method does nothing.</p> 407 * 408 * @param cameraId The unique identifier of the new camera. 409 */ onCameraAvailable(String cameraId)410 public void onCameraAvailable(String cameraId) { 411 // default empty implementation 412 } 413 414 /** 415 * A previously-available camera has become unavailable for use. 416 * 417 * <p>If an application had an active CameraDevice instance for the 418 * now-disconnected camera, that application will receive a 419 * {@link CameraDevice.StateCallback#onDisconnected disconnection error}.</p> 420 * 421 * <p>The default implementation of this method does nothing.</p> 422 * 423 * @param cameraId The unique identifier of the disconnected camera. 424 */ onCameraUnavailable(String cameraId)425 public void onCameraUnavailable(String cameraId) { 426 // default empty implementation 427 } 428 } 429 430 /** 431 * Return or create the list of currently connected camera devices. 432 * 433 * <p>In case of errors connecting to the camera service, will return an empty list.</p> 434 */ getOrCreateDeviceIdListLocked()435 private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException { 436 if (mDeviceIdList == null) { 437 int numCameras = 0; 438 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 439 ArrayList<String> deviceIdList = new ArrayList<>(); 440 441 // If no camera service, then no devices 442 if (cameraService == null) { 443 return deviceIdList; 444 } 445 446 try { 447 numCameras = cameraService.getNumberOfCameras(); 448 } catch(CameraRuntimeException e) { 449 throw e.asChecked(); 450 } catch (RemoteException e) { 451 // camera service just died - if no camera service, then no devices 452 return deviceIdList; 453 } 454 455 CameraMetadataNative info = new CameraMetadataNative(); 456 for (int i = 0; i < numCameras; ++i) { 457 // Non-removable cameras use integers starting at 0 for their 458 // identifiers 459 boolean isDeviceSupported = false; 460 try { 461 cameraService.getCameraCharacteristics(i, info); 462 if (!info.isEmpty()) { 463 isDeviceSupported = true; 464 } else { 465 throw new AssertionError("Expected to get non-empty characteristics"); 466 } 467 } catch(IllegalArgumentException e) { 468 // Got a BAD_VALUE from service, meaning that this 469 // device is not supported. 470 } catch(CameraRuntimeException e) { 471 // DISCONNECTED means that the HAL reported an low-level error getting the 472 // device info; skip listing the device. Other errors, 473 // propagate exception onward 474 if (e.getReason() != CameraAccessException.CAMERA_DISCONNECTED) { 475 throw e.asChecked(); 476 } 477 } catch(RemoteException e) { 478 // Camera service died - no devices to list 479 deviceIdList.clear(); 480 return deviceIdList; 481 } 482 483 if (isDeviceSupported) { 484 deviceIdList.add(String.valueOf(i)); 485 } else { 486 Log.w(TAG, "Error querying camera device " + i + " for listing."); 487 } 488 489 } 490 mDeviceIdList = deviceIdList; 491 } 492 return mDeviceIdList; 493 } 494 495 /** 496 * Queries the camera service if it supports the camera2 api directly, or needs a shim. 497 * 498 * @param cameraId a non-{@code null} camera identifier 499 * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise. 500 */ supportsCamera2ApiLocked(String cameraId)501 private boolean supportsCamera2ApiLocked(String cameraId) { 502 return supportsCameraApiLocked(cameraId, API_VERSION_2); 503 } 504 505 /** 506 * Queries the camera service if it supports a camera api directly, or needs a shim. 507 * 508 * @param cameraId a non-{@code null} camera identifier 509 * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2} 510 * @return {@code true} if connecting will work for that device version. 511 */ supportsCameraApiLocked(String cameraId, int apiVersion)512 private boolean supportsCameraApiLocked(String cameraId, int apiVersion) { 513 int id = Integer.parseInt(cameraId); 514 515 /* 516 * Possible return values: 517 * - NO_ERROR => CameraX API is supported 518 * - CAMERA_DEPRECATED_HAL => CameraX API is *not* supported (thrown as an exception) 519 * - Remote exception => If the camera service died 520 * 521 * Anything else is an unexpected error we don't want to recover from. 522 */ 523 try { 524 ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); 525 // If no camera service, no support 526 if (cameraService == null) return false; 527 528 int res = cameraService.supportsCameraApi(id, apiVersion); 529 530 if (res != CameraServiceBinderDecorator.NO_ERROR) { 531 throw new AssertionError("Unexpected value " + res); 532 } 533 return true; 534 } catch (CameraRuntimeException e) { 535 if (e.getReason() != CameraAccessException.CAMERA_DEPRECATED_HAL) { 536 throw e; 537 } 538 // API level is not supported 539 } catch (RemoteException e) { 540 // Camera service is now down, no support for any API level 541 } 542 return false; 543 } 544 545 /** 546 * A per-process global camera manager instance, to retain a connection to the camera service, 547 * and to distribute camera availability notices to API-registered callbacks 548 */ 549 private static final class CameraManagerGlobal extends ICameraServiceListener.Stub 550 implements IBinder.DeathRecipient { 551 552 private static final String TAG = "CameraManagerGlobal"; 553 private final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 554 555 // Singleton instance 556 private static final CameraManagerGlobal gCameraManager = 557 new CameraManagerGlobal(); 558 559 /** 560 * This must match the ICameraService definition 561 */ 562 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; 563 564 // Keep up-to-date with ICameraServiceListener.h 565 566 // Device physically unplugged 567 public static final int STATUS_NOT_PRESENT = 0; 568 // Device physically has been plugged in 569 // and the camera can be used exclusively 570 public static final int STATUS_PRESENT = 1; 571 // Device physically has been plugged in 572 // but it will not be connect-able until enumeration is complete 573 public static final int STATUS_ENUMERATING = 2; 574 // Camera is in use by another app and cannot be used exclusively 575 public static final int STATUS_NOT_AVAILABLE = 0x80000000; 576 577 // End enums shared with ICameraServiceListener.h 578 579 // Camera ID -> Status map 580 private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); 581 582 // Registered availablility callbacks and their handlers 583 private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap = 584 new ArrayMap<AvailabilityCallback, Handler>(); 585 586 private final Object mLock = new Object(); 587 588 // Access only through getCameraService to deal with binder death 589 private ICameraService mCameraService; 590 591 // Singleton, don't allow construction CameraManagerGlobal()592 private CameraManagerGlobal() { 593 } 594 get()595 public static CameraManagerGlobal get() { 596 return gCameraManager; 597 } 598 599 @Override asBinder()600 public IBinder asBinder() { 601 return this; 602 } 603 604 /** 605 * Return a best-effort ICameraService. 606 * 607 * <p>This will be null if the camera service is not currently available. If the camera 608 * service has died since the last use of the camera service, will try to reconnect to the 609 * service.</p> 610 */ getCameraService()611 public ICameraService getCameraService() { 612 synchronized(mLock) { 613 if (mCameraService == null) { 614 Log.i(TAG, "getCameraService: Reconnecting to camera service"); 615 connectCameraServiceLocked(); 616 if (mCameraService == null) { 617 Log.e(TAG, "Camera service is unavailable"); 618 } 619 } 620 return mCameraService; 621 } 622 } 623 624 /** 625 * Connect to the camera service if it's available, and set up listeners. 626 * 627 * <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p> 628 */ connectCameraServiceLocked()629 private void connectCameraServiceLocked() { 630 mCameraService = null; 631 IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME); 632 if (cameraServiceBinder == null) { 633 // Camera service is now down, leave mCameraService as null 634 return; 635 } 636 try { 637 cameraServiceBinder.linkToDeath(this, /*flags*/ 0); 638 } catch (RemoteException e) { 639 // Camera service is now down, leave mCameraService as null 640 return; 641 } 642 643 ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder); 644 645 /** 646 * Wrap the camera service in a decorator which automatically translates return codes 647 * into exceptions. 648 */ 649 ICameraService cameraService = 650 CameraServiceBinderDecorator.newInstance(cameraServiceRaw); 651 652 try { 653 CameraServiceBinderDecorator.throwOnError( 654 CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor()); 655 } catch (CameraRuntimeException e) { 656 handleRecoverableSetupErrors(e, "Failed to set up vendor tags"); 657 } 658 659 try { 660 cameraService.addListener(this); 661 mCameraService = cameraService; 662 } catch(CameraRuntimeException e) { 663 // Unexpected failure 664 throw new IllegalStateException("Failed to register a camera service listener", 665 e.asChecked()); 666 } catch (RemoteException e) { 667 // Camera service is now down, leave mCameraService as null 668 } 669 } 670 handleRecoverableSetupErrors(CameraRuntimeException e, String msg)671 private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) { 672 int problem = e.getReason(); 673 switch (problem) { 674 case CameraAccessException.CAMERA_DISCONNECTED: 675 String errorMsg = CameraAccessException.getDefaultMessage(problem); 676 Log.w(TAG, msg + ": " + errorMsg); 677 break; 678 default: 679 throw new IllegalStateException(msg, e.asChecked()); 680 } 681 } 682 isAvailable(int status)683 private boolean isAvailable(int status) { 684 switch (status) { 685 case STATUS_PRESENT: 686 return true; 687 default: 688 return false; 689 } 690 } 691 validStatus(int status)692 private boolean validStatus(int status) { 693 switch (status) { 694 case STATUS_NOT_PRESENT: 695 case STATUS_PRESENT: 696 case STATUS_ENUMERATING: 697 case STATUS_NOT_AVAILABLE: 698 return true; 699 default: 700 return false; 701 } 702 } 703 postSingleUpdate(final AvailabilityCallback callback, final Handler handler, final String id, final int status)704 private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler, 705 final String id, final int status) { 706 if (isAvailable(status)) { 707 handler.post( 708 new Runnable() { 709 @Override 710 public void run() { 711 callback.onCameraAvailable(id); 712 } 713 }); 714 } else { 715 handler.post( 716 new Runnable() { 717 @Override 718 public void run() { 719 callback.onCameraUnavailable(id); 720 } 721 }); 722 } 723 } 724 725 /** 726 * Send the state of all known cameras to the provided listener, to initialize 727 * the listener's knowledge of camera state. 728 */ updateCallbackLocked(AvailabilityCallback callback, Handler handler)729 private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) { 730 for (int i = 0; i < mDeviceStatus.size(); i++) { 731 String id = mDeviceStatus.keyAt(i); 732 Integer status = mDeviceStatus.valueAt(i); 733 postSingleUpdate(callback, handler, id, status); 734 } 735 } 736 onStatusChangedLocked(int status, String id)737 private void onStatusChangedLocked(int status, String id) { 738 if (DEBUG) { 739 Log.v(TAG, 740 String.format("Camera id %s has status changed to 0x%x", id, status)); 741 } 742 743 if (!validStatus(status)) { 744 Log.e(TAG, String.format("Ignoring invalid device %s status 0x%x", id, 745 status)); 746 return; 747 } 748 749 Integer oldStatus = mDeviceStatus.put(id, status); 750 751 if (oldStatus != null && oldStatus == status) { 752 if (DEBUG) { 753 Log.v(TAG, String.format( 754 "Device status changed to 0x%x, which is what it already was", 755 status)); 756 } 757 return; 758 } 759 760 // TODO: consider abstracting out this state minimization + transition 761 // into a separate 762 // more easily testable class 763 // i.e. (new State()).addState(STATE_AVAILABLE) 764 // .addState(STATE_NOT_AVAILABLE) 765 // .addTransition(STATUS_PRESENT, STATE_AVAILABLE), 766 // .addTransition(STATUS_NOT_PRESENT, STATE_NOT_AVAILABLE) 767 // .addTransition(STATUS_ENUMERATING, STATE_NOT_AVAILABLE); 768 // .addTransition(STATUS_NOT_AVAILABLE, STATE_NOT_AVAILABLE); 769 770 // Translate all the statuses to either 'available' or 'not available' 771 // available -> available => no new update 772 // not available -> not available => no new update 773 if (oldStatus != null && isAvailable(status) == isAvailable(oldStatus)) { 774 if (DEBUG) { 775 Log.v(TAG, 776 String.format( 777 "Device status was previously available (%d), " + 778 " and is now again available (%d)" + 779 "so no new client visible update will be sent", 780 isAvailable(status), isAvailable(status))); 781 } 782 return; 783 } 784 785 final int callbackCount = mCallbackMap.size(); 786 for (int i = 0; i < callbackCount; i++) { 787 Handler handler = mCallbackMap.valueAt(i); 788 final AvailabilityCallback callback = mCallbackMap.keyAt(i); 789 790 postSingleUpdate(callback, handler, id, status); 791 } 792 } // onStatusChangedLocked 793 794 /** 795 * Register a callback to be notified about camera device availability with the 796 * global listener singleton. 797 * 798 * @param callback the new callback to send camera availability notices to 799 * @param handler The handler on which the callback should be invoked. May not be null. 800 */ registerAvailabilityCallback(AvailabilityCallback callback, Handler handler)801 public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) { 802 synchronized (mLock) { 803 Handler oldHandler = mCallbackMap.put(callback, handler); 804 // For new callbacks, provide initial availability information 805 if (oldHandler == null) { 806 updateCallbackLocked(callback, handler); 807 } 808 } 809 } 810 811 /** 812 * Remove a previously-added callback; the callback will no longer receive connection and 813 * disconnection callbacks, and is no longer referenced by the global listener singleton. 814 * 815 * @param callback The callback to remove from the notification list 816 */ unregisterAvailabilityCallback(AvailabilityCallback callback)817 public void unregisterAvailabilityCallback(AvailabilityCallback callback) { 818 synchronized (mLock) { 819 mCallbackMap.remove(callback); 820 } 821 } 822 823 /** 824 * Callback from camera service notifying the process about camera availability changes 825 */ 826 @Override onStatusChanged(int status, int cameraId)827 public void onStatusChanged(int status, int cameraId) throws RemoteException { 828 synchronized(mLock) { 829 onStatusChangedLocked(status, String.valueOf(cameraId)); 830 } 831 } 832 833 /** 834 * Listener for camera service death. 835 * 836 * <p>The camera service isn't supposed to die under any normal circumstances, but can be 837 * turned off during debug, or crash due to bugs. So detect that and null out the interface 838 * object, so that the next calls to the manager can try to reconnect.</p> 839 */ binderDied()840 public void binderDied() { 841 synchronized(mLock) { 842 // Only do this once per service death 843 if (mCameraService == null) return; 844 845 mCameraService = null; 846 847 // Tell listeners that the cameras are _available_, because any existing clients 848 // will have gotten disconnected. This is optimistic under the assumption that 849 // the service will be back shortly. 850 // 851 // Without this, a camera service crash while a camera is open will never signal 852 // to listeners that previously in-use cameras are now available. 853 for (int i = 0; i < mDeviceStatus.size(); i++) { 854 String cameraId = mDeviceStatus.keyAt(i); 855 onStatusChangedLocked(STATUS_PRESENT, cameraId); 856 } 857 } 858 } 859 860 } // CameraManagerGlobal 861 862 } // CameraManager 863