Camera2: Listener rework and other API updates

 - Add Handlers to each callback-accepting function
 - Expand CameraDevice ErrorListener to CameraDeviceListener
   - Add idle callback
   - Split out disconnect error to its own callback
 - Add CameraDevice#getId
 - Rename CameraManager's listener to AvailabilityListener
 - Rename CameraManager register/unregister*Listener to
   add/remove*Listener
 - Rename getDeviceIdList to getCameraIdList

Bug: 10549567
Bug: 10549462
Change-Id: Idd2ae8ad8eb126f35a15d765306ada7c1cf74eea
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index 86a073f..966fcfa 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -29,6 +29,8 @@
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Surface;
@@ -53,10 +55,11 @@
     private final Object mLock = new Object();
     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
 
-    // XX: Make this a WeakReference<CaptureListener> ?
-    // TODO: Convert to SparseIntArray
-    private final HashMap<Integer, CaptureListenerHolder> mCaptureListenerMap =
-            new HashMap<Integer, CaptureListenerHolder>();
+    private CameraDeviceListener mDeviceListener;
+    private Handler mDeviceHandler;
+
+    private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
+            new SparseArray<CaptureListenerHolder>();
 
     private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
     // Map stream IDs to Surfaces
@@ -80,6 +83,11 @@
     }
 
     @Override
+    public String getId() {
+        return mCameraId;
+    }
+
+    @Override
     public CameraProperties getProperties() throws CameraAccessException {
 
         CameraProperties properties = new CameraProperties();
@@ -173,14 +181,14 @@
     }
 
     @Override
-    public void capture(CaptureRequest request, CaptureListener listener)
+    public void capture(CaptureRequest request, CaptureListener listener, Handler handler)
             throws CameraAccessException {
-        submitCaptureRequest(request, listener, /*streaming*/false);
+        submitCaptureRequest(request, listener, handler, /*streaming*/false);
     }
 
     @Override
-    public void captureBurst(List<CaptureRequest> requests, CaptureListener listener)
-            throws CameraAccessException {
+    public void captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException {
         if (requests.isEmpty()) {
             Log.w(TAG, "Capture burst request list is empty, do nothing!");
             return;
@@ -191,7 +199,18 @@
     }
 
     private void submitCaptureRequest(CaptureRequest request, CaptureListener listener,
-            boolean repeating) throws CameraAccessException {
+            Handler handler, boolean repeating) throws CameraAccessException {
+
+        // Need a valid handler, or current thread needs to have a looper, if
+        // listener is valid
+        if (handler == null && listener != null) {
+            Looper looper = Looper.myLooper();
+            if (looper == null) {
+                throw new IllegalArgumentException(
+                        "No handler given, and current thread has no looper!");
+            }
+            handler = new Handler(looper);
+        }
 
         synchronized (mLock) {
 
@@ -205,9 +224,10 @@
                 // impossible
                 return;
             }
-
-            mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
-                    repeating));
+            if (listener != null) {
+                mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
+                        handler, repeating));
+            }
 
             if (repeating) {
                 mRepeatingRequestIdStack.add(requestId);
@@ -217,14 +237,14 @@
     }
 
     @Override
-    public void setRepeatingRequest(CaptureRequest request, CaptureListener listener)
-            throws CameraAccessException {
-        submitCaptureRequest(request, listener, /*streaming*/true);
+    public void setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+            Handler handler) throws CameraAccessException {
+        submitCaptureRequest(request, listener, handler, /*streaming*/true);
     }
 
     @Override
-    public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener)
-            throws CameraAccessException {
+    public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
+            Handler handler) throws CameraAccessException {
         if (requests.isEmpty()) {
             Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
             return;
@@ -274,9 +294,11 @@
     }
 
     @Override
-    public void setErrorListener(ErrorListener listener) {
-        // TODO Auto-generated method stub
-
+    public void setDeviceListener(CameraDeviceListener listener, Handler handler) {
+        synchronized (mLock) {
+            mDeviceListener = listener;
+            mDeviceHandler = handler;
+        }
     }
 
     @Override
@@ -332,9 +354,16 @@
         private final boolean mRepeating;
         private final CaptureListener mListener;
         private final CaptureRequest mRequest;
+        private final Handler mHandler;
 
-        CaptureListenerHolder(CaptureListener listener, CaptureRequest request, boolean repeating) {
+        CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
+                boolean repeating) {
+            if (listener == null || handler == null) {
+                throw new UnsupportedOperationException(
+                    "Must have a valid handler and a valid listener");
+            }
             mRepeating = repeating;
+            mHandler = handler;
             mRequest = request;
             mListener = listener;
         }
@@ -350,6 +379,11 @@
         public CaptureRequest getRequest() {
             return mRequest;
         }
+
+        public Handler getHandler() {
+            return mHandler;
+        }
+
     }
 
     // TODO: unit tests
@@ -374,7 +408,7 @@
             if (DEBUG) {
                 Log.d(TAG, "Received result for id " + requestId);
             }
-            CaptureListenerHolder holder;
+            final CaptureListenerHolder holder;
 
             synchronized (mLock) {
                 // TODO: move this whole map into this class to make it more testable,
@@ -393,18 +427,23 @@
                 }
             }
 
+            // Check if we have a listener for this
             if (holder == null) {
-                Log.e(TAG, "Result had no listener holder associated with it, dropping result");
                 return;
             }
 
-            CaptureResult resultAsCapture = new CaptureResult();
+            final CaptureResult resultAsCapture = new CaptureResult();
             resultAsCapture.swap(result);
 
-            if (holder.getListener() != null) {
-                holder.getListener().onCaptureComplete(CameraDevice.this, holder.getRequest(),
-                        resultAsCapture);
-            }
+            holder.getHandler().post(
+                new Runnable() {
+                    public void run() {
+                        holder.getListener().onCaptureCompleted(
+                            CameraDevice.this,
+                            holder.getRequest(),
+                            resultAsCapture);
+                    }
+                });
         }
 
     }