Merge "CameraManager: Separate service listener into a singleton" into lmp-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
9b35b801fb
@@ -54,29 +54,17 @@ public final class CameraManager {
|
|||||||
private static final String TAG = "CameraManager";
|
private static final String TAG = "CameraManager";
|
||||||
private final boolean DEBUG;
|
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;
|
private static final int USE_CALLING_UID = -1;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static final int API_VERSION_1 = 1;
|
private static final int API_VERSION_1 = 1;
|
||||||
private static final int API_VERSION_2 = 2;
|
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 ArrayList<String> mDeviceIdList;
|
||||||
|
|
||||||
private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
|
|
||||||
new ArrayMap<AvailabilityCallback, Handler>();
|
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
private final CameraServiceListener mServiceListener = new CameraServiceListener();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -84,8 +72,6 @@ public final class CameraManager {
|
|||||||
DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
synchronized(mLock) {
|
synchronized(mLock) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
|
||||||
connectCameraServiceLocked();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +102,12 @@ public final class CameraManager {
|
|||||||
* <p>The first time a callback is registered, it is immediately called
|
* <p>The first time a callback is registered, it is immediately called
|
||||||
* with the availability status of all currently known camera devices.</p>
|
* 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 callback the new callback to send camera availability notices to
|
||||||
* @param handler The handler on which the callback should be invoked, or
|
* @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}.
|
* {@code null} to use the current thread's {@link android.os.Looper looper}.
|
||||||
@@ -130,13 +122,7 @@ public final class CameraManager {
|
|||||||
handler = new Handler(looper);
|
handler = new Handler(looper);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
CameraManagerGlobal.get().registerAvailabilityCallback(callback, handler);
|
||||||
Handler oldHandler = mCallbackMap.put(callback, handler);
|
|
||||||
// For new callbacks, provide initial availability information
|
|
||||||
if (oldHandler == null) {
|
|
||||||
mServiceListener.updateCallbackLocked(callback, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -148,9 +134,7 @@ public final class CameraManager {
|
|||||||
* @param callback The callback to remove from the notification list
|
* @param callback The callback to remove from the notification list
|
||||||
*/
|
*/
|
||||||
public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
|
public void unregisterAvailabilityCallback(AvailabilityCallback callback) {
|
||||||
synchronized (mLock) {
|
CameraManagerGlobal.get().unregisterAvailabilityCallback(callback);
|
||||||
mCallbackMap.remove(callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,7 +171,7 @@ public final class CameraManager {
|
|||||||
* otherwise get them from the legacy shim instead.
|
* otherwise get them from the legacy shim instead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ICameraService cameraService = getCameraServiceLocked();
|
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
|
||||||
if (cameraService == null) {
|
if (cameraService == null) {
|
||||||
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
|
throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
|
||||||
"Camera service is currently unavailable");
|
"Camera service is currently unavailable");
|
||||||
@@ -268,7 +252,7 @@ public final class CameraManager {
|
|||||||
try {
|
try {
|
||||||
if (supportsCamera2ApiLocked(cameraId)) {
|
if (supportsCamera2ApiLocked(cameraId)) {
|
||||||
// Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
|
// Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
|
||||||
ICameraService cameraService = getCameraServiceLocked();
|
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
|
||||||
if (cameraService == null) {
|
if (cameraService == null) {
|
||||||
throw new CameraRuntimeException(
|
throw new CameraRuntimeException(
|
||||||
CameraAccessException.CAMERA_DISCONNECTED,
|
CameraAccessException.CAMERA_DISCONNECTED,
|
||||||
@@ -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.
|
* Return or create the list of currently connected camera devices.
|
||||||
*
|
*
|
||||||
@@ -458,7 +435,7 @@ public final class CameraManager {
|
|||||||
private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
|
private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
|
||||||
if (mDeviceIdList == null) {
|
if (mDeviceIdList == null) {
|
||||||
int numCameras = 0;
|
int numCameras = 0;
|
||||||
ICameraService cameraService = getCameraServiceLocked();
|
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
|
||||||
ArrayList<String> deviceIdList = new ArrayList<>();
|
ArrayList<String> deviceIdList = new ArrayList<>();
|
||||||
|
|
||||||
// If no camera service, then no devices
|
// If no camera service, then no devices
|
||||||
@@ -515,18 +492,6 @@ public final class CameraManager {
|
|||||||
return mDeviceIdList;
|
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.
|
* Queries the camera service if it supports the camera2 api directly, or needs a shim.
|
||||||
*
|
*
|
||||||
@@ -556,7 +521,7 @@ public final class CameraManager {
|
|||||||
* Anything else is an unexpected error we don't want to recover from.
|
* Anything else is an unexpected error we don't want to recover from.
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
ICameraService cameraService = getCameraServiceLocked();
|
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
|
||||||
// If no camera service, no support
|
// If no camera service, no support
|
||||||
if (cameraService == null) return false;
|
if (cameraService == null) return false;
|
||||||
|
|
||||||
@@ -578,97 +543,23 @@ public final class CameraManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to the camera service if it's available, and set up listeners.
|
* 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
|
||||||
* <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
|
|
||||||
*/
|
*/
|
||||||
private void connectCameraServiceLocked() {
|
private static final class CameraManagerGlobal extends ICameraServiceListener.Stub
|
||||||
mCameraService = null;
|
implements IBinder.DeathRecipient {
|
||||||
IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
|
|
||||||
if (cameraServiceBinder == null) {
|
|
||||||
// Camera service is now down, leave mCameraService as null
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
cameraServiceBinder.linkToDeath(new CameraServiceDeathListener(), /*flags*/ 0);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// Camera service is now down, leave mCameraService as null
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
|
private static final String TAG = "CameraManagerGlobal";
|
||||||
|
private final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
|
|
||||||
|
// Singleton instance
|
||||||
|
private static final CameraManagerGlobal gCameraManager =
|
||||||
|
new CameraManagerGlobal();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap the camera service in a decorator which automatically translates return codes
|
* This must match the ICameraService definition
|
||||||
* into exceptions.
|
|
||||||
*/
|
*/
|
||||||
ICameraService cameraService = CameraServiceBinderDecorator.newInstance(cameraServiceRaw);
|
private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
|
||||||
|
|
||||||
try {
|
|
||||||
CameraServiceBinderDecorator.throwOnError(
|
|
||||||
CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
|
|
||||||
} catch (CameraRuntimeException e) {
|
|
||||||
handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
cameraService.addListener(mServiceListener);
|
|
||||||
mCameraService = cameraService;
|
|
||||||
} catch(CameraRuntimeException e) {
|
|
||||||
// Unexpected failure
|
|
||||||
throw new IllegalStateException("Failed to register a camera service listener",
|
|
||||||
e.asChecked());
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// Camera service is now down, leave mCameraService as null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
// Keep up-to-date with ICameraServiceListener.h
|
||||||
|
|
||||||
@@ -683,16 +574,112 @@ public final class CameraManager {
|
|||||||
// Camera is in use by another app and cannot be used exclusively
|
// Camera is in use by another app and cannot be used exclusively
|
||||||
public static final int STATUS_NOT_AVAILABLE = 0x80000000;
|
public static final int STATUS_NOT_AVAILABLE = 0x80000000;
|
||||||
|
|
||||||
|
// End enums shared with ICameraServiceListener.h
|
||||||
|
|
||||||
// Camera ID -> Status map
|
// Camera ID -> Status map
|
||||||
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
|
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
|
||||||
|
|
||||||
private static final String TAG = "CameraServiceListener";
|
// 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
|
@Override
|
||||||
public IBinder asBinder() {
|
public IBinder asBinder() {
|
||||||
return this;
|
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.
|
||||||
|
*
|
||||||
|
* <p>Sets mCameraService to a valid pointer or null if the connection does not succeed.</p>
|
||||||
|
*/
|
||||||
|
private void connectCameraServiceLocked() {
|
||||||
|
mCameraService = null;
|
||||||
|
IBinder cameraServiceBinder = ServiceManager.getService(CAMERA_SERVICE_BINDER_NAME);
|
||||||
|
if (cameraServiceBinder == null) {
|
||||||
|
// Camera service is now down, leave mCameraService as null
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// Camera service is now down, leave mCameraService as null
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the camera service in a decorator which automatically translates return codes
|
||||||
|
* into exceptions.
|
||||||
|
*/
|
||||||
|
ICameraService cameraService =
|
||||||
|
CameraServiceBinderDecorator.newInstance(cameraServiceRaw);
|
||||||
|
|
||||||
|
try {
|
||||||
|
CameraServiceBinderDecorator.throwOnError(
|
||||||
|
CameraMetadataNative.nativeSetupGlobalVendorTagDescriptor());
|
||||||
|
} catch (CameraRuntimeException e) {
|
||||||
|
handleRecoverableSetupErrors(e, "Failed to set up vendor tags");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cameraService.addListener(this);
|
||||||
|
mCameraService = cameraService;
|
||||||
|
} catch(CameraRuntimeException e) {
|
||||||
|
// Unexpected failure
|
||||||
|
throw new IllegalStateException("Failed to register a camera service listener",
|
||||||
|
e.asChecked());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// Camera service is now down, leave mCameraService as null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isAvailable(int status) {
|
private boolean isAvailable(int status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case STATUS_PRESENT:
|
case STATUS_PRESENT:
|
||||||
@@ -739,7 +726,7 @@ public final class CameraManager {
|
|||||||
* Send the state of all known cameras to the provided listener, to initialize
|
* Send the state of all known cameras to the provided listener, to initialize
|
||||||
* the listener's knowledge of camera state.
|
* 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++) {
|
for (int i = 0; i < mDeviceStatus.size(); i++) {
|
||||||
String id = mDeviceStatus.keyAt(i);
|
String id = mDeviceStatus.keyAt(i);
|
||||||
Integer status = mDeviceStatus.valueAt(i);
|
Integer status = mDeviceStatus.valueAt(i);
|
||||||
@@ -747,14 +734,7 @@ public final class CameraManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void onStatusChangedLocked(int status, String id) {
|
||||||
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) {
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.v(TAG,
|
Log.v(TAG,
|
||||||
String.format("Camera id %s has status changed to 0x%x", id, status));
|
String.format("Camera id %s has status changed to 0x%x", id, status));
|
||||||
@@ -811,5 +791,72 @@ public final class CameraManager {
|
|||||||
}
|
}
|
||||||
} // onStatusChangedLocked
|
} // 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
|
} // CameraManager
|
||||||
|
|||||||
Reference in New Issue
Block a user