Camera: update createStream API to add new rotation field

Change-Id: I0f4343a0bfa7bf09ba887c78a1da1c08daa35333
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index d286d38..01f2396 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -16,7 +16,7 @@
 
 package android.hardware.camera2;
 
-import android.view.Surface;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.CaptureRequest;
 
@@ -66,7 +66,7 @@
     int deleteStream(int streamId);
 
     // non-negative value is the stream ID. negative value is status_t
-    int createStream(in Surface surface);
+    int createStream(in OutputConfiguration outputConfiguration);
 
     int createDefaultRequest(int templateId, out CameraMetadataNative request);
 
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index c5267cb..38f8e39 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -79,7 +79,8 @@
     private int mRepeatingRequestId = REQUEST_ID_NONE;
     private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
     // Map stream IDs to Surfaces
-    private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
+    private final SparseArray<OutputConfiguration> mConfiguredOutputs =
+            new SparseArray<OutputConfiguration>();
 
     private final String mCameraId;
     private final CameraCharacteristics mCharacteristics;
@@ -315,7 +316,11 @@
 
     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
         // Leave this here for backwards compatibility with older code using this directly
-        configureOutputsChecked(outputs);
+        ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size());
+        for (Surface s : outputs) {
+            outputConfigs.add(new OutputConfiguration(s));
+        }
+        configureOutputsChecked(outputConfigs);
     }
 
     /**
@@ -334,28 +339,30 @@
      *
      * @throws CameraAccessException if there were any unexpected problems during configuration
      */
