Camera2: Handle binder death on ICameraDeviceUser
Call onError(CAMERA_SERVICE) when binder death happens on ICameraDeviceUser.
Bug: 27206055
Change-Id: Ic720933d057559966556658d478ff6d3536f0e3f
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b3c8e3b..04e64af 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -358,6 +358,8 @@
// TODO: factor out callback to be non-nested, then move setter to constructor
// For now, calling setRemoteDevice will fire initial
// onOpened/onUnconfigured callbacks.
+ // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
+ // cameraUser dies during setup.
deviceImpl.setRemoteDevice(cameraUser);
device = deviceImpl;
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 00dd780..37d2ea2 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -63,7 +63,8 @@
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
*/
-public class CameraDeviceImpl extends CameraDevice {
+public class CameraDeviceImpl extends CameraDevice
+ implements IBinder.DeathRecipient {
private final String TAG;
private final boolean DEBUG = false;
@@ -261,7 +262,14 @@
return mCallbacks;
}
- public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
+ /**
+ * Set remote device, which triggers initial onOpened/onUnconfigured callbacks
+ *
+ * <p>This function may post onDisconnected and throw CAMERA_DISCONNECTED if remoteDevice dies
+ * during setup.</p>
+ *
+ */
+ public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException {
synchronized(mInterfaceLock) {
// TODO: Move from decorator to direct binder-mediated exceptions
// If setRemoteFailure already called, do nothing
@@ -269,6 +277,20 @@
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
+ IBinder remoteDeviceBinder = remoteDevice.asBinder();
+ // For legacy camera device, remoteDevice is in the same process, and
+ // asBinder returns NULL.
+ if (remoteDeviceBinder != null) {
+ try {
+ remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
+ } catch (RemoteException e) {
+ CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "The camera device has encountered a serious error");
+ }
+ }
+
mDeviceHandler.post(mCallOnOpened);
mDeviceHandler.post(mCallOnUnconfigured);
}
@@ -1962,4 +1984,28 @@
return mCharacteristics;
}
+ /**
+ * Listener for binder death.
+ *
+ * <p> Handle binder death for ICameraDeviceUser. Trigger onError.</p>
+ */
+ public void binderDied() {
+ Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly");
+
+ if (mRemoteDevice == null) {
+ return; // Camera already closed
+ }
+
+ mInError = true;
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ if (!isClosed()) {
+ mDeviceCallback.onError(CameraDeviceImpl.this,
+ CameraDeviceCallbacks.ERROR_CAMERA_SERVICE);
+ }
+ }
+ };
+ CameraDeviceImpl.this.mDeviceHandler.post(r);
+ }
}