Loading core/java/android/hardware/camera2/CameraManager.java +191 −144 Original line number Diff line number Diff line Loading @@ -54,29 +54,17 @@ public final class CameraManager { private static final String TAG = "CameraManager"; private final boolean DEBUG; /** * This should match the ICameraService definition */ private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; private static final int USE_CALLING_UID = -1; @SuppressWarnings("unused") private static final int API_VERSION_1 = 1; private static final int API_VERSION_2 = 2; // Access only through getCameraServiceLocked to deal with binder death private ICameraService mCameraService; private ArrayList<String> mDeviceIdList; private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap = new ArrayMap<AvailabilityCallback, Handler>(); private final Context mContext; private final Object mLock = new Object(); private final CameraServiceListener mServiceListener = new CameraServiceListener(); /** * @hide */ Loading @@ -84,8 +72,6 @@ public final class CameraManager { DEBUG = Log.isLoggable(TAG, Log.DEBUG); synchronized(mLock) { mContext = context; connectCameraServiceLocked(); } } Loading Loading @@ -116,6 +102,12 @@ public final class CameraManager { * <p>The first time a callback is registered, it is immediately called * with the availability status of all currently known camera devices.</p> * * <p>Since this callback will be registered with the camera service, remember to unregister it * once it is no longer needed; otherwise the callback will continue to receive events * indefinitely and it may prevent other resources from being released. Specifically, the * callbacks will be invoked independently of the general activity lifecycle and independently * of the state of individual CameraManager instances.</p> * * @param callback the new callback to send camera availability notices to * @param handler The handler on which the callback should be invoked, or * {@code null} to use the current thread's {@link android.os.Looper looper}. Loading @@ -130,13 +122,7 @@ public final class CameraManager { handler = new Handler(looper); } synchronized (mLock) { Handler oldHandler = mCallbackMap.put(callback, handler); // For new callbacks, provide initial availability information if (oldHandler == null) { mServiceListener.updateCallbackLocked(callback, handler); } } CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler); } /** Loading @@ -148,9 +134,7 @@ public final class CameraManager { * @param callback The callback to remove from the notification list */ public void unregisterAvailabilityCallback(AvailabilityCallback callback) { synchronized (mLock) { mCallbackMap.remove(callback); } CameraManagerGlobal.get().unregisterAvailabilityCallback(callback); } /** Loading Loading @@ -187,7 +171,7 @@ public final class CameraManager { * otherwise get them from the legacy shim instead. */ ICameraService cameraService = getCameraServiceLocked(); ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); if (cameraService == null) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "Camera service is currently unavailable"); Loading Loading @@ -268,7 +252,7 @@ public final class CameraManager { try { if (supportsCamera2ApiLocked(cameraId)) { // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices ICameraService cameraService = getCameraServiceLocked(); ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); if (cameraService == null) { throw new CameraRuntimeException( CameraAccessException.CAMERA_DISCONNECTED, Loading Loading @@ -443,13 +427,6 @@ public final class CameraManager { } } /** * Temporary for migrating to Callback naming * @hide */ public static abstract class AvailabilityListener extends AvailabilityCallback { } /** * Return or create the list of currently connected camera devices. * Loading @@ -458,7 +435,7 @@ public final class CameraManager { private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException { if (mDeviceIdList == null) { int numCameras = 0; ICameraService cameraService = getCameraServiceLocked(); ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); ArrayList<String> deviceIdList = new ArrayList<>(); // If no camera service, then no devices Loading Loading @@ -515,18 +492,6 @@ public final class CameraManager { return mDeviceIdList; } private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) { int problem = e.getReason(); switch (problem) { case CameraAccessException.CAMERA_DISCONNECTED: String errorMsg = CameraAccessException.getDefaultMessage(problem); Log.w(TAG, msg + ": " + errorMsg); break; default: throw new IllegalStateException(msg, e.asChecked()); } } /** * Queries the camera service if it supports the camera2 api directly, or needs a shim. * Loading Loading @@ -556,7 +521,7 @@ public final class CameraManager { * Anything else is an unexpected error we don't want to recover from. */ try { ICameraService cameraService = getCameraServiceLocked(); ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); // If no camera service, no support if (cameraService == null) return false; Loading @@ -577,6 +542,85 @@ public final class CameraManager { return false; } /** * A per-process global camera manager instance, to retain a connection to the camera service, * and to distribute camera availability notices to API-registered callbacks */ private static final class CameraManagerGlobal extends ICameraServiceListener.Stub implements IBinder.DeathRecipient { private static final String TAG = "CameraManagerGlobal"; private final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); // Singleton instance private static final CameraManagerGlobal gCameraManager = new CameraManagerGlobal(); /** * This must match the ICameraService definition */ private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; // Keep up-to-date with ICameraServiceListener.h // Device physically unplugged public static final int STATUS_NOT_PRESENT = 0; // Device physically has been plugged in // and the camera can be used exclusively public static final int STATUS_PRESENT = 1; // Device physically has been plugged in // but it will not be connect-able until enumeration is complete public static final int STATUS_ENUMERATING = 2; // Camera is in use by another app and cannot be used exclusively public static final int STATUS_NOT_AVAILABLE = 0x80000000; // End enums shared with ICameraServiceListener.h // Camera ID -> Status map private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); // Registered availablility callbacks and their handlers private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap = new ArrayMap<AvailabilityCallback, Handler>(); private final Object mLock = new Object(); // Access only through getCameraService to deal with binder death private ICameraService mCameraService; // Singleton, don't allow construction private CameraManagerGlobal() { } public static CameraManagerGlobal get() { return gCameraManager; } @Override public IBinder asBinder() { return this; } /** * Return a best-effort ICameraService. * * <p>This will be null if the camera service is not currently available. If the camera * service has died since the last use of the camera service, will try to reconnect to the * service.</p> */ public ICameraService getCameraService() { synchronized(mLock) { if (mCameraService == null) { Log.i(TAG, "getCameraService: Reconnecting to camera service"); connectCameraServiceLocked(); if (mCameraService == null) { Log.e(TAG, "Camera service is unavailable"); } } return mCameraService; } } /** * Connect to the camera service if it's available, and set up listeners. * Loading @@ -590,7 +634,7 @@ public final class CameraManager { return; } try { cameraServiceBinder.linkToDeath(new CameraServiceDeathListener(), /*flags*/ 0); cameraServiceBinder.linkToDeath(this, /*flags*/ 0); } catch (RemoteException e) { // Camera service is now down, leave mCameraService as null return; Loading @@ -602,7 +646,8 @@ public final class CameraManager { * Wrap the camera service in a decorator which automatically translates return codes * into exceptions. */ ICameraService cameraService = CameraServiceBinderDecorator.newInstance(cameraServiceRaw); ICameraService cameraService = CameraServiceBinderDecorator.newInstance(cameraServiceRaw); try { CameraServiceBinderDecorator.throwOnError( Loading @@ -612,7 +657,7 @@ public final class CameraManager { } try { cameraService.addListener(mServiceListener); cameraService.addListener(this); mCameraService = cameraService; } catch(CameraRuntimeException e) { // Unexpected failure Loading @@ -623,74 +668,16 @@ public final class CameraManager { } } /** * Return a best-effort ICameraService. * * <p>This will be null if the camera service * is not currently available. If the camera service has died since the last * use of the camera service, will try to reconnect to the service.</p> */ private ICameraService getCameraServiceLocked() { if (mCameraService == null) { Log.i(TAG, "getCameraServiceLocked: Reconnecting to camera service"); connectCameraServiceLocked(); if (mCameraService == null) { Log.e(TAG, "Camera service is unavailable"); } } return mCameraService; } /** * Listener for camera service death. * * <p>The camera service isn't supposed to die under any normal circumstances, but can be turned * off during debug, or crash due to bugs. So detect that and null out the interface object, so * that the next calls to the manager can try to reconnect.</p> */ private class CameraServiceDeathListener implements IBinder.DeathRecipient { public void binderDied() { synchronized(mLock) { mCameraService = null; // Tell listeners that the cameras are _available_, because any existing clients // will have gotten disconnected. This is optimistic under the assumption that the // service will be back shortly. // // Without this, a camera service crash while a camera is open will never signal to // listeners that previously in-use cameras are now available. for (String cameraId : mDeviceIdList) { mServiceListener.onStatusChangedLocked(CameraServiceListener.STATUS_PRESENT, cameraId); } } } private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) { int problem = e.getReason(); switch (problem) { case CameraAccessException.CAMERA_DISCONNECTED: String errorMsg = CameraAccessException.getDefaultMessage(problem); Log.w(TAG, msg + ": " + errorMsg); break; default: throw new IllegalStateException(msg, e.asChecked()); } // TODO: this class needs unit tests // TODO: extract class into top level private class CameraServiceListener extends ICameraServiceListener.Stub { // Keep up-to-date with ICameraServiceListener.h // Device physically unplugged public static final int STATUS_NOT_PRESENT = 0; // Device physically has been plugged in // and the camera can be used exclusively public static final int STATUS_PRESENT = 1; // Device physically has been plugged in // but it will not be connect-able until enumeration is complete public static final int STATUS_ENUMERATING = 2; // Camera is in use by another app and cannot be used exclusively public static final int STATUS_NOT_AVAILABLE = 0x80000000; // Camera ID -> Status map private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); private static final String TAG = "CameraServiceListener"; @Override public IBinder asBinder() { return this; } private boolean isAvailable(int status) { Loading Loading @@ -739,7 +726,7 @@ public final class CameraManager { * Send the state of all known cameras to the provided listener, to initialize * the listener's knowledge of camera state. */ public void updateCallbackLocked(AvailabilityCallback callback, Handler handler) { private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) { for (int i = 0; i < mDeviceStatus.size(); i++) { String id = mDeviceStatus.keyAt(i); Integer status = mDeviceStatus.valueAt(i); Loading @@ -747,14 +734,7 @@ public final class CameraManager { } } @Override public void onStatusChanged(int status, int cameraId) throws RemoteException { synchronized(CameraManager.this.mLock) { onStatusChangedLocked(status, String.valueOf(cameraId)); } } public void onStatusChangedLocked(int status, String id) { private void onStatusChangedLocked(int status, String id) { if (DEBUG) { Log.v(TAG, String.format("Camera id %s has status changed to 0x%x", id, status)); Loading Loading @@ -811,5 +791,72 @@ public final class CameraManager { } } // onStatusChangedLocked } // CameraServiceListener /** * Register a callback to be notified about camera device availability with the * global listener singleton. * * @param callback the new callback to send camera availability notices to * @param handler The handler on which the callback should be invoked. May not be null. */ public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) { synchronized (mLock) { Handler oldHandler = mCallbackMap.put(callback, handler); // For new callbacks, provide initial availability information if (oldHandler == null) { updateCallbackLocked(callback, handler); } } } /** * Remove a previously-added callback; the callback will no longer receive connection and * disconnection callbacks, and is no longer referenced by the global listener singleton. * * @param callback The callback to remove from the notification list */ public void unregisterAvailabilityCallback(AvailabilityCallback callback) { synchronized (mLock) { mCallbackMap.remove(callback); } } /** * Callback from camera service notifying the process about camera availability changes */ @Override public void onStatusChanged(int status, int cameraId) throws RemoteException { synchronized(mLock) { onStatusChangedLocked(status, String.valueOf(cameraId)); } } /** * Listener for camera service death. * * <p>The camera service isn't supposed to die under any normal circumstances, but can be * turned off during debug, or crash due to bugs. So detect that and null out the interface * object, so that the next calls to the manager can try to reconnect.</p> */ public void binderDied() { synchronized(mLock) { // Only do this once per service death if (mCameraService == null) return; mCameraService = null; // Tell listeners that the cameras are _available_, because any existing clients // will have gotten disconnected. This is optimistic under the assumption that // the service will be back shortly. // // Without this, a camera service crash while a camera is open will never signal // to listeners that previously in-use cameras are now available. for (int i = 0; i < mDeviceStatus.size(); i++) { String cameraId = mDeviceStatus.keyAt(i); onStatusChangedLocked(STATUS_PRESENT, cameraId); } } } } // CameraManagerGlobal } // CameraManager Loading
core/java/android/hardware/camera2/CameraManager.java +191 −144 Original line number Diff line number Diff line Loading @@ -54,29 +54,17 @@ public final class CameraManager { private static final String TAG = "CameraManager"; private final boolean DEBUG; /** * This should match the ICameraService definition */ private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; private static final int USE_CALLING_UID = -1; @SuppressWarnings("unused") private static final int API_VERSION_1 = 1; private static final int API_VERSION_2 = 2; // Access only through getCameraServiceLocked to deal with binder death private ICameraService mCameraService; private ArrayList<String> mDeviceIdList; private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap = new ArrayMap<AvailabilityCallback, Handler>(); private final Context mContext; private final Object mLock = new Object(); private final CameraServiceListener mServiceListener = new CameraServiceListener(); /** * @hide */ Loading @@ -84,8 +72,6 @@ public final class CameraManager { DEBUG = Log.isLoggable(TAG, Log.DEBUG); synchronized(mLock) { mContext = context; connectCameraServiceLocked(); } } Loading Loading @@ -116,6 +102,12 @@ public final class CameraManager { * <p>The first time a callback is registered, it is immediately called * with the availability status of all currently known camera devices.</p> * * <p>Since this callback will be registered with the camera service, remember to unregister it * once it is no longer needed; otherwise the callback will continue to receive events * indefinitely and it may prevent other resources from being released. Specifically, the * callbacks will be invoked independently of the general activity lifecycle and independently * of the state of individual CameraManager instances.</p> * * @param callback the new callback to send camera availability notices to * @param handler The handler on which the callback should be invoked, or * {@code null} to use the current thread's {@link android.os.Looper looper}. Loading @@ -130,13 +122,7 @@ public final class CameraManager { handler = new Handler(looper); } synchronized (mLock) { Handler oldHandler = mCallbackMap.put(callback, handler); // For new callbacks, provide initial availability information if (oldHandler == null) { mServiceListener.updateCallbackLocked(callback, handler); } } CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler); } /** Loading @@ -148,9 +134,7 @@ public final class CameraManager { * @param callback The callback to remove from the notification list */ public void unregisterAvailabilityCallback(AvailabilityCallback callback) { synchronized (mLock) { mCallbackMap.remove(callback); } CameraManagerGlobal.get().unregisterAvailabilityCallback(callback); } /** Loading Loading @@ -187,7 +171,7 @@ public final class CameraManager { * otherwise get them from the legacy shim instead. */ ICameraService cameraService = getCameraServiceLocked(); ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); if (cameraService == null) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, "Camera service is currently unavailable"); Loading Loading @@ -268,7 +252,7 @@ public final class CameraManager { try { if (supportsCamera2ApiLocked(cameraId)) { // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices ICameraService cameraService = getCameraServiceLocked(); ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); if (cameraService == null) { throw new CameraRuntimeException( CameraAccessException.CAMERA_DISCONNECTED, Loading Loading @@ -443,13 +427,6 @@ public final class CameraManager { } } /** * Temporary for migrating to Callback naming * @hide */ public static abstract class AvailabilityListener extends AvailabilityCallback { } /** * Return or create the list of currently connected camera devices. * Loading @@ -458,7 +435,7 @@ public final class CameraManager { private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException { if (mDeviceIdList == null) { int numCameras = 0; ICameraService cameraService = getCameraServiceLocked(); ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); ArrayList<String> deviceIdList = new ArrayList<>(); // If no camera service, then no devices Loading Loading @@ -515,18 +492,6 @@ public final class CameraManager { return mDeviceIdList; } private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) { int problem = e.getReason(); switch (problem) { case CameraAccessException.CAMERA_DISCONNECTED: String errorMsg = CameraAccessException.getDefaultMessage(problem); Log.w(TAG, msg + ": " + errorMsg); break; default: throw new IllegalStateException(msg, e.asChecked()); } } /** * Queries the camera service if it supports the camera2 api directly, or needs a shim. * Loading Loading @@ -556,7 +521,7 @@ public final class CameraManager { * Anything else is an unexpected error we don't want to recover from. */ try { ICameraService cameraService = getCameraServiceLocked(); ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); // If no camera service, no support if (cameraService == null) return false; Loading @@ -577,6 +542,85 @@ public final class CameraManager { return false; } /** * A per-process global camera manager instance, to retain a connection to the camera service, * and to distribute camera availability notices to API-registered callbacks */ private static final class CameraManagerGlobal extends ICameraServiceListener.Stub implements IBinder.DeathRecipient { private static final String TAG = "CameraManagerGlobal"; private final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); // Singleton instance private static final CameraManagerGlobal gCameraManager = new CameraManagerGlobal(); /** * This must match the ICameraService definition */ private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; // Keep up-to-date with ICameraServiceListener.h // Device physically unplugged public static final int STATUS_NOT_PRESENT = 0; // Device physically has been plugged in // and the camera can be used exclusively public static final int STATUS_PRESENT = 1; // Device physically has been plugged in // but it will not be connect-able until enumeration is complete public static final int STATUS_ENUMERATING = 2; // Camera is in use by another app and cannot be used exclusively public static final int STATUS_NOT_AVAILABLE = 0x80000000; // End enums shared with ICameraServiceListener.h // Camera ID -> Status map private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); // Registered availablility callbacks and their handlers private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap = new ArrayMap<AvailabilityCallback, Handler>(); private final Object mLock = new Object(); // Access only through getCameraService to deal with binder death private ICameraService mCameraService; // Singleton, don't allow construction private CameraManagerGlobal() { } public static CameraManagerGlobal get() { return gCameraManager; } @Override public IBinder asBinder() { return this; } /** * Return a best-effort ICameraService. * * <p>This will be null if the camera service is not currently available. If the camera * service has died since the last use of the camera service, will try to reconnect to the * service.</p> */ public ICameraService getCameraService() { synchronized(mLock) { if (mCameraService == null) { Log.i(TAG, "getCameraService: Reconnecting to camera service"); connectCameraServiceLocked(); if (mCameraService == null) { Log.e(TAG, "Camera service is unavailable"); } } return mCameraService; } } /** * Connect to the camera service if it's available, and set up listeners. * Loading @@ -590,7 +634,7 @@ public final class CameraManager { return; } try { cameraServiceBinder.linkToDeath(new CameraServiceDeathListener(), /*flags*/ 0); cameraServiceBinder.linkToDeath(this, /*flags*/ 0); } catch (RemoteException e) { // Camera service is now down, leave mCameraService as null return; Loading @@ -602,7 +646,8 @@ public final class CameraManager { * Wrap the camera service in a decorator which automatically translates return codes * into exceptions. */ ICameraService cameraService = CameraServiceBinderDecorator.newInstance(cameraServiceRaw); ICameraService cameraService = CameraServiceBinderDecorator.newInstance(cameraServiceRaw); try { CameraServiceBinderDecorator.throwOnError( Loading @@ -612,7 +657,7 @@ public final class CameraManager { } try { cameraService.addListener(mServiceListener); cameraService.addListener(this); mCameraService = cameraService; } catch(CameraRuntimeException e) { // Unexpected failure Loading @@ -623,74 +668,16 @@ public final class CameraManager { } } /** * Return a best-effort ICameraService. * * <p>This will be null if the camera service * is not currently available. If the camera service has died since the last * use of the camera service, will try to reconnect to the service.</p> */ private ICameraService getCameraServiceLocked() { if (mCameraService == null) { Log.i(TAG, "getCameraServiceLocked: Reconnecting to camera service"); connectCameraServiceLocked(); if (mCameraService == null) { Log.e(TAG, "Camera service is unavailable"); } } return mCameraService; } /** * Listener for camera service death. * * <p>The camera service isn't supposed to die under any normal circumstances, but can be turned * off during debug, or crash due to bugs. So detect that and null out the interface object, so * that the next calls to the manager can try to reconnect.</p> */ private class CameraServiceDeathListener implements IBinder.DeathRecipient { public void binderDied() { synchronized(mLock) { mCameraService = null; // Tell listeners that the cameras are _available_, because any existing clients // will have gotten disconnected. This is optimistic under the assumption that the // service will be back shortly. // // Without this, a camera service crash while a camera is open will never signal to // listeners that previously in-use cameras are now available. for (String cameraId : mDeviceIdList) { mServiceListener.onStatusChangedLocked(CameraServiceListener.STATUS_PRESENT, cameraId); } } } private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) { int problem = e.getReason(); switch (problem) { case CameraAccessException.CAMERA_DISCONNECTED: String errorMsg = CameraAccessException.getDefaultMessage(problem); Log.w(TAG, msg + ": " + errorMsg); break; default: throw new IllegalStateException(msg, e.asChecked()); } // TODO: this class needs unit tests // TODO: extract class into top level private class CameraServiceListener extends ICameraServiceListener.Stub { // Keep up-to-date with ICameraServiceListener.h // Device physically unplugged public static final int STATUS_NOT_PRESENT = 0; // Device physically has been plugged in // and the camera can be used exclusively public static final int STATUS_PRESENT = 1; // Device physically has been plugged in // but it will not be connect-able until enumeration is complete public static final int STATUS_ENUMERATING = 2; // Camera is in use by another app and cannot be used exclusively public static final int STATUS_NOT_AVAILABLE = 0x80000000; // Camera ID -> Status map private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>(); private static final String TAG = "CameraServiceListener"; @Override public IBinder asBinder() { return this; } private boolean isAvailable(int status) { Loading Loading @@ -739,7 +726,7 @@ public final class CameraManager { * Send the state of all known cameras to the provided listener, to initialize * the listener's knowledge of camera state. */ public void updateCallbackLocked(AvailabilityCallback callback, Handler handler) { private void updateCallbackLocked(AvailabilityCallback callback, Handler handler) { for (int i = 0; i < mDeviceStatus.size(); i++) { String id = mDeviceStatus.keyAt(i); Integer status = mDeviceStatus.valueAt(i); Loading @@ -747,14 +734,7 @@ public final class CameraManager { } } @Override public void onStatusChanged(int status, int cameraId) throws RemoteException { synchronized(CameraManager.this.mLock) { onStatusChangedLocked(status, String.valueOf(cameraId)); } } public void onStatusChangedLocked(int status, String id) { private void onStatusChangedLocked(int status, String id) { if (DEBUG) { Log.v(TAG, String.format("Camera id %s has status changed to 0x%x", id, status)); Loading Loading @@ -811,5 +791,72 @@ public final class CameraManager { } } // onStatusChangedLocked } // CameraServiceListener /** * Register a callback to be notified about camera device availability with the * global listener singleton. * * @param callback the new callback to send camera availability notices to * @param handler The handler on which the callback should be invoked. May not be null. */ public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) { synchronized (mLock) { Handler oldHandler = mCallbackMap.put(callback, handler); // For new callbacks, provide initial availability information if (oldHandler == null) { updateCallbackLocked(callback, handler); } } } /** * Remove a previously-added callback; the callback will no longer receive connection and * disconnection callbacks, and is no longer referenced by the global listener singleton. * * @param callback The callback to remove from the notification list */ public void unregisterAvailabilityCallback(AvailabilityCallback callback) { synchronized (mLock) { mCallbackMap.remove(callback); } } /** * Callback from camera service notifying the process about camera availability changes */ @Override public void onStatusChanged(int status, int cameraId) throws RemoteException { synchronized(mLock) { onStatusChangedLocked(status, String.valueOf(cameraId)); } } /** * Listener for camera service death. * * <p>The camera service isn't supposed to die under any normal circumstances, but can be * turned off during debug, or crash due to bugs. So detect that and null out the interface * object, so that the next calls to the manager can try to reconnect.</p> */ public void binderDied() { synchronized(mLock) { // Only do this once per service death if (mCameraService == null) return; mCameraService = null; // Tell listeners that the cameras are _available_, because any existing clients // will have gotten disconnected. This is optimistic under the assumption that // the service will be back shortly. // // Without this, a camera service crash while a camera is open will never signal // to listeners that previously in-use cameras are now available. for (int i = 0; i < mDeviceStatus.size(); i++) { String cameraId = mDeviceStatus.keyAt(i); onStatusChangedLocked(STATUS_PRESENT, cameraId); } } } } // CameraManagerGlobal } // CameraManager