Merge "Camera2: various RecordingTest update" into lmp-mr1-dev
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
index 6454678..8dba5d6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
@@ -26,7 +26,7 @@
 import android.hardware.cts.helpers.SensorStats;
 import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
 import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -35,13 +35,13 @@
  */
 public abstract class SensorTestCase extends AndroidTestCase {
     // TODO: consolidate all log tags
-    protected final String LOG_TAG = "TestRunner";
+    protected static final String LOG_TAG = "TestRunner";
 
     /**
      * By default tests need to run in a {@link TestSensorEnvironment} that assumes each sensor is
      * running with a load of several listeners, requesting data at different rates.
      *
-     * In a better world the component acting as builder of {@link ISensorOperation} would compute
+     * In a better world the component acting as builder of {@link SensorOperation} would compute
      * this value based on the tests composed.
      *
      * Ideally, each {@link Sensor} object would expose this information to clients.
@@ -51,9 +51,9 @@
     protected SensorTestCase() {}
 
     @Override
-    public void runTest() throws Throwable {
+    public void runBare() throws Throwable {
         try {
-            super.runTest();
+            super.runBare();
         } catch (SensorTestStateNotSupportedException e) {
             // the sensor state is not supported in the device, log a warning and skip the test
             Log.w(LOG_TAG, e.getMessage());
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 6f98e86..a7ad94a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -17,7 +17,7 @@
 package android.hardware.cts.helpers;
 
 import android.hardware.Sensor;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
 import android.util.Log;
 
 import java.io.BufferedWriter;
@@ -34,7 +34,7 @@
 import java.util.Set;
 
 /**
- * Class used to store stats related to {@link ISensorOperation}s.  Sensor stats may be linked
+ * Class used to store stats related to {@link SensorOperation}s. Sensor stats may be linked
  * together so that they form a tree.
  */
 public class SensorStats {
@@ -55,8 +55,8 @@
     public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
     public static final String MAGNITUDE_KEY = "magnitude";
 
-    private final Map<String, Object> mValues = new HashMap<String, Object>();
-    private final Map<String, SensorStats> mSensorStats = new HashMap<String, SensorStats>();
+    private final Map<String, Object> mValues = new HashMap<>();
+    private final Map<String, SensorStats> mSensorStats = new HashMap<>();
 
     /**
      * Add a value.
@@ -73,7 +73,7 @@
 
     /**
      * Add a nested {@link SensorStats}. This is useful for keeping track of stats in a
-     * {@link ISensorOperation} tree.
+     * {@link SensorOperation} tree.
      *
      * @param key the key
      * @param stats the sub {@link SensorStats} object.
@@ -104,13 +104,13 @@
     /**
      * Flattens the map and all sub {@link SensorStats} objects. Keys will be flattened using
      * {@value #DELIMITER}. For example, if a sub {@link SensorStats} is added with key
-     * {@code "key1"} containing the key value pair {@code ("key2", "value")}, the flattened map
-     * will contain the entry {@code ("key1__key2", "value")}.
+     * {@code "key1"} containing the key value pair {@code \("key2", "value"\)}, the flattened map
+     * will contain the entry {@code \("key1__key2", "value"\)}.
      *
      * @return a {@link Map} containing all stats from the value and sub {@link SensorStats}.
      */
     public synchronized Map<String, Object> flatten() {
-        final Map<String, Object> flattenedMap = new HashMap<String, Object>(mValues);
+        final Map<String, Object> flattenedMap = new HashMap<>(mValues);
         for (Entry<String, SensorStats> statsEntry : mSensorStats.entrySet()) {
             for (Entry<String, Object> valueEntry : statsEntry.getValue().flatten().entrySet()) {
                 String key = statsEntry.getKey() + DELIMITER + valueEntry.getKey();
@@ -166,7 +166,7 @@
     }
 
     private static List<String> getSortedKeys(Map<String, Object> flattenedStats) {
-        List<String> keys = new ArrayList<String>(flattenedStats.keySet());
+        List<String> keys = new ArrayList<>(flattenedStats.keySet());
         Collections.sort(keys);
         return keys;
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 5be30a4..d59ea7a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -19,12 +19,12 @@
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
 
 import java.util.concurrent.TimeUnit;
 
 /**
- * A class that encapsulates base environment information for the {@link ISensorOperation}.
+ * A class that encapsulates base environment information for the {@link SensorOperation}.
  * The environment is self contained and carries its state around all the sensor test framework.
  */
 public class TestSensorEnvironment {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
deleted file mode 100644
index 5b969f2..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2014 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.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.SensorStats;
-
-/**
- * A {@link ISensorOperation} which contains a common implementation for gathering
- * {@link SensorStats}.
- */
-public abstract class AbstractSensorOperation implements ISensorOperation {
-
-    private final SensorStats mStats = new SensorStats();
-
-    /**
-     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
-     */
-    protected void addSensorStats(String key, SensorStats stats) {
-        mStats.addSensorStats(key, stats);
-    }
-
-    /**
-     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
-     * to be added. This is useful for {@link ISensorOperation}s that have many iterations or child
-     * operations. The key added is in the form {@code key + "_" + index} where index may be zero
-     * padded.
-     */
-    protected void addSensorStats(String key, int index, SensorStats stats) {
-        addSensorStats(String.format("%s_%03d", key, index), stats);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public SensorStats getStats() {
-        return mStats;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public abstract ISensorOperation clone();
-
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
index 88e4954..8848337 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -29,7 +29,7 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * An {@link ISensorOperation} which performs another {@link ISensorOperation} and then wakes up
+ * An {@link SensorOperation} which performs another {@link SensorOperation} and then wakes up
  * after a specified period of time and waits for the child operation to complete.
  * <p>
  * This operation can be used to allow the device to go to sleep and wake it up after a specified
@@ -40,11 +40,11 @@
  * but wake the device one time at the specified period.
  * </p>
  */
-public class AlarmOperation extends AbstractSensorOperation {
+public class AlarmOperation extends SensorOperation {
     private static final String ACTION = "AlarmOperationAction";
     private static final String WAKE_LOCK_TAG = "AlarmOperationWakeLock";
 
-    private final ISensorOperation mOperation;
+    private final SensorOperation mOperation;
     private final Context mContext;
     private final long mSleepDuration;
     private final TimeUnit mTimeUnit;
@@ -55,13 +55,17 @@
     /**
      * Constructor for {@link DelaySensorOperation}
      *
-     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param operation the child {@link SensorOperation} to perform after the delay
      * @param context the context used to access the alarm manager
      * @param sleepDuration the amount of time to sleep
      * @param timeUnit the unit of the duration
      */
-    public AlarmOperation(ISensorOperation operation, Context context, long sleepDuration,
+    public AlarmOperation(
+            SensorOperation operation,
+            Context context,
+            long sleepDuration,
             TimeUnit timeUnit) {
+        super(operation.getStats());
         mOperation = operation;
         mContext = context;
         mSleepDuration = sleepDuration;
@@ -120,7 +124,7 @@
      */
     private synchronized void acquireWakeLock() {
         // Don't acquire wake lock if the operation has already completed.
-        if (mCompleted == true || mWakeLock != null) {
+        if (mCompleted || mWakeLock != null) {
             return;
         }
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
index b4d1f23..3ee08f6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -22,22 +22,22 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * An {@link ISensorOperation} which delays for a specified period of time before performing another
- * {@link ISensorOperation}.
+ * An {@link SensorOperation} which delays for a specified period of time before performing another
+ * {@link SensorOperation}.
  */
-public class DelaySensorOperation implements ISensorOperation {
-    private final ISensorOperation mOperation;
+public class DelaySensorOperation extends SensorOperation {
+    private final SensorOperation mOperation;
     private final long mDelay;
     private final TimeUnit mTimeUnit;
 
     /**
      * Constructor for {@link DelaySensorOperation}
      *
-     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param operation the child {@link SensorOperation} to perform after the delay
      * @param delay the amount of time to delay
      * @param timeUnit the unit of the delay
      */
-    public DelaySensorOperation(ISensorOperation operation, long delay, TimeUnit timeUnit) {
+    public DelaySensorOperation(SensorOperation operation, long delay, TimeUnit timeUnit) {
         if (operation == null || timeUnit == null) {
             throw new IllegalArgumentException("Arguments cannot be null");
         }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
index bb64dfa..8cfd351 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
@@ -16,17 +16,17 @@
 
 package android.hardware.cts.helpers.sensoroperations;
 
-import android.hardware.cts.helpers.SensorStats;
-
 import junit.framework.Assert;
 
+import android.hardware.cts.helpers.SensorStats;
+
 import java.util.concurrent.TimeUnit;
 
 /**
- * A fake {@ISensorOperation} that will run for a specified time and then pass or fail. Useful when
- * debugging the framework.
+ * A fake {@link SensorOperation} that will run for a specified time and then pass or fail. Useful
+ * when debugging the framework.
  */
-public class FakeSensorOperation extends AbstractSensorOperation {
+public class FakeSensorOperation extends SensorOperation {
     private static final int NANOS_PER_MILLI = 1000000;
 
     private final boolean mFail;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
deleted file mode 100644
index 62a4e9e..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2014 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.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.SensorStats;
-
-/**
- * Interface used by all sensor operations. This allows for complex operations such as chaining
- * operations together or running operations in parallel.
- * <p>
- * Certain restrictions exist for {@link ISensorOperation}s:
- * <p><ul>
- * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
- * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
- * synchronization for operations.</li>
- * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
- * the returned value is undefined.</li>
- * <li>{@link #clone()} may be called any time and should return an operation with the same
- * parameters as the original.</li>
- * </ul>
- */
-public interface ISensorOperation {
-
-    /**
-     * Executes the sensor operation.  This may throw {@link RuntimeException}s such as
-     * {@link AssertionError}s.
-     *
-     * NOTE: the operation is expected to handle interruption by:
-     * - cleaning up on {@link InterruptedException}
-     * - propagating the exception down the stack
-     */
-    public void execute() throws InterruptedException;
-
-    /**
-     * Get the stats for the operation.
-     *
-     * @return The {@link SensorStats} for the operation.
-     */
-    public SensorStats getStats();
-
-    /**
-     * Clones the {@link ISensorOperation}. The implementation should also clone all child
-     * operations, so that a cloned operation will run with the exact same parameters as the
-     * original. The stats should not be cloned.
-     *
-     * @return The cloned {@link ISensorOperation}
-     */
-    public ISensorOperation clone();
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
index 5a4466c..e55c6cb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
@@ -22,7 +22,6 @@
 import android.os.SystemClock;
 
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -33,14 +32,14 @@
 import java.util.concurrent.TimeoutException;
 
 /**
- * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in parallel.
+ * A {@link SensorOperation} that executes a set of children {@link SensorOperation}s in parallel.
  * The children are run in parallel but are given an index label in the order they are added. This
- * class can be combined to compose complex {@link ISensorOperation}s.
+ * class can be combined to compose complex {@link SensorOperation}s.
  */
-public class ParallelSensorOperation extends AbstractSensorOperation {
+public class ParallelSensorOperation extends SensorOperation {
     public static final String STATS_TAG = "parallel";
 
-    private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+    private final ArrayList<SensorOperation> mOperations = new ArrayList<>();
     private final Long mTimeout;
     private final TimeUnit mTimeUnit;
 
@@ -65,10 +64,10 @@
     }
 
     /**
-     * Add a set of {@link ISensorOperation}s.
+     * Add a set of {@link SensorOperation}s.
      */
-    public void add(ISensorOperation ... operations) {
-        for (ISensorOperation operation : operations) {
+    public void add(SensorOperation ... operations) {
+        for (SensorOperation operation : operations) {
             if (operation == null) {
                 throw new IllegalArgumentException("Arguments cannot be null");
             }
@@ -77,7 +76,7 @@
     }
 
     /**
-     * Executes the {@link ISensorOperation}s in parallel. If an exception occurs one or more
+     * Executes the {@link SensorOperation}s in parallel. If an exception occurs one or more
      * operations, the first exception will be thrown once all operations are completed.
      */
     @Override
@@ -92,11 +91,11 @@
         executor.allowCoreThreadTimeOut(true);
         executor.prestartAllCoreThreads();
 
-        ArrayList<Future<ISensorOperation>> futures = new ArrayList<Future<ISensorOperation>>();
-        for (final ISensorOperation operation : mOperations) {
-            Future<ISensorOperation> future = executor.submit(new Callable<ISensorOperation>() {
+        ArrayList<Future<SensorOperation>> futures = new ArrayList<>();
+        for (final SensorOperation operation : mOperations) {
+            Future<SensorOperation> future = executor.submit(new Callable<SensorOperation>() {
                 @Override
-                public ISensorOperation call() throws Exception {
+                public SensorOperation call() throws Exception {
                     operation.execute();
                     return operation;
                 }
@@ -111,12 +110,12 @@
         }
 
         boolean hasAssertionErrors = false;
-        ArrayList<Integer> timeoutIndices = new ArrayList<Integer>();
-        ArrayList<Throwable> exceptions = new ArrayList<Throwable>();
+        ArrayList<Integer> timeoutIndices = new ArrayList<>();
+        ArrayList<Throwable> exceptions = new ArrayList<>();
         for (int i = 0; i < operationsCount; ++i) {
-            Future<ISensorOperation> future = futures.get(i);
+            Future<SensorOperation> future = futures.get(i);
             try {
-                ISensorOperation operation = getFutureResult(future, executionTimeNs);
+                SensorOperation operation = getFutureResult(future, executionTimeNs);
                 addSensorStats(STATS_TAG, i, operation.getStats());
             } catch (ExecutionException e) {
                 // extract the exception thrown by the worker thread
@@ -151,7 +150,7 @@
     @Override
     public ParallelSensorOperation clone() {
         ParallelSensorOperation operation = new ParallelSensorOperation();
-        for (ISensorOperation subOperation : mOperations) {
+        for (SensorOperation subOperation : mOperations) {
             operation.add(subOperation.clone());
         }
         return operation;
@@ -160,7 +159,7 @@
     /**
      * Helper method that waits for a {@link Future} to complete, and returns its result.
      */
-    private ISensorOperation getFutureResult(Future<ISensorOperation> future, Long timeoutNs)
+    private SensorOperation getFutureResult(Future<SensorOperation> future, Long timeoutNs)
             throws ExecutionException, TimeoutException, InterruptedException {
         if (timeoutNs == null) {
             return future.get();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
index 3d682fe..2e3af36 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
@@ -19,22 +19,22 @@
 import android.hardware.cts.helpers.SensorStats;
 
 /**
- * A {@link ISensorOperation} that executes a single {@link ISensorOperation} a given number of
- * times. This class can be combined to compose complex {@link ISensorOperation}s.
+ * A {@link SensorOperation} that executes a single {@link SensorOperation} a given number of
+ * times. This class can be combined to compose complex {@link SensorOperation}s.
  */
-public class RepeatingSensorOperation extends AbstractSensorOperation {
+public class RepeatingSensorOperation extends SensorOperation {
     public static final String STATS_TAG = "repeating";
 
-    private final ISensorOperation mOperation;
+    private final SensorOperation mOperation;
     private final int mIterations;
 
     /**
      * Constructor for {@link RepeatingSensorOperation}.
      *
-     * @param operation the {@link ISensorOperation} to run.
+     * @param operation the {@link SensorOperation} to run.
      * @param iterations the number of iterations to run the operation for.
      */
-    public RepeatingSensorOperation(ISensorOperation operation, int iterations) {
+    public RepeatingSensorOperation(SensorOperation operation, int iterations) {
         if (operation == null) {
             throw new IllegalArgumentException("Arguments cannot be null");
         }
@@ -44,13 +44,13 @@
     }
 
     /**
-     * Executes the {@link ISensorOperation}s the given number of times. If an exception occurs
-     * in one iterations, it is thrown and all subsequent iterations will not run.
+     * Executes the {@link SensorOperation}s the given number of times. If an exception occurs in
+     * one iterations, it is thrown and all subsequent iterations will not run.
      */
     @Override
     public void execute() throws InterruptedException {
         for(int i = 0; i < mIterations; ++i) {
-            ISensorOperation operation = mOperation.clone();
+            SensorOperation operation = mOperation.clone();
             try {
                 operation.execute();
             } catch (AssertionError e) {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
new file mode 100644
index 0000000..ea16716
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+/**
+ * Base class used by all sensor operations. This allows for complex operations such as chaining
+ * operations together or running operations in parallel.
+ * <p>
+ * Certain restrictions exist for {@link SensorOperation}s:
+ * <p><ul>
+ * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
+ * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
+ * synchronization for operations.</li>
+ * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
+ * the returned value is undefined.</li>
+ * <li>{@link #clone()} may be called any time and should return an operation with the same
+ * parameters as the original.</li>
+ * </ul>
+ */
+public abstract class SensorOperation {
+    private final SensorStats mStats;
+
+    protected SensorOperation() {
+        this(new SensorStats());
+    }
+
+    protected SensorOperation(SensorStats stats) {
+        mStats = stats;
+    }
+
+    /**
+     * @return The {@link SensorStats} for the operation.
+     */
+    public SensorStats getStats() {
+        return mStats;
+    }
+
+    /**
+     * Executes the sensor operation.
+     * This may throw {@link RuntimeException}s such as {@link AssertionError}s.
+     *
+     * NOTE: the operation is expected to handle interruption by:
+     * - cleaning up on {@link InterruptedException}
+     * - propagating the exception down the stack
+     */
+    public abstract void execute() throws InterruptedException;
+
+    /**
+     * @return The cloned {@link SensorOperation}.
+     *
+     * NOTE: The implementation should also clone all child operations, so that a cloned operation
+     * will run with the exact same parameters as the original. The stats should not be cloned.
+     */
+    public abstract SensorOperation clone();
+
+    /**
+     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
+     */
+    protected void addSensorStats(String key, SensorStats stats) {
+        getStats().addSensorStats(key, stats);
+    }
+
+    /**
+     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
+     * to be added. This is useful for {@link SensorOperation}s that have many iterations or child
+     * operations. The key added is in the form {@code key + "_" + index} where index may be zero
+     * padded.
+     */
+    protected void addSensorStats(String key, int index, SensorStats stats) {
+        addSensorStats(String.format("%s_%03d", key, index), stats);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
index bc48725..033f3c5 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
@@ -24,7 +24,7 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Tests for the primitive {@link ISensorOperation}s including {@link DelaySensorOperation},
+ * Tests for the primitive {@link SensorOperation}s including {@link DelaySensorOperation},
  * {@link ParallelSensorOperation}, {@link RepeatingSensorOperation} and
  * {@link SequentialSensorOperation}.
  */
@@ -38,7 +38,7 @@
     public void testFakeSensorOperation() throws InterruptedException {
         final int opDurationMs = 100;
 
-        ISensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
+        SensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
 
         assertFalse(op.getStats().flatten().containsKey("executed"));
         long start = System.currentTimeMillis();
@@ -65,12 +65,12 @@
         final int subOpDurationMs = 100;
 
         FakeSensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
-        ISensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
+        SensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
 
         long startMs = System.currentTimeMillis();
         op.execute();
-        long dirationMs = System.currentTimeMillis() - startMs;
-        long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - dirationMs);
+        long durationMs = System.currentTimeMillis() - startMs;
+        long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - durationMs);
         assertTrue(durationDeltaMs < TEST_DURATION_THRESHOLD_MS);
     }
 
@@ -83,7 +83,7 @@
 
         ParallelSensorOperation op = new ParallelSensorOperation();
         for (int i = 0; i < subOpCount; i++) {
-            ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+            SensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
                     TimeUnit.MILLISECONDS);
             op.add(subOp);
         }
@@ -124,7 +124,7 @@
         ParallelSensorOperation op = new ParallelSensorOperation();
         for (int i = 0; i < subOpCount; i++) {
             // Trigger failures in the 5th, 55th operations at t=5ms, t=55ms
-            ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
+            SensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
             op.add(subOp);
         }
 
@@ -164,7 +164,7 @@
         ParallelSensorOperation op = new ParallelSensorOperation(1, TimeUnit.SECONDS);
         for (int i = 0; i < subOpCount; i++) {
             // Trigger timeouts in the 5th, 55th operations (5 seconds vs 1 seconds)
-            ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
+            SensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
             op.add(subOp);
         }
 
@@ -196,8 +196,8 @@
         final int iterations = 10;
         final int subOpDurationMs = 100;
 
-        ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
-        ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+        SensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
+        SensorOperation op = new RepeatingSensorOperation(subOp, iterations);
 
         Set<String> statsKeys = op.getStats().flatten().keySet();
         assertEquals(0, statsKeys.size());
@@ -223,7 +223,7 @@
         final int iterations = 100;
         final int failCount = 75;
 
-        ISensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
+        SensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
             private int mExecutedCount = 0;
             private SensorStats mFakeStats = new SensorStats();
 
@@ -249,7 +249,7 @@
                 return mFakeStats;
             }
         };
-        ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+        SensorOperation op = new RepeatingSensorOperation(subOp, iterations);
 
         Set<String> statsKeys = op.getStats().flatten().keySet();
         assertEquals(0, statsKeys.size());
@@ -283,7 +283,7 @@
 
         SequentialSensorOperation op = new SequentialSensorOperation();
         for (int i = 0; i < subOpCount; i++) {
-            ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+            SensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
                     TimeUnit.MILLISECONDS);
             op.add(subOp);
         }
@@ -315,7 +315,7 @@
         SequentialSensorOperation op = new SequentialSensorOperation();
         for (int i = 0; i < subOpCount; i++) {
             // Trigger a failure in the 75th operation only
-            ISensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
+            SensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
                     TimeUnit.MILLISECONDS);
             op.add(subOp);
         }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
index 2ed0ca6..85d189a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
@@ -18,24 +18,23 @@
 
 import android.hardware.cts.helpers.SensorStats;
 
-import java.util.LinkedList;
-import java.util.List;
+import java.util.ArrayList;
 
 /**
- * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in a
+ * A {@link SensorOperation} that executes a set of children {@link SensorOperation}s in a
  * sequence. The children are executed in the order they are added. This class can be combined to
- * compose complex {@link ISensorOperation}s.
+ * compose complex {@link SensorOperation}s.
  */
-public class SequentialSensorOperation extends AbstractSensorOperation {
+public class SequentialSensorOperation extends SensorOperation {
     public static final String STATS_TAG = "sequential";
 
-    private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+    private final ArrayList<SensorOperation> mOperations = new ArrayList<>();
 
     /**
-     * Add a set of {@link ISensorOperation}s.
+     * Add a set of {@link SensorOperation}s.
      */
-    public void add(ISensorOperation ... operations) {
-        for (ISensorOperation operation : operations) {
+    public void add(SensorOperation ... operations) {
+        for (SensorOperation operation : operations) {
             if (operation == null) {
                 throw new IllegalArgumentException("Arguments cannot be null");
             }
@@ -44,13 +43,13 @@
     }
 
     /**
-     * Executes the {@link ISensorOperation}s in the order they were added. If an exception occurs
+     * Executes the {@link SensorOperation}s in the order they were added. If an exception occurs
      * in one operation, it is thrown and all subsequent operations will not run.
      */
     @Override
     public void execute() throws InterruptedException {
         for (int i = 0; i < mOperations.size(); i++) {
-            ISensorOperation operation = mOperations.get(i);
+            SensorOperation operation = mOperations.get(i);
             try {
                 operation.execute();
             } catch (AssertionError e) {
@@ -69,7 +68,7 @@
     @Override
     public SequentialSensorOperation clone() {
         SequentialSensorOperation operation = new SequentialSensorOperation();
-        for (ISensorOperation subOperation : mOperations) {
+        for (SensorOperation subOperation : mOperations) {
             operation.add(subOperation.clone());
         }
         return operation;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 6e53dbb..ded1522 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -35,21 +35,19 @@
 import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
 import android.os.Handler;
 
-import java.util.Collection;
 import java.util.HashSet;
 import java.util.concurrent.TimeUnit;
 
 /**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
+ * A {@link SensorOperation} used to verify that sensor events and sensor values are correct.
  * <p>
  * Provides methods to set test expectations as well as providing a set of default expectations
  * depending on sensor type.  When {{@link #execute()} is called, the sensor will collect the
  * events and then run all the tests.
  * </p>
  */
-public class TestSensorOperation extends AbstractSensorOperation {
-    private final Collection<ISensorVerification> mVerifications =
-            new HashSet<ISensorVerification>();
+public class TestSensorOperation extends SensorOperation {
+    private final HashSet<ISensorVerification> mVerifications = new HashSet<>();
 
     private final TestSensorManager mSensorManager;
     private final TestSensorEnvironment mEnvironment;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
index b500ea7..5f4f5d8 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
@@ -22,36 +22,36 @@
 import android.os.PowerManager.WakeLock;
 
 /**
- * An {@link ISensorOperation} which holds a wakelock while performing another
- * {@link ISensorOperation}.
+ * An {@link SensorOperation} which holds a wake-lock while performing another
+ * {@link SensorOperation}.
  */
-public class WakeLockOperation extends AbstractSensorOperation {
+public class WakeLockOperation extends SensorOperation {
     private static final String TAG = "WakeLockOperation";
 
-    private final ISensorOperation mOperation;
+    private final SensorOperation mOperation;
     private final Context mContext;
-    private final int mWakelockFlags;
+    private final int mWakeLockFlags;
 
     /**
      * Constructor for {@link WakeLockOperation}.
      *
-     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param operation the child {@link SensorOperation} to perform after the delay
      * @param context the context used to access the power manager
-     * @param wakelockFlags the flags used when acquiring the wakelock
+     * @param wakeLockFlags the flags used when acquiring the wake-lock
      */
-    public WakeLockOperation(ISensorOperation operation, Context context, int wakelockFlags) {
+    public WakeLockOperation(SensorOperation operation, Context context, int wakeLockFlags) {
         mOperation = operation;
         mContext = context;
-        mWakelockFlags = wakelockFlags;
+        mWakeLockFlags = wakeLockFlags;
     }
 
     /**
      * Constructor for {@link WakeLockOperation}.
      *
-     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param operation the child {@link SensorOperation} to perform after the delay
      * @param context the context used to access the power manager
      */
-    public WakeLockOperation(ISensorOperation operation, Context context) {
+    public WakeLockOperation(SensorOperation operation, Context context) {
         this(operation, context, PowerManager.PARTIAL_WAKE_LOCK);
     }
 
@@ -61,7 +61,7 @@
     @Override
     public void execute() throws InterruptedException {
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        WakeLock wakeLock = pm.newWakeLock(mWakelockFlags, TAG);
+        WakeLock wakeLock = pm.newWakeLock(mWakeLockFlags, TAG);
 
         wakeLock.acquire();
         try {
@@ -83,7 +83,7 @@
      * {@inheritDoc}
      */
     @Override
-    public ISensorOperation clone() {
-        return new WakeLockOperation(mOperation, mContext, mWakelockFlags);
+    public SensorOperation clone() {
+        return new WakeLockOperation(mOperation, mContext, mWakeLockFlags);
     }
 }
diff --git a/tests/tests/media/src/android/media/cts/PostProcTestBase.java b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
index b034763..c31693e 100644
--- a/tests/tests/media/src/android/media/cts/PostProcTestBase.java
+++ b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
@@ -16,7 +16,9 @@
 
 package android.media.cts;
 
+import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.AudioManager;
 import android.media.audiofx.AudioEffect;
 import android.os.Looper;
 import android.test.AndroidTestCase;
@@ -62,4 +64,13 @@
         }
         return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_ENV_REVERB);
     }
+
+    protected int getSessionId() {
+        AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+        assertNotNull("could not get AudioManager", am);
+        int sessionId = am.generateAudioSessionId();
+        assertTrue("Could not generate session id", sessionId>0);
+        return sessionId;
+    }
+
 }
diff --git a/tests/tests/media/src/android/media/cts/VirtualizerTest.java b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
index 51adf1d..ac8eb84 100644
--- a/tests/tests/media/src/android/media/cts/VirtualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
@@ -16,14 +16,18 @@
 
 package android.media.cts;
 
+import android.content.Context;
 import android.media.audiofx.AudioEffect;
 import android.media.AudioFormat;
-import android.media.AudioManager;
 import android.media.audiofx.Virtualizer;
 import android.os.Looper;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.cts.media.R;
+
+import java.util.Arrays;
+
 public class VirtualizerTest extends PostProcTestBase {
 
     private String TAG = "VirtualizerTest";
@@ -288,6 +292,256 @@
     }
 
     //-----------------------------------------------------------------
+    // 4 virtualizer capabilities
+    //----------------------------------
+
+    //Test case 4.0: test virtualization format / mode query: at least one of the following
+    //   combinations must be supported, otherwise the effect doesn't really qualify as
+    //   a virtualizer: AudioFormat.CHANNEL_OUT_STEREO or the quad and 5.1 side/back variants,
+    //   in VIRTUALIZATION_MODE_BINAURAL or VIRTUALIZATION_MODE_TRANSAURAL
+    public void test4_0FormatModeQuery() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            boolean isAtLeastOneConfigSupported = false;
+            boolean isConfigSupported = false;
+
+            // testing combinations of input channel mask and virtualization mode
+            for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+                for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+                    isConfigSupported = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m]);
+                    isAtLeastOneConfigSupported |= isConfigSupported;
+                    // optional logging
+                    String channelMask = Integer.toHexString(CHANNEL_MASKS[i]).toUpperCase();
+                    String nativeChannelMask =
+                            Integer.toHexString(CHANNEL_MASKS[i] >> 2).toUpperCase();
+                    Log.d(TAG, "content channel mask: 0x" + channelMask + " (native 0x"
+                            + nativeChannelMask
+                            + ") mode: " + VIRTUALIZATION_MODES[m]
+                            + " supported=" + isConfigSupported);
+                }
+            }
+
+            assertTrue("no valid configuration supported", isAtLeastOneConfigSupported);
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.1: test that the capabilities reported by Virtualizer.canVirtualize(int,int)
+    //   matches those returned by Virtualizer.getSpeakerAngles(int, int, int[])
+    public void test4_1SpeakerAnglesCapaMatchesFormatModeCapa() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            // 3: see size requirement in Virtualizer.getSpeakerAngles(int, int, int[])
+            // 6: for number of channels of 5.1 masks in CHANNEL_MASKS
+            int[] angles = new int[3*6];
+            for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+                for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+                    Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+                    boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m]);
+                    boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m], angles);
+                    assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+                            canVirtualize == canGetAngles);
+                    if(canGetAngles) {
+                        //check if the number of angles matched the expected number of channels for
+                        //each CHANNEL_MASKS
+                        int expectedChannelCount = CHANNEL_MASKS_CHANNEL_COUNT[i];
+                        for(int k=0; k<expectedChannelCount; k++) {
+                            int speakerIdentification = angles[k*3];
+                            assertTrue("found unexpected speaker identification or channel count",
+                                    speakerIdentification !=AudioFormat.CHANNEL_INVALID );
+                        }
+                    }
+                }
+            }
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.2: test forcing virtualization mode: at least binaural or transaural must be
+    //   supported
+    public void test4_2VirtualizationMode() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            mVirtualizer.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+            // testing binaural
+            boolean binauralSupported =
+                    mVirtualizer.forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_BINAURAL);
+            // testing transaural
+            boolean transauralSupported = mVirtualizer
+                    .forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_TRANSAURAL);
+            // testing at least one supported
+            assertTrue("doesn't support binaural nor transaural",
+                    binauralSupported || transauralSupported);
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.3: test disabling virtualization maps to VIRTUALIZATION_MODE_OFF
+    public void test4_3DisablingVirtualizationOff() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            mVirtualizer.setEnabled(false);
+            assertFalse("invalid state from getEnabled", mVirtualizer.getEnabled());
+            int virtMode = mVirtualizer.getVirtualizationMode();
+            assertTrue("disabled virtualization isn't reported as OFF",
+                    virtMode == Virtualizer.VIRTUALIZATION_MODE_OFF);
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.4: test forcing virtualization mode to AUTO
+    public void test4_4VirtualizationModeAuto() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            mVirtualizer.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+            boolean forceRes =
+                    mVirtualizer.forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_AUTO);
+            assertTrue("can't set virtualization to AUTO", forceRes);
+
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //Test case 4.5: test for consistent capabilities if virtualizer is enabled or disabled
+    public void test4_5ConsistentCapabilitiesWithEnabledDisabled() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+        getVirtualizer(getSessionId());
+        try {
+            // 3: see size requirement in Virtualizer.getSpeakerAngles(int, int, int[])
+            // 6: for number of channels of 5.1 masks in CHANNEL_MASKS
+            int[] angles = new int[3*6];
+            boolean[][] values = new boolean[VIRTUALIZATION_MODES.length][CHANNEL_MASKS.length];
+            mVirtualizer.setEnabled(true);
+            assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+            for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+                for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+                    Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+                    boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m]);
+                    boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m], angles);
+                    assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+                            canVirtualize == canGetAngles);
+                    values[m][i] = canVirtualize;
+                }
+            }
+
+            mVirtualizer.setEnabled(false);
+            assertTrue("invalid state from getEnabled", !mVirtualizer.getEnabled());
+            for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+                for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+                    Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+                    boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m]);
+                    boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+                            VIRTUALIZATION_MODES[m], angles);
+                    assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+                            canVirtualize == canGetAngles);
+                    assertTrue("mismatch capability between enabled and disabled virtualizer",
+                            canVirtualize == values[m][i]);
+                }
+            }
+
+        } catch (IllegalArgumentException e) {
+            fail("bad parameter value");
+        } catch (UnsupportedOperationException e) {
+            fail("command not supported");
+        } catch (IllegalStateException e) {
+            fail("command called in wrong state");
+        } finally {
+            releaseVirtualizer();
+        }
+    }
+
+    //-----------------------------------------------------------------
+    // private data
+    //----------------------------------
+    // channel masks to test at input of virtualizer
+    private static final int[] CHANNEL_MASKS = {
+            AudioFormat.CHANNEL_OUT_STEREO, //2 channels
+            AudioFormat.CHANNEL_OUT_QUAD, //4 channels
+            //AudioFormat.CHANNEL_OUT_QUAD_SIDE (definition is not public)
+            (AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
+                    AudioFormat.CHANNEL_OUT_SIDE_LEFT | AudioFormat.CHANNEL_OUT_SIDE_RIGHT), //4 channels
+            AudioFormat.CHANNEL_OUT_5POINT1, //6 channels
+            //AudioFormat.CHANNEL_OUT_5POINT1_SIDE (definition is not public)
+            (AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
+                    AudioFormat.CHANNEL_OUT_FRONT_CENTER |
+                    AudioFormat.CHANNEL_OUT_LOW_FREQUENCY |
+                    AudioFormat.CHANNEL_OUT_SIDE_LEFT | AudioFormat.CHANNEL_OUT_SIDE_RIGHT) //6 channels
+    };
+
+    private static final int[] CHANNEL_MASKS_CHANNEL_COUNT = {
+        2,
+        4,
+        4,
+        6,
+        6
+    };
+
+    private static final int[] VIRTUALIZATION_MODES = {
+            Virtualizer.VIRTUALIZATION_MODE_BINAURAL,
+            Virtualizer.VIRTUALIZATION_MODE_TRANSAURAL
+    };
+
+    //-----------------------------------------------------------------
     // private methods
     //----------------------------------
 
@@ -362,7 +616,6 @@
                 mLooper = Looper.myLooper();
 
                 mVirtualizer2 = new Virtualizer(0, 0);
-                assertNotNull("could not create virtualizer2", mVirtualizer2);
 
                 synchronized(mLock) {
                     if (mControl) {
@@ -436,4 +689,4 @@
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 4c92da7..3002ca3 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -28,7 +28,8 @@
 public class BuildVersionTest extends TestCase {
 
     private static final String LOG_TAG = "BuildVersionTest";
-    private static final Set<String> EXPECTED_RELEASES = new HashSet<String>(Arrays.asList("5.0.1"));
+    private static final Set<String> EXPECTED_RELEASES =
+            new HashSet<String>(Arrays.asList("5.0.1", "5.0.2"));
     private static final int EXPECTED_SDK = 21;
     private static final String EXPECTED_BUILD_VARIANT = "user";
     private static final String EXPECTED_TAG = "release-keys";
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
index 1eb4acb..62c268d 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
@@ -68,4 +68,5 @@
     public static final String SERIAL_NUMBER = "deviceID";
     public static final String STORAGE_DEVICES = "storage_devices";
     public static final String MULTI_USER = "multi_user";
+    public static final String ENCRYPTED = "encrypted";
 }
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
index 19349e5..52ddfe9 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
@@ -149,6 +149,9 @@
         // Multi-user support
         addResult(MULTI_USER, getMultiUserInfo());
 
+        // Encrypted
+        addResult(ENCRYPTED, getEncrypted());
+
         finish(Activity.RESULT_OK, mResults);
     }
 
@@ -394,4 +397,29 @@
 
         return "unknown";
     }
+
+    private static String getProperty(String property)
+            throws IOException {
+        Process process = new ProcessBuilder("getprop", property).start();
+        Scanner scanner = null;
+        String line = "";
+        try {
+            scanner = new Scanner(process.getInputStream());
+            line = scanner.nextLine();
+        } finally {
+            if (scanner != null) {
+                scanner.close();
+            }
+        }
+        return line;
+    }
+
+    private int getEncrypted() {
+        try {
+            return "encrypted".equals(getProperty("ro.crypto.state")) ? 1 : 0;
+        } catch (IOException e) {
+        }
+
+        return 0;
+    }
 }