Camera2: Fix null-listener capture for CameraCaptureSession

Correctly allow null listener/handler when invoked on a thread with no
looper; otherwise all capture/repeating requests will fail even if listener
is null.

Change-Id: I4c64c81ad7f14b5cb309b4f92822cb50dbd74ba6
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index f829f5e..690093b 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -141,7 +141,7 @@
         checkNotClosed();
         checkLegalToCapture();
 
-        handler = checkHandler(handler);
+        handler = checkHandler(handler, listener);
 
         if (VERBOSE) {
             Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" +
@@ -164,7 +164,7 @@
         checkNotClosed();
         checkLegalToCapture();
 
-        handler = checkHandler(handler);
+        handler = checkHandler(handler, listener);
 
         if (VERBOSE) {
             CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
@@ -186,7 +186,7 @@
         checkNotClosed();
         checkLegalToCapture();
 
-        handler = checkHandler(handler);
+        handler = checkHandler(handler, listener);
 
         if (VERBOSE) {
             Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener +
@@ -209,7 +209,7 @@
         checkNotClosed();
         checkLegalToCapture();
 
-        handler = checkHandler(handler);
+        handler = checkHandler(handler, listener);
 
         if (VERBOSE) {
             CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
@@ -379,15 +379,18 @@
          * - then forward the call to a handler
          * - then finally invoke the destination method on the session listener object
          */
-        Dispatchable<CaptureListener> userListenerSink;
-        if (listener == null) { // OK: API allows the user to not specify a listener
-            userListenerSink = new NullDispatcher<>();
-        } else {
-            userListenerSink = new InvokeDispatcher<>(listener);
+        if (listener == null) {
+            // OK: API allows the user to not specify a listener, and the handler may
+            // also be null in that case. Collapse whole dispatch chain to only call the local
+            // listener
+            return localListener;
         }
 
         InvokeDispatcher<CameraDevice.CaptureListener> localSink =
                 new InvokeDispatcher<>(localListener);
+
+        InvokeDispatcher<CaptureListener> userListenerSink =
+                new InvokeDispatcher<>(listener);
         HandlerDispatcher<CaptureListener> handlerPassthrough =
                 new HandlerDispatcher<>(userListenerSink, handler);
         DuckTypingDispatcher<CameraDevice.CaptureListener, CaptureListener> duckToSession
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 18b1202..3a94e02 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -543,9 +543,7 @@
 
         // Need a valid handler, or current thread needs to have a looper, if
         // listener is valid
-        if (listener != null) {
-            handler = checkHandler(handler);
-        }
+        handler = checkHandler(handler, listener);
 
         // Make sure that there all requests have at least 1 surface; all surfaces are non-null
         for (CaptureRequest request : requestList) {
@@ -1155,6 +1153,18 @@
         return handler;
     }
 
+    /**
+     * Default handler management, conditional on there being a listener.
+     *
+     * <p>If the listener isn't null, check the handler, otherwise pass it through.</p>
+     */
+    static <T> Handler checkHandler(Handler handler, T listener) {
+        if (listener != null) {
+            return checkHandler(handler);
+        }
+        return handler;
+    }
+
     private void checkIfCameraClosedOrInError() throws CameraAccessException {
         if (mInError) {
             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,