-    public boolean configureOutputsChecked(List<Surface> outputs) throws CameraAccessException {
+    public boolean configureOutputsChecked(List<OutputConfiguration> outputs)
+            throws CameraAccessException {
         // Treat a null input the same an empty list
         if (outputs == null) {
-            outputs = new ArrayList<Surface>();
+            outputs = new ArrayList<OutputConfiguration>();
         }
         boolean success = false;
 
         synchronized(mInterfaceLock) {
             checkIfCameraClosedOrInError();
-
-            HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
-            List<Integer> deleteList = new ArrayList<Integer>();        // Streams to delete
+            // Streams to create
+            HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
+         // Streams to delete
+            List<Integer> deleteList = new ArrayList<Integer>();
 
             // 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);
+                OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
 
-                if (!outputs.contains(s)) {
+                if (!outputs.contains(outConfig)) {
                     deleteList.add(streamId);
                 } else {
-                    addSet.remove(s);  // Don't create a stream previously created
+                    addSet.remove(outConfig);  // Don't create a stream previously created
                 }
             }
 
@@ -373,9 +380,11 @@
                 }
 
                 // Add all new streams
-                for (Surface s : addSet) {
-                    int streamId = mRemoteDevice.createStream(s);
-                    mConfiguredOutputs.put(streamId, s);
+                for (OutputConfiguration outConfig : outputs) {
+                    if (addSet.contains(outConfig)) {
+                        int streamId = mRemoteDevice.createStream(outConfig);
+                        mConfiguredOutputs.put(streamId, outConfig);
+                    }
                 }
 
                 try {
@@ -444,12 +453,9 @@
             // TODO: dont block for this
             boolean configureSuccess = true;
             CameraAccessException pendingException = null;
-            List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
-            for (OutputConfiguration config : outputConfigurations) {
-                outSurfaces.add(config.getSurface());
-            }
             try {
-                configureSuccess = configureOutputsChecked(outSurfaces); // and then block until IDLE
+                // configure outputs and then block until IDLE
+                configureSuccess = configureOutputsChecked(outputConfigurations);
             } catch (CameraAccessException e) {
                 configureSuccess = false;
                 pendingException = e;
@@ -458,6 +464,10 @@
                 }
             }
 
+            List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
+            for (OutputConfiguration config : outputConfigurations) {
+                outSurfaces.add(config.getSurface());
+            }
             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
             CameraCaptureSessionImpl newSession =
                     new CameraCaptureSessionImpl(mNextSessionId++,
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 26cd498..70f3463 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.utils.LongParcelable;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.os.ConditionVariable;
@@ -504,7 +505,7 @@
     }
 
     @Override
-    public int createStream(Surface surface) {
+    public int createStream(OutputConfiguration outputConfiguration) {
         if (DEBUG) {
             Log.d(TAG, "createStream called.");
         }
@@ -518,8 +519,12 @@
                 Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet.");
                 return CameraBinderDecorator.INVALID_OPERATION;
             }
+            if (outputConfiguration.getRotation() != OutputConfiguration.ROTATION_0) {
+                Log.e(TAG, "Cannot create stream, stream rotation is not supported.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
             int id = ++mSurfaceIdCounter;
-            mSurfaces.put(id, surface);
+            mSurfaces.put(id, outputConfiguration.getSurface());
             return id;
         }
     }
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.aidl b/core/java/android/hardware/camera2/params/OutputConfiguration.aidl
new file mode 100644
index 0000000..0921cd8
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+/** @hide */
+parcelable OutputConfiguration;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 47c784e..0a4ed39 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -18,19 +18,22 @@
 package android.hardware.camera2.params;
 
 import android.hardware.camera2.CameraDevice;
+import android.util.Log;
 import android.view.Surface;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import static com.android.internal.util.Preconditions.*;
 
 /**
- * Immutable class for describing camera output, which contains a {@link Surface} and its specific
+ * A class for describing camera output, which contains a {@link Surface} and its specific
  * configuration for creating capture session.
  *
  * @see CameraDevice#createCaptureSession
  *
  * @hide
  */
-public final class OutputConfiguration {
+public final class OutputConfiguration implements Parcelable {
 
     /**
      * Rotation constant: 0 degree rotation (no rotation)
@@ -93,6 +96,18 @@
     }
 
     /**
+     * Create an OutputConfiguration from Parcel.
+     */
+    private OutputConfiguration(Parcel source) {
+        int rotation = source.readInt();
+        Surface surface = Surface.CREATOR.createFromParcel(source);
+        checkNotNull(surface, "Surface must not be null");
+        checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
+        mSurface = surface;
+        mRotation = rotation;
+    }
+
+    /**
      * Get the {@link Surface} associated with this {@link OutputConfiguration}.
      *
      * @return the {@link Surface} associated with this {@link OutputConfiguration}.
@@ -111,6 +126,40 @@
         return mRotation;
     }
 
+    public static final Parcelable.Creator<OutputConfiguration> CREATOR =
+            new Parcelable.Creator<OutputConfiguration>() {
+        @Override
+        public OutputConfiguration createFromParcel(Parcel source) {
+            try {
+                OutputConfiguration outputConfiguration = new OutputConfiguration(source);
+                return outputConfiguration;
+            } catch (Exception e) {
+                Log.e(TAG, "Exception creating OutputConfiguration from parcel", e);
+                return null;
+            }
+        }
+
+        @Override
+        public OutputConfiguration[] newArray(int size) {
+            return new OutputConfiguration[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (dest == null) {
+            throw new IllegalArgumentException("dest must not be null");
+        }
+        dest.writeInt(mRotation);
+        mSurface.writeToParcel(dest, flags);
+    }
+
+    private static final String TAG = "OutputConfiguration";
     private final Surface mSurface;
     private final int mRotation;
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index d756d05..e05e1fc 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -25,6 +25,7 @@
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.BinderHolder;
 import android.media.Image;
 import android.media.ImageReader;
@@ -67,6 +68,7 @@
     private CameraBinderTestUtils mUtils;
     private ICameraDeviceCallbacks.Stub mMockCb;
     private Surface mSurface;
+    private OutputConfiguration mOutputConfiguration;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     ImageReader mImageReader;
@@ -147,6 +149,7 @@
                         MAX_NUM_IMAGES);
         mImageReader.setOnImageAvailableListener(new ImageDropperListener(), mHandler);
         mSurface = mImageReader.getSurface();
+        mOutputConfiguration = new OutputConfiguration(mSurface);
     }
 
     private CaptureRequest.Builder createDefaultBuilder(boolean needStream) throws Exception {
@@ -161,7 +164,7 @@
         assertFalse(request.isEmpty());
         assertFalse(metadata.isEmpty());
         if (needStream) {
-            int streamId = mCameraUser.createStream(mSurface);
+            int streamId = mCameraUser.createStream(mOutputConfiguration);
             assertEquals(0, streamId);
             request.addTarget(mSurface);
         }
@@ -234,11 +237,11 @@
 
     @SmallTest
     public void testCreateStream() throws Exception {
-        int streamId = mCameraUser.createStream(mSurface);
+        int streamId = mCameraUser.createStream(mOutputConfiguration);
         assertEquals(0, streamId);
 
         assertEquals(CameraBinderTestUtils.ALREADY_EXISTS,
-                mCameraUser.createStream(mSurface));
+                mCameraUser.createStream(mOutputConfiguration));
 
         assertEquals(CameraBinderTestUtils.NO_ERROR, mCameraUser.deleteStream(streamId));
     }
@@ -255,18 +258,19 @@
     public void testCreateStreamTwo() throws Exception {
 
         // Create first stream
-        int streamId = mCameraUser.createStream(mSurface);
+        int streamId = mCameraUser.createStream(mOutputConfiguration);
         assertEquals(0, streamId);
 
         assertEquals(CameraBinderTestUtils.ALREADY_EXISTS,
-                mCameraUser.createStream(mSurface));
+                mCameraUser.createStream(mOutputConfiguration));
 
         // Create second stream with a different surface.
         SurfaceTexture surfaceTexture = new SurfaceTexture(/* ignored */0);
         surfaceTexture.setDefaultBufferSize(640, 480);
         Surface surface2 = new Surface(surfaceTexture);
+        OutputConfiguration output2 = new OutputConfiguration(surface2);
 
-        int streamId2 = mCameraUser.createStream(surface2);
+        int streamId2 = mCameraUser.createStream(output2);
         assertEquals(1, streamId2);
 
         // Clean up streams