camera2: CameraDevice#configureOutputs now deletes unused streams

Bug: 10461554
Change-Id: I3dc48e434d83b18909d25723cc8855c82e1072bc
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index e7495d3..ac9f591 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -16,21 +16,26 @@
 
 package android.hardware.camera2.impl;
 
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.ICameraDeviceUser;
-import android.hardware.camera2.ICameraDeviceCallbacks;
+import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
+
 import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CameraProperties;
 import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.utils.CameraRuntimeException;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.utils.CameraBinderDecorator;
+import android.hardware.camera2.utils.CameraRuntimeException;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.Surface;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Stack;
 
@@ -54,6 +59,8 @@
             new HashMap<Integer, CaptureListenerHolder>();
 
     private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
+    // Map stream IDs to Surfaces
+    private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
 
     private final String mCameraId;
 
@@ -94,18 +101,49 @@
     @Override
     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
         synchronized (mLock) {
-            // TODO: delete outputs that aren't in this list that were configured previously
-            for (Surface s : outputs) {
-                try {
+            HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
+            List<Integer> deleteList = new ArrayList<Integer>();        // Streams to delete
+
+            // Determine which streams need to be created, which to be deleted
+            for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
+                int streamId = mConfiguredOutputs.keyAt(i);
+                Surface s = mConfiguredOutputs.valueAt(i);
+
+                if (!outputs.contains(s)) {
+                    deleteList.add(streamId);
+                } else {
+                    addSet.remove(s);  // Don't create a stream previously created
+                }
+            }
+
+            try {
+                // TODO: mRemoteDevice.beginConfigure
+
+                // Delete all streams first (to free up HW resources)
+                for (Integer streamId : deleteList) {
+                    mRemoteDevice.deleteStream(streamId);
+                    mConfiguredOutputs.delete(streamId);
+                }
+
+                // Add all new streams
+                for (Surface s : addSet) {
                     // TODO: remove width,height,format since we are ignoring
                     // it.
-                    mRemoteDevice.createStream(0, 0, 0, s);
-                } catch (CameraRuntimeException e) {
-                    throw e.asChecked();
-                } catch (RemoteException e) {
-                    // impossible
-                    return;
+                    int streamId = mRemoteDevice.createStream(0, 0, 0, s);
+                    mConfiguredOutputs.put(streamId, s);
                 }
+
+                // TODO: mRemoteDevice.endConfigure
+            } catch (CameraRuntimeException e) {
+                if (e.getReason() == CAMERA_IN_USE) {
+                    throw new IllegalStateException("The camera is currently busy." +
+                            " You must call waitUntilIdle before trying to reconfigure.");
+                }
+
+                throw e.asChecked();
+            } catch (RemoteException e) {
+                // impossible
+                return;
             }
         }
     }