Merge "Fix for CTS LoudnessEnhancer test using Visualizer" into mnc-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 4e1e008..9021921 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -62,7 +62,9 @@
     CtsKeySetSharedUserSigningBUpgradeB \
     CtsKeySetSigningABadUpgradeB \
     CtsKeySetSigningCBadAUpgradeAB \
-    CtsKeySetSigningANoDefUpgradeB
+    CtsKeySetSigningANoDefUpgradeB \
+    CtsKeySetSigningAUpgradeEcA \
+    CtsKeySetSigningEcAUpgradeA
 
 cts_support_packages := \
     CtsAccelerationTestStubs \
@@ -165,6 +167,7 @@
     CtsSignatureTestCases \
     CtsSpeechTestCases \
     CtsTelecomTestCases \
+    CtsTelecomTestCases2 \
     CtsTelephonyTestCases \
     CtsTextTestCases \
     CtsTextureViewTestCases \
diff --git a/apps/CameraITS/pymodules/its/caps.py b/apps/CameraITS/pymodules/its/caps.py
index 70bb2ca..b6d398f 100644
--- a/apps/CameraITS/pymodules/its/caps.py
+++ b/apps/CameraITS/pymodules/its/caps.py
@@ -283,6 +283,30 @@
             "android.shading.availableModes") and \
         0 in props["android.shading.availableModes"]
 
+def yuv_reprocess(props):
+    """Returns whether a device supports YUV reprocessing.
+
+    Args:
+        props: Camera properties object.
+
+    Returns:
+        Boolean.
+    """
+    return props.has_key("android.request.availableCapabilities") and \
+           7 in props["android.request.availableCapabilities"]
+
+def private_reprocess(props):
+    """Returns whether a device supports PRIVATE reprocessing.
+
+    Args:
+        props: Camera properties object.
+
+    Returns:
+        Boolean.
+    """
+    return props.has_key("android.request.availableCapabilities") and \
+           4 in props["android.request.availableCapabilities"]
+
 class __UnitTest(unittest.TestCase):
     """Run a suite of unit tests on this module.
     """
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index e396483..8773335 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -359,7 +359,7 @@
             raise its.error.Error('3A failed to converge')
         return ae_sens, ae_exp, awb_gains, awb_transform, af_dist
 
-    def do_capture(self, cap_request, out_surfaces=None):
+    def do_capture(self, cap_request, out_surfaces=None, reprocess_format=None):
         """Issue capture request(s), and read back the image(s) and metadata.
 
         The main top-level function for capturing one or more images using the
@@ -378,6 +378,18 @@
         surface. At most one output surface can be specified for a given format,
         and raw+dng, raw10+dng, and raw+raw10 are not supported as combinations.
 
+        If reprocess_format is not None, for each request, an intermediate
+        buffer of the given reprocess_format will be captured from camera and
+        the intermediate buffer will be reprocessed to the output surfaces. The
+        following settings will be turned off when capturing the intermediate
+        buffer and will be applied when reprocessing the intermediate buffer.
+            1. android.noiseReduction.mode
+            2. android.edge.mode
+            3. android.reprocess.effectiveExposureFactor
+
+        Supported reprocess format are "yuv" and "private". Supported output
+        surface formats when reprocessing is enabled are "yuv" and "jpeg".
+
         Example of a single capture request:
 
             {
@@ -449,6 +461,8 @@
                 will be converted to JSON and sent to the device.
             out_surfaces: (Optional) specifications of the output image formats
                 and sizes to use for each capture.
+            reprocess_format: (Optional) The reprocessing format. If not None,
+                reprocessing will be enabled.
 
         Returns:
             An object, list of objects, or list of lists of objects, where each
@@ -460,7 +474,11 @@
             * metadata: the capture result object (Python dictionary).
         """
         cmd = {}
-        cmd["cmdName"] = "doCapture"
+        if reprocess_format != None:
+            cmd["cmdName"] = "doReprocessCapture"
+            cmd["reprocessFormat"] = reprocess_format
+        else:
+            cmd["cmdName"] = "doCapture"
         if not isinstance(cap_request, list):
             cmd["captureRequests"] = [cap_request]
         else:
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
index 9b43a74..5fd8f73 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
@@ -41,10 +41,12 @@
         range_max = ev_compensation_range[1]
         ev_per_step = its.objects.rational_to_float(
                 props['android.control.aeCompensationStep'])
-        steps_per_ev = int(1.0 / ev_per_step)
-        evs = range(range_min, range_max + 1, steps_per_ev)
+        steps_per_ev = int(round(1.0 / ev_per_step))
+        ev_steps = range(range_min, range_max + 1, steps_per_ev)
+        imid = len(ev_steps) / 2
+        ev_shifts = [pow(2, step * ev_per_step) for step in ev_steps]
         lumas = []
-        for ev in evs:
+        for ev in ev_steps:
             # Re-converge 3A, and lock AE once converged. skip AF trigger as
             # dark/bright scene could make AF convergence fail and this test
             # doesn't care the image sharpness.
@@ -65,19 +67,16 @@
             tile = its.image.get_image_patch(y, 0.45,0.45,0.1,0.1)
             lumas.append(its.image.compute_image_means(tile)[0])
 
-        luma_increase_per_step = pow(2, ev_per_step)
         print "ev_step_size_in_stops", ev_per_step
-        imid = len(lumas) / 2
-        expected_lumas = [lumas[imid] / pow(luma_increase_per_step, i)
-                          for i in range(imid , 0, -1)]  + \
-                         [lumas[imid] * pow(luma_increase_per_step, i-imid)
-                          for i in range(imid, len(evs))]
+        shift_mid = ev_shifts[imid]
+        luma_normal = lumas[imid] / shift_mid
+        expected_lumas = [luma_normal * ev_shift for ev_shift in ev_shifts]
 
-        pylab.plot(evs, lumas, 'r')
-        pylab.plot(evs, expected_lumas, 'b')
+        pylab.plot(ev_steps, lumas, 'r')
+        pylab.plot(ev_steps, expected_lumas, 'b')
         matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
 
-        luma_diffs = [expected_lumas[i] - lumas[i] for i in range(len(evs))]
+        luma_diffs = [expected_lumas[i] - lumas[i] for i in range(len(ev_steps))]
         max_diff = max(abs(i) for i in luma_diffs)
         avg_diff = abs(numpy.array(luma_diffs)).mean()
         print "Max delta between modeled and measured lumas:", max_diff
diff --git a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
new file mode 100644
index 0000000..e9240ba
--- /dev/null
+++ b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
@@ -0,0 +1,112 @@
+# Copyright 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.
+
+import its.image
+import its.caps
+import its.device
+import its.objects
+import its.target
+import math
+import matplotlib
+import matplotlib.pyplot
+import os.path
+import pylab
+
+def main():
+    """Test that the android.noiseReduction.mode param is applied when set for
+       reprocessing requests.
+
+    Capture reprocessed images with the camera dimly lit. Uses a high analog
+    gain to ensure the captured image is noisy.
+
+    Captures three reprocessed images, for NR off, "fast", and "high quality".
+    Also captures a reprocessed image with low gain and NR off, and uses the
+    variance of this as the baseline.
+    """
+
+    NAME = os.path.basename(__file__).split(".")[0]
+
+    with its.device.ItsSession() as cam:
+        props = cam.get_camera_properties()
+
+        its.caps.skip_unless(its.caps.compute_target_exposure(props) and
+                             its.caps.per_frame_control(props) and
+                             (its.caps.yuv_reprocess(props) or
+                              its.caps.private_reprocess(props)))
+
+        reprocess_formats = []
+        if (its.caps.yuv_reprocess(props)):
+            reprocess_formats.append("yuv")
+        if (its.caps.private_reprocess(props)):
+            reprocess_formats.append("private")
+
+        for reprocess_format in reprocess_formats:
+            # List of variances for R, G, B.
+            variances = []
+            nr_modes_reported = []
+
+            # NR mode 0 with low gain
+            e, s = its.target.get_target_exposure_combos(cam)["minSensitivity"]
+            req = its.objects.manual_capture_request(s, e)
+            req["android.noiseReduction.mode"] = 0
+
+            # Test reprocess_format->JPEG reprocessing
+            # TODO: Switch to reprocess_format->YUV when YUV reprocessing is
+            #       supported.
+            size = its.objects.get_available_output_sizes("jpg", props)[0]
+            out_surface = {"width":size[0], "height":size[1], "format":"jpg"}
+            cap = cam.do_capture(req, out_surface, reprocess_format)
+            img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+            its.image.write_image(img, "%s_low_gain_fmt=jpg.jpg" % (NAME))
+            tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+            ref_variance = its.image.compute_image_variances(tile)
+            print "Ref variances:", ref_variance
+
+            for nr_mode in range(3):
+                # NR modes 0, 1, 2 with high gain
+                e, s = its.target.get_target_exposure_combos(cam) \
+                    ["maxSensitivity"]
+                req = its.objects.manual_capture_request(s, e)
+                req["android.noiseReduction.mode"] = nr_mode
+                cap = cam.do_capture(req, out_surface, reprocess_format)
+                nr_modes_reported.append(
+                    cap["metadata"]["android.noiseReduction.mode"])
+
+                img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+                its.image.write_image(
+                    img, "%s_high_gain_nr=%d_fmt=jpg.jpg" % (NAME, nr_mode))
+                tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+                # Get the variances for R, G, and B channels
+                variance = its.image.compute_image_variances(tile)
+                variances.append(
+                    [variance[chan] / ref_variance[chan] for chan in range(3)])
+            print "Variances with NR mode [0,1,2]:", variances
+
+            # Draw a plot.
+            for nr_mode in range(3):
+                pylab.plot(range(3), variances[nr_mode], "rgb"[nr_mode])
+            matplotlib.pyplot.savefig("%s_plot_%s_variances.png" %
+                                      (NAME, reprocess_format))
+
+            assert(nr_modes_reported == [0,1,2])
+
+            # Check that the variance of the NR=0 image is higher than for the
+            # NR=1 and NR=2 images.
+            for j in range(3):
+                for i in range(1,3):
+                    assert(variances[i][j] < variances[0][j])
+
+if __name__ == '__main__':
+    main()
+
diff --git a/apps/CtsVerifier/res/layout/hifi_ultrasound_popup_instru.xml b/apps/CtsVerifier/res/layout/hifi_ultrasound_popup_instru.xml
new file mode 100644
index 0000000..42af6e9
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/hifi_ultrasound_popup_instru.xml
@@ -0,0 +1,21 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:gravity="center"
+              android:background="@android:color/black"
+              android:padding="5dp"
+              android:orientation="vertical" >
+
+  <TextView
+      android:id="@+id/instru"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:layout_weight="5" />
+
+  <Button
+      android:id="@+id/ok"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:text="@string/hifi_ultrasound_test_ok"
+      android:layout_weight="1" />
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index fa4203d..7e67e9f 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -287,46 +287,53 @@
     <string name="empty"></string>
 
     <!-- Strings for HifiUltrasoundTestActivity -->
-    <string name="hifi_ultrasound_test">Hifi Ultrasound Test (microphone)</string>
+    <string name="hifi_ultrasound_test">Hifi Ultrasound Microphone Test</string>
     <string name="hifi_ultrasound_test_info">
-        This is a test for microphone near-ultrasound (18500Hz - 20000Hz) response.\n
+        This is a test for near-ultrasound (18500Hz - 20000Hz) microphone response.\n
         This test requires two devices.\n</string>
     <string name="hifi_ultrasound_test_play">PLAY</string>
     <string name="hifi_ultrasound_test_record">RECORD</string>
     <string name="hifi_ultrasound_test_plot">PLOT</string>
     <string name="hifi_ultrasound_test_dismiss">DISMISS</string>
+    <string name="hifi_ultrasound_test_ok">OK</string>
     <string name="hifi_ultrasound_test_instruction1">
-        Set the volume of the reference device at 70% and hold it with one hand.\n
+        Open Hifi Ultrasound Microphone Test on the test device and the reference device.\n
+        Set the media volume of the reference device at 70% and hold it with one hand.\n
         Hold the testing device with the other hand\n
         Press the RECORD button on the testing device, then the PLAY button on the reference device within one second.\n
-        After the test, report result on the testing device.\n</string>
+        After the test, report result on the testing (recording) device.\n</string>
     <string name="hifi_ultrasound_test_pass">PASS</string>
     <string name="hifi_ultrasound_test_fail">FAIL</string>
     <string name="hifi_ultrasound_test_default_false_string">false</string>
-    <string name="hifi_ultrasound_test_mic_prop">persist.audio.mic.ultrasound</string>
-    <string name="hifi_ultrasound_test_spkr_prop">persist.audio.spkr.ultrasound</string>
     <string name="hifi_ultrasound_test_mic_no_support">
         Device does not support near-ultrasound recording.\n
-        Please click pass if this is the testing device.\n</string>
+        All new phones and tablets MUST support near-ultrasound recording.\n
+        Report FAIL if this is a new device, report PASS if this is an updating device.\n</string>
     <string name="hifi_ultrasound_test_spkr_no_support">
         Device does not support near-ultrasound playback.\n
         If this is your reference device, please use a different reference device.\n</string>
 
-    <string name="hifi_ultrasound_speaker_test">Hifi Ultrasound Test (speaker)</string>
+    <string name="hifi_ultrasound_speaker_test">Hifi Ultrasound Speaker Test</string>
     <string name="hifi_ultrasound_speaker_test_info">
-        This is a test for speaker near-ultrasound (18500Hz - 20000Hz) response.\n
+        This is a test for near-ultrasound (18500Hz - 20000Hz) speaker response.\n
         This test requires two devices.\n</string>
     <string name="hifi_ultrasound_speaker_test_instruction1">
-        Set the volume of the testing device at 70% and hold it with one hand.\n
+        Open Hifi Ultrasound Speaker Test on the test device and the reference device.\n
+        Set the media volume of the testing device at 70% and hold it with one hand.\n
         Hold the reference device with the other hand\n
         Press the RECORD button on the reference device, then the PLAY button on the testing device within one second.\n
-        After the test, report result on the testing device.\n</string>
+        After the test, report result on the testing (playback) device.\n</string>
     <string name="hifi_ultrasound_speaker_test_mic_no_support">
         Device does not support near-ultrasound recording.\n
         If this is your reference device, please use a different reference device.\n</string>
     <string name="hifi_ultrasound_speaker_test_spkr_no_support">
         Device does not support near-ultrasound playback.\n
-        Please click pass if this is the testing device.\n</string>
+        All new phones and tablets MUST support near-ultrasound playback.\n
+        Report FAIL if this is a new device, report PASS if this is an updating device.\n</string>
+    <string name="hifi_ultrasound_speaker_test_test_side">
+        Please wait for the result on the reference device then report here.</string>
+    <string name="hifi_ultrasound_speaker_test_reference_side">
+        Please report on the testing device.\n</string>
 
     <!-- Strings for Location tests -->
     <string name="location_gps_test">GPS Test</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
index fa5ad81..dc81e19 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java
@@ -43,272 +43,310 @@
 
 public class HifiUltrasoundSpeakerTestActivity extends PassFailButtons.Activity {
 
-    public enum Status {
-        START, RECORDING, DONE, PLAYER
-    }
+  public enum Status {
+    START, RECORDING, DONE, PLAYER
+  }
 
-    private static final String TAG = "HifiUltrasoundTestActivity";
+  private static final String TAG = "HifiUltrasoundTestActivity";
 
-    private Status status = Status.START;
-    private boolean onPlotScreen = false;
-    private TextView info;
-    private Button playerButton;
-    private Button recorderButton;
-    private AudioTrack audioTrack;
-    private LayoutInflater layoutInflater;
-    private View popupView;
-    private PopupWindow popupWindow;
-    private boolean micSupport = true;
-    private boolean spkrSupport = true;
+  private Status status = Status.START;
+  private boolean onPlotScreen = false;
+  private boolean onInstruScreen = false;
+  private TextView info;
+  private Button playerButton;
+  private Button recorderButton;
+  private AudioTrack audioTrack;
+  private LayoutInflater layoutInflater;
+  private View popupView;
+  private View instruView;
+  private PopupWindow popupWindow;
+  private PopupWindow instruWindow;
+  private boolean micSupport = true;
+  private boolean spkrSupport = true;
 
-    @Override
-    public void onBackPressed () {
-        if (onPlotScreen) {
-            popupWindow.dismiss();
-            onPlotScreen = false;
-            recorderButton.setEnabled(true);
-        } else {
-            super.onBackPressed();
-        }
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.hifi_ultrasound);
-        setInfoResources(R.string.hifi_ultrasound_speaker_test,
-            R.string.hifi_ultrasound_speaker_test_info, -1);
-        setPassFailButtonClickListeners();
-        getPassButton().setEnabled(false);
-
-        info = (TextView) findViewById(R.id.info_text);
-        info.setMovementMethod(new ScrollingMovementMethod());
-        info.setText(R.string.hifi_ultrasound_speaker_test_instruction1);
-
-        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
-        String micSupportString = audioManager.getProperty(
-            getResources().getString(R.string.hifi_ultrasound_test_mic_prop));
-        String spkrSupportString = audioManager.getProperty(
-            getResources().getString(R.string.hifi_ultrasound_test_spkr_prop));
-
-        if (micSupportString == null) {
-          micSupportString = "null";
-        }
-        if (spkrSupportString == null) {
-          spkrSupportString = "null";
-        }
-        if (micSupportString.equalsIgnoreCase(getResources().getString(
-            R.string.hifi_ultrasound_test_default_false_string))) {
-          micSupport = false;
-          getPassButton().setEnabled(true);
-          info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_mic_no_support));
-        }
-        if (spkrSupportString.equalsIgnoreCase(getResources().getString(
-            R.string.hifi_ultrasound_test_default_false_string))) {
-          spkrSupport = false;
-          info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_spkr_no_support));
-        }
-
-        layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
-            LAYOUT_INFLATER_SERVICE);
-        popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
-        popupWindow = new PopupWindow(
-            popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-
-        final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
-        final int recordRate = audioRecorder.getSampleRate();
-
-        recorderButton = (Button) findViewById(R.id.recorder_button);
+  @Override
+  public void onBackPressed () {
+    if (onPlotScreen) {
+      popupWindow.dismiss();
+      onPlotScreen = false;
+      recorderButton.setEnabled(true);
+    } else if (onInstruScreen) {
+      instruWindow.dismiss();
+      onInstruScreen = false;
+      if (status == Status.PLAYER) {
+        playerButton.setEnabled(spkrSupport);
+      } else {
         recorderButton.setEnabled(micSupport);
-        recorderButton.setOnClickListener(new View.OnClickListener() {
-          private WavAnalyzerTask wavAnalyzerTask = null;
-          private void stopRecording() {
-            audioRecorder.stop();
-            wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
-            wavAnalyzerTask.execute();
-            status = Status.DONE;
-          }
-          @Override
-          public void onClick(View v) {
-            switch (status) {
-              case START:
-                info.append("Recording at " + recordRate + "Hz using ");
-                final int source = audioRecorder.getAudioSource();
-                switch (source) {
-                  case 1:
-                    info.append("MIC");
-                    break;
-                  case 6:
-                    info.append("VOICE_RECOGNITION");
-                    break;
-                  default:
-                    info.append("UNEXPECTED " + source);
-                    break;
-                }
-                info.append("\n");
-                status = Status.RECORDING;
-                playerButton.setEnabled(false);
-                recorderButton.setEnabled(false);
-                audioRecorder.start();
+      }
+      if (status == Status.PLAYER) {
+        getPassButton().setEnabled(true);
+      }
+    } else {
+      super.onBackPressed();
+    }
+  }
 
-                final View finalV = v;
-                new Thread() {
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.hifi_ultrasound);
+    setInfoResources(R.string.hifi_ultrasound_speaker_test,
+        R.string.hifi_ultrasound_speaker_test_info, -1);
+    setPassFailButtonClickListeners();
+    getPassButton().setEnabled(false);
+
+    info = (TextView) findViewById(R.id.info_text);
+    info.setMovementMethod(new ScrollingMovementMethod());
+    info.setText(R.string.hifi_ultrasound_speaker_test_instruction1);
+
+    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+    String micSupportString = audioManager.getProperty(
+        AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND);
+    String spkrSupportString = audioManager.getProperty(
+        AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND);
+    Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupportString);
+    Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupportString);
+
+    if (micSupportString == null) {
+      micSupportString = "null";
+    }
+    if (spkrSupportString == null) {
+      spkrSupportString = "null";
+    }
+    if (micSupportString.equalsIgnoreCase(getResources().getString(
+        R.string.hifi_ultrasound_test_default_false_string))) {
+      micSupport = false;
+      getPassButton().setEnabled(true);
+      info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_mic_no_support));
+    }
+    if (spkrSupportString.equalsIgnoreCase(getResources().getString(
+        R.string.hifi_ultrasound_test_default_false_string))) {
+      spkrSupport = false;
+      info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_spkr_no_support));
+    }
+
+    layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
+        LAYOUT_INFLATER_SERVICE);
+    popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
+    popupWindow = new PopupWindow(
+        popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+    instruView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup_instru, null);
+    instruWindow = new PopupWindow(
+        instruView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+
+    final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
+    final int recordRate = audioRecorder.getSampleRate();
+
+    recorderButton = (Button) findViewById(R.id.recorder_button);
+    recorderButton.setEnabled(micSupport);
+    recorderButton.setOnClickListener(new View.OnClickListener() {
+      private WavAnalyzerTask wavAnalyzerTask = null;
+      private void stopRecording() {
+        audioRecorder.stop();
+        wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
+        wavAnalyzerTask.execute();
+        status = Status.DONE;
+      }
+      @Override
+      public void onClick(View v) {
+        switch (status) {
+          case START:
+            info.append("Recording at " + recordRate + "Hz using ");
+            final int source = audioRecorder.getAudioSource();
+            switch (source) {
+              case 1:
+                info.append("MIC");
+                break;
+              case 6:
+                info.append("VOICE_RECOGNITION");
+                break;
+              default:
+                info.append("UNEXPECTED " + source);
+                break;
+            }
+            info.append("\n");
+            status = Status.RECORDING;
+            playerButton.setEnabled(false);
+            recorderButton.setEnabled(false);
+            audioRecorder.start();
+
+            final View finalV = v;
+            new Thread() {
+              @Override
+              public void run() {
+                Double recordingDuration_millis = new Double(1000 * (2.5
+                    + Common.PREFIX_LENGTH_S
+                    + Common.PAUSE_BEFORE_PREFIX_DURATION_S
+                    + Common.PAUSE_AFTER_PREFIX_DURATION_S
+                    + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
+                    * Common.REPETITIONS));
+                Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
+                try {
+                  Thread.sleep(recordingDuration_millis.intValue());
+                } catch (InterruptedException e) {
+                  throw new RuntimeException(e);
+                }
+                runOnUiThread(new Runnable() {
                   @Override
                   public void run() {
-                    Double recordingDuration_millis = new Double(1000 * (2.5
-                          + Common.PREFIX_LENGTH_S
-                          + Common.PAUSE_BEFORE_PREFIX_DURATION_S
-                          + Common.PAUSE_AFTER_PREFIX_DURATION_S
-                          + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
-                          * Common.REPETITIONS));
-                    Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
-                    try {
-                      Thread.sleep(recordingDuration_millis.intValue());
-                    } catch (InterruptedException e) {
-                      throw new RuntimeException(e);
-                    }
-                    runOnUiThread(new Runnable() {
-                      @Override
-                      public void run() {
-                        stopRecording();
-                      }
-                    });
+                    stopRecording();
                   }
-                }.start();
+                });
+              }
+            }.start();
 
-                break;
+            break;
 
-              case DONE:
-                plotResponse(wavAnalyzerTask);
-                break;
+          case DONE:
+            plotResponse(wavAnalyzerTask);
+            break;
 
-              default: break;
-            }
-          }
-        });
+          default: break;
+        }
+      }
+    });
 
-        playerButton = (Button) findViewById(R.id.player_button);
-        playerButton.setEnabled(spkrSupport);
-        playerButton.setOnClickListener(new View.OnClickListener() {
+    playerButton = (Button) findViewById(R.id.player_button);
+    playerButton.setEnabled(spkrSupport);
+    playerButton.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View v) {
+        recorderButton.setEnabled(false);
+        status = Status.PLAYER;
+        play();
+
+        Button okButton = (Button)instruView.findViewById(R.id.ok);
+        okButton.setOnClickListener(new Button.OnClickListener() {
           @Override
           public void onClick(View v) {
-              recorderButton.setEnabled(false);
-              status = Status.PLAYER;
-              play();
-              getPassButton().setEnabled(true);
+            instruWindow.dismiss();
+            onInstruScreen = false;
+            if (status == Status.PLAYER) {
+              playerButton.setEnabled(spkrSupport);
+            } else {
+              recorderButton.setEnabled(micSupport);
+            }
+            getPassButton().setEnabled(true);
           }
         });
+        TextView instruction = (TextView)instruView.findViewById(R.id.instru);
+        instruction.setText(R.string.hifi_ultrasound_speaker_test_test_side);
+        instruWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
+        recorderButton.setEnabled(false);
+        playerButton.setEnabled(false);
+        onInstruScreen = true;
+      }
+    });
+  }
+
+  private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
+    Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
+    dismissButton.setOnClickListener(new Button.OnClickListener(){
+      @Override
+      public void onClick(View v) {
+        popupWindow.dismiss();
+        onPlotScreen = false;
+        recorderButton.setEnabled(true);
+      }});
+    popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
+    onPlotScreen = true;
+
+    recorderButton.setEnabled(false);
+
+    XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
+    plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
+
+    Double[] frequencies = new Double[Common.PIP_NUM];
+    for (int i = 0; i < Common.PIP_NUM; i++) {
+      frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
     }
 
+    if (wavAnalyzerTask != null) {
 
-    private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
-      Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
-      dismissButton.setOnClickListener(new Button.OnClickListener(){
-        @Override
-        public void onClick(View v) {
-          popupWindow.dismiss();
-          onPlotScreen = false;
-          recorderButton.setEnabled(true);
-        }});
-      popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
-      onPlotScreen = true;
-
-      recorderButton.setEnabled(false);
-
-      XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
-      plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
-
-      Double[] frequencies = new Double[Common.PIP_NUM];
-      for (int i = 0; i < Common.PIP_NUM; i++) {
-        frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
-      }
-
-      if (wavAnalyzerTask != null) {
-
-        double[][] power = wavAnalyzerTask.getPower();
-        for(int i = 0; i < Common.REPETITIONS; i++) {
-          Double[] powerWrap = new Double[Common.PIP_NUM];
-          for (int j = 0; j < Common.PIP_NUM; j++) {
-            powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
-          }
-          XYSeries series = new SimpleXYSeries(
-              Arrays.asList(frequencies),
-              Arrays.asList(powerWrap),
-              "");
-          LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
-          seriesFormat.configure(getApplicationContext(),
-              R.xml.ultrasound_line_formatter_trials);
-          seriesFormat.setPointLabelFormatter(null);
-          plot.addSeries(series, seriesFormat);
+      double[][] power = wavAnalyzerTask.getPower();
+      for(int i = 0; i < Common.REPETITIONS; i++) {
+        Double[] powerWrap = new Double[Common.PIP_NUM];
+        for (int j = 0; j < Common.PIP_NUM; j++) {
+          powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
         }
-
-        double[] noiseDB = wavAnalyzerTask.getNoiseDB();
-        Double[] noiseDBWrap = new Double[Common.PIP_NUM];
-        for (int i = 0; i < Common.PIP_NUM; i++) {
-          noiseDBWrap[i] = new Double(noiseDB[i]);
-        }
-
-        XYSeries noiseSeries = new SimpleXYSeries(
-            Arrays.asList(frequencies),
-            Arrays.asList(noiseDBWrap),
-            "background noise");
-        LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
-        noiseSeriesFormat.configure(getApplicationContext(),
-            R.xml.ultrasound_line_formatter_noise);
-        noiseSeriesFormat.setPointLabelFormatter(null);
-        plot.addSeries(noiseSeries, noiseSeriesFormat);
-
-        double[] dB = wavAnalyzerTask.getDB();
-        Double[] dBWrap = new Double[Common.PIP_NUM];
-        for (int i = 0; i < Common.PIP_NUM; i++) {
-          dBWrap[i] = new Double(dB[i]);
-        }
-
         XYSeries series = new SimpleXYSeries(
             Arrays.asList(frequencies),
-            Arrays.asList(dBWrap),
-            "median");
+            Arrays.asList(powerWrap),
+            "");
         LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
         seriesFormat.configure(getApplicationContext(),
-            R.xml.ultrasound_line_formatter_median);
+            R.xml.ultrasound_line_formatter_trials);
         seriesFormat.setPointLabelFormatter(null);
         plot.addSeries(series, seriesFormat);
-
-        Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
-        Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
-        XYSeries passSeries = new SimpleXYSeries(
-            Arrays.asList(passX), Arrays.asList(passY), "passing");
-        LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
-        passSeriesFormat.configure(getApplicationContext(),
-            R.xml.ultrasound_line_formatter_pass);
-        passSeriesFormat.setPointLabelFormatter(null);
-        plot.addSeries(passSeries, passSeriesFormat);
       }
-    }
 
-    /**
-     * Plays the generated pips.
-     */
-    private void play() {
-      play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
-    }
-
-    /**
-     * Plays the sound data.
-     */
-    private void play(byte[] data, int sampleRate) {
-      if (audioTrack != null) {
-        audioTrack.stop();
-        audioTrack.release();
+      double[] noiseDB = wavAnalyzerTask.getNoiseDB();
+      Double[] noiseDBWrap = new Double[Common.PIP_NUM];
+      for (int i = 0; i < Common.PIP_NUM; i++) {
+        noiseDBWrap[i] = new Double(noiseDB[i]);
       }
-      audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
-          sampleRate, AudioFormat.CHANNEL_OUT_MONO,
-          AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
-          sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
-          AudioTrack.MODE_STATIC);
-      audioTrack.write(data, 0, data.length);
-      audioTrack.play();
+
+      XYSeries noiseSeries = new SimpleXYSeries(
+          Arrays.asList(frequencies),
+          Arrays.asList(noiseDBWrap),
+          "background noise");
+      LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
+      noiseSeriesFormat.configure(getApplicationContext(),
+          R.xml.ultrasound_line_formatter_noise);
+      noiseSeriesFormat.setPointLabelFormatter(null);
+      plot.addSeries(noiseSeries, noiseSeriesFormat);
+
+      double[] dB = wavAnalyzerTask.getDB();
+      Double[] dBWrap = new Double[Common.PIP_NUM];
+      for (int i = 0; i < Common.PIP_NUM; i++) {
+        dBWrap[i] = new Double(dB[i]);
+      }
+
+      XYSeries series = new SimpleXYSeries(
+          Arrays.asList(frequencies),
+          Arrays.asList(dBWrap),
+          "median");
+      LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
+      seriesFormat.configure(getApplicationContext(),
+          R.xml.ultrasound_line_formatter_median);
+      seriesFormat.setPointLabelFormatter(null);
+      plot.addSeries(series, seriesFormat);
+
+      Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
+      Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
+      XYSeries passSeries = new SimpleXYSeries(
+          Arrays.asList(passX), Arrays.asList(passY), "passing");
+      LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
+      passSeriesFormat.configure(getApplicationContext(),
+          R.xml.ultrasound_line_formatter_pass);
+      passSeriesFormat.setPointLabelFormatter(null);
+      plot.addSeries(passSeries, passSeriesFormat);
     }
+  }
+
+  /**
+   * Plays the generated pips.
+   */
+  private void play() {
+    play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
+  }
+
+  /**
+   * Plays the sound data.
+   */
+  private void play(byte[] data, int sampleRate) {
+    if (audioTrack != null) {
+      audioTrack.stop();
+      audioTrack.release();
+    }
+    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
+        sampleRate, AudioFormat.CHANNEL_OUT_MONO,
+        AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
+        sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
+        AudioTrack.MODE_STATIC);
+    audioTrack.write(data, 0, data.length);
+    audioTrack.play();
+  }
 
   /**
    * AsyncTask class for the analyzing.
@@ -354,6 +392,26 @@
       info.append(result);
       recorderButton.setEnabled(true);
       recorderButton.setText(R.string.hifi_ultrasound_test_plot);
+
+      Button okButton = (Button)instruView.findViewById(R.id.ok);
+      okButton.setOnClickListener(new Button.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+          instruWindow.dismiss();
+          onInstruScreen = false;
+          if (status == HifiUltrasoundSpeakerTestActivity.Status.PLAYER) {
+            playerButton.setEnabled(spkrSupport);
+          } else {
+            recorderButton.setEnabled(micSupport);
+          }
+        }
+      });
+      TextView instruction = (TextView) instruView.findViewById(R.id.instru);
+      instruction.setText(R.string.hifi_ultrasound_speaker_test_reference_side);
+      instruWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
+      recorderButton.setEnabled(false);
+      playerButton.setEnabled(false);
+      onInstruScreen = true;
     }
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java
index 690e109..85b3e37 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java
@@ -43,270 +43,271 @@
 
 public class HifiUltrasoundTestActivity extends PassFailButtons.Activity {
 
-    public enum Status {
-        START, RECORDING, DONE, PLAYER
+  public enum Status {
+    START, RECORDING, DONE, PLAYER
+  }
+
+  private static final String TAG = "HifiUltrasoundTestActivity";
+
+  private Status status = Status.START;
+  private boolean onPlotScreen = false;
+  private TextView info;
+  private Button playerButton;
+  private Button recorderButton;
+  private AudioTrack audioTrack;
+  private LayoutInflater layoutInflater;
+  private View popupView;
+  private PopupWindow popupWindow;
+  private boolean micSupport = true;
+  private boolean spkrSupport = true;
+
+  @Override
+  public void onBackPressed () {
+    if (onPlotScreen) {
+      popupWindow.dismiss();
+      onPlotScreen = false;
+      recorderButton.setEnabled(true);
+    } else {
+      super.onBackPressed();
+    }
+  }
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.hifi_ultrasound);
+    setInfoResources(R.string.hifi_ultrasound_test, R.string.hifi_ultrasound_test_info, -1);
+    setPassFailButtonClickListeners();
+    getPassButton().setEnabled(false);
+
+    info = (TextView) findViewById(R.id.info_text);
+    info.setMovementMethod(new ScrollingMovementMethod());
+    info.setText(R.string.hifi_ultrasound_test_instruction1);
+
+    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+    String micSupportString = audioManager.getProperty(
+        AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND);
+    String spkrSupportString = audioManager.getProperty(
+        AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND);
+    Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupportString);
+    Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupportString);
+
+    if (micSupportString == null) {
+      micSupportString = "null";
+    }
+    if (spkrSupportString == null) {
+      spkrSupportString = "null";
+    }
+    if (micSupportString.equalsIgnoreCase(getResources().getString(
+        R.string.hifi_ultrasound_test_default_false_string))) {
+      micSupport = false;
+      getPassButton().setEnabled(true);
+      info.append(getResources().getString(R.string.hifi_ultrasound_test_mic_no_support));
+    }
+    if (spkrSupportString.equalsIgnoreCase(getResources().getString(
+        R.string.hifi_ultrasound_test_default_false_string))) {
+      spkrSupport = false;
+      info.append(getResources().getString(R.string.hifi_ultrasound_test_spkr_no_support));
     }
 
-    private static final String TAG = "HifiUltrasoundTestActivity";
+    layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
+        LAYOUT_INFLATER_SERVICE);
+    popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
+    popupWindow = new PopupWindow(
+        popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
 
-    private Status status = Status.START;
-    private boolean onPlotScreen = false;
-    private TextView info;
-    private Button playerButton;
-    private Button recorderButton;
-    private AudioTrack audioTrack;
-    private LayoutInflater layoutInflater;
-    private View popupView;
-    private PopupWindow popupWindow;
-    private boolean micSupport = true;
-    private boolean spkrSupport = true;
+    final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
+    final int recordRate = audioRecorder.getSampleRate();
 
-    @Override
-    public void onBackPressed () {
-        if (onPlotScreen) {
-            popupWindow.dismiss();
-            onPlotScreen = false;
-            recorderButton.setEnabled(true);
-        } else {
-            super.onBackPressed();
-        }
-    }
+    recorderButton = (Button) findViewById(R.id.recorder_button);
+    recorderButton.setEnabled(micSupport);
+    recorderButton.setOnClickListener(new View.OnClickListener() {
+      private WavAnalyzerTask wavAnalyzerTask = null;
+      private void stopRecording() {
+        audioRecorder.stop();
+        wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
+        wavAnalyzerTask.execute();
+        status = Status.DONE;
+      }
+      @Override
+      public void onClick(View v) {
+        switch (status) {
+          case START:
+            info.append("Recording at " + recordRate + "Hz using ");
+            final int source = audioRecorder.getAudioSource();
+            switch (source) {
+              case 1:
+                info.append("MIC");
+                break;
+              case 6:
+                info.append("VOICE_RECOGNITION");
+                break;
+              default:
+                info.append("UNEXPECTED " + source);
+                break;
+            }
+            info.append("\n");
+            status = Status.RECORDING;
+            playerButton.setEnabled(false);
+            recorderButton.setEnabled(false);
+            audioRecorder.start();
 
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.hifi_ultrasound);
-        setInfoResources(R.string.hifi_ultrasound_test, R.string.hifi_ultrasound_test_info, -1);
-        setPassFailButtonClickListeners();
-        getPassButton().setEnabled(false);
-
-        info = (TextView) findViewById(R.id.info_text);
-        info.setMovementMethod(new ScrollingMovementMethod());
-        info.setText(R.string.hifi_ultrasound_test_instruction1);
-
-        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
-        String micSupportString = audioManager.getProperty(
-            getResources().getString(R.string.hifi_ultrasound_test_mic_prop));
-        String spkrSupportString = audioManager.getProperty(
-            getResources().getString(R.string.hifi_ultrasound_test_spkr_prop));
-
-        if (micSupportString == null) {
-          micSupportString = "null";
-        }
-        if (spkrSupportString == null) {
-          spkrSupportString = "null";
-        }
-        if (micSupportString.equalsIgnoreCase(getResources().getString(
-            R.string.hifi_ultrasound_test_default_false_string))) {
-          micSupport = false;
-          getPassButton().setEnabled(true);
-          info.append(getResources().getString(R.string.hifi_ultrasound_test_mic_no_support));
-        }
-        if (spkrSupportString.equalsIgnoreCase(getResources().getString(
-            R.string.hifi_ultrasound_test_default_false_string))) {
-          spkrSupport = false;
-          info.append(getResources().getString(R.string.hifi_ultrasound_test_spkr_no_support));
-        }
-
-        layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
-            LAYOUT_INFLATER_SERVICE);
-        popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
-        popupWindow = new PopupWindow(
-            popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-
-        final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
-        final int recordRate = audioRecorder.getSampleRate();
-
-        recorderButton = (Button) findViewById(R.id.recorder_button);
-        recorderButton.setEnabled(micSupport);
-        recorderButton.setOnClickListener(new View.OnClickListener() {
-          private WavAnalyzerTask wavAnalyzerTask = null;
-          private void stopRecording() {
-            audioRecorder.stop();
-            wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
-            wavAnalyzerTask.execute();
-            status = Status.DONE;
-          }
-          @Override
-          public void onClick(View v) {
-            switch (status) {
-              case START:
-                info.append("Recording at " + recordRate + "Hz using ");
-                final int source = audioRecorder.getAudioSource();
-                switch (source) {
-                  case 1:
-                    info.append("MIC");
-                    break;
-                  case 6:
-                    info.append("VOICE_RECOGNITION");
-                    break;
-                  default:
-                    info.append("UNEXPECTED " + source);
-                    break;
+            final View finalV = v;
+            new Thread() {
+              @Override
+              public void run() {
+                Double recordingDuration_millis = new Double(1000 * (2.5
+                    + Common.PREFIX_LENGTH_S
+                    + Common.PAUSE_BEFORE_PREFIX_DURATION_S
+                    + Common.PAUSE_AFTER_PREFIX_DURATION_S
+                    + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
+                    * Common.REPETITIONS));
+                Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
+                try {
+                  Thread.sleep(recordingDuration_millis.intValue());
+                } catch (InterruptedException e) {
+                  throw new RuntimeException(e);
                 }
-                info.append("\n");
-                status = Status.RECORDING;
-                playerButton.setEnabled(false);
-                recorderButton.setEnabled(false);
-                audioRecorder.start();
-
-                final View finalV = v;
-                new Thread() {
+                runOnUiThread(new Runnable() {
                   @Override
                   public void run() {
-                    Double recordingDuration_millis = new Double(1000 * (2.5
-                          + Common.PREFIX_LENGTH_S
-                          + Common.PAUSE_BEFORE_PREFIX_DURATION_S
-                          + Common.PAUSE_AFTER_PREFIX_DURATION_S
-                          + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
-                          * Common.REPETITIONS));
-                    Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
-                    try {
-                      Thread.sleep(recordingDuration_millis.intValue());
-                    } catch (InterruptedException e) {
-                      throw new RuntimeException(e);
-                    }
-                    runOnUiThread(new Runnable() {
-                      @Override
-                      public void run() {
-                        stopRecording();
-                      }
-                    });
+                    stopRecording();
                   }
-                }.start();
+                });
+              }
+            }.start();
 
-                break;
+            break;
 
-              case DONE:
-                plotResponse(wavAnalyzerTask);
-                break;
+          case DONE:
+            plotResponse(wavAnalyzerTask);
+            break;
 
-              default: break;
-            }
-          }
-        });
+          default: break;
+        }
+      }
+    });
 
-        playerButton = (Button) findViewById(R.id.player_button);
-        playerButton.setEnabled(spkrSupport);
-        playerButton.setOnClickListener(new View.OnClickListener() {
-          @Override
-          public void onClick(View v) {
-              recorderButton.setEnabled(false);
-              status = Status.PLAYER;
-              play();
-          }
-        });
+    playerButton = (Button) findViewById(R.id.player_button);
+    playerButton.setEnabled(spkrSupport);
+    playerButton.setOnClickListener(new View.OnClickListener() {
+      @Override
+      public void onClick(View v) {
+        recorderButton.setEnabled(false);
+        status = Status.PLAYER;
+        play();
+      }
+    });
+  }
+
+  private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
+    Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
+    dismissButton.setOnClickListener(new Button.OnClickListener(){
+      @Override
+      public void onClick(View v) {
+        popupWindow.dismiss();
+        onPlotScreen = false;
+        recorderButton.setEnabled(true);
+      }});
+    popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
+    onPlotScreen = true;
+
+    recorderButton.setEnabled(false);
+
+    XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
+    plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
+
+    Double[] frequencies = new Double[Common.PIP_NUM];
+    for (int i = 0; i < Common.PIP_NUM; i++) {
+      frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
     }
 
+    if (wavAnalyzerTask != null) {
 
-    private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
-      Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
-      dismissButton.setOnClickListener(new Button.OnClickListener(){
-        @Override
-        public void onClick(View v) {
-          popupWindow.dismiss();
-          onPlotScreen = false;
-          recorderButton.setEnabled(true);
-        }});
-      popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
-      onPlotScreen = true;
-
-      recorderButton.setEnabled(false);
-
-      XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
-      plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
-
-      Double[] frequencies = new Double[Common.PIP_NUM];
-      for (int i = 0; i < Common.PIP_NUM; i++) {
-        frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
-      }
-
-      if (wavAnalyzerTask != null) {
-
-        double[][] power = wavAnalyzerTask.getPower();
-        for(int i = 0; i < Common.REPETITIONS; i++) {
-          Double[] powerWrap = new Double[Common.PIP_NUM];
-          for (int j = 0; j < Common.PIP_NUM; j++) {
-            powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
-          }
-          XYSeries series = new SimpleXYSeries(
-              Arrays.asList(frequencies),
-              Arrays.asList(powerWrap),
-              "");
-          LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
-          seriesFormat.configure(getApplicationContext(),
-              R.xml.ultrasound_line_formatter_trials);
-          seriesFormat.setPointLabelFormatter(null);
-          plot.addSeries(series, seriesFormat);
+      double[][] power = wavAnalyzerTask.getPower();
+      for(int i = 0; i < Common.REPETITIONS; i++) {
+        Double[] powerWrap = new Double[Common.PIP_NUM];
+        for (int j = 0; j < Common.PIP_NUM; j++) {
+          powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
         }
-
-        double[] noiseDB = wavAnalyzerTask.getNoiseDB();
-        Double[] noiseDBWrap = new Double[Common.PIP_NUM];
-        for (int i = 0; i < Common.PIP_NUM; i++) {
-          noiseDBWrap[i] = new Double(noiseDB[i]);
-        }
-
-        XYSeries noiseSeries = new SimpleXYSeries(
-            Arrays.asList(frequencies),
-            Arrays.asList(noiseDBWrap),
-            "background noise");
-        LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
-        noiseSeriesFormat.configure(getApplicationContext(),
-            R.xml.ultrasound_line_formatter_noise);
-        noiseSeriesFormat.setPointLabelFormatter(null);
-        plot.addSeries(noiseSeries, noiseSeriesFormat);
-
-        double[] dB = wavAnalyzerTask.getDB();
-        Double[] dBWrap = new Double[Common.PIP_NUM];
-        for (int i = 0; i < Common.PIP_NUM; i++) {
-          dBWrap[i] = new Double(dB[i]);
-        }
-
         XYSeries series = new SimpleXYSeries(
             Arrays.asList(frequencies),
-            Arrays.asList(dBWrap),
-            "median");
+            Arrays.asList(powerWrap),
+            "");
         LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
         seriesFormat.configure(getApplicationContext(),
-            R.xml.ultrasound_line_formatter_median);
+            R.xml.ultrasound_line_formatter_trials);
         seriesFormat.setPointLabelFormatter(null);
         plot.addSeries(series, seriesFormat);
-
-        Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
-        Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
-        XYSeries passSeries = new SimpleXYSeries(
-            Arrays.asList(passX), Arrays.asList(passY), "passing");
-        LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
-        passSeriesFormat.configure(getApplicationContext(),
-            R.xml.ultrasound_line_formatter_pass);
-        passSeriesFormat.setPointLabelFormatter(null);
-        plot.addSeries(passSeries, passSeriesFormat);
       }
-    }
 
-    /**
-     * Plays the generated pips.
-     */
-    private void play() {
-      play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
-    }
-
-    /**
-     * Plays the sound data.
-     */
-    private void play(byte[] data, int sampleRate) {
-      if (audioTrack != null) {
-        audioTrack.stop();
-        audioTrack.release();
+      double[] noiseDB = wavAnalyzerTask.getNoiseDB();
+      Double[] noiseDBWrap = new Double[Common.PIP_NUM];
+      for (int i = 0; i < Common.PIP_NUM; i++) {
+        noiseDBWrap[i] = new Double(noiseDB[i]);
       }
-      audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
-          sampleRate, AudioFormat.CHANNEL_OUT_MONO,
-          AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
-          sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
-          AudioTrack.MODE_STATIC);
-      audioTrack.write(data, 0, data.length);
-      audioTrack.play();
+
+      XYSeries noiseSeries = new SimpleXYSeries(
+          Arrays.asList(frequencies),
+          Arrays.asList(noiseDBWrap),
+          "background noise");
+      LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
+      noiseSeriesFormat.configure(getApplicationContext(),
+          R.xml.ultrasound_line_formatter_noise);
+      noiseSeriesFormat.setPointLabelFormatter(null);
+      plot.addSeries(noiseSeries, noiseSeriesFormat);
+
+      double[] dB = wavAnalyzerTask.getDB();
+      Double[] dBWrap = new Double[Common.PIP_NUM];
+      for (int i = 0; i < Common.PIP_NUM; i++) {
+        dBWrap[i] = new Double(dB[i]);
+      }
+
+      XYSeries series = new SimpleXYSeries(
+          Arrays.asList(frequencies),
+          Arrays.asList(dBWrap),
+          "median");
+      LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
+      seriesFormat.configure(getApplicationContext(),
+          R.xml.ultrasound_line_formatter_median);
+      seriesFormat.setPointLabelFormatter(null);
+      plot.addSeries(series, seriesFormat);
+
+      Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
+      Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
+      XYSeries passSeries = new SimpleXYSeries(
+          Arrays.asList(passX), Arrays.asList(passY), "passing");
+      LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
+      passSeriesFormat.configure(getApplicationContext(),
+          R.xml.ultrasound_line_formatter_pass);
+      passSeriesFormat.setPointLabelFormatter(null);
+      plot.addSeries(passSeries, passSeriesFormat);
     }
+  }
+
+  /**
+   * Plays the generated pips.
+   */
+  private void play() {
+    play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
+  }
+
+  /**
+   * Plays the sound data.
+   */
+  private void play(byte[] data, int sampleRate) {
+    if (audioTrack != null) {
+      audioTrack.stop();
+      audioTrack.release();
+    }
+    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
+        sampleRate, AudioFormat.CHANNEL_OUT_MONO,
+        AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
+        sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
+        AudioTrack.MODE_STATIC);
+    audioTrack.write(data, 0, data.length);
+    audioTrack.play();
+  }
 
   /**
    * AsyncTask class for the analyzing.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 58b51a5..85b8a18 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -30,6 +30,7 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.DngCreator;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
@@ -37,6 +38,7 @@
 import android.hardware.SensorManager;
 import android.media.Image;
 import android.media.ImageReader;
+import android.media.ImageWriter;
 import android.net.Uri;
 import android.os.ConditionVariable;
 import android.os.Handler;
@@ -67,7 +69,6 @@
 import java.math.BigInteger;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketTimeoutException;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.security.MessageDigest;
@@ -78,6 +79,7 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -121,7 +123,8 @@
     private BlockingStateCallback mCameraListener = null;
     private CameraDevice mCamera = null;
     private CameraCaptureSession mSession = null;
-    private ImageReader[] mCaptureReaders = null;
+    private ImageReader[] mOutputImageReaders = null;
+    private ImageReader mInputImageReader = null;
     private CameraCharacteristics mCameraCharacteristics = null;
 
     private Vibrator mVibrator = null;
@@ -547,6 +550,8 @@
                     doVibrate(cmdObj);
                 } else if ("getCameraIds".equals(cmdObj.getString("cmdName"))) {
                     doGetCameraIds();
+                } else if ("doReprocessCapture".equals(cmdObj.getString("cmdName"))) {
+                    doReprocessCapture(cmdObj);
                 } else {
                     throw new ItsException("Unknown command: " + cmd);
                 }
@@ -766,18 +771,42 @@
         }
     }
 
-    private void prepareCaptureReader(int[] widths, int[] heights, int formats[], int numSurfaces) {
-        if (mCaptureReaders != null) {
-            for (int i = 0; i < mCaptureReaders.length; i++) {
-                if (mCaptureReaders[i] != null) {
-                    mCaptureReaders[i].close();
+    private void prepareImageReaders(Size[] outputSizes, int[] outputFormats, Size inputSize,
+            int inputFormat, int maxInputBuffers) {
+        closeImageReaders();
+        mOutputImageReaders = new ImageReader[outputSizes.length];
+        for (int i = 0; i < outputSizes.length; i++) {
+            // Check if the output image reader can be shared with the input image reader.
+            if (outputSizes[i].equals(inputSize) && outputFormats[i] == inputFormat) {
+                mOutputImageReaders[i] = ImageReader.newInstance(outputSizes[i].getWidth(),
+                        outputSizes[i].getHeight(), outputFormats[i],
+                        MAX_CONCURRENT_READER_BUFFERS + maxInputBuffers);
+                mInputImageReader = mOutputImageReaders[i];
+            } else {
+                mOutputImageReaders[i] = ImageReader.newInstance(outputSizes[i].getWidth(),
+                        outputSizes[i].getHeight(), outputFormats[i],
+                        MAX_CONCURRENT_READER_BUFFERS);
+            }
+        }
+
+        if (inputSize != null && mInputImageReader == null) {
+            mInputImageReader = ImageReader.newInstance(inputSize.getWidth(), inputSize.getHeight(),
+                    inputFormat, maxInputBuffers);
+        }
+    }
+
+    private void closeImageReaders() {
+        if (mOutputImageReaders != null) {
+            for (int i = 0; i < mOutputImageReaders.length; i++) {
+                if (mOutputImageReaders[i] != null) {
+                    mOutputImageReaders[i].close();
+                    mOutputImageReaders[i] = null;
                 }
             }
         }
-        mCaptureReaders = new ImageReader[numSurfaces];
-        for (int i = 0; i < numSurfaces; i++) {
-            mCaptureReaders[i] = ImageReader.newInstance(widths[i], heights[i], formats[i],
-                    MAX_CONCURRENT_READER_BUFFERS);
+        if (mInputImageReader != null) {
+            mInputImageReader.close();
+            mInputImageReader = null;
         }
     }
 
@@ -788,18 +817,17 @@
 
             // 3A happens on full-res frames.
             Size sizes[] = ItsUtils.getYuvOutputSizes(mCameraCharacteristics);
-            int widths[] = new int[1];
-            int heights[] = new int[1];
-            int formats[] = new int[1];
-            widths[0] = sizes[0].getWidth();
-            heights[0] = sizes[0].getHeight();
-            formats[0] = ImageFormat.YUV_420_888;
-            int width = widths[0];
-            int height = heights[0];
+            int outputFormats[] = new int[1];
+            outputFormats[0] = ImageFormat.YUV_420_888;
+            Size[] outputSizes = new Size[1];
+            outputSizes[0] = sizes[0];
+            int width = outputSizes[0].getWidth();
+            int height = outputSizes[0].getHeight();
 
-            prepareCaptureReader(widths, heights, formats, 1);
+            prepareImageReaders(outputSizes, outputFormats, /*inputSize*/null, /*inputFormat*/0,
+                    /*maxInputBuffers*/0);
             List<Surface> outputSurfaces = new ArrayList<Surface>(1);
-            outputSurfaces.add(mCaptureReaders[0].getSurface());
+            outputSurfaces.add(mOutputImageReaders[0].getSurface());
             BlockingSessionCallback sessionListener = new BlockingSessionCallback();
             mCamera.createCaptureSession(outputSurfaces, sessionListener, mCameraHandler);
             mSession = sessionListener.waitAndGetSession(TIMEOUT_IDLE_MS);
@@ -807,7 +835,7 @@
             // Add a listener that just recycles buffers; they aren't saved anywhere.
             ImageReader.OnImageAvailableListener readerListener =
                     createAvailableListenerDropper(mCaptureCallback);
-            mCaptureReaders[0].setOnImageAvailableListener(readerListener, mSaveHandlers[0]);
+            mOutputImageReaders[0].setOnImageAvailableListener(readerListener, mSaveHandlers[0]);
 
             // Get the user-specified regions for AE, AWB, AF.
             // Note that the user specifies normalized [x,y,w,h], which is converted below
@@ -953,7 +981,7 @@
                         triggeredAF = true;
                     }
 
-                    req.addTarget(mCaptureReaders[0].getSurface());
+                    req.addTarget(mOutputImageReaders[0].getSurface());
 
                     mIssuedRequest3A = true;
                     mSession.capture(req.build(), mCaptureResultListener, mResultHandler);
@@ -991,16 +1019,96 @@
         }
     }
 
+    /**
+     * Parse jsonOutputSpecs to get output surface sizes and formats. Create input and output
+     * image readers for the parsed output surface sizes, output formats, and the given input
+     * size and format.
+     */
+    private void prepareImageReadersWithOutputSpecs(JSONArray jsonOutputSpecs, Size inputSize,
+            int inputFormat, int maxInputBuffers) throws ItsException {
+        Size outputSizes[];
+        int outputFormats[];
+        int numSurfaces = 0;
+
+        if (jsonOutputSpecs != null) {
+            try {
+                numSurfaces = jsonOutputSpecs.length();
+                if (numSurfaces > MAX_NUM_OUTPUT_SURFACES) {
+                    throw new ItsException("Too many output surfaces");
+                }
+
+                outputSizes = new Size[numSurfaces];
+                outputFormats = new int[numSurfaces];
+                for (int i = 0; i < numSurfaces; i++) {
+                    // Get the specified surface.
+                    JSONObject surfaceObj = jsonOutputSpecs.getJSONObject(i);
+                    String sformat = surfaceObj.optString("format");
+                    Size sizes[];
+                    if ("yuv".equals(sformat) || "".equals(sformat)) {
+                        // Default to YUV if no format is specified.
+                        outputFormats[i] = ImageFormat.YUV_420_888;
+                        sizes = ItsUtils.getYuvOutputSizes(mCameraCharacteristics);
+                    } else if ("jpg".equals(sformat) || "jpeg".equals(sformat)) {
+                        outputFormats[i] = ImageFormat.JPEG;
+                        sizes = ItsUtils.getJpegOutputSizes(mCameraCharacteristics);
+                    } else if ("raw".equals(sformat)) {
+                        outputFormats[i] = ImageFormat.RAW_SENSOR;
+                        sizes = ItsUtils.getRaw16OutputSizes(mCameraCharacteristics);
+                    } else if ("raw10".equals(sformat)) {
+                        outputFormats[i] = ImageFormat.RAW10;
+                        sizes = ItsUtils.getRaw10OutputSizes(mCameraCharacteristics);
+                    } else if ("raw12".equals(sformat)) {
+                        outputFormats[i] = ImageFormat.RAW12;
+                        sizes = ItsUtils.getRaw12OutputSizes(mCameraCharacteristics);
+                    } else if ("dng".equals(sformat)) {
+                        outputFormats[i] = ImageFormat.RAW_SENSOR;
+                        sizes = ItsUtils.getRaw16OutputSizes(mCameraCharacteristics);
+                        mCaptureRawIsDng = true;
+                    } else {
+                        throw new ItsException("Unsupported format: " + sformat);
+                    }
+                    // If the size is omitted, then default to the largest allowed size for the
+                    // format.
+                    int width = surfaceObj.optInt("width");
+                    int height = surfaceObj.optInt("height");
+                    if (width <= 0) {
+                        if (sizes == null || sizes.length == 0) {
+                            throw new ItsException(String.format(
+                                    "Zero stream configs available for requested format: %s",
+                                    sformat));
+                        }
+                        width = sizes[0].getWidth();
+                    }
+                    if (height <= 0) {
+                        height = sizes[0].getHeight();
+                    }
+
+                    outputSizes[i] = new Size(width, height);
+                }
+            } catch (org.json.JSONException e) {
+                throw new ItsException("JSON error", e);
+            }
+        } else {
+            // No surface(s) specified at all.
+            // Default: a single output surface which is full-res YUV.
+            Size sizes[] = ItsUtils.getYuvOutputSizes(mCameraCharacteristics);
+            numSurfaces = 1;
+
+            outputSizes = new Size[1];
+            outputFormats = new int[1];
+            outputSizes[0] = sizes[0];
+            outputFormats[0] = ImageFormat.YUV_420_888;
+        }
+
+        prepareImageReaders(outputSizes, outputFormats, inputSize, inputFormat, maxInputBuffers);
+    }
+
     private void doCapture(JSONObject params) throws ItsException {
         try {
             // Parse the JSON to get the list of capture requests.
             List<CaptureRequest.Builder> requests = ItsSerializer.deserializeRequestList(
                     mCamera, params);
 
-            // Set the output surface(s) and listeners.
-            int widths[] = new int[MAX_NUM_OUTPUT_SURFACES];
-            int heights[] = new int[MAX_NUM_OUTPUT_SURFACES];
-            int formats[] = new int[MAX_NUM_OUTPUT_SURFACES];
             int numSurfaces = 0;
             try {
                 mCountRawOrDng.set(0);
@@ -1013,70 +1121,14 @@
                 mCaptureResults = new CaptureResult[requests.size()];
 
                 JSONArray jsonOutputSpecs = ItsUtils.getOutputSpecs(params);
-                if (jsonOutputSpecs != null) {
-                    numSurfaces = jsonOutputSpecs.length();
-                    if (numSurfaces > MAX_NUM_OUTPUT_SURFACES) {
-                        throw new ItsException("Too many output surfaces");
-                    }
-                    for (int i = 0; i < numSurfaces; i++) {
-                        // Get the specified surface.
-                        JSONObject surfaceObj = jsonOutputSpecs.getJSONObject(i);
-                        String sformat = surfaceObj.optString("format");
-                        Size sizes[];
-                        if ("yuv".equals(sformat) || "".equals(sformat)) {
-                            // Default to YUV if no format is specified.
-                            formats[i] = ImageFormat.YUV_420_888;
-                            sizes = ItsUtils.getYuvOutputSizes(mCameraCharacteristics);
-                        } else if ("jpg".equals(sformat) || "jpeg".equals(sformat)) {
-                            formats[i] = ImageFormat.JPEG;
-                            sizes = ItsUtils.getJpegOutputSizes(mCameraCharacteristics);
-                        } else if ("raw".equals(sformat)) {
-                            formats[i] = ImageFormat.RAW_SENSOR;
-                            sizes = ItsUtils.getRaw16OutputSizes(mCameraCharacteristics);
-                        } else if ("raw10".equals(sformat)) {
-                            formats[i] = ImageFormat.RAW10;
-                            sizes = ItsUtils.getRaw10OutputSizes(mCameraCharacteristics);
-                        } else if ("raw12".equals(sformat)) {
-                            formats[i] = ImageFormat.RAW12;
-                            sizes = ItsUtils.getRaw12OutputSizes(mCameraCharacteristics);
-                        } else if ("dng".equals(sformat)) {
-                            formats[i] = ImageFormat.RAW_SENSOR;
-                            sizes = ItsUtils.getRaw16OutputSizes(mCameraCharacteristics);
-                            mCaptureRawIsDng = true;
-                        } else {
-                            throw new ItsException("Unsupported format: " + sformat);
-                        }
-                        // If the size is omitted, then default to the largest allowed size for the
-                        // format.
-                        widths[i] = surfaceObj.optInt("width");
-                        heights[i] = surfaceObj.optInt("height");
-                        if (widths[i] <= 0) {
-                            if (sizes == null || sizes.length == 0) {
-                                throw new ItsException(String.format(
-                                        "Zero stream configs available for requested format: %s",
-                                        sformat));
-                            }
-                            widths[i] = sizes[0].getWidth();
-                        }
-                        if (heights[i] <= 0) {
-                            heights[i] = sizes[0].getHeight();
-                        }
-                    }
-                } else {
-                    // No surface(s) specified at all.
-                    // Default: a single output surface which is full-res YUV.
-                    Size sizes[] =
-                            ItsUtils.getYuvOutputSizes(mCameraCharacteristics);
-                    numSurfaces = 1;
-                    widths[0] = sizes[0].getWidth();
-                    heights[0] = sizes[0].getHeight();
-                    formats[0] = ImageFormat.YUV_420_888;
-                }
 
-                prepareCaptureReader(widths, heights, formats, numSurfaces);
+                prepareImageReadersWithOutputSpecs(jsonOutputSpecs, /*inputSize*/null,
+                        /*inputFormat*/0, /*maxInputBuffers*/0);
+                numSurfaces = mOutputImageReaders.length;
+
                 List<Surface> outputSurfaces = new ArrayList<Surface>(numSurfaces);
                 for (int i = 0; i < numSurfaces; i++) {
-                    outputSurfaces.add(mCaptureReaders[i].getSurface());
+                    outputSurfaces.add(mOutputImageReaders[i].getSurface());
                 }
                 BlockingSessionCallback sessionListener = new BlockingSessionCallback();
                 mCamera.createCaptureSession(outputSurfaces, sessionListener, mCameraHandler);
@@ -1085,7 +1137,8 @@
                 for (int i = 0; i < numSurfaces; i++) {
                     ImageReader.OnImageAvailableListener readerListener =
                             createAvailableListener(mCaptureCallback);
-                    mCaptureReaders[i].setOnImageAvailableListener(readerListener,mSaveHandlers[i]);
+                    mOutputImageReaders[i].setOnImageAvailableListener(readerListener,
+                            mSaveHandlers[i]);
                 }
 
                 // Plan for how many callbacks need to be received throughout the duration of this
@@ -1096,8 +1149,6 @@
 
             } catch (CameraAccessException e) {
                 throw new ItsException("Error configuring outputs", e);
-            } catch (org.json.JSONException e) {
-                throw new ItsException("JSON error", e);
             }
 
             // Initiate the captures.
@@ -1109,7 +1160,7 @@
 
                 CaptureRequest.Builder req = requests.get(i);
                 for (int j = 0; j < numSurfaces; j++) {
-                    req.addTarget(mCaptureReaders[j].getSurface());
+                    req.addTarget(mOutputImageReaders[j].getSurface());
                 }
                 mSession.capture(req.build(), mCaptureResultListener, mResultHandler);
             }
@@ -1135,6 +1186,181 @@
         }
     }
 
+    /**
+     * Perform reprocess captures.
+     *
+     * It takes captureRequests in a JSON object and perform capture requests in two steps:
+     * regular capture request to get reprocess input and reprocess capture request to get
+     * reprocess outputs.
+     *
+     * Regular capture requests:
+     *   1. For each capture request in the JSON object, create a full-size capture request with
+     *      the settings in the JSON object.
+     *   2. Remember and clear noise reduction, edge enhancement, and effective exposure factor
+     *      from the regular capture requests. (Those settings will be used for reprocess requests.)
+     *   3. Submit the regular capture requests.
+     *
+     * Reprocess capture requests:
+     *   4. Wait for the regular capture results and use them to create reprocess capture requests.
+     *   5. Wait for the regular capture output images and queue them to the image writer.
+     *   6. Set the noise reduction, edge enhancement, and effective exposure factor from #2.
+     *   7. Submit the reprocess capture requests.
+     *
+     * The output images and results for the regular capture requests won't be written to socket.
+     * The output images and results for the reprocess capture requests will be written to socket.
+     */
+    private void doReprocessCapture(JSONObject params) throws ItsException {
+        ImageWriter imageWriter = null;
+        ArrayList<Integer> noiseReductionModes = new ArrayList<>();
+        ArrayList<Integer> edgeModes = new ArrayList<>();
+        ArrayList<Float> effectiveExposureFactors = new ArrayList<>();
+
+        mCountRawOrDng.set(0);
+        mCountJpg.set(0);
+        mCountYuv.set(0);
+        mCountRaw10.set(0);
+        mCountRaw12.set(0);
+        mCountCapRes.set(0);
+        mCaptureRawIsDng = false;
+
+        try {
+            // Parse the JSON to get the list of capture requests.
+            List<CaptureRequest.Builder> inputRequests =
+                    ItsSerializer.deserializeRequestList(mCamera, params);
+
+            // Prepare the image readers for reprocess input and reprocess outputs.
+            int inputFormat = getReprocessInputFormat(params);
+            Size inputSize = ItsUtils.getMaxOutputSize(mCameraCharacteristics, inputFormat);
+            JSONArray jsonOutputSpecs = ItsUtils.getOutputSpecs(params);
+            prepareImageReadersWithOutputSpecs(jsonOutputSpecs, inputSize, inputFormat,
+                    inputRequests.size());
+
+            // Prepare a reprocessable session.
+            int numOutputSurfaces = mOutputImageReaders.length;
+            InputConfiguration inputConfig = new InputConfiguration(inputSize.getWidth(),
+                    inputSize.getHeight(), inputFormat);
+            List<Surface> outputSurfaces = new ArrayList<Surface>();
+            boolean addSurfaceForInput = true;
+            for (int i = 0; i < numOutputSurfaces; i++) {
+                outputSurfaces.add(mOutputImageReaders[i].getSurface());
+                if (mOutputImageReaders[i] == mInputImageReader) {
+                    // If input and one of the outputs share the same image reader, avoid
+                    // adding the same surfaces twice.
+                    addSurfaceForInput = false;
+                }
+            }
+
+            if (addSurfaceForInput) {
+                // Besides the output surfaces specified in JSON object, add an additional one
+                // for reprocess input.
+                outputSurfaces.add(mInputImageReader.getSurface());
+            }
+
+            BlockingSessionCallback sessionListener = new BlockingSessionCallback();
+            mCamera.createReprocessableCaptureSession(inputConfig, outputSurfaces, sessionListener,
+                    mCameraHandler);
+            mSession = sessionListener.waitAndGetSession(TIMEOUT_IDLE_MS);
+
+            // Create an image writer for reprocess input.
+            Surface inputSurface = mSession.getInputSurface();
+            imageWriter = ImageWriter.newInstance(inputSurface, inputRequests.size());
+
+            // Set up input reader listener and capture callback listener to get
+            // reprocess input buffers and the results in order to create reprocess capture
+            // requests.
+            ImageReaderListenerWaiter inputReaderListener = new ImageReaderListenerWaiter();
+            mInputImageReader.setOnImageAvailableListener(inputReaderListener, mSaveHandlers[0]);
+
+            CaptureCallbackWaiter captureCallbackWaiter = new CaptureCallbackWaiter();
+            // Prepare the reprocess input request
+            for (CaptureRequest.Builder inputReqest : inputRequests) {
+                // Remember and clear noise reduction, edge enhancement, and effective exposure
+                // factors.
+                noiseReductionModes.add(inputReqest.get(CaptureRequest.NOISE_REDUCTION_MODE));
+                edgeModes.add(inputReqest.get(CaptureRequest.EDGE_MODE));
+                effectiveExposureFactors.add(inputReqest.get(
+                        CaptureRequest.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR));
+
+                inputReqest.set(CaptureRequest.NOISE_REDUCTION_MODE,
+                        CaptureRequest.NOISE_REDUCTION_MODE_OFF);
+                inputReqest.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_OFF);
+                inputReqest.set(CaptureRequest.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR, null);
+                inputReqest.addTarget(mInputImageReader.getSurface());
+                mSession.capture(inputReqest.build(), captureCallbackWaiter, mResultHandler);
+            }
+
+            // Wait for reprocess input images
+            ArrayList<CaptureRequest.Builder> reprocessOutputRequests = new ArrayList<>();
+            for (int i = 0; i < inputRequests.size(); i++) {
+                TotalCaptureResult result =
+                        captureCallbackWaiter.getResult(TIMEOUT_CALLBACK * 1000);
+                reprocessOutputRequests.add(mCamera.createReprocessCaptureRequest(result));
+                imageWriter.queueInputImage(inputReaderListener.getImage(TIMEOUT_CALLBACK * 1000));
+            }
+
+            // Start performing reprocess captures.
+
+            mCaptureResults = new CaptureResult[inputRequests.size()];
+
+            // Prepare reprocess capture requests.
+            for (int i = 0; i < numOutputSurfaces; i++) {
+                ImageReader.OnImageAvailableListener outputReaderListener =
+                        createAvailableListener(mCaptureCallback);
+                mOutputImageReaders[i].setOnImageAvailableListener(outputReaderListener,
+                        mSaveHandlers[i]);
+            }
+
+            // Initiate the captures.
+            for (int i = 0; i < reprocessOutputRequests.size(); i++) {
+                CaptureRequest.Builder req = reprocessOutputRequests.get(i);
+                for (ImageReader outputImageReader : mOutputImageReaders) {
+                    req.addTarget(outputImageReader.getSurface());
+                }
+
+                req.set(CaptureRequest.NOISE_REDUCTION_MODE, noiseReductionModes.get(i));
+                req.set(CaptureRequest.EDGE_MODE, edgeModes.get(i));
+                req.set(CaptureRequest.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR,
+                        effectiveExposureFactors.get(i));
+
+                mSession.capture(req.build(), mCaptureResultListener, mResultHandler);
+            }
+
+            // Plan for how many callbacks need to be received throughout the duration of this
+            // sequence of capture requests. There is one callback per image surface, and one
+            // callback for the CaptureResult, for each capture.
+            int numCaptures = reprocessOutputRequests.size();
+            mCountCallbacksRemaining.set(numCaptures * (numOutputSurfaces + 1));
+
+            // Make sure all callbacks have been hit (wait until captures are done).
+            // If no timeouts are received after a timeout, then fail.
+            int currentCount = mCountCallbacksRemaining.get();
+            while (currentCount > 0) {
+                try {
+                    Thread.sleep(TIMEOUT_CALLBACK*1000);
+                } catch (InterruptedException e) {
+                    throw new ItsException("Timeout failure", e);
+                }
+                int newCount = mCountCallbacksRemaining.get();
+                if (newCount == currentCount) {
+                    throw new ItsException(
+                            "No callback received within timeout");
+                }
+                currentCount = newCount;
+            }
+        } catch (android.hardware.camera2.CameraAccessException e) {
+            throw new ItsException("Access error: ", e);
+        } finally {
+            closeImageReaders();
+            if (mSession != null) {
+                mSession.close();
+                mSession = null;
+            }
+            if (imageWriter != null) {
+                imageWriter.close();
+            }
+        }
+    }
+
     @Override
     public final void onSensorChanged(SensorEvent event) {
         synchronized(mEventLock) {
@@ -1364,7 +1590,7 @@
                     int count = mCountCapRes.getAndIncrement();
                     mCaptureResults[count] = result;
                     mSocketRunnableObj.sendResponseCaptureResult(mCameraCharacteristics,
-                            request, result, mCaptureReaders);
+                            request, result, mOutputImageReaders);
                     mCountCallbacksRemaining.decrementAndGet();
                 }
             } catch (ItsException e) {
@@ -1380,4 +1606,93 @@
             Logt.e(TAG, "Script error: capture failed");
         }
     };
+
+    private class CaptureCallbackWaiter extends CameraCaptureSession.CaptureCallback {
+        private final LinkedBlockingQueue<TotalCaptureResult> mResultQueue =
+                new LinkedBlockingQueue<>();
+
+        @Override
+        public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+                long timestamp, long frameNumber) {
+        }
+
+        @Override
+        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+                TotalCaptureResult result) {
+            try {
+                mResultQueue.put(result);
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException(
+                        "Can't handle InterruptedException in onImageAvailable");
+            }
+        }
+
+        @Override
+        public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+                CaptureFailure failure) {
+            Logt.e(TAG, "Script error: capture failed");
+        }
+
+        public TotalCaptureResult getResult(long timeoutMs) throws ItsException {
+            TotalCaptureResult result;
+            try {
+                result = mResultQueue.poll(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                throw new ItsException(e);
+            }
+
+            if (result == null) {
+                throw new ItsException("Getting an image timed out after " + timeoutMs +
+                        "ms");
+            }
+
+            return result;
+        }
+    }
+
+    private static class ImageReaderListenerWaiter implements ImageReader.OnImageAvailableListener {
+        private final LinkedBlockingQueue<Image> mImageQueue = new LinkedBlockingQueue<>();
+
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            try {
+                mImageQueue.put(reader.acquireNextImage());
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException(
+                        "Can't handle InterruptedException in onImageAvailable");
+            }
+        }
+
+        public Image getImage(long timeoutMs) throws ItsException {
+            Image image;
+            try {
+                image = mImageQueue.poll(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                throw new ItsException(e);
+            }
+
+            if (image == null) {
+                throw new ItsException("Getting an image timed out after " + timeoutMs +
+                        "ms");
+            }
+            return image;
+        }
+    }
+
+    private int getReprocessInputFormat(JSONObject params) throws ItsException {
+        String reprocessFormat;
+        try {
+            reprocessFormat = params.getString("reprocessFormat");
+        } catch (org.json.JSONException e) {
+            throw new ItsException("Error parsing reprocess format: " + e);
+        }
+
+        if (reprocessFormat.equals("yuv")) {
+            return ImageFormat.YUV_420_888;
+        } else if (reprocessFormat.equals("private")) {
+            return ImageFormat.PRIVATE;
+        }
+
+        throw new ItsException("Uknown reprocess format: " + reprocessFormat);
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
index b09b90c..fddee4d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
@@ -116,6 +116,11 @@
         return getOutputSizes(ccs, ImageFormat.YUV_420_888);
     }
 
+    public static Size getMaxOutputSize(CameraCharacteristics ccs, int format)
+            throws ItsException {
+        return getMaxSize(getOutputSizes(ccs, format));
+    }
+
     private static Size[] getOutputSizes(CameraCharacteristics ccs, int format)
             throws ItsException {
         StreamConfigurationMap configMap = ccs.get(
@@ -126,6 +131,22 @@
         return configMap.getOutputSizes(format);
     }
 
+    private static Size getMaxSize(Size[] sizes) {
+        if (sizes == null || sizes.length == 0) {
+            throw new IllegalArgumentException("sizes was empty");
+        }
+
+        Size maxSize = sizes[0];
+        for (int i = 1; i < sizes.length; i++) {
+            if (sizes[i].getWidth() * sizes[i].getHeight() >
+                    maxSize.getWidth() * maxSize.getHeight()) {
+                maxSize = sizes[i];
+            }
+        }
+
+        return maxSize;
+    }
+
     public static byte[] getDataFromImage(Image image)
             throws ItsException {
         int format = image.getFormat();
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-dsa-a.pk8 b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-dsa-a.pk8
new file mode 100644
index 0000000..ac0b0c1
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-dsa-a.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.pk8 b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.pk8
new file mode 100644
index 0000000..ec27be1
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.x509.pem b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.x509.pem
new file mode 100644
index 0000000..183691d
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBejCCAR+gAwIBAgIJAMsY4Fz5jr/IMAoGCCqGSM49BAMCMBkxFzAVBgNVBAMM
+DnVuaXRfdGVzdF9lY19hMB4XDTE1MDYwMTIxNDU1M1oXDTQyMTAxNzIxNDU1M1ow
+GTEXMBUGA1UEAwwOdW5pdF90ZXN0X2VjX2EwWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAAR8Q+7lg4KSOs2Be0XhFwlFCsiCCIh3iX2t6fE+V/MD+QBT1265hIyBKEH/
+oAsTpLy8FdGKLC0x+TwuCedui0SBo1AwTjAdBgNVHQ4EFgQUX4h7gPTgwQXorm0H
+7R12wN2yNrwwHwYDVR0jBBgwFoAUX4h7gPTgwQXorm0H7R12wN2yNrwwDAYDVR0T
+BAUwAwEB/zAKBggqhkjOPQQDAgNJADBGAiEA5kHO4aK20dwt81mCABAywD7Y6V1O
+vqoff9yIx3USW8oCIQDTzo8tbHuPc+i3vBsb5Uo1+4BE/pcOe/je6PGlRHG8rg==
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java
index 7f3737d..9637a6c 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/KeySetHostTest.java
@@ -76,6 +76,10 @@
             "CtsKeySetSigningCBadAUpgradeAB.apk";
     private static final String A_SIGNED_NO_B_B_UPGRADE =
             "CtsKeySetSigningANoDefUpgradeB.apk";
+    private static final String A_SIGNED_EC_A_UPGRADE =
+            "CtsKeySetSigningAUpgradeEcA.apk";
+    private static final String EC_A_SIGNED_A_UPGRADE =
+            "CtsKeySetSigningEcAUpgradeA.apk";
 
     /* package which defines the KEYSET_PERM_NAME signature permission */
     private static final String KEYSET_PERM_DEF_PKG =
@@ -486,4 +490,26 @@
         assertNotNull("Installation of apk with upgrade key referring to a bad public key succeeded!",
                 installResult);
     }
+
+    /*
+     * Check if an apk signed by RSA pub key can upgrade to apk signed by EC key.
+     */
+    public void testUpgradeKSRsaToEC() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, A_SIGNED_EC_A_UPGRADE,
+                EC_A_SIGNED_A_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from one signed by RSA key"
+                 + "to version signed by EC upgrade-key-set, Reason: %s", installResult),
+                 installResult);
+    }
+
+    /*
+     * Check if an apk signed by EC pub key can upgrade to apk signed by RSA key.
+     */
+    public void testUpgradeKSECToRSA() throws Exception {
+        String installResult = testPackageUpgrade(KEYSET_PKG, EC_A_SIGNED_A_UPGRADE,
+                A_SIGNED_EC_A_UPGRADE);
+        assertNull(String.format("failed to upgrade keyset app from one signed by EC key"
+                 + "to version signed by RSA upgrade-key-set, Reason: %s", installResult),
+                 installResult);
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
index fb8993c..14f215c 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/src/com/android/cts/documentprovider/MyDocumentsProvider.java
@@ -239,13 +239,16 @@
             new AsyncTask<Void, Void, Void>() {
                 @Override
                 protected Void doInBackground(Void... params) {
-                    try {
-                        final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
-                                pipe[0]);
-                        doc.contents = readFullyNoClose(is);
-                        is.close();
-                    } catch (IOException e) {
-                        Log.w(TAG, "Failed to stream", e);
+                    synchronized (doc) {
+                        try {
+                            final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
+                                    pipe[0]);
+                            doc.contents = readFullyNoClose(is);
+                            is.close();
+                            doc.notifyAll();
+                        } catch (IOException e) {
+                            Log.w(TAG, "Failed to stream", e);
+                        }
                     }
                     return null;
                 }
@@ -255,13 +258,20 @@
             new AsyncTask<Void, Void, Void>() {
                 @Override
                 protected Void doInBackground(Void... params) {
-                    try {
-                        final OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(
-                                pipe[1]);
-                        os.write(doc.contents);
-                        os.close();
-                    } catch (IOException e) {
-                        Log.w(TAG, "Failed to stream", e);
+                    synchronized (doc) {
+                        try {
+                            final OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(
+                                    pipe[1]);
+                            while (doc.contents == null) {
+                                doc.wait();
+                            }
+                            os.write(doc.contents);
+                            os.close();
+                        } catch (IOException e) {
+                            Log.w(TAG, "Failed to stream", e);
+                        } catch (InterruptedException e) {
+                            Log.w(TAG, "Interuppted", e);
+                        }
                     }
                     return null;
                 }
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
index 4d441de..79d053b 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
@@ -37,6 +37,18 @@
 
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
+#apks signed by cts-keyset-test-ec-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningEcAUpgradeA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-ec-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
 #apks signed by cts-keyset-test-a and cts-keyset-test-b
 include $(CLEAR_VARS)
 
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uEcA/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uEcA/Android.mk
new file mode 100644
index 0000000..3d0109a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uEcA/Android.mk
@@ -0,0 +1,27 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#apks signed by cts-keyset-test-a
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeEcA
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/keysets/cts-keyset-test-a
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uEcA/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/keysets/uEcA/AndroidManifest.xml
new file mode 100644
index 0000000..a84704a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/keysets/uEcA/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.keysets">
+    <application android:hasCode="false">
+    </application>
+    <key-sets>
+        <key-set android:name="EcA" >
+          <public-key android:name="keyEcA"
+                      android:value="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfEPu5YOCkjrNgXtF4RcJRQrIggiId4l9renxPlfzA/kAU9duuYSMgShB/6ALE6S8vBXRiiwtMfk8LgnnbotEgQ=="/>
+        </key-set>
+        <upgrade-key-set android:name="EcA"/>
+    </key-sets>
+</manifest>
diff --git a/hostsidetests/atrace/Android.mk b/hostsidetests/atrace/Android.mk
index 4bde535..1fd7102 100644
--- a/hostsidetests/atrace/Android.mk
+++ b/hostsidetests/atrace/Android.mk
@@ -18,8 +18,6 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_MODULE_TAGS := optional
-
 # Must match the package name in CtsTestCaseList.mk
 LOCAL_MODULE := CtsAtraceHostTestCases
 
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
index 88b7841..f6f8dbb 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
@@ -176,8 +176,7 @@
         stopAndFinish(UTILITY_ACTIVITY);
     }
 
-    // Verifies that updating the whitelisting during a lock task mode started with startLockTask
-    // does not kill the locked task.
+    // Verifies that updating the whitelisting during lock task mode finishes the locked task.
     public void testUpdateWhitelisting() {
         mDevicePolicyManager.setLockTaskPackages(getWho(), new String[] { PACKAGE_NAME });
         startLockTask(UTILITY_ACTIVITY);
@@ -191,10 +190,9 @@
             }
         }
 
-        assertLockTaskModeActive();
-        assertTrue(mIsActivityRunning);
-
-        stopAndFinish(UTILITY_ACTIVITY);
+        assertLockTaskModeInactive();
+        assertFalse(mIsActivityRunning);
+        assertFalse(mIsActivityResumed);
     }
 
     // This launches an activity that is in the current task.
@@ -289,20 +287,22 @@
     }
 
     // Test the lockTaskMode flag for an activity declaring if_whitelisted.
-    // An activity locked via manifest argument can finish without calling stopLockTask.
-    public void testManifestArgument_canFinish() {
+    // An activity locked via manifest argument cannot finish without calling stopLockTask.
+    public void testManifestArgument_cannotFinish() {
         mDevicePolicyManager.setLockTaskPackages(getWho(), new String[] { PACKAGE_NAME });
         startAndWait(getLockTaskUtility(UTILITY_ACTIVITY_IF_WHITELISTED));
         waitForResume();
-        finishAndWait(UTILITY_ACTIVITY_IF_WHITELISTED);
 
-        assertFalse(mIsActivityRunning);
-        assertFalse(mIsActivityResumed);
+        // If lock task has not exited then the activity shouldn't actually receive onDestroy.
+        finishAndWait(UTILITY_ACTIVITY_IF_WHITELISTED);
+        assertLockTaskModeActive();
+        assertTrue(mIsActivityRunning);
+
+        stopAndFinish(UTILITY_ACTIVITY_IF_WHITELISTED);
     }
 
     // Test the lockTaskMode flag for an activity declaring if_whitelisted.
-    // When a whitelisting is revoked, an activity locked via manifest argument should be killed
-    // by the system.
+    // Verifies that updating the whitelisting during lock task mode finishes the locked task.
     public void testManifestArgument_updateWhitelisting() {
         mDevicePolicyManager.setLockTaskPackages(getWho(), new String[] { PACKAGE_NAME });
         startAndWait(getLockTaskUtility(UTILITY_ACTIVITY_IF_WHITELISTED));
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java
new file mode 100644
index 0000000..ac96e0b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SystemUpdatePolicyTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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 com.android.cts.deviceowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.SystemUpdatePolicy;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test {@link SystemUpdatePolicy}, {@link DevicePolicyManager#setSystemUpdatePolicy} and
+ * {@link DevicePolicyManager#getSystemUpdatePolicy}
+ */
+public class SystemUpdatePolicyTest extends BaseDeviceOwnerTest {
+
+    private static final int TIMEOUT_MS = 20_000;
+
+    private final Semaphore mPolicyChangedSemaphore = new Semaphore(0);
+    private final BroadcastReceiver policyChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context arg0, Intent arg1) {
+            mPolicyChangedSemaphore.release();
+        }
+    };
+    private boolean mHasFeature;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mHasFeature = Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1;
+
+        if (mHasFeature) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED);
+            mContext.registerReceiver(policyChangedReceiver, filter);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHasFeature) {
+            mContext.unregisterReceiver(policyChangedReceiver);
+            mDevicePolicyManager.setSystemUpdatePolicy(getWho(), null);
+        }
+        super.tearDown();
+    }
+
+    public void testSetEmptytInstallPolicy() {
+        if (!mHasFeature) {
+            return;
+        }
+        testPolicy(null);
+    }
+
+    public void testSetAutomaticInstallPolicy() {
+        if (!mHasFeature) {
+            return;
+        }
+        testPolicy(SystemUpdatePolicy.createAutomaticInstallPolicy());
+    }
+
+    public void testSetWindowedInstallPolicy() {
+        if (!mHasFeature) {
+            return;
+        }
+        testPolicy(SystemUpdatePolicy.createWindowedInstallPolicy(0, 720));
+    }
+
+    public void testSetPostponeInstallPolicy() {
+        if (!mHasFeature) {
+            return;
+        }
+        testPolicy(SystemUpdatePolicy.createPostponeInstallPolicy());
+    }
+
+    public void testShouldFailInvalidWindowPolicy() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        try {
+            SystemUpdatePolicy.createWindowedInstallPolicy(24 * 60 + 1, 720);
+            fail("Invalid window start should not be accepted.");
+        } catch (IllegalArgumentException expected) { }
+        try {
+            SystemUpdatePolicy.createWindowedInstallPolicy(-1, 720);
+            fail("Invalid window start should not be accepted.");
+        } catch (IllegalArgumentException expected) { }
+        try {
+            SystemUpdatePolicy.createWindowedInstallPolicy(0, 24 * 60 + 1);
+            fail("Invalid window end should not be accepted.");
+        } catch (IllegalArgumentException expected) { }
+        try {
+            SystemUpdatePolicy.createWindowedInstallPolicy(0, -1);
+            fail("Invalid window end should not be accepted.");
+        } catch (IllegalArgumentException expected) { }
+    }
+
+    private void testPolicy(SystemUpdatePolicy policy) {
+        mDevicePolicyManager.setSystemUpdatePolicy(getWho(), policy);
+        waitForBroadcast();
+        SystemUpdatePolicy newPolicy = mDevicePolicyManager.getSystemUpdatePolicy();
+        if (policy == null) {
+            assertNull(newPolicy);
+        } else {
+            assertNotNull(newPolicy);
+            assertEquals(policy.toString(), newPolicy.toString());
+            assertEquals(policy.getPolicyType(), newPolicy.getPolicyType());
+            if (policy.getPolicyType() == SystemUpdatePolicy.TYPE_INSTALL_WINDOWED) {
+                assertEquals(policy.getInstallWindowStart(), newPolicy.getInstallWindowStart());
+                assertEquals(policy.getInstallWindowEnd(), newPolicy.getInstallWindowEnd());
+            }
+        }
+    }
+    private void waitForBroadcast() {
+        try {
+            assertTrue("Timeout while waiting for broadcast.",
+                    mPolicyChangedSemaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail("Interrupted while waiting for broadcast.");
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 2c59ed4..bd53e91 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -108,6 +108,10 @@
         }
     }
 
+    public void testSystemUpdatePolicy() throws Exception {
+        executeDeviceOwnerTest("SystemUpdatePolicyTest");
+    }
+
     private void executeDeviceOwnerTest(String testClassName) throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 7d8fa67..255c49e 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -217,6 +217,11 @@
         // If adb is running as root, then the adb uid is 0 instead of SHELL_UID,
         // so the DISALLOW_DEBUGGING_FEATURES restriction does not work and this test
         // fails.
+        if (getDevice().isAdbRoot()) {
+            CLog.logAndDisplay(LogLevel.INFO,
+                    "Cannot test testNoDebuggingFeaturesRestriction() in eng/userdebug build");
+            return;
+        }
         String restriction = "no_debugging_features";  // UserManager.DISALLOW_DEBUGGING_FEATURES
         String command = "add-restriction";
 
diff --git a/hostsidetests/dumpsys/Android.mk b/hostsidetests/dumpsys/Android.mk
index 51ea31f..5b63199 100644
--- a/hostsidetests/dumpsys/Android.mk
+++ b/hostsidetests/dumpsys/Android.mk
@@ -18,8 +18,6 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_MODULE_TAGS := optional
-
 # Must match the package name in CtsTestCaseList.mk
 LOCAL_MODULE := CtsDumpsysHostTestCases
 
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index fec4b40..4e70264 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -49,11 +49,6 @@
      * @throws Exception
      */
     public void testProcstatsOutput() throws Exception {
-        if (mDevice.getApiLevel() < 19) {
-            Log.i(TAG, "No Procstats output before KitKat, skipping test.");
-            return;
-        }
-
         String procstats = mDevice.executeShellCommand("dumpsys procstats -c");
         assertNotNull(procstats);
         assertTrue(procstats.length() > 0);
@@ -338,11 +333,6 @@
      * @throws Exception
      */
     public void testBatterystatsOutput() throws Exception {
-        if (mDevice.getApiLevel() < 21) {
-            Log.i(TAG, "Batterystats output before Lollipop, skipping test.");
-            return;
-        }
-
         String batterystats = mDevice.executeShellCommand("dumpsys batterystats --checkin");
         assertNotNull(batterystats);
         assertTrue(batterystats.length() > 0);
diff --git a/hostsidetests/monkey/src/com/android/cts/monkey/AbstractMonkeyTest.java b/hostsidetests/monkey/src/com/android/cts/monkey/AbstractMonkeyTest.java
old mode 100644
new mode 100755
index 9e04274..b31a32d
--- a/hostsidetests/monkey/src/com/android/cts/monkey/AbstractMonkeyTest.java
+++ b/hostsidetests/monkey/src/com/android/cts/monkey/AbstractMonkeyTest.java
@@ -20,7 +20,7 @@
     /**
      * Base monkey command with flags to avoid side effects like airplane mode.
      */
-    static final String MONKEY_CMD = "monkey --pct-touch 0 --pct-motion 0 --pct-majornav 0 --pct-syskeys 0 --pct-anyevent 0 --pct-rotation 0";
+    static final String MONKEY_CMD = "monkey --pct-motion 0 --pct-majornav 0 --pct-syskeys 0 --pct-anyevent 0 --pct-rotation 0";
 
     IAbi mAbi;
     CtsBuildHelper mBuild;
diff --git a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
index d7bda32..19f19c0 100644
--- a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
+++ b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
@@ -33,11 +33,14 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.lang.Class;
+import java.lang.ReflectiveOperationException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.net.Authenticator;
 import java.net.CookieHandler;
 import java.net.ResponseCache;
+import java.text.DateFormat;
 import java.util.Locale;
 import java.util.Properties;
 import java.util.TimeZone;
@@ -177,11 +180,22 @@
 
     // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
     static class TestEnvironment {
+        private static final Field sDateFormatIs24HourField;
+        static {
+            try {
+                Class<?> dateFormatClass = Class.forName("java.text.DateFormat");
+                sDateFormatIs24HourField = dateFormatClass.getDeclaredField("is24Hour");
+            } catch (ReflectiveOperationException e) {
+                throw new AssertionError("Missing DateFormat.is24Hour", e);
+            }
+        }
+
         private final Locale mDefaultLocale;
         private final TimeZone mDefaultTimeZone;
         private final HostnameVerifier mHostnameVerifier;
         private final SSLSocketFactory mSslSocketFactory;
         private final Properties mProperties = new Properties();
+        private final Boolean mDefaultIs24Hour;
 
         TestEnvironment(Context context) {
             mDefaultLocale = Locale.getDefault();
@@ -196,7 +210,7 @@
             PackageManager pm = context.getPackageManager();
             mProperties.setProperty("android.cts.device.multicast",
                     Boolean.toString(pm.hasSystemFeature(PackageManager.FEATURE_WIFI)));
-
+            mDefaultIs24Hour = getDateFormatIs24Hour();
         }
 
         void reset() {
@@ -209,6 +223,23 @@
             ResponseCache.setDefault(null);
             HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
             HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
+            setDateFormatIs24Hour(mDefaultIs24Hour);
+        }
+
+        private static Boolean getDateFormatIs24Hour() {
+            try {
+                return (Boolean) sDateFormatIs24HourField.get(null);
+            } catch (ReflectiveOperationException e) {
+                throw new AssertionError("Unable to get java.text.DateFormat.is24Hour", e);
+            }
+        }
+
+        private static void setDateFormatIs24Hour(Boolean value) {
+            try {
+                sDateFormatIs24HourField.set(null, value);
+            } catch (ReflectiveOperationException e) {
+                throw new AssertionError("Unable to set java.text.DateFormat.is24Hour", e);
+            }
         }
     }
 
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index b11248a..71bf6cf 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -313,12 +313,14 @@
 
         // create the notification to send
         final int notificationId = 1;
-        final Notification notification = new Notification();
-        notification.icon = android.R.drawable.stat_notify_call_mute;
-        notification.contentIntent = PendingIntent.getActivity(getActivity(), 0, new Intent(),
-                PendingIntent.FLAG_CANCEL_CURRENT);
-        notification.tickerText = message;
-        notification.setLatestEventInfo(getActivity(), "", "", notification.contentIntent);
+        final Notification notification = new Notification.Builder(getActivity())
+                .setSmallIcon(android.R.drawable.stat_notify_call_mute)
+                .setContentIntent(PendingIntent.getActivity(getActivity(), 0, new Intent(),
+                        PendingIntent.FLAG_CANCEL_CURRENT))
+                .setTicker(message)
+                .setContentTitle("")
+                .setContentText("")
+                .build();
 
         // create and populate the expected event
         final AccessibilityEvent expected = AccessibilityEvent.obtain();
diff --git a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 41332c7..7bc08c6 100644
--- a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -133,26 +133,29 @@
                 mDevicePolicyManager.getPasswordQuality(mComponent));
         assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertTrue(mDevicePolicyManager.resetPassword("123", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
-        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+        String caseDescription = "initial";
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
 
         mDevicePolicyManager.setPasswordMinimumLength(mComponent, 10);
+        caseDescription = "minimum password length = 10";
         assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
         assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("abcd1234", caseDescription);
 
-        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 3);
-        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 4);
+        caseDescription = "minimum password length = 4";
+        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+                mComponent));
         assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertTrue(mDevicePolicyManager.resetPassword("123", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
     }
 
     public void testPasswordQuality_numeric() {
@@ -166,26 +169,29 @@
                 mDevicePolicyManager.getPasswordQuality(mComponent));
         assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertTrue(mDevicePolicyManager.resetPassword("123", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
-        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+        String caseDescription = "initial";
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
 
         mDevicePolicyManager.setPasswordMinimumLength(mComponent, 10);
+        caseDescription = "minimum password length = 10";
         assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
         assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("abcd1234", caseDescription);
 
-        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 3);
-        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 4);
+        caseDescription = "minimum password length = 4";
+        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+                mComponent));
         assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertTrue(mDevicePolicyManager.resetPassword("123", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
     }
 
     public void testPasswordQuality_alphabetic() {
@@ -199,26 +205,29 @@
                 mDevicePolicyManager.getPasswordQuality(mComponent));
         assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
-        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+        String caseDescription = "initial";
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
 
         mDevicePolicyManager.setPasswordMinimumLength(mComponent, 10);
+        caseDescription = "minimum password length = 10";
         assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
         assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("abcd1234", caseDescription);
 
-        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 3);
-        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 4);
+        caseDescription = "minimum password length = 4";
+        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+                mComponent));
         assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
     }
 
     public void testPasswordQuality_alphanumeric() {
@@ -232,26 +241,29 @@
                 mDevicePolicyManager.getPasswordQuality(mComponent));
         assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
-        assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
+        String caseDescription = "initial";
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
 
         mDevicePolicyManager.setPasswordMinimumLength(mComponent, 10);
+        caseDescription = "minimum password length = 10";
         assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
         assertFalse(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordFails("abcd1234", caseDescription);
 
-        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 3);
-        assertEquals(3, mDevicePolicyManager.getPasswordMinimumLength(mComponent));
+        mDevicePolicyManager.setPasswordMinimumLength(mComponent, 4);
+        caseDescription = "minimum password length = 4";
+        assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
+                mComponent));
         assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
 
-        assertFalse(mDevicePolicyManager.resetPassword("123", 0));
-        assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
-        assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
+        assertPasswordFails("1234", caseDescription);
+        assertPasswordFails("abcd", caseDescription);
+        assertPasswordSucceeds("abcd1234", caseDescription);
     }
 
     public void testPasswordQuality_complexUpperCase() {
@@ -265,26 +277,29 @@
         resetComplexPasswordRestrictions();
 
         String caseDescription = "minimum UpperCase=0";
-        assertPasswordSucceeds("abc", caseDescription);
-        assertPasswordSucceeds("aBc", caseDescription);
-        assertPasswordSucceeds("ABC", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
+        assertPasswordSucceeds("aBc1", caseDescription);
+        assertPasswordSucceeds("ABC1", caseDescription);
         assertPasswordSucceeds("ABCD", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumUpperCase(mComponent, 1);
         assertEquals(1, mDevicePolicyManager.getPasswordMinimumUpperCase(mComponent));
         caseDescription = "minimum UpperCase=1";
-        assertPasswordFails("abc", caseDescription);
-        assertPasswordSucceeds("aBc", caseDescription);
-        assertPasswordSucceeds("ABC", caseDescription);
+        assertPasswordFails("abc1", caseDescription);
+        assertPasswordSucceeds("aBc1", caseDescription);
+        assertPasswordSucceeds("ABC1", caseDescription);
         assertPasswordSucceeds("ABCD", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumUpperCase(mComponent, 3);
         assertEquals(3, mDevicePolicyManager.getPasswordMinimumUpperCase(mComponent));
         caseDescription = "minimum UpperCase=3";
-        assertPasswordFails("abc", caseDescription);
-        assertPasswordFails("aBC", caseDescription);
-        assertPasswordSucceeds("ABC", caseDescription);
+        assertPasswordFails("abc1", caseDescription);
+        assertPasswordFails("aBC1", caseDescription);
+        assertPasswordSucceeds("ABC1", caseDescription);
         assertPasswordSucceeds("ABCD", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
     }
 
     public void testPasswordQuality_complexLowerCase() {
@@ -299,25 +314,28 @@
 
         String caseDescription = "minimum LowerCase=0";
         assertPasswordSucceeds("ABCD", caseDescription);
-        assertPasswordSucceeds("aBC", caseDescription);
-        assertPasswordSucceeds("abc", caseDescription);
+        assertPasswordSucceeds("aBC1", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumLowerCase(mComponent, 1);
         assertEquals(1, mDevicePolicyManager.getPasswordMinimumLowerCase(mComponent));
         caseDescription = "minimum LowerCase=1";
         assertPasswordFails("ABCD", caseDescription);
-        assertPasswordSucceeds("aBC", caseDescription);
-        assertPasswordSucceeds("abc", caseDescription);
+        assertPasswordSucceeds("aBC1", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumLowerCase(mComponent, 3);
         assertEquals(3, mDevicePolicyManager.getPasswordMinimumLowerCase(mComponent));
         caseDescription = "minimum LowerCase=3";
         assertPasswordFails("ABCD", caseDescription);
-        assertPasswordFails("aBC", caseDescription);
-        assertPasswordSucceeds("abc", caseDescription);
+        assertPasswordFails("aBC1", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
     }
 
     public void testPasswordQuality_complexLetters() {
@@ -332,25 +350,28 @@
 
         String caseDescription = "minimum Letters=0";
         assertPasswordSucceeds("1234", caseDescription);
-        assertPasswordSucceeds("a23", caseDescription);
-        assertPasswordSucceeds("abc", caseDescription);
+        assertPasswordSucceeds("a123", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumLetters(mComponent, 1);
         assertEquals(1, mDevicePolicyManager.getPasswordMinimumLetters(mComponent));
         caseDescription = "minimum Letters=1";
         assertPasswordFails("1234", caseDescription);
-        assertPasswordSucceeds("a23", caseDescription);
-        assertPasswordSucceeds("abc", caseDescription);
+        assertPasswordSucceeds("a123", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumLetters(mComponent, 3);
         assertEquals(3, mDevicePolicyManager.getPasswordMinimumLetters(mComponent));
         caseDescription = "minimum Letters=3";
         assertPasswordFails("1234", caseDescription);
-        assertPasswordFails("a23", caseDescription);
-        assertPasswordSucceeds("abc", caseDescription);
+        assertPasswordFails("a123", caseDescription);
+        assertPasswordSucceeds("abc1", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
     }
 
     public void testPasswordQuality_complexNumeric() {
@@ -365,25 +386,28 @@
 
         String caseDescription = "minimum Numeric=0";
         assertPasswordSucceeds("abcd", caseDescription);
-        assertPasswordSucceeds("1bc", caseDescription);
-        assertPasswordSucceeds("123", caseDescription);
+        assertPasswordSucceeds("1abc", caseDescription);
+        assertPasswordSucceeds("123a", caseDescription);
         assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumNumeric(mComponent, 1);
         assertEquals(1, mDevicePolicyManager.getPasswordMinimumNumeric(mComponent));
         caseDescription = "minimum Numeric=1";
         assertPasswordFails("abcd", caseDescription);
-        assertPasswordSucceeds("1bc", caseDescription);
-        assertPasswordSucceeds("123", caseDescription);
+        assertPasswordSucceeds("1abc", caseDescription);
+        assertPasswordSucceeds("123a", caseDescription);
         assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumNumeric(mComponent, 3);
         assertEquals(3, mDevicePolicyManager.getPasswordMinimumNumeric(mComponent));
         caseDescription = "minimum Numeric=3";
         assertPasswordFails("abcd", caseDescription);
-        assertPasswordFails("1bc", caseDescription);
-        assertPasswordSucceeds("123", caseDescription);
+        assertPasswordFails("1abc", caseDescription);
+        assertPasswordSucceeds("123a", caseDescription);
         assertPasswordSucceeds("1234", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
     }
 
     public void testPasswordQuality_complexSymbols() {
@@ -398,25 +422,28 @@
 
         String caseDescription = "minimum Symbols=0";
         assertPasswordSucceeds("abcd", caseDescription);
-        assertPasswordSucceeds("_bc", caseDescription);
-        assertPasswordSucceeds("@#!", caseDescription);
+        assertPasswordSucceeds("_bc1", caseDescription);
+        assertPasswordSucceeds("@#!1", caseDescription);
         assertPasswordSucceeds("_@#!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumSymbols(mComponent, 1);
         assertEquals(1, mDevicePolicyManager.getPasswordMinimumSymbols(mComponent));
         caseDescription = "minimum Symbols=1";
         assertPasswordFails("abcd", caseDescription);
-        assertPasswordSucceeds("_bc", caseDescription);
-        assertPasswordSucceeds("@#!", caseDescription);
+        assertPasswordSucceeds("_bc1", caseDescription);
+        assertPasswordSucceeds("@#!1", caseDescription);
         assertPasswordSucceeds("_@#!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumSymbols(mComponent, 3);
         assertEquals(3, mDevicePolicyManager.getPasswordMinimumSymbols(mComponent));
         caseDescription = "minimum Symbols=3";
         assertPasswordFails("abcd", caseDescription);
-        assertPasswordFails("_bc", caseDescription);
-        assertPasswordSucceeds("@#!", caseDescription);
+        assertPasswordFails("_bc1", caseDescription);
+        assertPasswordSucceeds("@#!1", caseDescription);
         assertPasswordSucceeds("_@#!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
     }
 
     public void testPasswordQuality_complexNonLetter() {
@@ -435,6 +462,7 @@
         assertPasswordSucceeds("3bcd", caseDescription);
         assertPasswordSucceeds("_@3c", caseDescription);
         assertPasswordSucceeds("_25!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumNonLetter(mComponent, 1);
         assertEquals(1, mDevicePolicyManager.getPasswordMinimumNonLetter(mComponent));
@@ -444,6 +472,7 @@
         assertPasswordSucceeds("3bcd", caseDescription);
         assertPasswordSucceeds("_@3c", caseDescription);
         assertPasswordSucceeds("_25!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
 
         mDevicePolicyManager.setPasswordMinimumNonLetter(mComponent, 3);
         assertEquals(3, mDevicePolicyManager.getPasswordMinimumNonLetter(mComponent));
@@ -451,8 +480,9 @@
         assertPasswordFails("Abcd", caseDescription);
         assertPasswordFails("_bcd", caseDescription);
         assertPasswordFails("3bcd", caseDescription);
-        assertPasswordSucceeds("c_@3c", caseDescription);
+        assertPasswordSucceeds("_@3c", caseDescription);
         assertPasswordSucceeds("_25!", caseDescription);
+        assertPasswordFails("123", caseDescription); // too short
     }
 
     public void testPasswordHistoryLength() {
@@ -1016,9 +1046,13 @@
     }
 
     private void assertPasswordFails(String password, String restriction) {
-        boolean passwordResetResult = mDevicePolicyManager.resetPassword(password, /* flags= */0);
-        assertFalse("Password '" + password + "' should have failed on " + restriction,
-                passwordResetResult);
+        try {
+            boolean passwordResetResult = mDevicePolicyManager.resetPassword(password, /* flags= */0);
+            assertFalse("Password '" + password + "' should have failed on " + restriction,
+                    passwordResetResult);
+        } catch (IllegalArgumentException e) {
+            // yesss, we have failed!
+        }
     }
 
     private void assertPasswordSucceeds(String password, String restriction) {
@@ -1052,4 +1086,17 @@
             assertProfileOwnerMessage(e.getMessage());
         }
     }
+
+    public void testSetSystemUpdatePolicy_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetSystemUpdatePolicy_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setSystemUpdatePolicy(mComponent, null);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
 }
diff --git a/tests/tests/app.usage/AndroidManifest.xml b/tests/tests/app.usage/AndroidManifest.xml
index 11065d4..3bd795a 100644
--- a/tests/tests/app.usage/AndroidManifest.xml
+++ b/tests/tests/app.usage/AndroidManifest.xml
@@ -21,6 +21,9 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.SET_TIME" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
new file mode 100644
index 0000000..c3ba834
--- /dev/null
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -0,0 +1,339 @@
+/**
+ * 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.app.usage.cts;
+
+import android.app.AppOpsManager;
+import android.app.usage.NetworkStatsManager;
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import dalvik.system.SocketTagger;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.net.URL;
+import java.text.MessageFormat;
+import javax.net.ssl.HttpsURLConnection;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+public class NetworkUsageStatsTest extends InstrumentationTestCase {
+    private static final String LOG_TAG = "NetworkUsageStatsTest";
+    private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} " +
+            AppOpsManager.OPSTR_GET_USAGE_STATS + " {1}";
+
+    private static final long MINUTE = 1000 * 60;
+
+    private static final int[] sNetworkTypesToTest = new int[] {
+        ConnectivityManager.TYPE_WIFI,
+        ConnectivityManager.TYPE_MOBILE,
+    };
+
+    // Order corresponds to sNetworkTypesToTest
+    private static final int[] sTransportTypesToTest = new int[] {
+        NetworkCapabilities.TRANSPORT_WIFI,
+        NetworkCapabilities.TRANSPORT_CELLULAR,
+    };
+
+    private NetworkStatsManager mNsm;
+    private PackageManager mPm;
+    private ConnectivityManager mCm;
+    private long mStartTime;
+    private long mEndTime;
+
+    private long mBytesRead;
+
+    private void exerciseRemoteHost(int transportType) throws Exception {
+        final int timeout = 60000;
+        mCm.requestNetwork(new NetworkRequest.Builder()
+            .addTransportType(transportType)
+            .build(), new ConnectivityManager.NetworkCallback() {
+                @Override
+                public void onAvailable(Network network) {
+                    NetworkInfo networkInfo = mCm.getNetworkInfo(network);
+                    if (networkInfo == null) {
+                        Log.w(LOG_TAG, "Network info is null");
+                    } else {
+                        Log.w(LOG_TAG, "Network: " + networkInfo.toString());
+                    }
+                    InputStreamReader in = null;
+                    HttpsURLConnection urlc = null;
+                    String originalKeepAlive = System.getProperty("http.keepAlive");
+                    System.setProperty("http.keepAlive", "false");
+                    try {
+                        urlc = (HttpsURLConnection) network.openConnection(new URL(
+                                "https://www.google.com"));
+                        urlc.setConnectTimeout(timeout);
+                        urlc.setUseCaches(false);
+                        urlc.connect();
+                        boolean ping = urlc.getResponseCode() == 200;
+                        if (ping) {
+                            in = new InputStreamReader(
+                                    (InputStream) urlc.getContent());
+
+                            mBytesRead = 0;
+                            while (in.read() != -1) ++mBytesRead;
+                        }
+                    } catch (Exception e) {
+                        Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
+                    } finally {
+                        if (in != null) {
+                            try {
+                                in.close();
+                            } catch (IOException e) {
+                                // don't care
+                            }
+                        }
+                        if (urlc != null) {
+                            urlc.disconnect();
+                        }
+                        if (originalKeepAlive == null) {
+                            System.clearProperty("http.keepAlive");
+                        } else {
+                            System.setProperty("http.keepAlive", originalKeepAlive);
+                        }
+                    }
+                }
+            });
+        try {
+            Thread.sleep(timeout);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mNsm = (NetworkStatsManager) getInstrumentation().getContext()
+                .getSystemService(Context.NETWORK_STATS_SERVICE);
+
+        mPm = getInstrumentation().getContext().getPackageManager();
+        mCm = (ConnectivityManager) getInstrumentation().getContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private void setAppOpsMode(String mode) throws Exception {
+        final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND,
+                getInstrumentation().getContext().getPackageName(), mode);
+        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+                .executeShellCommand(command);
+        try {
+            Streams.readFully(new FileInputStream(pfd.getFileDescriptor()));
+        } finally {
+            IoUtils.closeQuietly(pfd.getFileDescriptor());
+        }
+    }
+
+    private boolean shouldTestThisNetworkType(int networkTypeIndex) throws Exception {
+        NetworkInfo networkInfo = mCm.getNetworkInfo(sNetworkTypesToTest[networkTypeIndex]);
+        if (networkInfo == null || !networkInfo.isAvailable()) {
+            return false;
+        }
+        mStartTime = System.currentTimeMillis() - MINUTE/2;
+        exerciseRemoteHost(sTransportTypesToTest[networkTypeIndex]);
+        mEndTime = System.currentTimeMillis() + MINUTE/2;
+        return true;
+    }
+
+    public void testDeviceSummary() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats.Bucket bucket = null;
+            try {
+                bucket = mNsm.querySummaryForDevice(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime);
+            } catch (RemoteException | SecurityException e) {
+                fail("testDeviceSummary fails with exception: " + e.toString());
+            }
+            assertTrue(bucket != null);
+            setAppOpsMode("deny");
+            try {
+                bucket = mNsm.querySummaryForDevice(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime);
+                fail("negative testDeviceSummary fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testDeviceSummary fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testUserSummary() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats.Bucket bucket = null;
+            try {
+                bucket = mNsm.querySummaryForUser(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime);
+            } catch (RemoteException | SecurityException e) {
+                fail("testUserSummary fails with exception: " + e.toString());
+            }
+            assertTrue(bucket != null);
+            setAppOpsMode("deny");
+            try {
+                bucket = mNsm.querySummaryForUser(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime);
+                fail("negative testUserSummary fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testUserSummary fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testAppSummary() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats result = null;
+            try {
+                result = mNsm.querySummary(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime);
+                assertTrue(result != null);
+                NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+                long totalTxPackets = 0;
+                long totalRxPackets = 0;
+                long totalTxBytes = 0;
+                long totalRxBytes = 0;
+                while (result.getNextBucket(bucket)) {
+                    if (bucket.getUid() == Process.myUid()) {
+                        totalTxPackets += bucket.getTxPackets();
+                        totalRxPackets += bucket.getRxPackets();
+                        totalTxBytes += bucket.getTxBytes();
+                        totalRxBytes += bucket.getRxBytes();
+                    }
+                }
+                assertTrue("No Rx bytes usage for uid " + Process.myUid(), totalRxBytes > 0);
+                assertTrue("No Rx packets usage for uid " + Process.myUid(), totalRxPackets > 0);
+                assertTrue("No Tx bytes usage for uid " + Process.myUid(), totalTxBytes > 0);
+                assertTrue("No Tx packets usage for uid " + Process.myUid(), totalTxPackets > 0);
+            } catch (RemoteException | SecurityException e) {
+                fail("testAppSummary fails with exception: " + e.toString());
+            } finally {
+                if (result != null) {
+                    result.close();
+                }
+            }
+            setAppOpsMode("deny");
+            try {
+                result = mNsm.querySummary(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime);
+                fail("negative testAppSummary fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testAppSummary fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testAppDetails() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats result = null;
+            try {
+                result = mNsm.queryDetails(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime);
+                assertTrue(result != null);
+            } catch (RemoteException | SecurityException e) {
+                fail("testAppDetails fails with exception: " + e.toString());
+            } finally {
+                if (result != null) {
+                    result.close();
+                }
+            }
+            setAppOpsMode("deny");
+            try {
+                result = mNsm.queryDetails(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime);
+                fail("negative testAppDetails fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testAppDetails fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testUidDetails() throws Exception {
+        for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+            if (!shouldTestThisNetworkType(i)) {
+                continue;
+            }
+            setAppOpsMode("allow");
+            NetworkStats result = null;
+            try {
+                result = mNsm.queryDetailsForUid(
+                        sNetworkTypesToTest[i], "", mStartTime, mEndTime, Process.myUid());
+                assertTrue(result != null);
+            } catch (RemoteException | SecurityException e) {
+                fail("testUidDetails fails with exception: " + e.toString());
+            } finally {
+                if (result != null) {
+                    result.close();
+                }
+            }
+            setAppOpsMode("deny");
+            try {
+                result = mNsm.queryDetailsForUid(
+                        ConnectivityManager.TYPE_WIFI, "", mStartTime, mEndTime, Process.myUid());
+                fail("negative testUidDetails fails: no exception thrown.");
+            } catch (RemoteException e) {
+                fail("testUidDetails fails with exception: " + e.toString());
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+}
diff --git a/tests/tests/app/AndroidManifest.xml b/tests/tests/app/AndroidManifest.xml
index 8e17396..d05648c 100644
--- a/tests/tests/app/AndroidManifest.xml
+++ b/tests/tests/app/AndroidManifest.xml
@@ -18,6 +18,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.cts.app">
 
+    <uses-sdk android:minSdkVersion="11" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.BODY_SENSORS" />
     <application>
diff --git a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
index 23dac25..5781442 100644
--- a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -100,9 +100,6 @@
     }
 
     private void sendNotification(final int id, final int icon) {
-        final Notification notification = new Notification(
-                icon, "No intent", System.currentTimeMillis());
-
         final Intent intent = new Intent(Intent.ACTION_MAIN, Threads.CONTENT_URI);
 
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
@@ -110,8 +107,13 @@
         intent.setAction(Intent.ACTION_MAIN);
 
         final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
-        notification.setLatestEventInfo(mContext, "notify#" + id, "This is #" + id
-                + "notification  ", pendingIntent);
+        final Notification notification = new Notification.Builder(mContext)
+                .setSmallIcon(icon)
+                .setWhen(System.currentTimeMillis())
+                .setContentTitle("notify#" + id)
+                .setContentText("This is #" + id + "notification  ")
+                .setContentIntent(pendingIntent)
+                .build();
         mNotificationManager.notify(id, notification);
 
         StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
diff --git a/tests/tests/app/src/android/app/cts/NotificationTest.java b/tests/tests/app/src/android/app/cts/NotificationTest.java
index c2f62c3..614d7f2 100644
--- a/tests/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/tests/app/src/android/app/cts/NotificationTest.java
@@ -150,6 +150,22 @@
         assertNull(result.sound);
     }
 
+    public void testBuilder() {
+        final Intent intent = new Intent();
+        final PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+        mNotification = new Notification.Builder(mContext)
+                .setSmallIcon(1)
+                .setContentTitle(CONTENT_TITLE)
+                .setContentText(CONTENT_TEXT)
+                .setContentIntent(contentIntent)
+                .build();
+        assertEquals(CONTENT_TEXT, mNotification.extras.getString(Notification.EXTRA_TEXT));
+        assertEquals(CONTENT_TITLE, mNotification.extras.getString(Notification.EXTRA_TITLE));
+        assertEquals(1, mNotification.icon);
+        assertEquals(contentIntent, mNotification.contentIntent);
+        assertNotNull(mNotification.contentView);
+    }
+
     public void testSetLatestEventInfo() {
         mNotification = new Notification();
         mNotification.icon = 1;
diff --git a/tests/tests/content/res/color/color2.xml b/tests/tests/content/res/color/color2.xml
new file mode 100644
index 0000000..154325f
--- /dev/null
+++ b/tests/tests/content/res/color/color2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:color="?attr/testcolor3"/>
+    <item android:color="?attr/testcolor4"/>
+</selector>
diff --git a/tests/tests/content/res/values/attrs.xml b/tests/tests/content/res/values/attrs.xml
index 4c3d9db..ac88ef6 100644
--- a/tests/tests/content/res/values/attrs.xml
+++ b/tests/tests/content/res/values/attrs.xml
@@ -125,6 +125,9 @@
         <attr name="textColorHint"/>
         <attr name="textColorLink"/>
     </declare-styleable>
+    <!-- colors referred by theme. -->
+    <attr name="testcolor3" format="color"/>
+    <attr name="testcolor4" format="color"/>
     <!-- Integer used to uniquely identify theme overrides. -->
     <attr name="themeType" format="integer"/>
     <!-- Theme reference used to override parent theme. -->
diff --git a/tests/tests/content/res/values/styles.xml b/tests/tests/content/res/values/styles.xml
index 72d4047..b27968d 100644
--- a/tests/tests/content/res/values/styles.xml
+++ b/tests/tests/content/res/values/styles.xml
@@ -136,6 +136,8 @@
         <item name="android:windowNoTitle">true</item>
         <item name="android:panelColorForeground">#ff000000</item>
         <item name="android:panelColorBackground">#ffffffff</item>
+        <item name="testcolor3">#ffff0000</item>
+        <item name="testcolor4">#ffffff00</item>
     </style>
 
     <style name="Theme_OverrideOuter">
diff --git a/tests/tests/content/src/android/content/cts/ContextTest.java b/tests/tests/content/src/android/content/cts/ContextTest.java
index c87a3e7..8ee10d4 100644
--- a/tests/tests/content/src/android/content/cts/ContextTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextTest.java
@@ -22,6 +22,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.content.res.Resources.NotFoundException;
@@ -173,6 +174,32 @@
                 mContext.getSystemService(WindowManager.class));
     }
 
+    public void testGetColorStateList() {
+        try {
+            mContext.getColorStateList(0);
+            fail("Failed at testGetColorStateList");
+        } catch (NotFoundException e) {
+            //expected
+        }
+
+        final ColorStateList colorStateList = mContext.getColorStateList(R.color.color2);
+        final int[] focusedState = {android.R.attr.state_focused};
+        final int focusColor = colorStateList.getColorForState(focusedState, R.color.failColor);
+        assertEquals(0xffff0000, focusColor);
+    }
+
+    public void testGetColor() {
+        try {
+            mContext.getColor(0);
+            fail("Failed at testGetColor");
+        } catch (NotFoundException e) {
+            //expected
+        }
+
+        final int color = mContext.getColor(R.color.color2);
+        assertEquals(0xffffff00, color);
+    }
+
     private AttributeSet getAttributeSet(int resourceId) {
         final XmlResourceParser parser = getContext().getResources().getXml(
                 resourceId);
diff --git a/tests/tests/database/src/android/database/cts/AbstractCursorTest.java b/tests/tests/database/src/android/database/cts/AbstractCursorTest.java
index c4371af..14cd6d5 100644
--- a/tests/tests/database/src/android/database/cts/AbstractCursorTest.java
+++ b/tests/tests/database/src/android/database/cts/AbstractCursorTest.java
@@ -376,6 +376,12 @@
         }
     }
 
+    public void testSetExtras() {
+        Bundle b = new Bundle();
+        mTestAbstractCursor.setExtras(b);
+        assertSame(b, mTestAbstractCursor.getExtras());
+    }
+
     @SuppressWarnings("unchecked")
     private static ArrayList<ArrayList> createTestList(int rows, int cols) {
         ArrayList<ArrayList> list = new ArrayList<ArrayList>();
diff --git a/tests/tests/database/src/android/database/cts/CursorWrapperTest.java b/tests/tests/database/src/android/database/cts/CursorWrapperTest.java
index 9d517dc..7c0ce56 100644
--- a/tests/tests/database/src/android/database/cts/CursorWrapperTest.java
+++ b/tests/tests/database/src/android/database/cts/CursorWrapperTest.java
@@ -402,7 +402,7 @@
         cursorWrapper.close();
     }
 
-    public void testContentObsererOperations() throws IllegalStateException {
+    public void testContentObserverOperations() throws IllegalStateException {
         CursorWrapper cursorWrapper = new CursorWrapper(getCursor());
         MockContentObserver observer = new MockContentObserver(null);
 
@@ -437,6 +437,18 @@
         cursorWrapper.close();
     }
 
+    public void testSetExtras() {
+        Cursor cursor = getCursor();
+        CursorWrapper cursorWrapper = new CursorWrapper(cursor);
+        try {
+            Bundle b = new Bundle();
+            cursorWrapper.setExtras(b);
+            assertSame(b, cursor.getExtras());
+        } finally {
+            cursorWrapper.close();
+        }
+    }
+
     private class MockContentObserver extends ContentObserver {
 
         public MockContentObserver(Handler handler) {
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index d5391a3..1dd6777 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -1681,6 +1681,115 @@
         mCanvas.drawText(t5, 0, 7, 10, 30, mPaint);
     }
 
+    public void testDrawTextRun() {
+        final String text = "android";
+        final Paint paint = new Paint();
+
+        mCanvas.drawTextRun(text, 0, 0, 0, 0, 0.0f, 0.0f, false, paint);
+        mCanvas.drawTextRun(text, 0, text.length(), 0, text.length(), 0.0f, 0.0f, false, paint);
+        mCanvas.drawTextRun(text, text.length(), text.length(), text.length(), text.length(),
+                0.0f, 0.0f, false, paint);
+
+        try {
+            mCanvas.drawTextRun((char[])null, 0, 0, 0, 0, 0.0f, 0.0f, false, paint);
+            fail("should throw out NullPointerException because text is null");
+        } catch (NullPointerException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun((CharSequence)null, 0, 0, 0, 0, 0.0f, 0.0f, false, paint);
+            fail("should throw out NullPointerException because text is null");
+        } catch (NullPointerException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text.toCharArray(), 0, 0, 0, 0, 0.0f, 0.0f, false, null);
+            fail("should throw out NullPointerException because paint is null");
+        } catch (NullPointerException e) {
+        }
+        try {
+            mCanvas.drawTextRun(text, 0, 0, 0, 0, 0.0f, 0.0f, false, null);
+            fail("should throw out NullPointerException because paint is null");
+        } catch (NullPointerException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text.toCharArray(), -1, text.length(), 0, text.length(), 0.0f, 0.0f,
+                    false, paint);
+            fail("should throw out IndexOutOfBoundsException because index is less than 0");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text.toCharArray(), 0, -1, 0, text.length(), 0.0f, 0.0f, false,
+                    paint);
+            fail("should throw out IndexOutOfBoundsException because count is less than 0");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text.toCharArray(), 0, text.length(), 1, text.length(), 0.0f, 0.0f,
+                    false, paint);
+            fail("should throw out IndexOutOfBoundsException because contextIndex is bigger than "
+                    + "index");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+
+        try {
+            mCanvas.drawTextRun(text, 0, text.length(), 0, text.length() - 1, 0.0f, 0.0f, false,
+                    paint);
+            fail("should throw out IndexOutOfBoundsException because contexIndex + contextCount "
+                    + "is less than index + count");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text.toCharArray(), 0, text.length() + 1, 0, text.length() + 1,
+                    0.0f, 0.0f, false, paint);
+            fail("should throw out IndexOutOfBoundsException because index + count is bigger than "
+                    + "text length");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text, 0, text.length(), -1, text.length(), 0.0f, 0.0f, false,
+                    paint);
+            fail("should throw out IndexOutOfBoundsException because contextStart is less than 0");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text, 0, text.length(), 1, text.length(), 0.0f, 0.0f, false,
+                    paint);
+            fail("should throw out IndexOutOfBoundsException because start is less than "
+                    + "contextStart");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text, 1, 0, 0, text.length(), 0.0f, 0.0f, false,
+                    paint);
+            fail("should throw out IndexOutOfBoundsException because end is less than start");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text, 0, text.length(), 0, text.length() - 1, 0.0f, 0.0f, false,
+                    paint);
+            fail("should throw out IndexOutOfBoundsException because contextEnd is less than end");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            mCanvas.drawTextRun(text, 0, text.length(), 0, text.length() + 1, 0.0f, 0.0f, false,
+                    paint);
+            fail("should throw out IndexOutOfBoundsException because contextEnd is bigger than "
+                    + "text length");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
     public void testDrawPosText1() {
         final char[] text = {
                 'a', 'n', 'd', 'r', 'o', 'i', 'd'
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index a4a7bfd..8d96d91 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.test.AndroidTestCase;
 import android.text.SpannedString;
+import android.util.Log;
 
 import java.util.Locale;
 
@@ -1064,4 +1065,300 @@
         // TODO: when we support variation selectors, add positive tests
     }
 
+    public void testGetRunAdvance() {
+        Paint p = new Paint();
+        {
+            // LTR
+            String string = "abcdef";
+            {
+                final float width = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), false, 0);
+                assertEquals(0.0f, width);
+            }
+            {
+                final float widthToMid = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), false, string.length() / 2);
+                final float widthToTail = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), false, string.length());
+                assertTrue(widthToMid > 0.0f);
+                assertTrue(widthToTail > widthToMid);
+            }
+            {
+                final float widthFromHead = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), false, string.length());
+                final float widthFromSecond = p.getRunAdvance(string, 1, string.length(), 0,
+                        string.length(), false, string.length());
+                assertTrue(widthFromHead > widthFromSecond);
+            }
+        }
+        {
+            // RTL
+            String string = "\u0644\u063A\u0629 \u0639\u0631\u0628\u064A\u0629"; // Arabic
+            {
+                final float widthToMid = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), true, string.length() / 2);
+                final float widthToTail = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), true, string.length());
+                assertTrue(widthToMid > 0.0f);
+                assertTrue(widthToTail > widthToMid);
+            }
+            {
+                final float widthFromHead = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), true, string.length());
+                final float widthFromSecond = p.getRunAdvance(string, 1, string.length(), 0,
+                        string.length(), true, string.length());
+                assertTrue(widthFromHead > widthFromSecond);
+            }
+        }
+    }
+
+    public void testGetRunAdvance_invalidArguments() {
+        Paint p = new Paint();
+        try {
+            p.getRunAdvance((CharSequence)null, 0, 0, 0, 0, false, 0);
+            fail("Should throw an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+        } catch (Exception e) {
+            fail("Should throw an IllegalArgumentException.");
+        }
+
+        try {
+            p.getRunAdvance((char[])null, 0, 0, 0, 0, false, 0);
+            fail("Should throw an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+        } catch (Exception e) {
+            fail("Should throw an IllegalArgumentException.");
+        }
+
+        final String string = "abcde";
+
+        try {
+            // text length < context end
+            p.getRunAdvance(string, 0, string.length(), 0, string.length() + 1, false,
+                    string.length());
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+        try {
+            // context end < end
+            p.getRunAdvance(string, 0, string.length(), 0, string.length() - 1, false, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+        try {
+            // end < offset
+            p.getRunAdvance(string, 0, string.length() - 1, 0, string.length() - 1, false,
+                    string.length());
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+        try {
+            // offset < start
+            p.getRunAdvance(string, 1, string.length(), 1, string.length(), false, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+        try {
+            // start < context start
+            p.getRunAdvance(string, 0, string.length(), 1, string.length(), false, 1);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+        try {
+            // context start < 0
+            p.getRunAdvance(string, 0, string.length(), -1, string.length(), false, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+    }
+
+    public void testGetOffsetForAdvance() {
+        Paint p = new Paint();
+        {
+            // LTR
+            String string = "abcdef";
+            {
+                for (int offset = 0; offset <= string.length(); ++offset) {
+                    final float widthToOffset = p.getRunAdvance(string, 0,
+                            string.length(), 0, string.length(), false, offset);
+                    final int restoredOffset = p.getOffsetForAdvance(string, 0,
+                            string.length(), 0, string.length(), false, widthToOffset);
+                    assertEquals(offset, restoredOffset);
+                }
+            }
+            {
+                final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
+                        string.length(), false, -10.0f);
+                assertEquals(0, offset);
+            }
+            {
+                final float widthToEnd = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), true, string.length());
+                final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
+                        string.length(), true, widthToEnd + 10.0f);
+                assertEquals(string.length(), offset);
+            }
+        }
+        {
+            // RTL
+            String string = "\u0639\u0631\u0628\u0649"; // Arabic
+            {
+                for (int offset = 0; offset <= string.length(); ++offset) {
+                    final float widthToOffset = p.getRunAdvance(string, 0,
+                            string.length(), 0, string.length(), true, offset);
+                    final int restoredOffset = p.getOffsetForAdvance(string, 0,
+                            string.length(), 0, string.length(), true, widthToOffset);
+                    assertEquals(offset, restoredOffset);
+                }
+            }
+            {
+                final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
+                        string.length(), true, -10.0f);
+                assertEquals(0, offset);
+            }
+            {
+                final float widthToEnd = p.getRunAdvance(string, 0, string.length(), 0,
+                        string.length(), true, string.length());
+                final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
+                        string.length(), true, widthToEnd + 10.0f);
+                assertEquals(string.length(), offset);
+            }
+        }
+    }
+
+    public void testGetOffsetForAdvance_invalidArguments() {
+        Paint p = new Paint();
+        try {
+            p.getOffsetForAdvance((CharSequence)null, 0, 0, 0, 0, false, 0.0f);
+            fail("Should throw an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+        } catch (Exception e) {
+            fail("Should throw an IllegalArgumentException.");
+        }
+        try {
+            p.getOffsetForAdvance((char[])null, 0, 0, 0, 0, false, 0.0f);
+            fail("Should throw an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+        } catch (Exception e) {
+            fail("Should throw an IllegalArgumentException.");
+        }
+
+        final String string = "abcde";
+
+        try {
+            // context start < 0
+            p.getOffsetForAdvance(string, -1, string.length(), 0, string.length(), false, 0.0f);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+
+        try {
+            // start < context start
+            p.getOffsetForAdvance(string, 0, string.length(), 1, string.length(), false, 0.0f);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+
+        try {
+            // end < start
+            p.getOffsetForAdvance(string, 1, 0, 0, 0, false, 0);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+
+        try {
+            // context end < end
+            p.getOffsetForAdvance(string, 0, string.length(), 0, string.length() - 1, false, 0.0f);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+
+        try {
+            // text length < context end
+            p.getOffsetForAdvance(string, 0, string.length(), 0, string.length() + 1, false, 0.0f);
+            fail("Should throw an IndexOutOfBoundsException.");
+        } catch (IndexOutOfBoundsException e) {
+        } catch (Exception e) {
+            fail("Should throw an IndexOutOfBoundsException.");
+        }
+    }
+
+    public void testGetOffsetForAdvance_grahpemeCluster() {
+        Paint p = new Paint();
+        {
+            String string = "\uD83C\uDF37"; // U+1F337: TULIP
+            {
+                final float widthToOffset = p.getRunAdvance(string, 0,
+                        string.length(), 0, string.length(), false, 1);
+                final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
+                        string.length(), false, widthToOffset);
+                assertFalse(1 == offset);
+                assertTrue(0 == offset || string.length() == offset);
+            }
+        }
+        {
+            String string = "\uD83C\uDDFA\uD83C\uDDF8"; // US flag
+            {
+                final float widthToOffset = p.getRunAdvance(string, 0,
+                        string.length(), 0, string.length(), false, 2);
+                final int offset = p.getOffsetForAdvance(string, 0, string.length(), 0,
+                        string.length(), false, widthToOffset);
+                assertFalse(2 == offset);
+                assertTrue(0 == offset || string.length() == offset);
+            }
+            {
+                final float widthToOffset = p.getRunAdvance(string, 0, 2, 0, 2, false, 2);
+                final int offset = p.getOffsetForAdvance(string, 0, 2,
+                        0, 2, false, widthToOffset);
+                assertEquals(2, offset);
+            }
+        }
+        {
+            // HANGUL CHOSEONG KIYEOK, HANGUL JUNGSEONG A, HANDUL JONGSEONG KIYEOK
+            String string = "\u1100\u1161\u11A8";
+            {
+                for (int offset = 0; offset <= string.length(); ++offset) {
+                    final float widthToOffset = p.getRunAdvance(string, 0,
+                            string.length(), 0, string.length(), false, offset);
+                    final int offsetForAdvance = p.getOffsetForAdvance(string, 0, string.length(),
+                            0, string.length(), false, widthToOffset);
+                    assertTrue(0 == offsetForAdvance || string.length() == offsetForAdvance);
+                }
+                for (int offset = 0; offset <= string.length(); ++offset) {
+                    final float widthToOffset = p.getRunAdvance(string, 0, offset, 0, offset,
+                            false, offset);
+                    final int offsetForAdvance = p.getOffsetForAdvance(string, 0, string.length(),
+                            0, string.length(), false, widthToOffset);
+                    assertTrue(0 == offsetForAdvance || string.length() == offsetForAdvance);
+                }
+                for (int offset = 0; offset <= string.length(); ++offset) {
+                    final float widthToOffset = p.getRunAdvance(string, 0, offset, 0, offset,
+                            false, offset);
+                    final int offsetForAdvance = p.getOffsetForAdvance(string, 0, offset, 0,
+                            offset, false, widthToOffset);
+                    assertEquals(offset, offsetForAdvance);
+                }
+            }
+        }
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
index c895d0d..457c688 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -21,7 +21,6 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
@@ -135,6 +134,22 @@
         assertTrue(bitmapDrawable.getPaint().isFilterBitmap());
     }
 
+    public void testIsFilterBitmap() {
+        InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
+        BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
+
+        assertTrue(bitmapDrawable.isFilterBitmap());
+
+        bitmapDrawable.setFilterBitmap(false);
+        assertFalse(bitmapDrawable.isFilterBitmap());
+        assertEquals(bitmapDrawable.isFilterBitmap(), bitmapDrawable.getPaint().isFilterBitmap());
+
+
+        bitmapDrawable.setFilterBitmap(true);
+        assertTrue(bitmapDrawable.isFilterBitmap());
+        assertEquals(bitmapDrawable.isFilterBitmap(), bitmapDrawable.getPaint().isFilterBitmap());
+    }
+
     public void testSetDither() {
         InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
         BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
@@ -149,6 +164,21 @@
 
     }
 
+    public void testGetDither() {
+        InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
+        BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
+
+        assertTrue(bitmapDrawable.getPaint().isDither());
+
+        bitmapDrawable.setDither(false);
+        assertFalse(bitmapDrawable.isDither());
+        assertEquals(bitmapDrawable.isDither(), bitmapDrawable.getPaint().isDither());
+
+        bitmapDrawable.setDither(true);
+        assertTrue(bitmapDrawable.isDither());
+        assertEquals(bitmapDrawable.isDither(), bitmapDrawable.getPaint().isDither());
+    }
+
     public void testAccessTileMode() {
         InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
         BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
index 036c756..0d3c22f 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
@@ -20,7 +20,6 @@
 
 import java.util.Arrays;
 
-import android.content.res.ColorStateList;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
@@ -185,6 +184,63 @@
         assertTrue(dr.hasSetDitherCalled());
     }
 
+    public void testGetDither() {
+        assertConstantStateNotSet();
+        assertNull(mDrawableContainer.getCurrent());
+
+        mDrawableContainer.setConstantState(mDrawableContainerState);
+
+        MockDrawable dr = new MockDrawable();
+        addAndSelectDrawable(dr);
+
+
+        mDrawableContainer.setDither(true);
+        assertTrue(mDrawableContainer.isDither());
+
+        mDrawableContainer.setDither(false);
+        assertFalse(mDrawableContainer.isDither());
+
+        dr.reset();
+    }
+
+    public void testSetHotspotBounds() {
+        Rect bounds = new Rect(10, 15, 100, 150);
+        assertConstantStateNotSet();
+        assertNull(mDrawableContainer.getCurrent());
+
+        mDrawableContainer.setConstantState(mDrawableContainerState);
+
+        MockDrawable dr = new MockDrawable();
+        addAndSelectDrawable(dr);
+
+        dr.reset();
+        mDrawableContainer.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        Rect outRect = new Rect();
+        mDrawableContainer.getHotspotBounds(outRect);
+        assertEquals(bounds, outRect);
+
+        dr.reset();
+    }
+
+    public void testGetHotspotBounds() {
+        Rect bounds = new Rect(10, 15, 100, 150);
+        assertConstantStateNotSet();
+        assertNull(mDrawableContainer.getCurrent());
+
+        mDrawableContainer.setConstantState(mDrawableContainerState);
+
+        MockDrawable dr = new MockDrawable();
+        addAndSelectDrawable(dr);
+
+        dr.reset();
+        mDrawableContainer.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        Rect outRect = new Rect();
+        mDrawableContainer.getHotspotBounds(outRect);
+        assertEquals(bounds, outRect);
+
+        dr.reset();
+    }
+
     public void testSetColorFilter() {
         assertConstantStateNotSet();
         assertNull(mDrawableContainer.getCurrent());
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
index a48372e..608805a 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
@@ -16,6 +16,7 @@
 
 package android.graphics.drawable.cts;
 
+import android.view.View;
 import com.android.cts.graphics.R;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -545,6 +546,54 @@
         mockDrawable.setDither(false);
     }
 
+    public void testGetDither() {
+        MockDrawable mockDrawable = new MockDrawable();
+
+        // isDither simply returns false for Drawable superclass
+        assertFalse((mockDrawable.isDither()));
+    }
+
+    public void testSetHotspotBounds() {
+        MockDrawable mockDrawable = new MockDrawable();
+
+        // setHotspotBounds is a non-operation function.
+        mockDrawable.setHotspotBounds(10, 15, 100, 150);
+    }
+
+    public void testGetHotspotBounds() {
+        MockDrawable mockDrawable = new MockDrawable();
+
+        // getHotspotBounds doesn't do anything interesting in the Drawable superclass
+        mockDrawable.getHotspotBounds(new Rect());
+    }
+
+    public void testSetLayoutDirection() {
+        MockDrawable mockDrawable = new MockDrawable();
+
+        mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+        assertEquals(View.LAYOUT_DIRECTION_LTR, mockDrawable.getLayoutDirection());
+
+        mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+        assertEquals(View.LAYOUT_DIRECTION_RTL, mockDrawable.getLayoutDirection());
+    }
+
+    public void testGetLayoutDirection() {
+        MockDrawable mockDrawable = new MockDrawable();
+
+        mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
+        assertEquals(View.LAYOUT_DIRECTION_LTR, mockDrawable.getLayoutDirection());
+
+        mockDrawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+        assertEquals(View.LAYOUT_DIRECTION_RTL, mockDrawable.getLayoutDirection());
+    }
+
+    public void testOnLayoutDirectionChanged() {
+        MockDrawable mockDrawable = new MockDrawable();
+
+        // onLayoutDirectionChanged is a non-operation function.
+        mockDrawable.onLayoutDirectionChanged(View.LAYOUT_DIRECTION_LTR);
+    }
+
     public void testSetFilterBitmap() {
         MockDrawable mockDrawable = new MockDrawable();
 
@@ -552,6 +601,13 @@
         mockDrawable.setFilterBitmap(false);
     }
 
+    public void testIsFilterBitmap() {
+        MockDrawable mockDrawable = new MockDrawable();
+
+        // setFilterBitmap is a non-operation function.
+        mockDrawable.isFilterBitmap();
+    }
+
     public void testUnscheduleSelf() {
         MockDrawable mockDrawable = new MockDrawable();
         MockCallback mockCallback = new MockCallback();
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
new file mode 100644
index 0000000..40680c1
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
@@ -0,0 +1,549 @@
+/*
+ * 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.graphics.drawable.cts;
+
+import android.graphics.drawable.DrawableWrapper;
+import com.android.cts.graphics.R;
+
+
+import java.util.Arrays;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Drawable.ConstantState;
+import android.test.AndroidTestCase;
+import android.util.StateSet;
+
+public class DrawableWrapperTest extends AndroidTestCase {
+
+    static class MyWrapper extends DrawableWrapper {
+        public MyWrapper(Drawable dr) {
+            super(dr);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testConstructor() {
+        Drawable d = new BitmapDrawable();
+        MyWrapper wrapper = new MyWrapper(d);
+        assertSame(d, wrapper.getDrawable());
+
+        new MyWrapper(null);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetDrawable() {
+        Drawable d = new BitmapDrawable();
+        MyWrapper wrapper = new MyWrapper(d);
+        assertSame(d, wrapper.getDrawable());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetDrawable() {
+        Drawable d = new BitmapDrawable();
+        MyWrapper wrapper = new MyWrapper(null);
+        assertSame(null, wrapper.getDrawable());
+
+        wrapper.setDrawable(d);
+        assertSame(d, wrapper.getDrawable());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testInvalidateDrawable() {
+        MyWrapper wrapper = new MyWrapper(new BitmapDrawable());
+
+        MockCallback cb = new MockCallback();
+        wrapper.setCallback(cb);
+        wrapper.invalidateDrawable(null);
+        assertTrue(cb.hasCalledInvalidate());
+
+        cb.reset();
+        wrapper.invalidateDrawable(new BitmapDrawable());
+        assertTrue(cb.hasCalledInvalidate());
+
+        cb.reset();
+        wrapper.setCallback(null);
+        wrapper.invalidateDrawable(null);
+        assertFalse(cb.hasCalledInvalidate());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testScheduleDrawable() {
+        MyWrapper wrapper = new MyWrapper(new BitmapDrawable());
+
+        MockCallback cb = new MockCallback();
+        wrapper.setCallback(cb);
+        wrapper.scheduleDrawable(null, null, 0);
+        assertTrue(cb.hasCalledSchedule());
+
+        cb.reset();
+        wrapper.scheduleDrawable(new BitmapDrawable(), new Runnable() {
+            public void run() {
+            }
+        }, 1000L);
+        assertTrue(cb.hasCalledSchedule());
+
+        cb.reset();
+        wrapper.setCallback(null);
+        wrapper.scheduleDrawable(null, null, 0);
+        assertFalse(cb.hasCalledSchedule());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testUnscheduleDrawable() {
+        MyWrapper wrapper = new MyWrapper(new BitmapDrawable());
+
+        MockCallback cb = new MockCallback();
+        wrapper.setCallback(cb);
+        wrapper.unscheduleDrawable(null, null);
+        assertTrue(cb.hasCalledUnschedule());
+
+        cb.reset();
+        wrapper.unscheduleDrawable(new BitmapDrawable(), new Runnable() {
+            public void run() {
+            }
+        });
+        assertTrue(cb.hasCalledUnschedule());
+
+        cb.reset();
+        wrapper.setCallback(null);
+        wrapper.unscheduleDrawable(null, null);
+        assertFalse(cb.hasCalledUnschedule());
+    }
+
+    private static class MockCallback implements Drawable.Callback {
+        private boolean mCalledInvalidate;
+        private boolean mCalledSchedule;
+        private boolean mCalledUnschedule;
+
+        public void invalidateDrawable(Drawable who) {
+            mCalledInvalidate = true;
+        }
+
+        public void scheduleDrawable(Drawable who, Runnable what, long when) {
+            mCalledSchedule = true;
+        }
+
+        public void unscheduleDrawable(Drawable who, Runnable what) {
+            mCalledUnschedule = true;
+        }
+
+        public boolean hasCalledInvalidate() {
+            return mCalledInvalidate;
+        }
+
+        public boolean hasCalledSchedule() {
+            return mCalledSchedule;
+        }
+
+        public boolean hasCalledUnschedule() {
+            return mCalledUnschedule;
+        }
+
+        public int getResolvedLayoutDirection(Drawable who) {
+            return 0;
+        }
+
+        public void reset() {
+            mCalledInvalidate = false;
+            mCalledSchedule = false;
+            mCalledUnschedule = false;
+        }
+    }
+
+    public void testDraw() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+        wrapper.draw(new Canvas());
+        assertTrue(mockDrawable.hasCalledDraw());
+
+        mockDrawable.reset();
+        wrapper.draw(null);
+        assertTrue(mockDrawable.hasCalledDraw());
+    }
+
+    public void testGetChangingConfigurations() {
+        final int SUPER_CONFIG = 1;
+        final int CONTAINED_DRAWABLE_CONFIG = 2;
+
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+        assertEquals(0, wrapper.getChangingConfigurations());
+
+        mockDrawable.setChangingConfigurations(CONTAINED_DRAWABLE_CONFIG);
+        assertEquals(CONTAINED_DRAWABLE_CONFIG, wrapper.getChangingConfigurations());
+
+        wrapper.setChangingConfigurations(SUPER_CONFIG);
+        assertEquals(SUPER_CONFIG | CONTAINED_DRAWABLE_CONFIG,
+                wrapper.getChangingConfigurations());
+    }
+
+    public void testGetPadding() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+        // this method will call contained drawable's getPadding method.
+        wrapper.getPadding(new Rect());
+        assertTrue(mockDrawable.hasCalledGetPadding());
+
+        // input null as param
+        try {
+            wrapper.getPadding(null);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+        }
+    }
+
+    public void testSetVisible() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+        assertTrue(wrapper.isVisible());
+
+        assertTrue(wrapper.setVisible(false, false));
+        assertFalse(wrapper.isVisible());
+        assertTrue(mockDrawable.hasCalledSetVisible());
+
+        mockDrawable.reset();
+        assertFalse(wrapper.setVisible(false, false));
+        assertFalse(wrapper.isVisible());
+        assertTrue(mockDrawable.hasCalledSetVisible());
+
+        mockDrawable.reset();
+        assertTrue(wrapper.setVisible(true, false));
+        assertTrue(wrapper.isVisible());
+        assertTrue(mockDrawable.hasCalledSetVisible());
+    }
+
+    public void testSetAlpha() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+        // this method will call contained drawable's setAlpha method.
+        wrapper.setAlpha(100);
+        assertTrue(mockDrawable.hasCalledSetAlpha());
+
+        mockDrawable.reset();
+        wrapper.setAlpha(Integer.MAX_VALUE);
+        assertTrue(mockDrawable.hasCalledSetAlpha());
+
+        mockDrawable.reset();
+        wrapper.setAlpha(-1);
+        assertTrue(mockDrawable.hasCalledSetAlpha());
+    }
+
+    public void testSetColorFilter() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+        // this method will call contained drawable's setColorFilter method.
+        wrapper.setColorFilter(new ColorFilter());
+        assertTrue(mockDrawable.hasCalledSetColorFilter());
+
+        mockDrawable.reset();
+        wrapper.setColorFilter(null);
+        assertTrue(mockDrawable.hasCalledSetColorFilter());
+    }
+
+    public void testGetOpacity() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+        // This method will call contained drawable's getOpacity method.
+        wrapper.setLevel(1);
+        wrapper.getOpacity();
+        assertTrue(mockDrawable.hasCalledGetOpacity());
+    }
+
+    public void testIsStateful() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+        // this method will call contained drawable's isStateful method.
+        wrapper.isStateful();
+        assertTrue(mockDrawable.hasCalledIsStateful());
+    }
+
+    public void testOnStateChange() {
+        Drawable d = new MockDrawable();
+        MockDrawableWrapper wrapper = new MockDrawableWrapper(d);
+        assertEquals("initial child state is empty", d.getState(), StateSet.WILD_CARD);
+
+        int[] state = new int[] {1, 2, 3};
+        assertFalse("child did not change", wrapper.onStateChange(state));
+        assertEquals("child state did not change", d.getState(), StateSet.WILD_CARD);
+
+        d = mContext.getDrawable(R.drawable.statelistdrawable);
+        wrapper = new MockDrawableWrapper(d);
+        assertEquals("initial child state is empty", d.getState(), StateSet.WILD_CARD);
+        wrapper.onStateChange(state);
+        assertTrue("child state changed", Arrays.equals(state, d.getState()));
+
+        // input null as param
+        wrapper.onStateChange(null);
+        // expected, no Exception thrown out, test success
+    }
+
+    public void testOnLevelChange() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MockDrawableWrapper mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
+
+        assertEquals(0, mockDrawable.getLevel());
+        assertFalse(mockDrawableWrapper.onLevelChange(0));
+        assertFalse(mockDrawable.hasCalledOnLevelChange());
+
+        assertFalse(mockDrawableWrapper.onLevelChange(1000));
+        assertTrue(mockDrawable.hasCalledOnLevelChange());
+        assertEquals(1000, mockDrawable.getLevel());
+
+        mockDrawable.reset();
+        mockDrawableWrapper.reset();
+        assertFalse(mockDrawableWrapper.onLevelChange(Integer.MIN_VALUE));
+        assertTrue(mockDrawable.hasCalledOnLevelChange());
+    }
+
+    public void testOnBoundsChange() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MockDrawableWrapper mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
+        Rect bounds = new Rect(2, 2, 26, 32);
+        mockDrawable.setBounds(bounds);
+        mockDrawableWrapper.onBoundsChange(bounds);
+
+        mockDrawableWrapper = new MockDrawableWrapper(mockDrawable);
+        mockDrawable.setBounds(bounds);
+        mockDrawableWrapper.onBoundsChange(bounds);
+        assertEquals(bounds.left, mockDrawable.getBounds().left);
+        assertEquals(bounds.top, mockDrawable.getBounds().top);
+        assertEquals(bounds.right, mockDrawable.getBounds().right);
+        assertEquals(bounds.bottom, mockDrawable.getBounds().bottom);
+
+        bounds = mockDrawable.getBounds();
+        assertEquals(2, bounds.left);
+        assertEquals(2, bounds.top);
+        assertEquals(26, bounds.right);
+        assertEquals(32, bounds.bottom);
+
+        // input null as param
+        try {
+            mockDrawableWrapper.onBoundsChange(null);
+            fail("There should be a NullPointerException thrown out.");
+        } catch (NullPointerException e) {
+            // expected, test success
+        }
+
+    }
+
+    public void testGetIntrinsicWidth() {
+        MockDrawable mockDrawable = new MockDrawable();
+        MyWrapper wrapper = new MyWrapper(mockDrawable);
+
+        // this method will call contained drawable's getIntrinsicWidth method.
+        wrapper.getIntrinsicWidth();
+        assertTrue(mockDrawable.hasCalledGetIntrinsicWidth());
+    }
+
+    public void testGetIntrinsicHeight() {
+        MockDrawable mockDrawable = new MockDrawable();
+        DrawableWrapper wrapper = new MyWrapper(mockDrawable);
+
+        // this method will call contained drawable's getIntrinsicHeight method.
+        wrapper.getIntrinsicHeight();
+        assertTrue(mockDrawable.hasCalledGetIntrinsicHeight());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetConstantState() {
+        DrawableWrapper wrapper = new MyWrapper(new BitmapDrawable());
+        ConstantState constantState = wrapper.getConstantState();
+    }
+
+    private static class MockDrawable extends Drawable {
+        private boolean mCalledDraw = false;
+        private boolean mCalledGetPadding = false;
+        private boolean mCalledSetVisible = false;
+        private boolean mCalledSetAlpha = false;
+        private boolean mCalledGetOpacity = false;
+        private boolean mCalledSetColorFilter = false;
+        private boolean mCalledIsStateful = false;
+        private boolean mCalledGetIntrinsicWidth = false;
+        private boolean mCalledGetIntrinsicHeight = false;
+        private boolean mCalledSetState = false;
+        private boolean mCalledOnLevelChange = false;
+
+        @Override
+        public void draw(Canvas canvas) {
+            mCalledDraw = true;
+        }
+
+        @Override
+        public int getOpacity() {
+            mCalledGetOpacity = true;
+            return 0;
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            mCalledSetAlpha = true;
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {
+            mCalledSetColorFilter = true;
+        }
+
+        @Override
+        public boolean getPadding(Rect padding) {
+            mCalledGetPadding = true;
+            return super.getPadding(padding);
+        }
+
+        @Override
+        public boolean setVisible(boolean visible, boolean restart) {
+            mCalledSetVisible = true;
+            return super.setVisible(visible, restart);
+        }
+
+        @Override
+        public boolean isStateful() {
+            mCalledIsStateful = true;
+            return super.isStateful();
+        }
+
+        @Override
+        public int getIntrinsicWidth() {
+            mCalledGetIntrinsicWidth = true;
+            return super.getIntrinsicWidth();
+        }
+
+        @Override
+        public int getIntrinsicHeight() {
+            mCalledGetIntrinsicHeight = true;
+            return super.getIntrinsicHeight();
+
+        }
+
+        @Override
+        public boolean setState(final int[] stateSet) {
+            mCalledSetState = true;
+            return super.setState(stateSet);
+        }
+
+        @Override
+        protected boolean onLevelChange(int level) {
+            mCalledOnLevelChange = true;
+            return super.onLevelChange(level);
+        }
+
+        public boolean hasCalledDraw() {
+            return mCalledDraw;
+        }
+
+        public boolean hasCalledGetPadding() {
+            return mCalledGetPadding;
+        }
+
+        public boolean hasCalledSetVisible() {
+            return mCalledSetVisible;
+        }
+
+        public boolean hasCalledSetAlpha() {
+            return mCalledSetAlpha;
+        }
+
+        public boolean hasCalledGetOpacity() {
+            return mCalledGetOpacity;
+        }
+
+        public boolean hasCalledSetColorFilter() {
+            return mCalledSetColorFilter;
+        }
+
+        public boolean hasCalledIsStateful() {
+            return mCalledIsStateful;
+        }
+
+        public boolean hasCalledGetIntrinsicWidth() {
+            return mCalledGetIntrinsicWidth;
+        }
+
+        public boolean hasCalledGetIntrinsicHeight() {
+            return mCalledGetIntrinsicHeight;
+        }
+
+        public boolean hasCalledSetState() {
+            return mCalledSetState;
+        }
+
+        public boolean hasCalledOnLevelChange() {
+            return mCalledOnLevelChange;
+        }
+
+        public void reset() {
+            mCalledDraw = false;
+            mCalledGetPadding = false;
+            mCalledSetVisible = false;
+            mCalledSetAlpha = false;
+            mCalledGetOpacity = false;
+            mCalledSetColorFilter = false;
+            mCalledIsStateful = false;
+            mCalledGetIntrinsicWidth = false;
+            mCalledGetIntrinsicHeight = false;
+            mCalledSetState = false;
+            mCalledOnLevelChange = false;
+        }
+    }
+
+    private static class MockDrawableWrapper extends DrawableWrapper {
+        private boolean mCalledOnBoundsChange = false;
+
+        MockDrawableWrapper() {
+            super(null);
+        }
+
+        public MockDrawableWrapper(Drawable drawable) {
+            super(drawable);
+        }
+
+        @Override
+        protected boolean onStateChange(int[] state) {
+            return super.onStateChange(state);
+        }
+
+        @Override
+        protected boolean onLevelChange(int level) {
+            return super.onLevelChange(level);
+        }
+
+        @Override
+        protected void onBoundsChange(Rect bounds) {
+            mCalledOnBoundsChange = true;
+            super.onBoundsChange(bounds);
+        }
+
+        public boolean hasCalledOnBoundsChange() {
+            return mCalledOnBoundsChange;
+        }
+
+        public void reset() {
+            mCalledOnBoundsChange = false;
+        }
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
index eeda22c..9d9f52f 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -189,6 +189,16 @@
         gradientDrawable.setDither(false);
     }
 
+    public void testGetDither() {
+        GradientDrawable gradientDrawable = new GradientDrawable();
+
+        gradientDrawable.setDither(true);
+        assertTrue(gradientDrawable.isDither());
+
+        gradientDrawable.setDither(false);
+        assertFalse(gradientDrawable.isDither());
+    }
+
     public void testSetColorFilter() {
         GradientDrawable gradientDrawable = new GradientDrawable();
         ColorFilter cf = new ColorFilter();
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
index e930671..f24d6e2 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
@@ -16,6 +16,7 @@
 
 package android.graphics.drawable.cts;
 
+import android.view.Gravity;
 import com.android.cts.graphics.R;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -501,6 +502,45 @@
         assertTrue(mockDrawable2.hasCalledSetDither());
     }
 
+    public void testGetDither() {
+        MockDrawable mockDrawable1 = new MockDrawable();
+        MockDrawable mockDrawable2 = new MockDrawable();
+        Drawable[] array = new Drawable[] { mockDrawable1, mockDrawable2 };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setDither(true);
+        assertTrue(layerDrawable.isDither());
+
+        layerDrawable.setDither(false);
+        assertFalse(layerDrawable.isDither());
+    }
+
+    public void testSetHotspotBounds() {
+        Rect bounds = new Rect(10, 15, 100, 150);
+        MockDrawable mockDrawable1 = new MockDrawable();
+        MockDrawable mockDrawable2 = new MockDrawable();
+        Drawable[] array = new Drawable[] { mockDrawable1, mockDrawable2 };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        Rect outRect = new Rect();
+        layerDrawable.getHotspotBounds(outRect);
+        assertTrue(bounds.equals(outRect));
+    }
+
+    public void testGetHotspotBounds() {
+        Rect bounds = new Rect(10, 15, 100, 150);
+        MockDrawable mockDrawable1 = new MockDrawable();
+        MockDrawable mockDrawable2 = new MockDrawable();
+        Drawable[] array = new Drawable[] { mockDrawable1, mockDrawable2 };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setHotspotBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        Rect outRect = new Rect();
+        layerDrawable.getHotspotBounds(outRect);
+        assertTrue(bounds.equals(outRect));
+    }
+
     public void testSetAlpha() {
         MockDrawable mockDrawable1 = new MockDrawable();
         MockDrawable mockDrawable2 = new MockDrawable();
@@ -760,6 +800,611 @@
         assertEquals(1, constantState.getChangingConfigurations());
     }
 
+    @SuppressWarnings("deprecation")
+    public void testAddLayer() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable(), new ColorDrawable(Color.BLUE) };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        BitmapDrawable newDrawable = new BitmapDrawable();
+        int index = layerDrawable.addLayer(newDrawable);
+
+        final int numLayers = layerDrawable.getNumberOfLayers();
+        assertEquals(index, numLayers - 1);
+        assertEquals(newDrawable, layerDrawable.getDrawable(index));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetDrawable() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable(), new ColorDrawable(Color.BLUE) };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        final int numLayers = layerDrawable.getNumberOfLayers();
+        assertEquals(array[0], layerDrawable.getDrawable(0));
+        assertEquals(array[1], layerDrawable.getDrawable(1));
+        try {
+            assertEquals(null, layerDrawable.getDrawable(2));
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testFindIndexByLayerId() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable(), new ColorDrawable(Color.BLUE) };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setId(0, 10);
+        layerDrawable.setId(1, 20);
+
+        assertEquals(0, layerDrawable.findIndexByLayerId(10));
+        assertEquals(1, layerDrawable.findIndexByLayerId(20));
+        assertEquals(-1, layerDrawable.findIndexByLayerId(30));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetDrawable() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        BitmapDrawable newBitmapDrawable = new BitmapDrawable();
+        ColorDrawable newColorDrawable = new ColorDrawable(Color.GREEN);
+        layerDrawable.setDrawable(0, newColorDrawable);
+        layerDrawable.setDrawable(1, newBitmapDrawable);
+
+        final int numLayers = layerDrawable.getNumberOfLayers();
+        assertEquals(2, numLayers);
+        assertEquals(newColorDrawable, layerDrawable.getDrawable(0));
+        assertEquals(newBitmapDrawable, layerDrawable.getDrawable(1));
+        try {
+            assertEquals(null, layerDrawable.getDrawable(2));
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLeftPadding() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable()};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        layerDrawable.setPadding(10, 11, 20, 21);
+
+        assertEquals(10, layerDrawable.getLeftPadding());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetTopPadding() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable()};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        layerDrawable.setPadding(10, 11, 20, 21);
+
+        assertEquals(11, layerDrawable.getTopPadding());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetRightPadding() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable()};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        layerDrawable.setPadding(10, 11, 20, 21);
+
+        assertEquals(20, layerDrawable.getRightPadding());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetBottomPadding() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable()};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        layerDrawable.setPadding(10, 11, 20, 21);
+
+        assertEquals(21, layerDrawable.getBottomPadding());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetStartPadding() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable()};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        layerDrawable.setPadding(10, 11, 20, 21);
+
+        assertEquals(-1, layerDrawable.getStartPadding());
+        layerDrawable.setPaddingRelative(10, 11, 20, 21);
+        assertEquals(10, layerDrawable.getStartPadding());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetEndPadding() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable()};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        layerDrawable.setPadding(10, 11, 20, 21);
+
+        assertEquals(-1, layerDrawable.getEndPadding());
+        layerDrawable.setPaddingRelative(10, 11, 20, 21);
+        assertEquals(20, layerDrawable.getEndPadding());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetPadding() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable()};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        layerDrawable.setPadding(10, 11, 20, 21);
+
+        assertEquals(10, layerDrawable.getLeftPadding());
+        assertEquals(11, layerDrawable.getTopPadding());
+        assertEquals(20, layerDrawable.getRightPadding());
+        assertEquals(21, layerDrawable.getBottomPadding());
+        assertEquals(-1, layerDrawable.getStartPadding());
+        assertEquals(-1, layerDrawable.getEndPadding());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetPaddingRelative() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable()};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+        layerDrawable.setPaddingRelative(10, 11, 20, 21);
+
+        assertEquals(10, layerDrawable.getStartPadding());
+        assertEquals(11, layerDrawable.getTopPadding());
+        assertEquals(20, layerDrawable.getEndPadding());
+        assertEquals(21, layerDrawable.getBottomPadding());
+        assertEquals(-1, layerDrawable.getLeftPadding());
+        assertEquals(-1, layerDrawable.getRightPadding());
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerGravity() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setLayerGravity(0, Gravity.CENTER);
+        layerDrawable.setLayerGravity(1, Gravity.NO_GRAVITY);
+
+        try {
+            layerDrawable.setLayerGravity(2, Gravity.TOP);
+            fail("Should throw ArrayIndexOutOfBoundsException");
+        } catch (ArrayIndexOutOfBoundsException e) {
+        }
+        assertEquals(Gravity.CENTER, layerDrawable.getLayerGravity(0));
+        assertEquals(Gravity.NO_GRAVITY, layerDrawable.getLayerGravity(1));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerGravity() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setLayerGravity(0, Gravity.CENTER);
+        layerDrawable.setLayerGravity(1, Gravity.NO_GRAVITY);
+
+        assertEquals(Gravity.CENTER, layerDrawable.getLayerGravity(0));
+        assertEquals(Gravity.NO_GRAVITY, layerDrawable.getLayerGravity(1));
+        try {
+            layerDrawable.getLayerGravity(2);
+            fail("Should throw ArrayIndexOutOfBoundsException");
+        } catch (ArrayIndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerWidth() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setLayerWidth(0, 100);
+        layerDrawable.setLayerWidth(1, 200);
+
+        try {
+            layerDrawable.setLayerWidth(2, 300);
+            fail("Should throw ArrayIndexOutOfBoundsException");
+        } catch (ArrayIndexOutOfBoundsException e) {
+        }
+        assertEquals(100, layerDrawable.getLayerWidth(0));
+        assertEquals(200, layerDrawable.getLayerWidth(1));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerWidth() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setLayerWidth(0, 100);
+        layerDrawable.setLayerWidth(1, 200);
+
+        assertEquals(100, layerDrawable.getLayerWidth(0));
+        assertEquals(200, layerDrawable.getLayerWidth(1));
+        try {
+            layerDrawable.getLayerWidth(2);
+            fail("Should throw ArrayIndexOutOfBoundsException");
+        } catch (ArrayIndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerHeight() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setLayerHeight(0, 100);
+        layerDrawable.setLayerHeight(1, 200);
+
+        try {
+            layerDrawable.setLayerHeight(2, 300);
+            fail("Should throw ArrayIndexOutOfBoundsException");
+        } catch (ArrayIndexOutOfBoundsException e) {
+        }
+        assertEquals(100, layerDrawable.getLayerHeight(0));
+        assertEquals(200, layerDrawable.getLayerHeight(1));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerHeight() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setLayerHeight(0, 100);
+        layerDrawable.setLayerHeight(1, 200);
+
+        assertEquals(100, layerDrawable.getLayerHeight(0));
+        assertEquals(200, layerDrawable.getLayerHeight(1));
+        try {
+            layerDrawable.getLayerHeight(2);
+            fail("Should throw ArrayIndexOutOfBoundsException");
+        } catch (ArrayIndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerSize() {
+        Drawable[] array = new Drawable[]{new BitmapDrawable(), new ColorDrawable(Color.BLUE)};
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        layerDrawable.setLayerSize(0, 100, 200);
+        layerDrawable.setLayerSize(1, 300, 400);
+
+        try {
+            layerDrawable.setLayerSize(2, 500, 600);
+            fail("Should throw ArrayIndexOutOfBoundsException");
+        } catch (ArrayIndexOutOfBoundsException e) {
+        }
+        assertEquals(100, layerDrawable.getLayerWidth(0));
+        assertEquals(200, layerDrawable.getLayerHeight(0));
+        assertEquals(300, layerDrawable.getLayerWidth(1));
+        assertEquals(400, layerDrawable.getLayerHeight(1));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerInsetRelative() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable(), new ColorDrawable(Color.BLUE) };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int start = 10;
+        int top = 20;
+        int end = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+                layerDrawable.getIntrinsicWidth());
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+                layerDrawable.getIntrinsicHeight());
+        assertEquals(10, layerDrawable.getLayerInsetStart(0));
+        assertEquals(20, layerDrawable.getLayerInsetTop(0));
+        assertEquals(30, layerDrawable.getLayerInsetEnd(0));
+        assertEquals(40, layerDrawable.getLayerInsetBottom(0));
+        assertEquals(0, layerDrawable.getLayerInsetLeft(0));
+        assertEquals(0, layerDrawable.getLayerInsetRight(0));
+
+        // set bigger inset for layer 1
+        start += 10;
+        top += 10;
+        end += 10;
+        bottom += 10;
+        layerDrawable.setLayerInsetRelative(1, start, top, end, bottom);
+        assertEquals(layerDrawable.getDrawable(1).getIntrinsicWidth() + start + end,
+                layerDrawable.getIntrinsicWidth());
+        assertEquals(layerDrawable.getDrawable(1).getIntrinsicHeight() + top + bottom,
+                layerDrawable.getIntrinsicHeight());
+
+
+        try {
+            layerDrawable.setLayerInsetRelative(-1, start, top, end, bottom);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerInsetLeft() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int left = 10;
+        int top = 20;
+        int right = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInset(0, left, top, right, bottom);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + left + right,
+                layerDrawable.getIntrinsicWidth());
+        left += 5;
+        layerDrawable.setLayerInsetLeft(0, left);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + left + right,
+                layerDrawable.getIntrinsicWidth());
+        assertEquals(left, layerDrawable.getLayerInsetLeft(0));
+
+        try {
+            layerDrawable.setLayerInsetLeft(1, left);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerInsetLeft() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int left = 10;
+        int top = 20;
+        int right = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInset(0, left, top, right, bottom);
+        assertEquals(left, layerDrawable.getLayerInsetLeft(0));
+        left += 5;
+        layerDrawable.setLayerInsetLeft(0, left);
+        assertEquals(left, layerDrawable.getLayerInsetLeft(0));
+
+        try {
+            layerDrawable.getLayerInsetLeft(1);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerInsetTop() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int left = 10;
+        int top = 20;
+        int right = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInset(0, left, top, right, bottom);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+                layerDrawable.getIntrinsicHeight());
+        top += 5;
+        layerDrawable.setLayerInsetTop(0, top);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+                layerDrawable.getIntrinsicHeight());
+        assertEquals(top, layerDrawable.getLayerInsetTop(0));
+
+        try {
+            layerDrawable.setLayerInsetTop(1, top);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerInsetTop() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int left = 10;
+        int top = 20;
+        int right = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInset(0, left, top, right, bottom);
+        assertEquals(top, layerDrawable.getLayerInsetTop(0));
+        top += 5;
+        layerDrawable.setLayerInsetTop(0, top);
+        assertEquals(top, layerDrawable.getLayerInsetTop(0));
+
+        try {
+            layerDrawable.getLayerInsetTop(1);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerInsetRight() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int left = 10;
+        int top = 20;
+        int right = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInset(0, left, top, right, bottom);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + left + right,
+                layerDrawable.getIntrinsicWidth());
+        right += 5;
+        layerDrawable.setLayerInsetRight(0, right);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + left + right,
+                layerDrawable.getIntrinsicWidth());
+        assertEquals(right, layerDrawable.getLayerInsetRight(0));
+
+        try {
+            layerDrawable.setLayerInsetRight(1, right);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerInsetRight() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int left = 10;
+        int top = 20;
+        int right = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInset(0, left, top, right, bottom);
+        assertEquals(right, layerDrawable.getLayerInsetRight(0));
+        right += 5;
+        layerDrawable.setLayerInsetRight(0, right);
+        assertEquals(right, layerDrawable.getLayerInsetRight(0));
+
+        try {
+            layerDrawable.getLayerInsetRight(1);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerInsetBottom() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int left = 10;
+        int top = 20;
+        int right = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInset(0, left, top, right, bottom);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+                layerDrawable.getIntrinsicHeight());
+        bottom += 5;
+        layerDrawable.setLayerInsetBottom(0, bottom);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicHeight() + top + bottom,
+                layerDrawable.getIntrinsicHeight());
+        assertEquals(bottom, layerDrawable.getLayerInsetBottom(0));
+
+        try {
+            layerDrawable.setLayerInsetBottom(1, bottom);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerInsetBottom() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int left = 10;
+        int top = 20;
+        int right = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInset(0, left, top, right, bottom);
+        assertEquals(bottom, layerDrawable.getLayerInsetBottom(0));
+        bottom += 5;
+        layerDrawable.setLayerInsetBottom(0, bottom);
+        assertEquals(bottom, layerDrawable.getLayerInsetBottom(0));
+
+        try {
+            layerDrawable.getLayerInsetBottom(1);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerInsetStart() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int start = 10;
+        int top = 20;
+        int end = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+                layerDrawable.getIntrinsicWidth());
+        start += 5;
+        layerDrawable.setLayerInsetStart(0, start);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+                layerDrawable.getIntrinsicWidth());
+        assertEquals(start, layerDrawable.getLayerInsetStart(0));
+
+        try {
+            layerDrawable.setLayerInset(1, start, top, end, bottom);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerInsetStart() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int start = 10;
+        int top = 20;
+        int end = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+        assertEquals(start, layerDrawable.getLayerInsetStart(0));
+        start += 5;
+        layerDrawable.setLayerInsetStart(0, start);
+        assertEquals(start, layerDrawable.getLayerInsetStart(0));
+
+        try {
+            layerDrawable.getLayerInsetStart(1);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testSetLayerInsetEnd() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int start = 10;
+        int top = 20;
+        int end = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+        assertEquals(end, layerDrawable.getLayerInsetEnd(0));
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+                layerDrawable.getIntrinsicWidth());
+        end += 5;
+        layerDrawable.setLayerInsetEnd(0, end);
+        assertEquals(layerDrawable.getDrawable(0).getIntrinsicWidth() + start + end,
+                layerDrawable.getIntrinsicWidth());
+        assertEquals(end, layerDrawable.getLayerInsetEnd(0));
+
+        try {
+            layerDrawable.setLayerInsetEnd(1, end);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testGetLayerInsetEnd() {
+        Drawable[] array = new Drawable[] { new BitmapDrawable() };
+        LayerDrawable layerDrawable = new LayerDrawable(array);
+
+        // set inset for layer 0
+        int start = 10;
+        int top = 20;
+        int end = 30;
+        int bottom = 40;
+        layerDrawable.setLayerInsetRelative(0, start, top, end, bottom);
+        assertEquals(end, layerDrawable.getLayerInsetEnd(0));
+        end += 5;
+        layerDrawable.setLayerInsetEnd(0, end);
+        assertEquals(end, layerDrawable.getLayerInsetEnd(0));
+
+        try {
+            layerDrawable.getLayerInsetEnd(1);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+        }
+    }
+
+
+
     private static class MockDrawable extends Drawable {
         private boolean mCalledSetDither = false;
         private boolean mCalledSetAlpha = false;
@@ -776,6 +1421,8 @@
 
         private int mOpacity = PixelFormat.OPAQUE;
 
+        private boolean mDither = false;
+
         Rect mPadding = null;
 
         public MockDrawable() {
@@ -816,9 +1463,15 @@
 
         @Override
         public void setDither(boolean dither) {
+            mDither = dither;
             mCalledSetDither = true;
         }
 
+        @Override
+        public boolean isDither() {
+            return mDither;
+        }
+
         public boolean hasCalledSetDither() {
             return mCalledSetDither;
         }
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
index 39ed55c..2c7209b 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
@@ -21,7 +21,6 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
@@ -37,9 +36,7 @@
 import android.graphics.Bitmap.Config;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.NinePatchDrawable;
-import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.Drawable.ConstantState;
-import android.graphics.drawable.shapes.RectShape;
 import android.test.InstrumentationTestCase;
 import android.util.AttributeSet;
 import android.util.Xml;
@@ -200,6 +197,37 @@
         assertTrue(mNinePatchDrawable.getPaint().isDither());
     }
 
+    public void testGetDither() {
+        mNinePatchDrawable.setDither(false);
+        assertFalse(mNinePatchDrawable.isDither());
+        assertEquals(mNinePatchDrawable.isDither(), mNinePatchDrawable.getPaint().isDither());
+
+        mNinePatchDrawable.setDither(true);
+        assertTrue(mNinePatchDrawable.isDither());
+        assertEquals(mNinePatchDrawable.isDither(), mNinePatchDrawable.getPaint().isDither());
+    }
+
+    public void testSetFilterBitmap() {
+        mNinePatchDrawable.setFilterBitmap(false);
+        assertFalse(mNinePatchDrawable.getPaint().isFilterBitmap());
+
+        mNinePatchDrawable.setFilterBitmap(true);
+        assertTrue(mNinePatchDrawable.getPaint().isFilterBitmap());
+    }
+
+    public void testIsFilterBitmap() {
+        mNinePatchDrawable.setFilterBitmap(false);
+        assertFalse(mNinePatchDrawable.isFilterBitmap());
+        assertEquals(mNinePatchDrawable.isFilterBitmap(),
+                mNinePatchDrawable.getPaint().isFilterBitmap());
+
+
+        mNinePatchDrawable.setFilterBitmap(true);
+        assertTrue(mNinePatchDrawable.isFilterBitmap());
+        assertEquals(mNinePatchDrawable.isFilterBitmap(),
+                mNinePatchDrawable.getPaint().isFilterBitmap());
+    }
+
     public void testGetPaint() {
         Paint paint = mNinePatchDrawable.getPaint();
         assertNotNull(paint);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
index b77139a..186010b 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
@@ -16,7 +16,6 @@
 
 package android.graphics.drawable.cts;
 
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -333,6 +332,18 @@
         assertFalse(shapeDrawable.getPaint().isDither());
     }
 
+    public void testGetDither() {
+        ShapeDrawable shapeDrawable = new ShapeDrawable();
+
+        shapeDrawable.setDither(true);
+        assertTrue(shapeDrawable.isDither());
+        assertEquals(shapeDrawable.isDither(), shapeDrawable.getPaint().isDither());
+
+        shapeDrawable.setDither(false);
+        assertFalse(shapeDrawable.isDither());
+        assertEquals(shapeDrawable.isDither(), shapeDrawable.getPaint().isDither());
+    }
+
     public void testMutate() {
         // How to load a ShapeDrawable from resources.
     }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
index 2430dd0..df3f151 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -519,17 +519,23 @@
             waiverKeys.add(CaptureResult.SHADING_MODE);
         }
 
-        //Keys not required if manual sensor control is not supported
+        //Keys not required if neither MANUAL_SENSOR nor READ_SENSOR_SETTINGS is supported
+        if (!mStaticInfo.isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) &&
+            !mStaticInfo.isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
+            waiverKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
+            waiverKeys.add(CaptureResult.SENSOR_SENSITIVITY);
+            waiverKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
+            waiverKeys.add(CaptureResult.LENS_APERTURE);
+        }
+
         if (!mStaticInfo.isCapabilitySupported(
                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
-            waiverKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
             waiverKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
-            waiverKeys.add(CaptureResult.SENSOR_SENSITIVITY);
             waiverKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
             waiverKeys.add(CaptureResult.LENS_FOCUS_RANGE);
-            waiverKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
             waiverKeys.add(CaptureResult.LENS_STATE);
-            waiverKeys.add(CaptureResult.LENS_APERTURE);
             waiverKeys.add(CaptureResult.LENS_FILTER_DENSITY);
         }
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
index a8f1b48..8184226 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -46,6 +47,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
+import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -365,38 +367,35 @@
 
                 Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(),
                         Bitmap.Config.ARGB_8888);
+
+                Size rawBitmapSize = new Size(rawBitmap.getWidth(), rawBitmap.getHeight());
+                assertTrue("Raw bitmap size must be equal to active array size.",
+                        rawBitmapSize.equals(activeArraySize));
+
                 byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
 
                 // Render RAW image to a bitmap
                 raw.getPlanes()[0].getBuffer().get(rawPlane);
                 raw.getPlanes()[0].getBuffer().rewind();
+
                 RawConverter.convertToSRGB(RenderScriptSingleton.getRS(), raw.getWidth(),
                         raw.getHeight(), raw.getPlanes()[0].getRowStride(), rawPlane,
                         characteristics, resultPair.second, /*offsetX*/0, /*offsetY*/0,
                         /*out*/rawBitmap);
 
-                // Decompress JPEG image to a bitmap
-                byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpeg);
-
-                BitmapFactory.Options opt = new BitmapFactory.Options();
-                opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
-                Bitmap fullSizeJpegBmap = BitmapFactory.decodeByteArray(compressedJpegData,
-                        /*offset*/0, compressedJpegData.length, /*inout*/opt);
-                Rect jpegDimens = new Rect(0, 0, fullSizeJpegBmap.getWidth(),
-                        fullSizeJpegBmap.getHeight());
+                rawPlane = null;
+                System.gc(); // Hint to VM
 
                 if (VERBOSE) {
                     // Generate DNG file
                     DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
-                    outputStream = new ByteArrayOutputStream();
-                    dngCreator.writeImage(outputStream, raw);
 
                     // Write DNG to file
                     String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_" +
                             DEBUG_DNG_FILE;
                     // Write out captured DNG file for the first camera device if setprop is enabled
                     fileStream = new FileOutputStream(dngFilePath);
-                    fileStream.write(outputStream.toByteArray());
+                    dngCreator.writeImage(fileStream, raw);
                     fileStream.flush();
                     fileStream.close();
                     Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
@@ -405,8 +404,10 @@
                     String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_jpeg.jpg";
                     // Write out captured DNG file for the first camera device if setprop is enabled
                     fileChannel = new FileOutputStream(jpegFilePath).getChannel();
-                    fileChannel.write(jpeg.getPlanes()[0].getBuffer());
+                    ByteBuffer jPlane = jpeg.getPlanes()[0].getBuffer();
+                    fileChannel.write(jPlane);
                     fileChannel.close();
+                    jPlane.rewind();
                     Log.v(TAG, "Test JPEG file for camera " + deviceId + " saved to " +
                             jpegFilePath);
 
@@ -421,11 +422,21 @@
                             rawFilePath);
                 }
 
-                Size rawBitmapSize = new Size(rawBitmap.getWidth(), rawBitmap.getHeight());
-                assertTrue("Raw bitmap size must be equal to active array size.",
-                        rawBitmapSize.equals(activeArraySize));
+                raw.close();
 
-                // Get square center patch from JPEG and RAW bitmaps
+                // Decompress JPEG image to a bitmap
+                byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpeg);
+
+                jpeg.close();
+
+                // Get JPEG dimensions without decoding
+                BitmapFactory.Options opt0 = new BitmapFactory.Options();
+                opt0.inJustDecodeBounds = true;
+                BitmapFactory.decodeByteArray(compressedJpegData, /*offset*/0,
+                        compressedJpegData.length, /*inout*/opt0);
+                Rect jpegDimens = new Rect(0, 0, opt0.outWidth, opt0.outHeight);
+
+                // Find square center patch from JPEG and RAW bitmaps
                 RectF jpegRect = new RectF(jpegDimens);
                 RectF rawRect = new RectF(0, 0, rawBitmap.getWidth(), rawBitmap.getHeight());
                 int sideDimen = Math.min(Math.min(Math.min(Math.min(DEFAULT_PATCH_DIMEN,
@@ -435,6 +446,7 @@
                 RectF jpegIntermediate = new RectF(0, 0, sideDimen, sideDimen);
                 jpegIntermediate.offset(jpegRect.centerX() - jpegIntermediate.centerX(),
                         jpegRect.centerY() - jpegIntermediate.centerY());
+
                 RectF rawIntermediate = new RectF(0, 0, sideDimen, sideDimen);
                 rawIntermediate.offset(rawRect.centerX() - rawIntermediate.centerX(),
                         rawRect.centerY() - rawIntermediate.centerY());
@@ -443,10 +455,18 @@
                 Rect rawFinal = new Rect();
                 rawIntermediate.roundOut(rawFinal);
 
-                Bitmap jpegPatch = Bitmap.createBitmap(fullSizeJpegBmap, jpegFinal.left,
-                        jpegFinal.top, jpegFinal.width(), jpegFinal.height());
+                // Get RAW center patch, and free up rest of RAW image
                 Bitmap rawPatch = Bitmap.createBitmap(rawBitmap, rawFinal.left, rawFinal.top,
                         rawFinal.width(), rawFinal.height());
+                rawBitmap.recycle();
+                rawBitmap = null;
+                System.gc(); // Hint to VM
+
+                BitmapFactory.Options opt = new BitmapFactory.Options();
+                opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
+                Bitmap jpegPatch = BitmapRegionDecoder.newInstance(compressedJpegData,
+                        /*offset*/0, compressedJpegData.length, /*isShareable*/true).
+                        decodeRegion(jpegFinal, opt);
 
                 // Compare center patch from JPEG and rendered RAW bitmap
                 double difference = BitmapUtils.calcDifferenceMetric(jpegPatch, rawPatch);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index c69053b..aa1c3fc 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -247,11 +247,8 @@
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES             , LEGACY   ,   NONE                 );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN                      , FULL     ,   MANUAL_SENSOR, RAW   );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1                   , OPT      ,   RAW                  );
-                expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2                   , OPT      ,   RAW                  );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM1                         , OPT      ,   RAW                  );
-                expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2                         , OPT      ,   RAW                  );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX1                          , OPT      ,   RAW                  );
-                expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2                          , OPT      ,   RAW                  );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   , LEGACY   ,   BC, RAW              );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT            , FULL     ,   RAW                  );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE                 , FULL     ,   MANUAL_SENSOR        );
@@ -264,7 +261,6 @@
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY                   , FULL     ,   MANUAL_SENSOR        );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION                              , LEGACY   ,   BC                   );
                 expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1                    , OPT      ,   RAW                  );
-                expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2                    , OPT      ,   RAW                  );
                 expectKeyAvailable(c, CameraCharacteristics.SHADING_AVAILABLE_MODES                         , LIMITED  ,   MANUAL_POSTPROC, RAW );
                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     , LEGACY   ,   BC                   );
                 expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES   , OPT      ,   RAW                  );
@@ -279,6 +275,14 @@
                 // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
             }
 
+            // Only check for these if the second reference illuminant is included
+            if (allKeys.contains(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2)) {
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2                    , OPT      ,   RAW                  );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_COLOR_TRANSFORM2                         , OPT      ,   RAW                  );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2                   , OPT      ,   RAW                  );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_FORWARD_MATRIX2                          , OPT      ,   RAW                  );
+            }
+
             counter++;
         }
     }
@@ -612,10 +616,12 @@
             for (int format : supportedFormats) {
                 assertTrue("Format " + format + " fails cross check",
                         config.isOutputSupportedFor(format));
-                Size[] supportedSizes = config.getOutputSizes(format);
+                List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
+                        Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
                 assertTrue("Supported format " + format + " has no sizes listed",
-                        supportedSizes.length > 0);
-                for (Size size : supportedSizes) {
+                        supportedSizes.size() > 0);
+                for (int i = 0; i < supportedSizes.size(); i++) {
+                    Size size = supportedSizes.get(i);
                     if (VERBOSE) {
                         Log.v(TAG,
                                 String.format("Testing camera %s, format %d, size %s",
@@ -628,6 +634,28 @@
                             assertTrue("YUV_420_888 may not have a non-zero stall duration",
                                     stallDuration == 0);
                             break;
+                        case ImageFormat.JPEG:
+                        case ImageFormat.RAW_SENSOR:
+                            final float TOLERANCE_FACTOR = 2.0f;
+                            long prevDuration = 0;
+                            if (i > 0) {
+                                prevDuration = config.getOutputStallDuration(
+                                        format, supportedSizes.get(i - 1));
+                            }
+                            long nextDuration = Long.MAX_VALUE;
+                            if (i < (supportedSizes.size() - 1)) {
+                                nextDuration = config.getOutputStallDuration(
+                                        format, supportedSizes.get(i + 1));
+                            }
+                            long curStallDuration = config.getOutputStallDuration(format, size);
+                            // Stall duration should be in a reasonable range: larger size should
+                            // normally have larger stall duration.
+                            mCollector.expectInRange("Stall duration (format " + format +
+                                    " and size " + size + ") is not in the right range",
+                                    curStallDuration,
+                                    (long) (prevDuration / TOLERANCE_FACTOR),
+                                    (long) (nextDuration * TOLERANCE_FACTOR));
+                            break;
                         default:
                             assertTrue("Negative stall duration for format " + format,
                                     stallDuration >= 0);
@@ -812,8 +840,15 @@
      * Create an invalid size that's close to one of the good sizes in the list, but not one of them
      */
     private Size findInvalidSize(Size[] goodSizes) {
-        Size invalidSize = new Size(goodSizes[0].getWidth() + 1, goodSizes[0].getHeight());
-        while(arrayContains(goodSizes, invalidSize)) {
+        return findInvalidSize(Arrays.asList(goodSizes));
+    }
+
+    /**
+     * Create an invalid size that's close to one of the good sizes in the list, but not one of them
+     */
+    private Size findInvalidSize(List<Size> goodSizes) {
+        Size invalidSize = new Size(goodSizes.get(0).getWidth() + 1, goodSizes.get(0).getHeight());
+        while(goodSizes.contains(invalidSize)) {
             invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
         }
         return invalidSize;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index c10deac..8cba6fd 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -669,7 +669,7 @@
 
             // TODO: Update this to use availableResultKeys once shim supports this.
             if (mStaticInfo.isCapabilitySupported(
-                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
                 Long exposureTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
                 Integer sensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
                 mCollector.expectInRange(
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index 1fa429d..7368376 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -522,6 +522,7 @@
         if (useHighSpeedSession) {
             CaptureRequest.Builder requestBuilder =
                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+            requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
             requestBuilder.addTarget(mPreviewSurface);
             requestBuilder.addTarget(mRecordingSurface);
             slowMoRequests = mCamera.createConstrainedHighSpeedRequestList(requestBuilder.build());
@@ -762,12 +763,6 @@
 
             CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
             Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
-            if (!mSupportedVideoSizes.contains(videoSz)) {
-                mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " +
-                        profileId + " must be one of the camera device supported video size!");
-                continue;
-            }
-
             Size maxPreviewSize = mOrderedPreviewSizes.get(0);
 
             if (mStaticInfo.isHardwareLevelLegacy() &&
@@ -777,6 +772,12 @@
                 continue;
             }
 
+            if (!mSupportedVideoSizes.contains(videoSz)) {
+                mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " +
+                        profileId + " must be one of the camera device supported video size!");
+                continue;
+            }
+
             // For LEGACY, find closest supported smaller or equal JPEG size to the current video
             // size; if no size is smaller than the video, pick the smallest JPEG size.  The assert
             // for video size above guarantees that for LIMITED or FULL, we select videoSz here.
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index 6a9415c..6242776 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -79,7 +79,8 @@
         MIXED_BURST,
         ABORT_CAPTURE,
         TIMESTAMPS,
-        JPEG_EXIF
+        JPEG_EXIF,
+        REQUEST_KEYS,
     }
 
     /**
@@ -425,6 +426,32 @@
         }
     }
 
+    public void testReprocessRequestKeys() throws Exception {
+        for (String id : mCameraIds) {
+            if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
+                continue;
+            }
+
+            try {
+                // open Camera device
+                openDevice(id);
+
+                int[] supportedInputFormats =
+                    mStaticInfo.getAvailableFormats(StaticMetadata.StreamDirection.Input);
+                for (int inputFormat : supportedInputFormats) {
+                    int[] supportedReprocessOutputFormats =
+                            mStaticInfo.getValidOutputFormatsForInput(inputFormat);
+                    for (int reprocessOutputFormat : supportedReprocessOutputFormats) {
+                        testReprocessingMaxSizes(id, inputFormat, reprocessOutputFormat,
+                                /*previewSize*/null, CaptureTestCase.REQUEST_KEYS);
+                    }
+                }
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
     /**
      * Test the input format and output format with the largest input and output sizes.
      */
@@ -467,6 +494,10 @@
             case JPEG_EXIF:
                 testReprocessJpegExif(cameraId, maxInputSize, inputFormat, maxReprocessOutputSize);
                 break;
+            case REQUEST_KEYS:
+                testReprocessRequestKeys(cameraId, maxInputSize, inputFormat,
+                        maxReprocessOutputSize, reprocessOutputFormat);
+                break;
             default:
                 throw new IllegalArgumentException("Invalid test case");
         }
@@ -878,6 +909,10 @@
         }
     }
 
+    /**
+     * Test JPEG tags for reprocess requests. Reprocess result's JPEG tags and JPEG image's tags
+     * match reprocess request's JPEG tags.
+     */
     private void testReprocessJpegExif(String cameraId, Size inputSize, int inputFormat,
             Size reprocessOutputSize) throws Exception {
         if (VERBOSE) {
@@ -936,6 +971,94 @@
         }
     }
 
+
+
+    /**
+     * Test the following keys in reprocess results match the keys in reprocess requests:
+     *   1. EDGE_MODE
+     *   2. NOISE_REDUCTION_MODE
+     *   3. REPROCESS_EFFECTIVE_EXPOSURE_FACTOR (only for YUV reprocess)
+     */
+
+    private void testReprocessRequestKeys(String cameraId, Size inputSize, int inputFormat,
+            Size reprocessOutputSize, int reprocessOutputFormat) throws Exception {
+        if (VERBOSE) {
+            Log.v(TAG, "testReprocessRequestKeys: cameraId: " + cameraId + " inputSize: " +
+                    inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " +
+                    reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat);
+        }
+
+        final Integer[] EDGE_MODES = {CaptureRequest.EDGE_MODE_FAST,
+                CaptureRequest.EDGE_MODE_HIGH_QUALITY, CaptureRequest.EDGE_MODE_OFF};
+        final Integer[] NR_MODES = {CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY,
+                CaptureRequest.NOISE_REDUCTION_MODE_OFF, CaptureRequest.NOISE_REDUCTION_MODE_FAST};
+        final Float[] EFFECTIVE_EXP_FACTORS = {null, 1.0f, 2.5f};
+        int numFrames = EDGE_MODES.length;
+
+        try {
+            setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat,
+                    numFrames);
+            setupReprocessableSession(/*previewSurface*/null, numFrames);
+
+            // Prepare reprocess capture requests.
+            ArrayList<CaptureRequest> reprocessRequests = new ArrayList<>(numFrames);
+
+            for (int i = 0; i < numFrames; i++) {
+                TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(),
+                        /*inputResult*/null);
+                mImageWriter.queueInputImage(
+                        mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS));
+
+                CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result);
+                builder.addTarget(getReprocessOutputImageReader().getSurface());
+
+                // Set reprocess request keys
+                builder.set(CaptureRequest.EDGE_MODE, EDGE_MODES[i]);
+                builder.set(CaptureRequest.NOISE_REDUCTION_MODE, NR_MODES[i]);
+                if (inputFormat == ImageFormat.YUV_420_888) {
+                    builder.set(CaptureRequest.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR,
+                            EFFECTIVE_EXP_FACTORS[i]);
+                }
+                reprocessRequests.add(builder.build());
+            }
+
+            // Submit reprocess requests.
+            SimpleCaptureCallback captureCallback = new SimpleCaptureCallback();
+            mSession.captureBurst(reprocessRequests, captureCallback, mHandler);
+
+            TotalCaptureResult[] reprocessResults =
+                    captureCallback.getTotalCaptureResultsForRequests(reprocessRequests,
+                    CAPTURE_TIMEOUT_FRAMES);
+
+            for (int i = 0; i < numFrames; i++) {
+                // Verify result's keys
+                Integer resultEdgeMode = reprocessResults[i].get(CaptureResult.EDGE_MODE);
+                Integer resultNoiseReductionMode =
+                        reprocessResults[i].get(CaptureResult.NOISE_REDUCTION_MODE);
+
+                assertEquals("Reprocess result edge mode (" + resultEdgeMode +
+                        ") doesn't match requested edge mode (" + EDGE_MODES[i] + ")",
+                        resultEdgeMode, EDGE_MODES[i]);
+                assertEquals("Reprocess result noise reduction mode (" + resultNoiseReductionMode +
+                        ") doesn't match requested noise reduction mode (" +
+                        NR_MODES[i] + ")", resultNoiseReductionMode,
+                        NR_MODES[i]);
+
+                if (inputFormat == ImageFormat.YUV_420_888) {
+                    Float resultEffectiveExposureFactor = reprocessResults[i].get(
+                            CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR);
+                    assertEquals("Reprocess effective exposure factor (" +
+                            resultEffectiveExposureFactor + ") doesn't match requested " +
+                            "effective exposure factor (" + EFFECTIVE_EXP_FACTORS[i] + ")",
+                            resultEffectiveExposureFactor, EFFECTIVE_EXP_FACTORS[i]);
+                }
+            }
+        } finally {
+            closeReprossibleSession();
+            closeImageReaders();
+        }
+    }
+
     /**
      * Set up two image readers: one for regular capture (used for reprocess input) and one for
      * reprocess capture.
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
index 9c00510..62403cf 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -60,7 +60,7 @@
     private static final int MAX_REGIONS_AE_INDEX = 0;
     private static final int MAX_REGIONS_AWB_INDEX = 1;
     private static final int MAX_REGIONS_AF_INDEX = 2;
-    private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 3000;
+    private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 6000;
     private static final double AE_COMPENSATION_ERROR_TOLERANCE = 0.2;
     private static final int NUM_FRAMES_WAITED = 30;
     // 5 percent error margin for resulting metering regions
@@ -931,7 +931,7 @@
         long maxExposureValuePreview = -1;
         long maxExposureValueStill = -1;
         if (mStaticInfo.isCapabilitySupported(
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
             // Minimum exposure settings is mostly static while maximum exposure setting depends on
             // frame rate range which in term depends on capture request.
             minExposureValue = mStaticInfo.getSensitivityMinimumOrDefault() *
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 602e253..3890b33 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -1717,7 +1717,7 @@
 
         checkArrayValuesInRange(key, availableCaps,
                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
-                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO);
         capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
         return capList;
     }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
index a9a6fce..f97c08f 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
@@ -77,7 +77,7 @@
      * polynomial approximates the default tonemapping curve used for ACR3.
      */
     private static final float[] DEFAULT_ACR3_TONEMAP_CURVE_COEFFS = new float[] {
-            -1.087f,  1.643f,  0.443f, 0f
+            -0.7836f, 0.8469f, 0.943f, 0.0209f
     };
 
     /**
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java
index 08dae1e..5ed169b 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyPairGeneratorTest.java
@@ -17,26 +17,37 @@
 package android.keystore.cts;
 
 import android.security.KeyPairGeneratorSpec;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
 import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
 
 import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.Socket;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import java.security.Principal;
 import java.security.PrivateKey;
-import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
 import java.security.interfaces.ECPublicKey;
 import java.security.interfaces.RSAPublicKey;
-import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
 import java.security.spec.RSAKeyGenParameterSpec;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
+import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -46,7 +57,6 @@
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509ExtendedKeyManager;
 import javax.security.auth.x500.X500Principal;
 
@@ -55,14 +65,16 @@
 import libcore.javax.net.ssl.TestSSLContext;
 
 public class AndroidKeyPairGeneratorTest extends AndroidTestCase {
-    private KeyPairGenerator mGenerator;
-
     private KeyStore mKeyStore;
 
+    private CountingSecureRandom mRng;
+
     private static final String TEST_ALIAS_1 = "test1";
 
     private static final String TEST_ALIAS_2 = "test2";
 
+    private static final String TEST_ALIAS_3 = "test3";
+
     private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
 
     private static final X500Principal TEST_DN_2 = new X500Principal("CN=test2");
@@ -79,56 +91,89 @@
     @SuppressWarnings("deprecation")
     private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
 
+    private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
+    private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
+    private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
+    private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
+
     @Override
     protected void setUp() throws Exception {
+        super.setUp();
+        mRng = new CountingSecureRandom();
         mKeyStore = KeyStore.getInstance("AndroidKeyStore");
         mKeyStore.load(null, null);
-
-        mGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
     }
 
-    public void testKeyPairGenerator_Initialize_Params_Unencrypted_Success() throws Exception {
-        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+    public void testInitialize_LegacySpec() throws Exception {
+        @SuppressWarnings("deprecation")
+        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
                 .setAlias(TEST_ALIAS_1)
                 .setSubject(TEST_DN_1)
                 .setSerialNumber(TEST_SERIAL_1)
                 .setStartDate(NOW)
                 .setEndDate(NOW_PLUS_10_YEARS)
-                .build());
+                .build();
+        getRsaGenerator().initialize(spec);
+        getRsaGenerator().initialize(spec, new SecureRandom());
+
+        getEcGenerator().initialize(spec);
+        getEcGenerator().initialize(spec, new SecureRandom());
     }
 
-    public void testKeyPairGenerator_Initialize_KeySize_Unencrypted_Failure() throws Exception {
+    public void testInitialize_ModernSpec() throws Exception {
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .build();
+        getRsaGenerator().initialize(spec);
+        getRsaGenerator().initialize(spec, new SecureRandom());
+
+        getEcGenerator().initialize(spec);
+        getEcGenerator().initialize(spec, new SecureRandom());
+    }
+
+    public void testInitialize_KeySizeOnly() throws Exception {
         try {
-            mGenerator.initialize(1024);
+            getRsaGenerator().initialize(1024);
+            fail("KeyPairGenerator should not support setting the key size");
+        } catch (IllegalArgumentException success) {
+        }
+
+        try {
+            getEcGenerator().initialize(256);
             fail("KeyPairGenerator should not support setting the key size");
         } catch (IllegalArgumentException success) {
         }
     }
 
-    public void testKeyPairGenerator_Initialize_KeySizeAndSecureRandom_Unencrypted_Failure()
+    public void testInitialize_KeySizeAndSecureRandomOnly()
             throws Exception {
         try {
-            mGenerator.initialize(1024, new SecureRandom());
+            getRsaGenerator().initialize(1024, new SecureRandom());
+            fail("KeyPairGenerator should not support setting the key size");
+        } catch (IllegalArgumentException success) {
+        }
+
+        try {
+            getEcGenerator().initialize(1024, new SecureRandom());
             fail("KeyPairGenerator should not support setting the key size");
         } catch (IllegalArgumentException success) {
         }
     }
 
-    public void testKeyPairGenerator_Initialize_ParamsAndSecureRandom_Unencrypted_Failure()
-            throws Exception {
-        mGenerator.initialize(
-                new KeyPairGeneratorSpec.Builder(getContext())
-                        .setAlias(TEST_ALIAS_1)
-                        .setSubject(TEST_DN_1)
-                        .setSerialNumber(TEST_SERIAL_1)
-                        .setStartDate(NOW)
-                        .setEndDate(NOW_PLUS_10_YEARS)
-                        .build(),
-                new SecureRandom());
-    }
+    @SuppressWarnings("deprecation")
+    public void testGenerate_EC_LegacySpec() throws Exception {
+        // There are three legacy ways to generate an EC key pair using Android Keystore
+        // KeyPairGenerator:
+        // 1. Use an RSA KeyPairGenerator and specify EC as key type,
+        // 2. Use an EC KeyPairGenerator and specify EC as key type,
+        // 3. Use an EC KeyPairGenerator and leave the key type unspecified.
+        //
+        // We test all three.
 
-    public void testKeyPairGenerator_GenerateKeyPair_EC_Unencrypted_Success() throws Exception {
-        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+        // 1. Use an RSA KeyPairGenerator and specify EC as key type.
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
                 .setAlias(TEST_ALIAS_1)
                 .setKeyType("EC")
                 .setSubject(TEST_DN_1)
@@ -136,226 +181,980 @@
                 .setStartDate(NOW)
                 .setEndDate(NOW_PLUS_10_YEARS)
                 .build());
-
-        final KeyPair pair = mGenerator.generateKeyPair();
-        assertNotNull("The KeyPair returned should not be null", pair);
-
-        assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 256, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "EC",
+                256,
+                TEST_DN_1,
+                TEST_SERIAL_1, NOW,
                 NOW_PLUS_10_YEARS);
-    }
+        assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_1);
+        assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_1);
+        TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+                ECCurves.NIST_P_256_SPEC, ((ECPublicKey) keyPair.getPublic()).getParams());
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(256, keyInfo.getKeySize());
+        assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+        assertOneOf(keyInfo.getOrigin(),
+                KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+        assertEquals(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY,
+                keyInfo.getPurposes());
+        assertFalse(keyInfo.isUserAuthenticationRequired());
+        assertEquals(null, keyInfo.getKeyValidityStart());
+        assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+        assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+                KeyProperties.DIGEST_NONE);
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
 
-    public void testKeyPairGenerator_GenerateKeyPair_EC_P521_Unencrypted_Success() throws Exception {
-        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
-                .setAlias(TEST_ALIAS_1)
+        // 2. Use an EC KeyPairGenerator and specify EC as key type.
+        generator = getEcGenerator();
+        generator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_2)
                 .setKeyType("EC")
-                .setKeySize(521)
                 .setSubject(TEST_DN_1)
                 .setSerialNumber(TEST_SERIAL_1)
                 .setStartDate(NOW)
                 .setEndDate(NOW_PLUS_10_YEARS)
                 .build());
-
-        final KeyPair pair = mGenerator.generateKeyPair();
-        assertNotNull("The KeyPair returned should not be null", pair);
-
-        assertKeyPairCorrect(pair, TEST_ALIAS_1, "EC", 521, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+        keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_2,
+                "EC",
+                256,
+                TEST_DN_1,
+                TEST_SERIAL_1,
+                NOW,
                 NOW_PLUS_10_YEARS);
+        assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_2);
+        assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_2);
+        TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+                ECCurves.NIST_P_256_SPEC, ((ECPublicKey) keyPair.getPublic()).getParams());
+        keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(256, keyInfo.getKeySize());
+        assertEquals(TEST_ALIAS_2, keyInfo.getKeystoreAlias());
+        assertOneOf(keyInfo.getOrigin(),
+                KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+        assertEquals(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY,
+                keyInfo.getPurposes());
+        assertFalse(keyInfo.isUserAuthenticationRequired());
+        assertEquals(null, keyInfo.getKeyValidityStart());
+        assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+        assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+                KeyProperties.DIGEST_NONE);
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+
+        // 3. Use an EC KeyPairGenerator and leave the key type unspecified.
+        generator = getEcGenerator();
+        generator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_3)
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(TEST_SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .build());
+        keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_3,
+                "EC",
+                256,
+                TEST_DN_1,
+                TEST_SERIAL_1,
+                NOW,
+                NOW_PLUS_10_YEARS);
+        assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_3);
+        assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_3);
+        TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+                ECCurves.NIST_P_256_SPEC, ((ECPublicKey) keyPair.getPublic()).getParams());
+        keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(256, keyInfo.getKeySize());
+        assertEquals(TEST_ALIAS_3, keyInfo.getKeystoreAlias());
+        assertOneOf(keyInfo.getOrigin(),
+                KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+        assertEquals(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY,
+                keyInfo.getPurposes());
+        assertFalse(keyInfo.isUserAuthenticationRequired());
+        assertEquals(null, keyInfo.getKeyValidityStart());
+        assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+        assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+                KeyProperties.DIGEST_NONE);
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
     }
 
-    public void testKeyPairGenerator_GenerateKeyPair_RSA_Unencrypted_Success() throws Exception {
-        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
+    public void testGenerate_RSA_LegacySpec() throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
                 .setAlias(TEST_ALIAS_1)
                 .setSubject(TEST_DN_1)
                 .setSerialNumber(TEST_SERIAL_1)
                 .setStartDate(NOW)
                 .setEndDate(NOW_PLUS_10_YEARS)
                 .build());
-
-        final KeyPair pair = mGenerator.generateKeyPair();
-        assertNotNull("The KeyPair returned should not be null", pair);
-
-        assertKeyPairCorrect(pair, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                2048,
+                TEST_DN_1,
+                TEST_SERIAL_1,
+                NOW,
                 NOW_PLUS_10_YEARS);
+        assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_1);
+        assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_1);
+        assertEquals(RSAKeyGenParameterSpec.F4,
+                ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(2048, keyInfo.getKeySize());
+        assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+        assertOneOf(keyInfo.getOrigin(),
+                KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+        assertEquals(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+                        | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
+                keyInfo.getPurposes());
+        assertFalse(keyInfo.isUserAuthenticationRequired());
+        assertEquals(null, keyInfo.getKeyValidityStart());
+        assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+        assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+                KeyProperties.DIGEST_NONE);
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getEncryptionPaddings()),
+                KeyProperties.ENCRYPTION_PADDING_NONE);
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
     }
 
-    public void testKeyPairGenerator_GenerateKeyPair_Replaced_Unencrypted_Success()
+    public void testGenerate_ReplacesOldEntryWithSameAlias()
             throws Exception {
         // Generate the first key
         {
-            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
-                    .setAlias(TEST_ALIAS_1).setSubject(TEST_DN_1).setSerialNumber(TEST_SERIAL_1)
-                    .setStartDate(NOW).setEndDate(NOW_PLUS_10_YEARS).build());
-            final KeyPair pair1 = mGenerator.generateKeyPair();
-            assertNotNull("The KeyPair returned should not be null", pair1);
-            assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1,
-                    NOW, NOW_PLUS_10_YEARS);
+            KeyPairGenerator generator = getRsaGenerator();
+            generator.initialize(new KeyGenParameterSpec.Builder(
+                    TEST_ALIAS_1,
+                    KeyProperties.PURPOSE_SIGN
+                            | KeyProperties.PURPOSE_VERIFY
+                            | KeyProperties.PURPOSE_ENCRYPT
+                            | KeyProperties.PURPOSE_DECRYPT)
+                    .setDigests(KeyProperties.DIGEST_NONE)
+                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                    .setCertificateSubject(TEST_DN_1)
+                    .setCertificateSerialNumber(TEST_SERIAL_1)
+                    .setCertificateNotBefore(NOW)
+                    .setCertificateNotAfter(NOW_PLUS_10_YEARS)
+                    .build());
+            assertGeneratedKeyPairAndSelfSignedCertificate(
+                    generator.generateKeyPair(),
+                    TEST_ALIAS_1,
+                    "RSA",
+                    2048,
+                    TEST_DN_1,
+                    TEST_SERIAL_1,
+                    NOW,
+                    NOW_PLUS_10_YEARS);
         }
 
         // Replace the original key
         {
-            mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
-                    .setAlias(TEST_ALIAS_1).setSubject(TEST_DN_2).setSerialNumber(TEST_SERIAL_2)
-                    .setStartDate(NOW).setEndDate(NOW_PLUS_10_YEARS).build());
-            final KeyPair pair2 = mGenerator.generateKeyPair();
-            assertNotNull("The KeyPair returned should not be null", pair2);
-            assertKeyPairCorrect(pair2, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2,
-                    NOW, NOW_PLUS_10_YEARS);
+            KeyPairGenerator generator = getRsaGenerator();
+            generator.initialize(new KeyGenParameterSpec.Builder(
+                    TEST_ALIAS_1,
+                    KeyProperties.PURPOSE_SIGN
+                            | KeyProperties.PURPOSE_VERIFY
+                            | KeyProperties.PURPOSE_ENCRYPT
+                            | KeyProperties.PURPOSE_DECRYPT)
+                    .setDigests(KeyProperties.DIGEST_NONE)
+                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                    .setCertificateSubject(TEST_DN_2)
+                    .setCertificateSerialNumber(TEST_SERIAL_2)
+                    .setCertificateNotBefore(NOW)
+                    .setCertificateNotAfter(NOW_PLUS_10_YEARS)
+                    .build());
+            assertGeneratedKeyPairAndSelfSignedCertificate(
+                    generator.generateKeyPair(),
+                    TEST_ALIAS_1,
+                    "RSA",
+                    2048,
+                    TEST_DN_2,
+                    TEST_SERIAL_2,
+                    NOW,
+                    NOW_PLUS_10_YEARS);
         }
     }
 
-    public void testKeyPairGenerator_GenerateKeyPair_No_Collision_Unencrypted_Success()
+    public void testGenerate_DoesNotReplaceOtherEntries()
             throws Exception {
         // Generate the first key
-        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
-                .setAlias(TEST_ALIAS_1)
-                .setSubject(TEST_DN_1)
-                .setSerialNumber(TEST_SERIAL_1)
-                .setStartDate(NOW)
-                .setEndDate(NOW_PLUS_10_YEARS)
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN
+                        | KeyProperties.PURPOSE_VERIFY
+                        | KeyProperties.PURPOSE_ENCRYPT
+                        | KeyProperties.PURPOSE_DECRYPT)
+                .setDigests(KeyProperties.DIGEST_NONE)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .setCertificateSubject(TEST_DN_1)
+                .setCertificateSerialNumber(TEST_SERIAL_1)
+                .setCertificateNotBefore(NOW)
+                .setCertificateNotAfter(NOW_PLUS_10_YEARS)
                 .build());
-        final KeyPair pair1 = mGenerator.generateKeyPair();
-        assertNotNull("The KeyPair returned should not be null", pair1);
-        assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+        KeyPair keyPair1 = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair1,
+                TEST_ALIAS_1,
+                "RSA",
+                2048,
+                TEST_DN_1,
+                TEST_SERIAL_1,
+                NOW,
                 NOW_PLUS_10_YEARS);
 
         // Generate the second key
-        mGenerator.initialize(new KeyPairGeneratorSpec.Builder(getContext())
-                .setAlias(TEST_ALIAS_2)
-                .setSubject(TEST_DN_2)
-                .setSerialNumber(TEST_SERIAL_2)
-                .setStartDate(NOW)
-                .setEndDate(NOW_PLUS_10_YEARS)
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_2,
+                KeyProperties.PURPOSE_SIGN
+                        | KeyProperties.PURPOSE_VERIFY
+                        | KeyProperties.PURPOSE_ENCRYPT
+                        | KeyProperties.PURPOSE_DECRYPT)
+                .setDigests(KeyProperties.DIGEST_NONE)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .setCertificateSubject(TEST_DN_2)
+                .setCertificateSerialNumber(TEST_SERIAL_2)
+                .setCertificateNotBefore(NOW)
+                .setCertificateNotAfter(NOW_PLUS_10_YEARS)
                 .build());
-        final KeyPair pair2 = mGenerator.generateKeyPair();
-        assertNotNull("The KeyPair returned should not be null", pair2);
-        assertKeyPairCorrect(pair2, TEST_ALIAS_2, "RSA", 2048, null, TEST_DN_2, TEST_SERIAL_2, NOW,
+        KeyPair keyPair2 = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair2,
+                TEST_ALIAS_2,
+                "RSA",
+                2048,
+                TEST_DN_2,
+                TEST_SERIAL_2,
+                NOW,
                 NOW_PLUS_10_YEARS);
 
-        // Check the first key again
-        assertKeyPairCorrect(pair1, TEST_ALIAS_1, "RSA", 2048, null, TEST_DN_1, TEST_SERIAL_1, NOW,
+        // Check the first key pair again
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair1,
+                TEST_ALIAS_1,
+                "RSA",
+                2048,
+                TEST_DN_1,
+                TEST_SERIAL_1,
+                NOW,
                 NOW_PLUS_10_YEARS);
     }
 
-    private void assertKeyPairCorrect(KeyPair pair, String alias, String keyType, int keySize,
-            AlgorithmParameterSpec spec, X500Principal dn, BigInteger serial, Date start, Date end)
-            throws Exception {
-        final PublicKey pubKey = pair.getPublic();
-        assertNotNull("The PublicKey for the KeyPair should be not null", pubKey);
-        assertEquals(keyType, pubKey.getAlgorithm());
-        assertEquals("Public keys should be in X.509 format", "X.509", pubKey.getFormat());
-        assertNotNull("Public keys should be encodable", pubKey.getEncoded());
+    public void testGenerate_EC_ModernSpec_Defaults() throws Exception {
+        KeyPairGenerator generator = getEcGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "EC",
+                256,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+                ECCurves.NIST_P_256_SPEC, ((ECKey) keyPair.getPrivate()).getParams());
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(256, keyInfo.getKeySize());
+        assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+        assertOneOf(keyInfo.getOrigin(),
+                KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+        assertEquals(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY, keyInfo.getPurposes());
+        assertFalse(keyInfo.isUserAuthenticationRequired());
+        assertEquals(null, keyInfo.getKeyValidityStart());
+        assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+        assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getDigests()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+    }
 
-        if ("EC".equalsIgnoreCase(keyType)) {
-            assertEquals("Curve should be what was specified during initialization", keySize,
-                    ((ECPublicKey) pubKey).getParams().getCurve().getField().getFieldSize());
-        } else if ("RSA".equalsIgnoreCase(keyType)) {
-            RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey;
-            assertEquals("Modulus size should be what is specified during initialization",
-                    (keySize + 7) & ~7, (rsaPubKey.getModulus().bitLength() + 7) & ~7);
-            if (spec != null) {
-                RSAKeyGenParameterSpec params = (RSAKeyGenParameterSpec) spec;
-                assertEquals((keySize + 7) & ~7, (params.getKeysize() + 7) & ~7);
-                assertEquals(params.getPublicExponent(), rsaPubKey.getPublicExponent());
+    public void testGenerate_RSA_ModernSpec_Defaults() throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                2048,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        assertEquals(RSAKeyGenParameterSpec.F4,
+                ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(2048, keyInfo.getKeySize());
+        assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+        assertOneOf(keyInfo.getOrigin(),
+                KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+        assertEquals(
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
+                keyInfo.getPurposes());
+        assertFalse(keyInfo.isUserAuthenticationRequired());
+        assertEquals(null, keyInfo.getKeyValidityStart());
+        assertEquals(null, keyInfo.getKeyValidityForOriginationEnd());
+        assertEquals(null, keyInfo.getKeyValidityForConsumptionEnd());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getDigests()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+    }
+
+    public void testGenerate_EC_ModernSpec_AsCustomAsPossible() throws Exception {
+        KeyPairGenerator generator = getEcGenerator();
+        Date keyValidityStart = new Date(System.currentTimeMillis());
+        Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 1000000);
+        Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 10000000);
+
+        Date certNotBefore = new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 7);
+        Date certNotAfter = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7);
+        BigInteger certSerialNumber = new BigInteger("12345678");
+        X500Principal certSubject = new X500Principal("cn=hello");
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+                        | KeyProperties.PURPOSE_ENCRYPT)
+                .setKeySize(224)
+                .setDigests(KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512)
+                .setKeyValidityStart(keyValidityStart)
+                .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+                .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+                .setCertificateSerialNumber(certSerialNumber)
+                .setCertificateSubject(certSubject)
+                .setCertificateNotBefore(certNotBefore)
+                .setCertificateNotAfter(certNotAfter)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(600)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "EC",
+                224,
+                certSubject,
+                certSerialNumber,
+                certNotBefore,
+                certNotAfter);
+        TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+                ECCurves.NIST_P_224_SPEC, ((ECKey) keyPair.getPrivate()).getParams());
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(224, keyInfo.getKeySize());
+        assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+        assertOneOf(keyInfo.getOrigin(),
+                KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+        assertEquals(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+                        | KeyProperties.PURPOSE_ENCRYPT,
+                keyInfo.getPurposes());
+        assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
+        assertEquals(keyValidityEndDateForOrigination, keyInfo.getKeyValidityForOriginationEnd());
+        assertEquals(keyValidityEndDateForConsumption, keyInfo.getKeyValidityForConsumptionEnd());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+                KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512);
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+        assertTrue(keyInfo.isUserAuthenticationRequired());
+        assertEquals(600, keyInfo.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testGenerate_RSA_ModernSpec_AsCustomAsPossible() throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        Date keyValidityStart = new Date(System.currentTimeMillis());
+        Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 1000000);
+        Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 10000000);
+
+        Date certNotBefore = new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 210);
+        Date certNotAfter = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 210);
+        BigInteger certSerialNumber = new BigInteger("1234567890");
+        X500Principal certSubject = new X500Principal("cn=hello2");
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+                        | KeyProperties.PURPOSE_ENCRYPT)
+                .setAlgorithmParameterSpec(
+                        new RSAKeyGenParameterSpec(3072, RSAKeyGenParameterSpec.F0))
+                .setKeySize(3072)
+                .setDigests(KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512)
+                .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+                        KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+                        KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
+                .setKeyValidityStart(keyValidityStart)
+                .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+                .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+                .setCertificateSerialNumber(certSerialNumber)
+                .setCertificateSubject(certSubject)
+                .setCertificateNotBefore(certNotBefore)
+                .setCertificateNotAfter(certNotAfter)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(600)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                3072,
+                certSubject,
+                certSerialNumber,
+                certNotBefore,
+                certNotAfter);
+        assertEquals(RSAKeyGenParameterSpec.F0,
+                ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(3072, keyInfo.getKeySize());
+        assertEquals(TEST_ALIAS_1, keyInfo.getKeystoreAlias());
+        assertOneOf(keyInfo.getOrigin(),
+                KeyProperties.ORIGIN_GENERATED, KeyProperties.ORIGIN_UNKNOWN);
+        assertEquals(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
+                        | KeyProperties.PURPOSE_ENCRYPT,
+                keyInfo.getPurposes());
+        assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
+        assertEquals(keyValidityEndDateForOrigination, keyInfo.getKeyValidityForOriginationEnd());
+        assertEquals(keyValidityEndDateForConsumption, keyInfo.getKeyValidityForConsumptionEnd());
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+                KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512);
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getSignaturePaddings()),
+                KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+                KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getBlockModes()),
+                KeyProperties.BLOCK_MODE_ECB);
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getEncryptionPaddings()),
+                KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+                KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
+        assertTrue(keyInfo.isUserAuthenticationRequired());
+        assertEquals(600, keyInfo.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testGenerate_EC_ModernSpec_UsableForTLSPeerAuth() throws Exception {
+        KeyPairGenerator generator = getEcGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setDigests(KeyProperties.DIGEST_NONE)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "EC",
+                256,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+                KeyProperties.DIGEST_NONE);
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
+        assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_1);
+        assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_1);
+    }
+
+    public void testGenerate_RSA_ModernSpec_UsableForTLSPeerAuth() throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN
+                        | KeyProperties.PURPOSE_VERIFY
+                        | KeyProperties.PURPOSE_DECRYPT)
+                .setDigests(KeyProperties.DIGEST_NONE)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                2048,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getBlockModes()));
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getDigests()),
+                KeyProperties.DIGEST_NONE);
+        MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+        MoreAsserts.assertContentsInAnyOrder(Arrays.asList(keyInfo.getEncryptionPaddings()),
+                KeyProperties.ENCRYPTION_PADDING_NONE);
+        assertSelfSignedCertificateSignatureVerifies(TEST_ALIAS_1);
+        assertKeyPairAndCertificateUsableForTLSPeerAuthentication(TEST_ALIAS_1);
+    }
+
+    public void testGenerate_EC_ModernSpec_PerOpAuthRequired() throws Exception {
+        KeyPairGenerator generator = getEcGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setDigests(KeyProperties.DIGEST_NONE)
+                .setUserAuthenticationRequired(true)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "EC",
+                256,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertTrue(keyInfo.isUserAuthenticationRequired());
+        assertEquals(-1, keyInfo.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testGenerate_RSA_ModernSpec_PerOpAuthRequired() throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setDigests(KeyProperties.DIGEST_NONE)
+                .setUserAuthenticationRequired(true)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                2048,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertTrue(keyInfo.isUserAuthenticationRequired());
+        assertEquals(-1, keyInfo.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testGenerate_EC_ModernSpec_TimeoutBasedAuthRequired() throws Exception {
+        KeyPairGenerator generator = getEcGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setDigests(KeyProperties.DIGEST_NONE)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(60)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "EC",
+                256,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertTrue(keyInfo.isUserAuthenticationRequired());
+        assertEquals(60, keyInfo.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testGenerate_RSA_ModernSpec_TimeoutBasedAuthRequired() throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setDigests(KeyProperties.DIGEST_NONE)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(60)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                2048,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertTrue(keyInfo.isUserAuthenticationRequired());
+        assertEquals(60, keyInfo.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testGenerate_EC_ModernSpec_SupportedSizes() throws Exception {
+        assertKeyGenUsingECSizeOnlyUsesCorrectCurve(224, ECCurves.NIST_P_224_SPEC);
+        assertKeyGenUsingECSizeOnlyUsesCorrectCurve(256, ECCurves.NIST_P_256_SPEC);
+        assertKeyGenUsingECSizeOnlyUsesCorrectCurve(384, ECCurves.NIST_P_384_SPEC);
+        assertKeyGenUsingECSizeOnlyUsesCorrectCurve(521, ECCurves.NIST_P_521_SPEC);
+    }
+
+    public void testGenerate_EC_ModernSpec_UnsupportedSizesRejected() throws Exception {
+        for (int keySizeBits = 0; keySizeBits <= 1024; keySizeBits++) {
+            if ((keySizeBits == 224) || (keySizeBits == 256) || (keySizeBits == 384)
+                    || (keySizeBits == 521)) {
+                // Skip supported sizes
+                continue;
+            }
+            KeyPairGenerator generator = getEcGenerator();
+
+            try {
+                generator.initialize(new KeyGenParameterSpec.Builder(
+                        TEST_ALIAS_1,
+                        KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                        .setKeySize(keySizeBits)
+                        .build());
+                fail("EC KeyPairGenerator initialized with unsupported key size: "
+                        + keySizeBits + " bits");
+            } catch (InvalidAlgorithmParameterException expected) {
             }
         }
-
-        final PrivateKey privKey = pair.getPrivate();
-        assertNotNull("The PrivateKey for the KeyPair should be not null", privKey);
-        assertEquals(keyType, privKey.getAlgorithm());
-        assertNull("getFormat() should return null", privKey.getFormat());
-        assertNull("getEncoded() should return null", privKey.getEncoded());
-
-        KeyStore.Entry entry = mKeyStore.getEntry(alias, null);
-        assertNotNull("Entry should exist", entry);
-
-        assertTrue("Entry should be a PrivateKeyEntry", entry instanceof KeyStore.PrivateKeyEntry);
-        KeyStore.PrivateKeyEntry privEntry = (KeyStore.PrivateKeyEntry) entry;
-
-        Certificate userCert = privEntry.getCertificate();
-        assertTrue("Certificate should be in X.509 format", userCert instanceof X509Certificate);
-
-        final X509Certificate x509userCert = (X509Certificate) userCert;
-
-        assertEquals("PublicKey used to sign certificate should match one returned in KeyPair",
-                pubKey, x509userCert.getPublicKey());
-
-        assertEquals("The Subject DN should be the one passed into the params", dn,
-                x509userCert.getSubjectDN());
-
-        assertEquals("The Issuer DN should be the same as the Subject DN", dn,
-                x509userCert.getIssuerDN());
-
-        assertEquals("The Serial should be the one passed into the params", serial,
-                x509userCert.getSerialNumber());
-
-        assertDateEquals("The notBefore date should be the one passed into the params", start,
-                x509userCert.getNotBefore());
-
-        assertDateEquals("The notAfter date should be the one passed into the params", end,
-                x509userCert.getNotAfter());
-
-        x509userCert.verify(pubKey);
-
-        Certificate[] chain = privEntry.getCertificateChain();
-        assertEquals("A list of CA certificates should not exist for the generated entry", 1,
-                chain.length);
-
-        assertUsableInSSLConnection(privKey, x509userCert);
-
-        assertEquals("Retrieved key and generated key should be equal", privKey,
-                privEntry.getPrivateKey());
     }
 
-    private static void assertUsableInSSLConnection(final PrivateKey privKey,
-            final X509Certificate x509userCert) throws Exception {
-        // TODO this should probably be in something like:
-        // TestKeyStore.createForClientSelfSigned(...)
-        TrustManager[] clientTrustManagers = TestKeyStore.createTrustManagers(
-                TestKeyStore.getIntermediateCa().keyStore);
-        SSLContext clientContext = TestSSLContext.createSSLContext("TLS",
-                new KeyManager[] {
-                    TestKeyManager.wrap(new MyKeyManager(privKey, x509userCert))
-                }, clientTrustManagers);
+    public void testGenerate_EC_ModernSpec_SupportedNamedCurves() throws Exception {
+        assertKeyGenUsingECNamedCurveSupported("P-224", ECCurves.NIST_P_224_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("p-224", ECCurves.NIST_P_224_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("secp224r1", ECCurves.NIST_P_224_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("SECP224R1", ECCurves.NIST_P_224_SPEC);
+
+        assertKeyGenUsingECNamedCurveSupported("P-256", ECCurves.NIST_P_256_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("p-256", ECCurves.NIST_P_256_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("secp256r1", ECCurves.NIST_P_256_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("SECP256R1", ECCurves.NIST_P_256_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("prime256v1", ECCurves.NIST_P_256_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("PRIME256V1", ECCurves.NIST_P_256_SPEC);
+
+        assertKeyGenUsingECNamedCurveSupported("P-384", ECCurves.NIST_P_384_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("p-384", ECCurves.NIST_P_384_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("secp384r1", ECCurves.NIST_P_384_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("SECP384R1", ECCurves.NIST_P_384_SPEC);
+
+        assertKeyGenUsingECNamedCurveSupported("P-521", ECCurves.NIST_P_521_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("p-521", ECCurves.NIST_P_521_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("secp521r1", ECCurves.NIST_P_521_SPEC);
+        assertKeyGenUsingECNamedCurveSupported("SECP521R1", ECCurves.NIST_P_521_SPEC);
+    }
+
+    public void testGenerate_RSA_ModernSpec_SupportedSizes() throws Exception {
+        assertKeyGenUsingRSASizeOnlySupported(512);
+        assertKeyGenUsingRSASizeOnlySupported(768);
+        assertKeyGenUsingRSASizeOnlySupported(1024);
+        assertKeyGenUsingRSASizeOnlySupported(2048);
+        assertKeyGenUsingRSASizeOnlySupported(3072);
+        assertKeyGenUsingRSASizeOnlySupported(4096);
+
+        // The above use F4. Check that F0 is supported as well, just in case somebody is crazy
+        // enough.
+        assertKeyGenUsingRSAKeyGenParameterSpecSupported(new RSAKeyGenParameterSpec(
+                2048, RSAKeyGenParameterSpec.F0));
+    }
+
+    private void assertKeyGenUsingECSizeOnlyUsesCorrectCurve(
+            int keySizeBits, ECParameterSpec expectedParams) throws Exception {
+        KeyPairGenerator generator = getEcGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setKeySize(keySizeBits)
+                .build(),
+                mRng);
+        mRng.resetCounters();
+        KeyPair keyPair = generator.generateKeyPair();
+        long consumedEntropyAmountBytes = mRng.getOutputSizeBytes();
+        int expectedKeySize = expectedParams.getCurve().getField().getFieldSize();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "EC",
+                expectedKeySize,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(expectedKeySize, keyInfo.getKeySize());
+        TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+                expectedParams,
+                ((ECKey) keyPair.getPublic()).getParams());
+        assertEquals(((keySizeBits + 7) / 8) * 8, consumedEntropyAmountBytes * 8);
+    }
+
+    private void assertKeyGenUsingECNamedCurveSupported(
+            String curveName, ECParameterSpec expectedParams) throws Exception {
+        KeyPairGenerator generator = getEcGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setAlgorithmParameterSpec(new ECGenParameterSpec(curveName))
+                .build(),
+                mRng);
+        mRng.resetCounters();
+        KeyPair keyPair = generator.generateKeyPair();
+        long consumedEntropyAmountBytes = mRng.getOutputSizeBytes();
+        int expectedKeySize = expectedParams.getCurve().getField().getFieldSize();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "EC",
+                expectedKeySize,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(expectedKeySize, keyInfo.getKeySize());
+        TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+                expectedParams,
+                ((ECKey) keyPair.getPublic()).getParams());
+        assertEquals(((expectedKeySize + 7) / 8) * 8, consumedEntropyAmountBytes * 8);
+    }
+
+    private void assertKeyGenUsingRSASizeOnlySupported(int keySizeBits) throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setKeySize(keySizeBits)
+                .build(),
+                mRng);
+        mRng.resetCounters();
+        KeyPair keyPair = generator.generateKeyPair();
+        long consumedEntropyAmountBytes = mRng.getOutputSizeBytes();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                keySizeBits,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(keySizeBits, keyInfo.getKeySize());
+        assertEquals(((keySizeBits + 7) / 8) * 8, consumedEntropyAmountBytes * 8);
+    }
+
+    private void assertKeyGenUsingRSAKeyGenParameterSpecSupported(
+            RSAKeyGenParameterSpec spec) throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setAlgorithmParameterSpec(spec)
+                .build(),
+                mRng);
+        mRng.resetCounters();
+        KeyPair keyPair = generator.generateKeyPair();
+        long consumedEntropyAmountBytes = mRng.getOutputSizeBytes();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                spec.getKeysize(),
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        assertEquals(spec.getPublicExponent(),
+                ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(spec.getKeysize(), keyInfo.getKeySize());
+        assertEquals(((spec.getKeysize() + 7) / 8) * 8, consumedEntropyAmountBytes * 8);
+    }
+
+    private static void assertSelfSignedCertificateSignatureVerifies(Certificate certificate) {
+        try {
+            certificate.verify(certificate.getPublicKey());
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to verify self-signed certificate signature", e);
+        }
+    }
+
+    private void assertGeneratedKeyPairAndSelfSignedCertificate(
+            KeyPair keyPair, String alias,
+            String expectedKeyAlgorithm,
+            int expectedKeySize,
+            X500Principal expectedCertSubject,
+            BigInteger expectedCertSerialNumber,
+            Date expectedCertNotBefore,
+            Date expectedCertNotAfter)
+            throws Exception {
+        assertNotNull(keyPair);
+        TestUtils.assertKeyPairSelfConsistent(keyPair);
+        TestUtils.assertKeySize(expectedKeySize, keyPair);
+        assertEquals(expectedKeyAlgorithm, keyPair.getPublic().getAlgorithm());
+        TestUtils.assertKeyStoreKeyPair(mKeyStore, alias, keyPair);
+
+        X509Certificate cert = (X509Certificate) mKeyStore.getCertificate(alias);
+        assertEquals(keyPair.getPublic(), cert.getPublicKey());
+        assertX509CertificateParameters(cert,
+                expectedCertSubject,
+                expectedCertSerialNumber,
+                expectedCertNotBefore,
+                expectedCertNotAfter);
+        // Assert that the certificate chain consists only of the above certificate
+        MoreAsserts.assertContentsInOrder(
+                Arrays.asList(mKeyStore.getCertificateChain(alias)), cert);
+    }
+
+    private void assertSelfSignedCertificateSignatureVerifies(String alias) throws Exception {
+        assertSelfSignedCertificateSignatureVerifies(mKeyStore.getCertificate(alias));
+    }
+
+    private void assertKeyPairAndCertificateUsableForTLSPeerAuthentication(String alias)
+            throws Exception {
+        assertUsableForTLSPeerAuthentication(
+                (PrivateKey) mKeyStore.getKey(alias, null),
+                mKeyStore.getCertificateChain(alias));
+    }
+
+    private static void assertX509CertificateParameters(
+            X509Certificate actualCert,
+            X500Principal expectedSubject, BigInteger expectedSerialNumber,
+            Date expectedNotBefore, Date expectedNotAfter) {
+        assertEquals(expectedSubject, actualCert.getSubjectDN());
+        assertEquals(expectedSubject, actualCert.getIssuerDN());
+        assertEquals(expectedSerialNumber, actualCert.getSerialNumber());
+        assertDateEquals(expectedNotBefore, actualCert.getNotBefore());
+        assertDateEquals(expectedNotAfter, actualCert.getNotAfter());
+    }
+
+    private static void assertUsableForTLSPeerAuthentication(
+            PrivateKey privateKey,
+            Certificate[] certificateChain) throws Exception {
+        // Set up both client and server to use the same private key + cert, and to trust that cert
+        // when it's presented by peer. This exercises the use of the private key both in client
+        // and server scenarios.
+        X509Certificate[] x509CertificateChain = new X509Certificate[certificateChain.length];
+        for (int i = 0; i < certificateChain.length; i++) {
+            x509CertificateChain[i] = (X509Certificate) certificateChain[i];
+        }
         TestKeyStore serverKeyStore = TestKeyStore.getServer();
-        serverKeyStore.keyStore.setCertificateEntry("client-selfSigned", x509userCert);
+        // Make the peer trust the root certificate in the chain. As opposed to making the peer
+        // trust the leaf certificate, this will ensure that the whole chain verifies.
+        serverKeyStore.keyStore.setCertificateEntry(
+                "trusted", certificateChain[certificateChain.length - 1]);
         SSLContext serverContext = TestSSLContext.createSSLContext("TLS",
-                serverKeyStore.keyManagers,
+                new KeyManager[] {
+                    TestKeyManager.wrap(new MyKeyManager(privateKey, x509CertificateChain))
+                },
                 TestKeyStore.createTrustManagers(serverKeyStore.keyStore));
+        SSLContext clientContext = serverContext;
+
+        if ("EC".equalsIgnoreCase(privateKey.getAlgorithm())) {
+            // As opposed to RSA (see below) EC keys are used in the same way in all cipher suites.
+            // Assert that the key works with the default list of cipher suites.
+            assertSSLConnectionWithClientAuth(
+                    clientContext, serverContext, null, x509CertificateChain, x509CertificateChain);
+        } else if ("RSA".equalsIgnoreCase(privateKey.getAlgorithm())) {
+            // RSA keys are used differently between Forward Secure and non-Forward Secure cipher
+            // suites. For example, RSA key exchange requires the server to decrypt using its RSA
+            // private key, whereas ECDHE_RSA key exchange requires the server to sign usnig its
+            // RSA private key. We thus assert that the key works with Forward Secure cipher suites
+            // and that it works with non-Forward Secure cipher suites.
+            List<String> fsCipherSuites = new ArrayList<String>();
+            List<String> nonFsCipherSuites = new ArrayList<String>();
+            for (String cipherSuite : clientContext.getDefaultSSLParameters().getCipherSuites()) {
+                if (cipherSuite.contains("_ECDHE_RSA_") || cipherSuite.contains("_DHE_RSA_")) {
+                    fsCipherSuites.add(cipherSuite);
+                } else if (cipherSuite.contains("_RSA_WITH_")) {
+                    nonFsCipherSuites.add(cipherSuite);
+                }
+            }
+            assertFalse("No FS RSA cipher suites enabled by default", fsCipherSuites.isEmpty());
+            assertFalse("No non-FS RSA cipher suites enabled", nonFsCipherSuites.isEmpty());
+
+            // Assert that the key works with RSA Forward Secure cipher suites.
+            assertSSLConnectionWithClientAuth(
+                    clientContext, serverContext, fsCipherSuites.toArray(new String[0]),
+                    x509CertificateChain, x509CertificateChain);
+            // Assert that the key works with RSA non-Forward Secure cipher suites.
+            assertSSLConnectionWithClientAuth(
+                    clientContext, serverContext, nonFsCipherSuites.toArray(new String[0]),
+                    x509CertificateChain, x509CertificateChain);
+        } else {
+            fail("Unsupported key algorithm: " + privateKey.getAlgorithm());
+        }
+    }
+
+    private static void assertSSLConnectionWithClientAuth(
+            SSLContext clientContext, SSLContext serverContext, String[] enabledCipherSuites,
+            X509Certificate[] expectedClientCertChain, X509Certificate[] expectedServerCertChain)
+            throws Exception {
         SSLServerSocket serverSocket = (SSLServerSocket) serverContext.getServerSocketFactory()
                 .createServerSocket(0);
         InetAddress host = InetAddress.getLocalHost();
         int port = serverSocket.getLocalPort();
-
         SSLSocket client = (SSLSocket) clientContext.getSocketFactory().createSocket(host, port);
+
         final SSLSocket server = (SSLSocket) serverSocket.accept();
         ExecutorService executor = Executors.newSingleThreadExecutor();
-        Future<Void> future = executor.submit(new Callable<Void>() {
+        Future<Certificate[]> future = executor.submit(new Callable<Certificate[]>() {
             @Override
-            public Void call() throws Exception {
+            public Certificate[] call() throws Exception {
                 server.setNeedClientAuth(true);
                 server.setWantClientAuth(true);
                 server.startHandshake();
-                return null;
+                return server.getSession().getPeerCertificates();
             }
         });
         executor.shutdown();
+        if (enabledCipherSuites != null) {
+            client.setEnabledCipherSuites(enabledCipherSuites);
+        }
         client.startHandshake();
-        Certificate[] usedClientCerts = client.getSession().getLocalCertificates();
-        assertNotNull(usedClientCerts);
-        assertEquals(1, usedClientCerts.length);
-        assertEquals(x509userCert, usedClientCerts[0]);
-        future.get();
+        Certificate[] usedServerCerts = client.getSession().getPeerCertificates();
+        Certificate[] usedClientCerts = future.get();
         client.close();
         server.close();
+
+        assertNotNull(usedServerCerts);
+        assertEquals(Arrays.asList(expectedServerCertChain), Arrays.asList(usedServerCerts));
+
+        assertNotNull(usedClientCerts);
+        assertEquals(Arrays.asList(expectedClientCertChain), Arrays.asList(usedClientCerts));
     }
 
     private static class MyKeyManager extends X509ExtendedKeyManager {
         private final PrivateKey key;
         private final X509Certificate[] chain;
 
-        public MyKeyManager(PrivateKey key, X509Certificate cert) {
+        public MyKeyManager(PrivateKey key, X509Certificate[] certChain) {
             this.key = key;
-            this.chain = new X509Certificate[] { cert };
+            this.chain = certChain;
         }
 
         @Override
@@ -365,7 +1164,7 @@
 
         @Override
         public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
-            throw new UnsupportedOperationException("Not implemented");
+            return "fake";
         }
 
         @Override
@@ -380,7 +1179,7 @@
 
         @Override
         public String[] getServerAliases(String keyType, Principal[] issuers) {
-            throw new UnsupportedOperationException("Not implemented");
+            return new String[] { "fake" };
         }
 
         @Override
@@ -389,7 +1188,12 @@
         }
     }
 
-    private static void assertDateEquals(String message, Date date1, Date date2) throws Exception {
+
+    private static void assertDateEquals(Date date1, Date date2) {
+        assertDateEquals(null, date1, date2);
+    }
+
+    private static void assertDateEquals(String message, Date date1, Date date2) {
         SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
 
         String result1 = formatter.format(date1);
@@ -397,4 +1201,29 @@
 
         assertEquals(message, result1, result2);
     }
+
+    private KeyPairGenerator getRsaGenerator()
+            throws NoSuchAlgorithmException, NoSuchProviderException {
+        return KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
+    }
+
+    private KeyPairGenerator getEcGenerator()
+            throws NoSuchAlgorithmException, NoSuchProviderException {
+        return KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
+    }
+
+    private static void assertOneOf(int actual, int... expected) {
+        assertOneOf(null, actual, expected);
+    }
+
+    private static void assertOneOf(String message, int actual, int... expected) {
+        for (int expectedValue : expected) {
+            if (actual == expectedValue) {
+                return;
+            }
+        }
+        fail(((message != null) ? message + ". " : "")
+                + "Expected one of " + Arrays.asList(expected)
+                + ", actual: <" + actual + ">");
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java b/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java
new file mode 100644
index 0000000..a93cc35
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java
@@ -0,0 +1,89 @@
+package android.keystore.cts;
+
+import java.security.SecureRandom;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * {@link SecureRandom} which counts how many bytes it has output.
+ */
+public class CountingSecureRandom extends SecureRandom {
+
+    private final SecureRandom mDelegate = new SecureRandom();
+    private final AtomicLong mOutputSizeBytes = new AtomicLong();
+
+    public long getOutputSizeBytes() {
+        return mOutputSizeBytes.get();
+    }
+
+    public void resetCounters() {
+        mOutputSizeBytes.set(0);
+    }
+
+    @Override
+    public byte[] generateSeed(int numBytes) {
+        if (numBytes > 0) {
+            mOutputSizeBytes.addAndGet(numBytes);
+        }
+        return mDelegate.generateSeed(numBytes);
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return mDelegate.getAlgorithm();
+    }
+
+    @Override
+    public synchronized void nextBytes(byte[] bytes) {
+        if ((bytes != null) && (bytes.length > 0)) {
+            mOutputSizeBytes.addAndGet(bytes.length);
+        }
+        mDelegate.nextBytes(bytes);
+    }
+
+    @Override
+    public synchronized void setSeed(byte[] seed) {
+        // Ignore seeding -- not needed in tests and may impact the quality of the output of the
+        // delegate SecureRandom by preventing it from self-seeding
+    }
+
+    @Override
+    public void setSeed(long seed) {
+        // Ignore seeding -- not needed in tests and may impact the quality of the output of the
+        // delegate SecureRandom by preventing it from self-seeding
+    }
+
+    @Override
+    public boolean nextBoolean() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public double nextDouble() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public float nextFloat() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public synchronized double nextGaussian() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int nextInt() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int nextInt(int n) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public long nextLong() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/ECCurves.java b/tests/tests/keystore/src/android/keystore/cts/ECCurves.java
new file mode 100644
index 0000000..24184e6
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/ECCurves.java
@@ -0,0 +1,123 @@
+/*
+ * 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.keystore.cts;
+
+import java.math.BigInteger;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.EllipticCurve;
+
+abstract class ECCurves {
+    private ECCurves() {}
+
+    // NIST EC curve parameters copied from "Standards for Efficient Cryptography 2 (SEC 2)".
+
+    static ECParameterSpec NIST_P_192_SPEC = createNistPCurveSpec(
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", 16),
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", 16),
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", 16),
+            new BigInteger("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", 16),
+            new BigInteger("188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", 16),
+            new BigInteger("07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", 16),
+            1,
+            HexEncoding.decode("3045AE6FC8422F64ED579528D38120EAE12196D5"));
+
+    static ECParameterSpec NIST_P_224_SPEC = createNistPCurveSpec(
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", 16),
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", 16),
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", 16),
+            new BigInteger("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", 16),
+            new BigInteger("B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", 16),
+            new BigInteger("BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", 16),
+            1,
+            HexEncoding.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"));
+
+    static ECParameterSpec NIST_P_256_SPEC = createNistPCurveSpec(
+            new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFF"
+                    + "FFFFFFFF", 16),
+            new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2"
+                    + "FC632551", 16),
+            new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFF"
+                    + "FFFFFFFC", 16),
+            new BigInteger("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E"
+                    + "27D2604B", 16),
+            new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945"
+                    + "D898C296", 16),
+            new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB64068"
+                    + "37BF51F5", 16),
+            1,
+            HexEncoding.decode("C49D360886E704936A6678E1139D26B7819F7E90"));
+
+    static ECParameterSpec NIST_P_384_SPEC = createNistPCurveSpec(
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "FFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16),
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81"
+                    + "F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16),
+            new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "FFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16),
+            new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F"
+                    + "5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16),
+            new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E0"
+                    + "82542A385502F25DBF55296C3A545E3872760AB7", 16),
+            new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113"
+                    + "B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16),
+            1,
+            HexEncoding.decode("A335926AA319A27A1D00896A6773A4827ACDAC73"));
+
+    static ECParameterSpec NIST_P_521_SPEC = createNistPCurveSpec(
+            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "FFFFFFFFFFFFFFFFFFFFFFFF", 16),
+            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "FFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8"
+                    + "899C47AEBB6FB71E91386409", 16),
+            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+                    + "FFFFFFFFFFFFFFFFFFFFFFFC", 16),
+            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3"
+                    + "B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF88"
+                    + "3D2C34F1EF451FD46B503F00", 16),
+            new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521"
+                    + "F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1"
+                    + "856A429BF97E7E31C2E5BD66", 16),
+            new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B4468"
+                    + "17AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086"
+                    + "A272C24088BE94769FD16650", 16),
+            1,
+            HexEncoding.decode("D09E8800291CB85396CC6717393284AAA0DA64BA"));
+
+    private static ECParameterSpec createNistPCurveSpec(
+            BigInteger p,
+            BigInteger order,
+            BigInteger a,
+            BigInteger b,
+            BigInteger gx,
+            BigInteger gy,
+            int cofactor,
+            byte[] seed) {
+        ECField field = new ECFieldFp(p);
+        EllipticCurve curve = new EllipticCurve(field, a, b, seed);
+        ECPoint generator = new ECPoint(gx, gy);
+        return new ECParameterSpec(
+                curve,
+                generator,
+                order,
+                cofactor);
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/HexEncoding.java b/tests/tests/keystore/src/android/keystore/cts/HexEncoding.java
new file mode 100644
index 0000000..9346f2d
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/HexEncoding.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 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.keystore.cts;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Hexadecimal encoding where each byte is represented by two hexadecimal digits.
+ *
+ * @hide
+ */
+public class HexEncoding {
+
+  /** Hidden constructor to prevent instantiation. */
+  private HexEncoding() {}
+
+  private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
+
+  /**
+   * Encodes the provided data as a hexadecimal string.
+   */
+  public static String encode(byte[] data) {
+    return encode(data, 0, data.length);
+  }
+
+  /**
+   * Encodes the provided data as a hexadecimal string.
+   */
+  public static String encode(byte[] data, int offset, int len) {
+    StringBuilder result = new StringBuilder(len * 2);
+    for (int i = 0; i < len; i++) {
+      byte b = data[offset + i];
+      result.append(HEX_DIGITS[(b >>> 4) & 0x0f]);
+      result.append(HEX_DIGITS[b & 0x0f]);
+    }
+    return result.toString();
+  }
+
+  /**
+   * Encodes the provided data as a hexadecimal string.
+   */
+  public static String encode(ByteBuffer buf) {
+    return encode(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
+  }
+
+  /**
+   * Decodes the provided hexadecimal string into an array of bytes.
+   */
+  public static byte[] decode(String encoded) {
+    // IMPLEMENTATION NOTE: Special care is taken to permit odd number of hexadecimal digits.
+    int resultLengthBytes = (encoded.length() + 1) / 2;
+    byte[] result = new byte[resultLengthBytes];
+    int resultOffset = 0;
+    int encodedCharOffset = 0;
+    if ((encoded.length() % 2) != 0) {
+      // Odd number of digits -- the first digit is the lower 4 bits of the first result byte.
+      result[resultOffset++] = (byte) getHexadecimalDigitValue(encoded.charAt(encodedCharOffset));
+      encodedCharOffset++;
+    }
+    for (int len = encoded.length(); encodedCharOffset < len; encodedCharOffset += 2) {
+      result[resultOffset++] = (byte)
+          ((getHexadecimalDigitValue(encoded.charAt(encodedCharOffset)) << 4)
+          | getHexadecimalDigitValue(encoded.charAt(encodedCharOffset + 1)));
+    }
+    return result;
+  }
+
+  private static int getHexadecimalDigitValue(char c) {
+    if ((c >= 'a') && (c <= 'f')) {
+      return (c - 'a') + 0x0a;
+    } else if ((c >= 'A') && (c <= 'F')) {
+      return (c - 'A') + 0x0a;
+    } else if ((c >= '0') && (c <= '9')) {
+      return c - '0';
+    } else {
+      throw new IllegalArgumentException(
+          "Invalid hexadecimal digit at position : '" + c + "' (0x" + Integer.toHexString(c) + ")");
+    }
+  }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyGenParameterSpecTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyGenParameterSpecTest.java
new file mode 100644
index 0000000..a3e5d96
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyGenParameterSpecTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 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.keystore.cts;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.test.MoreAsserts;
+
+import junit.framework.TestCase;
+
+import java.math.BigInteger;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+public class KeyGenParameterSpecTest extends TestCase {
+
+    private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
+    private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
+    private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
+    private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
+
+    public void testDefaults() {
+        // Set only the mandatory parameters and assert values returned by getters.
+
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                "arbitrary", KeyProperties.PURPOSE_ENCRYPT)
+                .build();
+
+        assertEquals("arbitrary", spec.getKeystoreAlias());
+        assertEquals(KeyProperties.PURPOSE_ENCRYPT, spec.getPurposes());
+        assertNull(null, spec.getAlgorithmParameterSpec());
+        MoreAsserts.assertEmpty(Arrays.asList(spec.getBlockModes()));
+        assertEquals(DEFAULT_CERT_NOT_BEFORE, spec.getCertificateNotBefore());
+        assertEquals(DEFAULT_CERT_NOT_AFTER, spec.getCertificateNotAfter());
+        assertEquals(DEFAULT_CERT_SERIAL_NUMBER, spec.getCertificateSerialNumber());
+        assertEquals(DEFAULT_CERT_SUBJECT, spec.getCertificateSubject());
+        assertFalse(spec.isDigestsSpecified());
+        try {
+            spec.getDigests();
+            fail();
+        } catch (IllegalStateException expected) {}
+        MoreAsserts.assertEmpty(Arrays.asList(spec.getEncryptionPaddings()));
+        assertEquals(-1, spec.getKeySize());
+        assertNull(spec.getKeyValidityStart());
+        assertNull(spec.getKeyValidityForOriginationEnd());
+        assertNull(spec.getKeyValidityForConsumptionEnd());
+        assertTrue(spec.isRandomizedEncryptionRequired());
+        MoreAsserts.assertEmpty(Arrays.asList(spec.getSignaturePaddings()));
+        assertFalse(spec.isUserAuthenticationRequired());
+        assertEquals(-1, spec.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testSettersReflectedInGetters() {
+        // Set all parameters to non-default values and then assert that getters reflect that.
+
+        Date certNotBeforeDate = new Date(System.currentTimeMillis());
+        Date certNotAfterDate = new Date(System.currentTimeMillis() + 12345678);
+        Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+        Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+        Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+        AlgorithmParameterSpec algSpecificParams = new ECGenParameterSpec("secp256r1");
+
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                "arbitrary", KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+                .setAlgorithmParameterSpec(algSpecificParams)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC)
+                .setCertificateNotBefore(certNotBeforeDate)
+                .setCertificateNotAfter(certNotAfterDate)
+                .setCertificateSerialNumber(new BigInteger("13946146"))
+                .setCertificateSubject(new X500Principal("CN=test"))
+                .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+                        KeyProperties.ENCRYPTION_PADDING_PKCS7)
+                .setKeySize(1234)
+                .setKeyValidityStart(keyValidityStartDate)
+                .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+                .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+                .setRandomizedEncryptionRequired(false)
+                .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+                        KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(12345)
+                .build();
+
+        assertEquals("arbitrary", spec.getKeystoreAlias());
+        assertEquals(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT, spec.getPurposes());
+        assertSame(algSpecificParams, spec.getAlgorithmParameterSpec());
+        MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getBlockModes()),
+                KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC);
+        assertEquals(certNotBeforeDate, spec.getCertificateNotBefore());
+        assertEquals(certNotAfterDate, spec.getCertificateNotAfter());
+        assertEquals(new BigInteger("13946146"), spec.getCertificateSerialNumber());
+        assertEquals(new X500Principal("CN=test"), spec.getCertificateSubject());
+        assertTrue(spec.isDigestsSpecified());
+        MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getDigests()),
+                KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384);
+        MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getEncryptionPaddings()),
+                KeyProperties.ENCRYPTION_PADDING_RSA_OAEP, KeyProperties.ENCRYPTION_PADDING_PKCS7);
+        assertEquals(1234, spec.getKeySize());
+        assertEquals(keyValidityStartDate, spec.getKeyValidityStart());
+        assertEquals(keyValidityEndDateForOrigination, spec.getKeyValidityForOriginationEnd());
+        assertEquals(keyValidityEndDateForConsumption, spec.getKeyValidityForConsumptionEnd());
+        assertFalse(spec.isRandomizedEncryptionRequired());
+        MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getSignaturePaddings()),
+                KeyProperties.SIGNATURE_PADDING_RSA_PSS, KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+        assertTrue(spec.isUserAuthenticationRequired());
+        assertEquals(12345, spec.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testNullAliasNotPermitted() {
+        try {
+            new KeyGenParameterSpec.Builder(null, KeyProperties.PURPOSE_ENCRYPT);
+            fail();
+        } catch (NullPointerException expected) {}
+    }
+
+    public void testEmptyAliasNotPermitted() {
+        try {
+            new KeyGenParameterSpec.Builder("", KeyProperties.PURPOSE_ENCRYPT);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    public void testSetKeyValidityEndDateAppliesToBothEndDates() {
+        Date date = new Date(System.currentTimeMillis() + 555555);
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                "ignore", KeyProperties.PURPOSE_VERIFY)
+                .setKeyValidityEnd(date)
+                .build();
+        assertEquals(date, spec.getKeyValidityForOriginationEnd());
+        assertEquals(date, spec.getKeyValidityForConsumptionEnd());
+    }
+
+    public void testSetUserAuthenticationValidityDurationSecondsValidityCheck() {
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder("alias", 0);
+        try {
+            builder.setUserAuthenticationValidityDurationSeconds(-2);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+
+        try {
+            builder.setUserAuthenticationValidityDurationSeconds(-100);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+
+        try {
+            builder.setUserAuthenticationValidityDurationSeconds(Integer.MIN_VALUE);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+
+        builder.setUserAuthenticationValidityDurationSeconds(-1);
+        builder.setUserAuthenticationValidityDurationSeconds(0);
+        builder.setUserAuthenticationValidityDurationSeconds(1);
+        builder.setUserAuthenticationValidityDurationSeconds(Integer.MAX_VALUE);
+
+        try {
+            builder.setUserAuthenticationValidityDurationSeconds(-2);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    public void testImmutabilityViaSetterParams() {
+        // Assert that all mutable parameters provided to setters are copied to ensure that values
+        // returned by getters never change.
+        String[] blockModes =
+                new String[] {KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC};
+        String[] originalBlockModes = blockModes.clone();
+        Date certNotBeforeDate = new Date(System.currentTimeMillis());
+        Date originalCertNotBeforeDate = new Date(certNotBeforeDate.getTime());
+        Date certNotAfterDate = new Date(System.currentTimeMillis() + 12345678);
+        Date originalCertNotAfterDate = new Date(certNotAfterDate.getTime());
+        Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+        Date originalKeyValidityStartDate = new Date(keyValidityStartDate.getTime());
+        Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+        Date originalKeyValidityEndDateForOrigination =
+                new Date(keyValidityEndDateForOrigination.getTime());
+        Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+        Date originalKeyValidityEndDateForConsumption =
+                new Date(keyValidityEndDateForConsumption.getTime());
+        String[] digests = new String[] {KeyProperties.DIGEST_MD5, KeyProperties.DIGEST_SHA512};
+        String[] originalDigests = digests.clone();
+        String[] encryptionPaddings = new String[] {
+                KeyProperties.ENCRYPTION_PADDING_RSA_OAEP, KeyProperties.ENCRYPTION_PADDING_PKCS7};
+        String[] originalEncryptionPaddings = encryptionPaddings.clone();
+        String[] signaturePaddings = new String[] {
+                KeyProperties.SIGNATURE_PADDING_RSA_PSS, KeyProperties.SIGNATURE_PADDING_RSA_PKCS1};
+        String[] originalSignaturePaddings = signaturePaddings.clone();
+
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                "arbitrary", KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+                .setBlockModes(blockModes)
+                .setCertificateNotBefore(certNotBeforeDate)
+                .setCertificateNotAfter(certNotAfterDate)
+                .setDigests(digests)
+                .setEncryptionPaddings(encryptionPaddings)
+                .setKeyValidityStart(keyValidityStartDate)
+                .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+                .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+                .setSignaturePaddings(signaturePaddings)
+                .build();
+
+        assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+        blockModes[0] = null;
+        assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+
+        assertEquals(originalCertNotBeforeDate, spec.getCertificateNotBefore());
+        certNotBeforeDate.setTime(1234567890L);
+        assertEquals(originalCertNotBeforeDate, spec.getCertificateNotBefore());
+
+        assertEquals(originalCertNotAfterDate, spec.getCertificateNotAfter());
+        certNotAfterDate.setTime(1234567890L);
+        assertEquals(originalCertNotAfterDate, spec.getCertificateNotAfter());
+
+        assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+        digests[1] = null;
+        assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+
+        assertEquals(Arrays.asList(originalEncryptionPaddings),
+                Arrays.asList(spec.getEncryptionPaddings()));
+        encryptionPaddings[0] = null;
+        assertEquals(Arrays.asList(originalEncryptionPaddings),
+                Arrays.asList(spec.getEncryptionPaddings()));
+
+        assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+        keyValidityStartDate.setTime(1234567890L);
+        assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+
+        assertEquals(originalKeyValidityEndDateForOrigination,
+                spec.getKeyValidityForOriginationEnd());
+        keyValidityEndDateForOrigination.setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForOrigination,
+                spec.getKeyValidityForOriginationEnd());
+
+        assertEquals(originalKeyValidityEndDateForConsumption,
+                spec.getKeyValidityForConsumptionEnd());
+        keyValidityEndDateForConsumption.setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForConsumption,
+                spec.getKeyValidityForConsumptionEnd());
+
+        assertEquals(Arrays.asList(originalSignaturePaddings),
+                Arrays.asList(spec.getSignaturePaddings()));
+        signaturePaddings[1] = null;
+        assertEquals(Arrays.asList(originalSignaturePaddings),
+                Arrays.asList(spec.getSignaturePaddings()));
+    }
+
+    public void testImmutabilityViaGetterReturnValues() {
+        // Assert that none of the mutable return values from getters modify the state of the spec.
+
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
+                "arbitrary", KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC)
+                .setCertificateNotBefore(new Date(System.currentTimeMillis()))
+                .setCertificateNotAfter(new Date(System.currentTimeMillis() + 12345678))
+                .setDigests(KeyProperties.DIGEST_MD5, KeyProperties.DIGEST_SHA512)
+                .setEncryptionPaddings(
+                        KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+                        KeyProperties.ENCRYPTION_PADDING_PKCS7)
+                .setKeyValidityStart(new Date(System.currentTimeMillis() - 2222222))
+                .setKeyValidityForOriginationEnd(new Date(System.currentTimeMillis() + 11111111))
+                .setKeyValidityForConsumptionEnd(new Date(System.currentTimeMillis() + 33333333))
+                .setSignaturePaddings(
+                        KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+                        KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                .build();
+
+        String[] originalBlockModes = spec.getBlockModes().clone();
+        spec.getBlockModes()[0] = null;
+        assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+
+        Date originalCertNotBeforeDate = (Date) spec.getCertificateNotBefore().clone();
+        spec.getCertificateNotBefore().setTime(1234567890L);
+        assertEquals(originalCertNotBeforeDate, spec.getCertificateNotBefore());
+
+        Date originalCertNotAfterDate = (Date) spec.getCertificateNotAfter().clone();
+        spec.getCertificateNotAfter().setTime(1234567890L);
+        assertEquals(originalCertNotAfterDate, spec.getCertificateNotAfter());
+
+        String[] originalDigests = spec.getDigests().clone();
+        spec.getDigests()[0] = null;
+        assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+
+        String[] originalEncryptionPaddings = spec.getEncryptionPaddings().clone();
+        spec.getEncryptionPaddings()[0] = null;
+        assertEquals(Arrays.asList(originalEncryptionPaddings),
+                Arrays.asList(spec.getEncryptionPaddings()));
+
+        Date originalKeyValidityStartDate = (Date) spec.getKeyValidityStart().clone();
+        spec.getKeyValidityStart().setTime(1234567890L);
+        assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+
+        Date originalKeyValidityEndDateForOrigination =
+                (Date) spec.getKeyValidityForOriginationEnd().clone();
+        spec.getKeyValidityForOriginationEnd().setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForOrigination,
+                spec.getKeyValidityForOriginationEnd());
+
+        Date originalKeyValidityEndDateForConsumption =
+                (Date) spec.getKeyValidityForConsumptionEnd().clone();
+        spec.getKeyValidityForConsumptionEnd().setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForConsumption,
+                spec.getKeyValidityForConsumptionEnd());
+
+        String[] originalSignaturePaddings = spec.getSignaturePaddings().clone();
+        spec.getSignaturePaddings()[0] = null;
+        assertEquals(Arrays.asList(originalSignaturePaddings),
+                Arrays.asList(spec.getSignaturePaddings()));
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyInfoTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyInfoTest.java
new file mode 100644
index 0000000..2b1d6fc
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyInfoTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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.keystore.cts;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+
+import junit.framework.TestCase;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.util.Arrays;
+import java.util.Date;
+
+public class KeyInfoTest extends TestCase {
+
+    public void testImmutabilityViaGetterReturnValues() throws Exception {
+        // Assert that none of the mutable return values from getters modify the state of the
+        // instance.
+
+        Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+        Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+        Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+
+        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
+        keyPairGenerator.initialize(new KeyGenParameterSpec.Builder(
+                KeyInfoTest.class.getSimpleName(),
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+                .setKeySize(1024) // use smaller key size to speed the test up
+                .setKeyValidityStart(keyValidityStartDate)
+                .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+                .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+                        KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
+                .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+                        KeyProperties.SIGNATURE_PADDING_RSA_PSS)
+                .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+                .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
+                .build());
+        KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+        PrivateKey key = keyPair.getPrivate();
+        KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore");
+        KeyInfo info = keyFactory.getKeySpec(key, KeyInfo.class);
+
+        Date originalKeyValidityStartDate = (Date) info.getKeyValidityStart().clone();
+        info.getKeyValidityStart().setTime(1234567890L);
+        assertEquals(originalKeyValidityStartDate, info.getKeyValidityStart());
+
+        Date originalKeyValidityEndDateForOrigination =
+                (Date) info.getKeyValidityForOriginationEnd().clone();
+        info.getKeyValidityForOriginationEnd().setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForOrigination,
+                info.getKeyValidityForOriginationEnd());
+
+        Date originalKeyValidityEndDateForConsumption =
+                (Date) info.getKeyValidityForConsumptionEnd().clone();
+        info.getKeyValidityForConsumptionEnd().setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForConsumption,
+                info.getKeyValidityForConsumptionEnd());
+
+        String[] originalEncryptionPaddings = info.getEncryptionPaddings().clone();
+        info.getEncryptionPaddings()[0] = null;
+        assertEquals(Arrays.asList(originalEncryptionPaddings),
+                Arrays.asList(info.getEncryptionPaddings()));
+
+        String[] originalSignaturePaddings = info.getSignaturePaddings().clone();
+        info.getSignaturePaddings()[0] = null;
+        assertEquals(Arrays.asList(originalSignaturePaddings),
+                Arrays.asList(info.getSignaturePaddings()));
+
+        String[] originalDigests = info.getDigests().clone();
+        info.getDigests()[0] = null;
+        assertEquals(Arrays.asList(originalDigests), Arrays.asList(info.getDigests()));
+
+        String[] originalBlockModes = info.getBlockModes().clone();
+        info.getBlockModes()[0] = null;
+        assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(info.getBlockModes()));
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java
new file mode 100644
index 0000000..ee24eed
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 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.keystore.cts;
+
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.test.MoreAsserts;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.Date;
+
+public class KeyProtectionTest extends TestCase {
+    public void testDefaults() {
+        // Set only the mandatory parameters and assert values returned by getters.
+
+        KeyProtection spec = new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
+                .build();
+
+        assertEquals(KeyProperties.PURPOSE_ENCRYPT, spec.getPurposes());
+        MoreAsserts.assertEmpty(Arrays.asList(spec.getBlockModes()));
+        assertFalse(spec.isDigestsSpecified());
+        try {
+            spec.getDigests();
+            fail();
+        } catch (IllegalStateException expected) {}
+        MoreAsserts.assertEmpty(Arrays.asList(spec.getEncryptionPaddings()));
+        assertNull(spec.getKeyValidityStart());
+        assertNull(spec.getKeyValidityForOriginationEnd());
+        assertNull(spec.getKeyValidityForConsumptionEnd());
+        assertTrue(spec.isRandomizedEncryptionRequired());
+        MoreAsserts.assertEmpty(Arrays.asList(spec.getSignaturePaddings()));
+        assertFalse(spec.isUserAuthenticationRequired());
+        assertEquals(-1, spec.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testSettersReflectedInGetters() {
+        // Set all parameters to non-default values and then assert that getters reflect that.
+
+        Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+        Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+        Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+
+        KeyProtection spec = new KeyProtection.Builder(
+                KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_VERIFY)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CTR)
+                .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+                        KeyProperties.ENCRYPTION_PADDING_PKCS7)
+                .setKeyValidityStart(keyValidityStartDate)
+                .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+                .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+                .setRandomizedEncryptionRequired(false)
+                .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
+                        KeyProperties.SIGNATURE_PADDING_RSA_PSS)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(123456)
+                .build();
+
+        assertEquals(
+                KeyProperties.PURPOSE_DECRYPT| KeyProperties.PURPOSE_VERIFY, spec.getPurposes());
+        MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getBlockModes()),
+                KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CTR);
+        assertTrue(spec.isDigestsSpecified());
+        MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getDigests()),
+                KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512);
+        MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getEncryptionPaddings()),
+                KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1, KeyProperties.ENCRYPTION_PADDING_PKCS7);
+        assertEquals(keyValidityStartDate, spec.getKeyValidityStart());
+        assertEquals(keyValidityEndDateForOrigination, spec.getKeyValidityForOriginationEnd());
+        assertEquals(keyValidityEndDateForConsumption, spec.getKeyValidityForConsumptionEnd());
+        assertFalse(spec.isRandomizedEncryptionRequired());
+        MoreAsserts.assertContentsInOrder(Arrays.asList(spec.getSignaturePaddings()),
+                KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+        assertTrue(spec.isUserAuthenticationRequired());
+        assertEquals(123456, spec.getUserAuthenticationValidityDurationSeconds());
+    }
+
+    public void testSetKeyValidityEndDateAppliesToBothEndDates() {
+        Date date = new Date(System.currentTimeMillis() + 555555);
+        KeyProtection spec = new KeyProtection.Builder(
+                KeyProperties.PURPOSE_SIGN)
+                .setKeyValidityEnd(date)
+                .build();
+        assertEquals(date, spec.getKeyValidityForOriginationEnd());
+        assertEquals(date, spec.getKeyValidityForConsumptionEnd());
+    }
+
+    public void testSetUserAuthenticationValidityDurationSecondsValidityCheck() {
+        KeyProtection.Builder builder = new KeyProtection.Builder(0);
+        try {
+            builder.setUserAuthenticationValidityDurationSeconds(-2);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+
+        try {
+            builder.setUserAuthenticationValidityDurationSeconds(-100);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+
+        try {
+            builder.setUserAuthenticationValidityDurationSeconds(Integer.MIN_VALUE);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+
+        builder.setUserAuthenticationValidityDurationSeconds(-1);
+        builder.setUserAuthenticationValidityDurationSeconds(0);
+        builder.setUserAuthenticationValidityDurationSeconds(1);
+        builder.setUserAuthenticationValidityDurationSeconds(Integer.MAX_VALUE);
+
+        try {
+            builder.setUserAuthenticationValidityDurationSeconds(-2);
+            fail();
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    public void testImmutabilityViaSetterParams() {
+        // Assert that all mutable parameters provided to setters are copied to ensure that values
+        // returned by getters never change.
+        String[] blockModes =
+                new String[] {KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC};
+        String[] originalBlockModes = blockModes.clone();
+        Date keyValidityStartDate = new Date(System.currentTimeMillis() - 2222222);
+        Date originalKeyValidityStartDate = new Date(keyValidityStartDate.getTime());
+        Date keyValidityEndDateForOrigination = new Date(System.currentTimeMillis() + 11111111);
+        Date originalKeyValidityEndDateForOrigination =
+                new Date(keyValidityEndDateForOrigination.getTime());
+        Date keyValidityEndDateForConsumption = new Date(System.currentTimeMillis() + 33333333);
+        Date originalKeyValidityEndDateForConsumption =
+                new Date(keyValidityEndDateForConsumption.getTime());
+        String[] digests = new String[] {KeyProperties.DIGEST_MD5, KeyProperties.DIGEST_SHA512};
+        String[] originalDigests = digests.clone();
+        String[] encryptionPaddings = new String[] {
+                KeyProperties.ENCRYPTION_PADDING_RSA_OAEP, KeyProperties.ENCRYPTION_PADDING_PKCS7};
+        String[] originalEncryptionPaddings = encryptionPaddings.clone();
+        String[] signaturePaddings = new String[] {
+                KeyProperties.SIGNATURE_PADDING_RSA_PSS, KeyProperties.SIGNATURE_PADDING_RSA_PKCS1};
+        String[] originalSignaturePaddings = signaturePaddings.clone();
+
+        KeyProtection spec = new KeyProtection.Builder(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+                .setBlockModes(blockModes)
+                .setDigests(digests)
+                .setEncryptionPaddings(encryptionPaddings)
+                .setKeyValidityStart(keyValidityStartDate)
+                .setKeyValidityForOriginationEnd(keyValidityEndDateForOrigination)
+                .setKeyValidityForConsumptionEnd(keyValidityEndDateForConsumption)
+                .setSignaturePaddings(signaturePaddings)
+                .build();
+
+        assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+        blockModes[0] = null;
+        assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+
+        assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+        digests[1] = null;
+        assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+
+        assertEquals(Arrays.asList(originalEncryptionPaddings),
+                Arrays.asList(spec.getEncryptionPaddings()));
+        encryptionPaddings[0] = null;
+        assertEquals(Arrays.asList(originalEncryptionPaddings),
+                Arrays.asList(spec.getEncryptionPaddings()));
+
+        assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+        keyValidityStartDate.setTime(1234567890L);
+        assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+
+        assertEquals(originalKeyValidityEndDateForOrigination,
+                spec.getKeyValidityForOriginationEnd());
+        keyValidityEndDateForOrigination.setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForOrigination,
+                spec.getKeyValidityForOriginationEnd());
+
+        assertEquals(originalKeyValidityEndDateForConsumption,
+                spec.getKeyValidityForConsumptionEnd());
+        keyValidityEndDateForConsumption.setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForConsumption,
+                spec.getKeyValidityForConsumptionEnd());
+
+        assertEquals(Arrays.asList(originalSignaturePaddings),
+                Arrays.asList(spec.getSignaturePaddings()));
+        signaturePaddings[1] = null;
+        assertEquals(Arrays.asList(originalSignaturePaddings),
+                Arrays.asList(spec.getSignaturePaddings()));
+    }
+
+    public void testImmutabilityViaGetterReturnValues() {
+        // Assert that none of the mutable return values from getters modify the state of the spec.
+
+        KeyProtection spec = new KeyProtection.Builder(
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC)
+                .setDigests(KeyProperties.DIGEST_MD5, KeyProperties.DIGEST_SHA512)
+                .setEncryptionPaddings(
+                        KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+                        KeyProperties.ENCRYPTION_PADDING_PKCS7)
+                .setKeyValidityStart(new Date(System.currentTimeMillis() - 2222222))
+                .setKeyValidityForOriginationEnd(new Date(System.currentTimeMillis() + 11111111))
+                .setKeyValidityForConsumptionEnd(new Date(System.currentTimeMillis() + 33333333))
+                .setSignaturePaddings(
+                        KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+                        KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                .build();
+
+        String[] originalBlockModes = spec.getBlockModes().clone();
+        spec.getBlockModes()[0] = null;
+        assertEquals(Arrays.asList(originalBlockModes), Arrays.asList(spec.getBlockModes()));
+
+        String[] originalDigests = spec.getDigests().clone();
+        spec.getDigests()[0] = null;
+        assertEquals(Arrays.asList(originalDigests), Arrays.asList(spec.getDigests()));
+
+        String[] originalEncryptionPaddings = spec.getEncryptionPaddings().clone();
+        spec.getEncryptionPaddings()[0] = null;
+        assertEquals(Arrays.asList(originalEncryptionPaddings),
+                Arrays.asList(spec.getEncryptionPaddings()));
+
+        Date originalKeyValidityStartDate = (Date) spec.getKeyValidityStart().clone();
+        spec.getKeyValidityStart().setTime(1234567890L);
+        assertEquals(originalKeyValidityStartDate, spec.getKeyValidityStart());
+
+        Date originalKeyValidityEndDateForOrigination =
+                (Date) spec.getKeyValidityForOriginationEnd().clone();
+        spec.getKeyValidityForOriginationEnd().setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForOrigination,
+                spec.getKeyValidityForOriginationEnd());
+
+        Date originalKeyValidityEndDateForConsumption =
+                (Date) spec.getKeyValidityForConsumptionEnd().clone();
+        spec.getKeyValidityForConsumptionEnd().setTime(1234567890L);
+        assertEquals(originalKeyValidityEndDateForConsumption,
+                spec.getKeyValidityForConsumptionEnd());
+
+        String[] originalSignaturePaddings = spec.getSignaturePaddings().clone();
+        spec.getSignaturePaddings()[0] = null;
+        assertEquals(Arrays.asList(originalSignaturePaddings),
+                Arrays.asList(spec.getSignaturePaddings()));
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
new file mode 100644
index 0000000..cbcbc45
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -0,0 +1,234 @@
+/*
+ * 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.keystore.cts;
+
+import android.security.keystore.KeyInfo;
+import android.test.MoreAsserts;
+
+import junit.framework.Assert;
+
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.EllipticCurve;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.SecretKeySpec;
+
+abstract class TestUtils extends Assert {
+    private TestUtils() {}
+
+    /**
+     * Asserts the the key algorithm and algorithm-specific parameters of the two keys in the
+     * provided pair match.
+     */
+    static void assertKeyPairSelfConsistent(KeyPair keyPair) {
+        assertKeyPairSelfConsistent(keyPair.getPublic(), keyPair.getPrivate());
+    }
+
+    /**
+     * Asserts the the key algorithm and public algorithm-specific parameters of the two provided
+     * keys match.
+     */
+    static void assertKeyPairSelfConsistent(PublicKey publicKey, PrivateKey privateKey) {
+        assertNotNull(publicKey);
+        assertNotNull(privateKey);
+        assertEquals(publicKey.getAlgorithm(), privateKey.getAlgorithm());
+        String keyAlgorithm = publicKey.getAlgorithm();
+        if ("EC".equalsIgnoreCase(keyAlgorithm)) {
+            assertTrue("EC public key must be instanceof ECKey: "
+                    + publicKey.getClass().getName(),
+                    publicKey instanceof ECKey);
+            assertTrue("EC private key must be instanceof ECKey: "
+                    + privateKey.getClass().getName(),
+                    privateKey instanceof ECKey);
+            assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+                    "Private key must have the same EC parameters as public key",
+                    ((ECKey) publicKey).getParams(), ((ECKey) privateKey).getParams());
+        } else if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
+            assertTrue("RSA public key must be instance of RSAKey: "
+                    + publicKey.getClass().getName(),
+                    publicKey instanceof RSAKey);
+            assertTrue("RSA private key must be instance of RSAKey: "
+                    + privateKey.getClass().getName(),
+                    privateKey instanceof RSAKey);
+            assertEquals("Private and public key must have the same RSA modulus",
+                    ((RSAKey) publicKey).getModulus(), ((RSAKey) privateKey).getModulus());
+        } else {
+            fail("Unsuported key algorithm: " + keyAlgorithm);
+        }
+    }
+
+    private static int getKeySizeBits(Key key) {
+        if (key instanceof ECKey) {
+            return ((ECKey) key).getParams().getCurve().getField().getFieldSize();
+        } else if (key instanceof RSAKey) {
+            return ((RSAKey) key).getModulus().bitLength();
+        } else {
+            throw new IllegalArgumentException("Unsupported key type: " + key.getClass());
+        }
+    }
+
+    static void assertKeySize(int expectedSizeBits, KeyPair keyPair) {
+        assertEquals(expectedSizeBits, getKeySizeBits(keyPair.getPrivate()));
+        assertEquals(expectedSizeBits, getKeySizeBits(keyPair.getPublic()));
+    }
+
+    /**
+     * Asserts that the provided key pair is an Android Keystore key pair stored under the provided
+     * alias.
+     */
+    static void assertKeyStoreKeyPair(KeyStore keyStore, String alias, KeyPair keyPair) {
+        assertKeyMaterialExportable(keyPair.getPublic());
+        assertKeyMaterialNotExportable(keyPair.getPrivate());
+        assertTransparentKey(keyPair.getPublic());
+        assertOpaqueKey(keyPair.getPrivate());
+
+        KeyStore.Entry entry;
+        Certificate cert;
+        try {
+            entry = keyStore.getEntry(alias, null);
+            cert = keyStore.getCertificate(alias);
+        } catch (KeyStoreException | UnrecoverableEntryException | NoSuchAlgorithmException e) {
+            throw new RuntimeException("Failed to load entry: " + alias, e);
+        }
+        assertNotNull(entry);
+
+        assertTrue(entry instanceof KeyStore.PrivateKeyEntry);
+        KeyStore.PrivateKeyEntry privEntry = (KeyStore.PrivateKeyEntry) entry;
+        assertEquals(cert, privEntry.getCertificate());
+        assertTrue("Certificate must be an X.509 certificate: " + cert.getClass(),
+                cert instanceof X509Certificate);
+        final X509Certificate x509Cert = (X509Certificate) cert;
+
+        PrivateKey keystorePrivateKey = privEntry.getPrivateKey();
+        PublicKey keystorePublicKey = cert.getPublicKey();
+        assertEquals(keyPair.getPrivate(), keystorePrivateKey);
+        assertEquals(keyPair.getPublic(), keystorePublicKey);
+
+        assertEquals(
+                "Public key used to sign certificate should have the same algorithm as in KeyPair",
+                keystorePublicKey.getAlgorithm(), x509Cert.getPublicKey().getAlgorithm());
+
+        Certificate[] chain = privEntry.getCertificateChain();
+        if (chain.length == 0) {
+            fail("Empty certificate chain");
+            return;
+        }
+        assertEquals(cert, chain[0]);
+    }
+
+
+    private static void assertKeyMaterialExportable(Key key) {
+        if (key instanceof PublicKey) {
+            assertEquals("X.509", key.getFormat());
+        } else if (key instanceof PrivateKey) {
+            assertEquals("PKCS#8", key.getFormat());
+        } else if (key instanceof SecretKey) {
+            assertEquals("RAW", key.getFormat());
+        } else {
+            fail("Unsupported key type: " + key.getClass().getName());
+        }
+        byte[] encodedForm = key.getEncoded();
+        assertNotNull(encodedForm);
+        if (encodedForm.length == 0) {
+            fail("Empty encoded form");
+        }
+    }
+
+    private static void assertKeyMaterialNotExportable(Key key) {
+        assertEquals(null, key.getFormat());
+        assertEquals(null, key.getEncoded());
+    }
+
+    private static void assertOpaqueKey(Key key) {
+        assertFalse(key.getClass().getName() + " is a transparent key", isTransparentKey(key));
+    }
+
+    private static void assertTransparentKey(Key key) {
+        assertTrue(key.getClass().getName() + " is not a transparent key", isTransparentKey(key));
+    }
+
+    private static boolean isTransparentKey(Key key) {
+        if (key instanceof PrivateKey) {
+            return (key instanceof ECPrivateKey) || (key instanceof RSAPrivateKey);
+        } else if (key instanceof PublicKey) {
+            return (key instanceof ECPublicKey) || (key instanceof RSAPublicKey);
+        } else if (key instanceof SecretKey) {
+            return (key instanceof SecretKeySpec);
+        } else {
+            throw new IllegalArgumentException("Unsupported key type: " + key.getClass().getName());
+        }
+    }
+
+    static void assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+            ECParameterSpec expected, ECParameterSpec actual) {
+        assertECParameterSpecEqualsIgnoreSeedIfNotPresent(null, expected, actual);
+    }
+
+    static void assertECParameterSpecEqualsIgnoreSeedIfNotPresent(String message,
+            ECParameterSpec expected, ECParameterSpec actual) {
+        EllipticCurve expectedCurve = expected.getCurve();
+        EllipticCurve actualCurve = actual.getCurve();
+        String msgPrefix = (message != null) ? message + ": " : "";
+        assertEquals(msgPrefix + "curve field", expectedCurve.getField(), actualCurve.getField());
+        assertEquals(msgPrefix + "curve A", expectedCurve.getA(), actualCurve.getA());
+        assertEquals(msgPrefix + "curve B", expectedCurve.getB(), actualCurve.getB());
+        assertEquals(msgPrefix + "order", expected.getOrder(), actual.getOrder());
+        assertEquals(msgPrefix + "generator",
+                expected.getGenerator(), actual.getGenerator());
+        assertEquals(msgPrefix + "cofactor", expected.getCofactor(), actual.getCofactor());
+
+        // If present, the seed must be the same
+        byte[] expectedSeed = expectedCurve.getSeed();
+        byte[] actualSeed = expectedCurve.getSeed();
+        if ((expectedSeed != null) && (actualSeed != null)) {
+            MoreAsserts.assertEquals(expectedSeed, actualSeed);
+        }
+    }
+
+    static KeyInfo getKeyInfo(Key key) throws InvalidKeySpecException, NoSuchAlgorithmException,
+            NoSuchProviderException {
+        if ((key instanceof PrivateKey) || (key instanceof PublicKey)) {
+            return KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore")
+                    .getKeySpec(key, KeyInfo.class);
+        } else if (key instanceof SecretKey) {
+            return (KeyInfo) SecretKeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore")
+                    .getKeySpec((SecretKey) key, KeyInfo.class);
+        } else {
+            throw new IllegalArgumentException("Unexpected key type: " + key.getClass());
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 1fb3ea7..0edbc61 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -35,11 +35,13 @@
 import javax.microedition.khronos.opengles.GL10;
 
 import java.io.IOException;
+import java.lang.System;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Locale;
+import java.util.Vector;
 import java.util.zip.CRC32;
 
 public class AdaptivePlaybackTest extends MediaPlayerTestBase {
@@ -812,7 +814,7 @@
         return items;
     }
 
-    class Decoder {
+    class Decoder implements MediaCodec.OnFrameRenderedListener {
         private final static String TAG = "AdaptiveDecoder";
         final long kTimeOutUs = 5000;
         MediaCodec mCodec;
@@ -823,6 +825,9 @@
         boolean mQueuedEos;
         ArrayList<Long> mTimeStamps;
         ArrayList<String> mWarnings;
+        Vector<Long> mRenderedTimeStamps; // using Vector as it is implicitly synchronized
+        long mLastRenderNanoTime;
+        int mFramesNotifiedRendered;
 
         public Decoder(String codecName) {
             MediaCodec codec = null;
@@ -837,6 +842,23 @@
             mQueuedEos = false;
             mTimeStamps = new ArrayList<Long>();
             mWarnings = new ArrayList<String>();
+            mRenderedTimeStamps = new Vector<Long>();
+            mLastRenderNanoTime = System.nanoTime();
+            mFramesNotifiedRendered = 0;
+
+            codec.setOnFrameRenderedListener(this, null);
+        }
+
+        public void onFrameRendered(MediaCodec codec, long presentationTimeUs, long nanoTime) {
+            final long NSECS_IN_1SEC = 1000000000;
+            if (!mRenderedTimeStamps.remove(presentationTimeUs)) {
+                warn("invalid timestamp " + presentationTimeUs + ", queued " +
+                        collectionString(mRenderedTimeStamps));
+            }
+            assert nanoTime > mLastRenderNanoTime;
+            mLastRenderNanoTime = nanoTime;
+            ++mFramesNotifiedRendered;
+            assert nanoTime > System.nanoTime() - NSECS_IN_1SEC;
         }
 
         public String getName() {
@@ -878,11 +900,20 @@
                   mOutputBuffers.length + "output[" +
                   (mOutputBuffers[0] == null ? null : mOutputBuffers[0].capacity()) + "]");
             mQueuedEos = false;
+            mRenderedTimeStamps.clear();
+            mLastRenderNanoTime = System.nanoTime();
+            mFramesNotifiedRendered = 0;
         }
 
         public void stop() {
             Log.i(TAG, "stop");
             mCodec.stop();
+            // if we have queued 32 frames or more, at least one should have been notified
+            // to have rendered.
+            if (mRenderedTimeStamps.size() > 32 && mFramesNotifiedRendered == 0) {
+                fail("rendered " + mRenderedTimeStamps.size() +
+                        " frames, but none have been notified.");
+            }
         }
 
         public void flush() {
@@ -939,6 +970,7 @@
             }
 
             if (doRender) {
+                mRenderedTimeStamps.add(info.presentationTimeUs);
                 if (!mTimeStamps.remove(info.presentationTimeUs)) {
                     warn("invalid timestamp " + info.presentationTimeUs + ", queued " +
                             collectionString(mTimeStamps));
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 56d2b68..6ba1aeb 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -416,6 +416,39 @@
         assertEquals(TEST_NAME + ": state", expectedState, observedState);
     }
 
+    // Test AudioRecord to ensure we can build after a failure.
+    public void testAudioRecordBufferSize() throws Exception {
+        // constants for test
+        final String TEST_NAME = "testAudioRecordBufferSize";
+
+        // use builder with parameters that should fail
+        final int superBigBufferSize = 1 << 28;
+        try {
+            final AudioRecord record = new AudioRecord.Builder()
+                .setBufferSizeInBytes(superBigBufferSize)
+                .build();
+            record.release();
+            fail(TEST_NAME + ": should throw exception on failure");
+        } catch (UnsupportedOperationException e) {
+            ;
+        }
+
+        // we should be able to create again with minimum buffer size
+        final int verySmallBufferSize = 2 * 3 * 4; // frame size multiples
+        final AudioRecord record2 = new AudioRecord.Builder()
+                .setBufferSizeInBytes(verySmallBufferSize)
+                .build();
+
+        final int observedState2 = record2.getState();
+        final int observedBufferSize2 = record2.getBufferSizeInFrames();
+        record2.release();
+
+        // succeeds for minimum buffer size
+        assertEquals(TEST_NAME + ": state", AudioRecord.STATE_INITIALIZED, observedState2);
+        // should force the minimum size buffer which is > 0
+        assertTrue(TEST_NAME + ": buffer frame count", observedBufferSize2 > 0);
+    }
+
     public void testSynchronizedRecord() throws Exception {
         if (!hasMicrophone()) {
             return;
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index dfb83eb..4c03183 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -2214,6 +2214,39 @@
         track.release();
     }
 
+    // Test AudioTrack to ensure we can build after a failure.
+    public void testAudioTrackBufferSize() throws Exception {
+        // constants for test
+        final String TEST_NAME = "testAudioTrackBufferSize";
+
+        // use builder with parameters that should fail
+        final int superBigBufferSize = 1 << 28;
+        try {
+            final AudioTrack track = new AudioTrack.Builder()
+                .setBufferSizeInBytes(superBigBufferSize)
+                .build();
+            track.release();
+            fail(TEST_NAME + ": should throw exception on failure");
+        } catch (UnsupportedOperationException e) {
+            ;
+        }
+
+        // we should be able to create again with minimum buffer size
+        final int verySmallBufferSize = 2 * 3 * 4; // frame size multiples
+        final AudioTrack track2 = new AudioTrack.Builder()
+                .setBufferSizeInBytes(verySmallBufferSize)
+                .build();
+
+        final int observedState2 = track2.getState();
+        final int observedBufferSize2 = track2.getBufferSizeInFrames();
+        track2.release();
+
+        // succeeds for minimum buffer size
+        assertEquals(TEST_NAME + ": state", AudioTrack.STATE_INITIALIZED, observedState2);
+        // should force the minimum size buffer which is > 0
+        assertTrue(TEST_NAME + ": buffer frame count", observedBufferSize2 > 0);
+    }
+
 /* Do not run in JB-MR1. will be re-opened in the next platform release.
     public void testResourceLeakage() throws Exception {
         final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
index 996ddf7..02bdd7f 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
@@ -35,31 +35,44 @@
     private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
             "com.android.cts.media", "android.media.cts.StubMediaBrowserService");
     private final Object mWaitLock = new Object();
+
     private final MediaBrowser.ConnectionCallback mConnectionCallback =
             new MediaBrowser.ConnectionCallback() {
-                @Override
-                public void onConnected() {
-                    synchronized (mWaitLock) {
-                        mMediaBrowserService = StubMediaBrowserService.sInstance;
-                        mWaitLock.notify();
-                    }
-                }
-            };
+        @Override
+        public void onConnected() {
+            synchronized (mWaitLock) {
+                mMediaBrowserService = StubMediaBrowserService.sInstance;
+                mWaitLock.notify();
+            }
+        }
+    };
 
     private final MediaBrowser.SubscriptionCallback mSubscriptionCallback  =
             new MediaBrowser.SubscriptionCallback() {
-                @Override
-                public void onChildrenLoaded(String parentId, List<MediaItem> children) {
-                    synchronized (mWaitLock) {
-                        mOnChildrenLoaded = true;
-                        mWaitLock.notify();
-                    }
+            @Override
+            public void onChildrenLoaded(String parentId, List<MediaItem> children) {
+                synchronized (mWaitLock) {
+                    mOnChildrenLoaded = true;
+                    mWaitLock.notify();
                 }
-            };
+            }
+        };
+
+    private final MediaBrowser.ItemCallback mItemCallback  =
+            new MediaBrowser.ItemCallback() {
+        @Override
+        public void onItemLoaded(MediaItem item) {
+            synchronized (mWaitLock) {
+                mOnItemLoaded = true;
+                mWaitLock.notify();
+            }
+        }
+    };
 
     private MediaBrowser mMediaBrowser;
     private StubMediaBrowserService mMediaBrowserService;
     private boolean mOnChildrenLoaded;
+    private boolean mOnItemLoaded;
 
     @Override
     protected void setUp() throws Exception {
@@ -119,6 +132,20 @@
         }
     }
 
+    public void testDelayedItem() throws Exception {
+        synchronized (mWaitLock) {
+            mOnItemLoaded = false;
+            mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED,
+                    mItemCallback);
+            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+            assertFalse(mOnItemLoaded);
+
+            mMediaBrowserService.sendDelayedItemLoaded();
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mOnItemLoaded);
+        }
+    }
+
     public void testBrowserRoot() {
         final String id = "test-id";
         final String key = "test-key";
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
index 51d43d9..7dd978f 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -18,6 +18,7 @@
 import android.content.ComponentName;
 import android.cts.util.PollingCheck;
 import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
 import android.test.InstrumentationTestCase;
 
 import java.util.List;
@@ -34,6 +35,7 @@
             "invalid.package", "invalid.ServiceClassName");
     private final StubConnectionCallback mConnectionCallback = new StubConnectionCallback();
     private final StubSubscriptionCallback mSubscriptionCallback = new StubSubscriptionCallback();
+    private final StubItemCallback mItemCallback = new StubItemCallback();
 
     private MediaBrowser mMediaBrowser;
 
@@ -115,6 +117,37 @@
         }
     }
 
+    public void testGetItem() {
+        resetCallbacks();
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        connectMediaBrowserService();
+        mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return mItemCallback.mLastMediaItem != null;
+            }
+        }.run();
+
+        assertEquals(StubMediaBrowserService.MEDIA_ID_CHILDREN[0],
+                mItemCallback.mLastMediaItem.getMediaId());
+    }
+
+    public void testGetItemFailure() {
+        resetCallbacks();
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        connectMediaBrowserService();
+        mMediaBrowser.getItem("does-not-exist", mItemCallback);
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return mItemCallback.mLastErrorId != null;
+            }
+        }.run();
+
+        assertEquals("does-not-exist", mItemCallback.mLastErrorId);
+    }
+
     private void createMediaBrowser(final ComponentName component) {
         getInstrumentation().runOnMainSync(new Runnable() {
             @Override
@@ -138,6 +171,7 @@
     private void resetCallbacks() {
         mConnectionCallback.reset();
         mSubscriptionCallback.reset();
+        mItemCallback.reset();
     }
 
     private static class StubConnectionCallback extends MediaBrowser.ConnectionCallback {
@@ -169,14 +203,12 @@
 
     private static class StubSubscriptionCallback extends MediaBrowser.SubscriptionCallback {
         private volatile int mChildrenLoadedCount;
-        private volatile int mErrorCount;
         private volatile String mLastErrorId;
         private volatile String mLastParentId;
         private volatile List<MediaBrowser.MediaItem> mLastChildMediaItems;
 
         public void reset() {
             mChildrenLoadedCount = 0;
-            mErrorCount = 0;
             mLastErrorId = null;
             mLastParentId = null;
             mLastChildMediaItems = null;
@@ -194,4 +226,24 @@
             mLastErrorId = id;
         }
     }
+
+    private static class StubItemCallback extends MediaBrowser.ItemCallback {
+        private volatile MediaBrowser.MediaItem mLastMediaItem;
+        private volatile String mLastErrorId;
+
+        public void reset() {
+            mLastMediaItem = null;
+            mLastErrorId = null;
+        }
+
+        @Override
+        public void onItemLoaded(MediaItem item) {
+            mLastMediaItem = item;
+        }
+
+        @Override
+        public void onError(String id) {
+            mLastErrorId = id;
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 494e823..bb05ea0 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -21,6 +21,8 @@
 import android.content.res.AssetFileDescriptor;
 import android.cts.util.MediaUtils;
 import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodec.CodecException;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
@@ -28,13 +30,20 @@
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaCodecInfo.CodecProfileLevel;
 import android.opengl.GLES20;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.view.Surface;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * General MediaCodec tests.
@@ -457,6 +466,241 @@
         }
     }
 
+    public void testReleaseAfterFlush() throws IOException, InterruptedException {
+        CountDownLatch buffersExhausted = null;
+        CountDownLatch codecFlushed = null;
+        AtomicInteger numBuffers = null;
+
+        // sync flush from same thread
+        MediaCodec encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+        runReleaseAfterFlush(encoder, buffersExhausted, codecFlushed, numBuffers);
+
+        // sync flush from different thread
+        encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+        buffersExhausted = new CountDownLatch(1);
+        codecFlushed = new CountDownLatch(1);
+        numBuffers = new AtomicInteger();
+        Thread flushThread = new FlushThread(encoder, buffersExhausted, codecFlushed);
+        flushThread.start();
+        runReleaseAfterFlush(encoder, buffersExhausted, codecFlushed, numBuffers);
+        flushThread.join();
+
+        // async
+        // This value is calculated in getOutputBufferIndices by calling dequeueOutputBuffer
+        // with a fixed timeout until buffers are exhausted; it is possible that random timing
+        // in dequeueOutputBuffer can result in a smaller `nBuffs` than the max possible value.
+        int nBuffs = numBuffers.get();
+        HandlerThread callbackThread = new HandlerThread("ReleaseAfterFlushCallbackThread");
+        callbackThread.start();
+        Handler handler = new Handler(callbackThread.getLooper());
+
+        // async flush from same thread
+        encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+        buffersExhausted = null;
+        codecFlushed = null;
+        ReleaseAfterFlushCallback callback =
+                new ReleaseAfterFlushCallback(encoder, buffersExhausted, codecFlushed, nBuffs);
+        encoder.setCallback(callback, handler); // setCallback before configure, which is called in run
+        callback.run(); // drive input on main thread
+
+        // async flush from different thread
+        encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+        buffersExhausted = new CountDownLatch(1);
+        codecFlushed = new CountDownLatch(1);
+        callback = new ReleaseAfterFlushCallback(encoder, buffersExhausted, codecFlushed, nBuffs);
+        encoder.setCallback(callback, handler);
+        flushThread = new FlushThread(encoder, buffersExhausted, codecFlushed);
+        flushThread.start();
+        callback.run();
+        flushThread.join();
+
+        callbackThread.quitSafely();
+        callbackThread.join();
+    }
+
+    private static class FlushThread extends Thread {
+        final MediaCodec mEncoder;
+        final CountDownLatch mBuffersExhausted;
+        final CountDownLatch mCodecFlushed;
+
+        FlushThread(MediaCodec encoder, CountDownLatch buffersExhausted,
+                CountDownLatch codecFlushed) {
+            mEncoder = encoder;
+            mBuffersExhausted = buffersExhausted;
+            mCodecFlushed = codecFlushed;
+        }
+
+        @Override
+        public void run() {
+            try {
+                mBuffersExhausted.await();
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                Log.w(TAG, "buffersExhausted wait interrupted; flushing immediately.", e);
+            }
+            mEncoder.flush();
+            mCodecFlushed.countDown();
+        }
+    }
+
+    private static class ReleaseAfterFlushCallback extends MediaCodec.Callback implements Runnable {
+        final MediaCodec mEncoder;
+        final CountDownLatch mBuffersExhausted, mCodecFlushed;
+        final int mNumBuffersBeforeFlush;
+
+        CountDownLatch mStopInput = new CountDownLatch(1);
+        List<Integer> mOutputBufferIndices = new ArrayList<>();
+
+        ReleaseAfterFlushCallback(MediaCodec encoder,
+                CountDownLatch buffersExhausted,
+                CountDownLatch codecFlushed,
+                int numBuffersBeforeFlush) {
+            mEncoder = encoder;
+            mBuffersExhausted = buffersExhausted;
+            mCodecFlushed = codecFlushed;
+            mNumBuffersBeforeFlush = numBuffersBeforeFlush;
+        }
+
+        @Override
+        public void onInputBufferAvailable(MediaCodec codec, int index) {
+            fail(codec + " onInputBufferAvailable " + index);
+        }
+
+        @Override
+        public void onOutputBufferAvailable(MediaCodec codec, int index, BufferInfo info) {
+            mOutputBufferIndices.add(index);
+            if (mOutputBufferIndices.size() == mNumBuffersBeforeFlush) {
+                releaseAfterFlush(codec, mOutputBufferIndices, mBuffersExhausted, mCodecFlushed);
+                mStopInput.countDown();
+            }
+        }
+
+        @Override
+        public void onError(MediaCodec codec, CodecException e) {
+            Log.e(TAG, codec + " onError", e);
+            fail(codec + " onError " + e.getMessage());
+        }
+
+        @Override
+        public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+            Log.v(TAG, codec + " onOutputFormatChanged " + format);
+        }
+
+        @Override
+        public void run() {
+            InputSurface inputSurface = null;
+            try {
+                inputSurface = initCodecAndSurface(mEncoder);
+                do {
+                    GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
+                    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+                    inputSurface.swapBuffers();
+                } while (!mStopInput.await(TIMEOUT_USEC, TimeUnit.MICROSECONDS));
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                Log.w(TAG, "mEncoder input frames interrupted/stopped", e);
+            } finally {
+                cleanupCodecAndSurface(mEncoder, inputSurface);
+            }
+        }
+    }
+
+    private static void runReleaseAfterFlush(
+            MediaCodec encoder,
+            CountDownLatch buffersExhausted,
+            CountDownLatch codecFlushed,
+            AtomicInteger numBuffers) {
+        InputSurface inputSurface = null;
+        try {
+            inputSurface = initCodecAndSurface(encoder);
+            List<Integer> outputBufferIndices = getOutputBufferIndices(encoder, inputSurface);
+            if (numBuffers != null) {
+                numBuffers.set(outputBufferIndices.size());
+            }
+            releaseAfterFlush(encoder, outputBufferIndices, buffersExhausted, codecFlushed);
+        } finally {
+            cleanupCodecAndSurface(encoder, inputSurface);
+        }
+    }
+
+    private static InputSurface initCodecAndSurface(MediaCodec encoder) {
+        InputSurface inputSurface;
+        CodecInfo info = getAvcSupportedFormatInfo();
+        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, info.mMaxW, info.mMaxH);
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, info.mBitRate);
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, info.mFps);
+        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+        OutputSurface outputSurface = new OutputSurface(1, 1);
+        encoder.configure(format, outputSurface.getSurface(), null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+        inputSurface = new InputSurface(encoder.createInputSurface());
+        inputSurface.makeCurrent();
+        encoder.start();
+        return inputSurface;
+    }
+
+    private static void cleanupCodecAndSurface(MediaCodec encoder, InputSurface inputSurface) {
+        if (encoder != null) {
+            encoder.stop();
+            encoder.release();
+        }
+
+        if (inputSurface != null) {
+            inputSurface.release();
+        }
+    }
+
+    private static List<Integer> getOutputBufferIndices(MediaCodec encoder, InputSurface inputSurface) {
+        boolean feedMoreFrames;
+        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+        List<Integer> indices = new ArrayList<>();
+        do {
+            feedMoreFrames = indices.isEmpty();
+            GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
+            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+            inputSurface.swapBuffers();
+            // dequeue buffers until not available
+            int index = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
+            while (index >= 0) {
+                feedMoreFrames = true;
+                indices.add(index);
+                index = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC_SHORT);
+            }
+        } while (feedMoreFrames);
+        assertFalse(indices.isEmpty());
+        return indices;
+    }
+
+    private static void releaseAfterFlush(
+            MediaCodec encoder,
+            List<Integer> outputBufferIndices,
+            CountDownLatch buffersExhausted,
+            CountDownLatch codecFlushed) {
+        if (buffersExhausted == null) {
+            // flush from same thread
+            encoder.flush();
+        } else {
+            assertNotNull(codecFlushed);
+            buffersExhausted.countDown();
+            try {
+                codecFlushed.await();
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                Log.w(TAG, "codecFlushed wait interrupted; releasing buffers immediately.", e);
+            }
+        }
+
+        for (int index : outputBufferIndices) {
+            try {
+                encoder.releaseOutputBuffer(index, true);
+                fail("MediaCodec releaseOutputBuffer after flush() does not throw exception");
+            } catch (MediaCodec.CodecException e) {
+                // Expected
+            }
+        }
+    }
+
     /**
      * Tests:
      * <br> dequeueInputBuffer() fails when encoder configured with an input Surface
@@ -553,6 +797,121 @@
         }
     }
 
+    public void testDecodeAfterFlush() throws InterruptedException {
+        final int INPUT_RESOURCE_ID =
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
+
+        // The test should fail if the decoder never produces output frames for the input.
+        // Time out decoding, as we have no way to query whether the decoder will produce output.
+        final int DECODING_TIMEOUT_MS = 10000;
+
+        final AtomicBoolean completed = new AtomicBoolean(false);
+        Thread videoDecodingThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                OutputSurface outputSurface = null;
+                MediaExtractor mediaExtractor = null;
+                MediaCodec mediaCodec = null;
+                try {
+                    outputSurface = new OutputSurface(1, 1);
+                    mediaExtractor = getMediaExtractorForMimeType(INPUT_RESOURCE_ID, "video/");
+                    MediaFormat mediaFormat =
+                            mediaExtractor.getTrackFormat(mediaExtractor.getSampleTrackIndex());
+                    if (!MediaUtils.checkDecoderForFormat(mediaFormat)) {
+                        return; // skip
+                    }
+                    String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
+                    mediaCodec = MediaCodec.createDecoderByType(mimeType);
+                    mediaCodec.configure(mediaFormat, outputSurface.getSurface(),
+                            null /* crypto */, 0 /* flags */);
+                    mediaCodec.start();
+
+                    if (!runDecodeTillFirstOutput(mediaCodec, mediaExtractor)) {
+                        throw new RuntimeException("decoder does not generate non-empty output.");
+                    }
+
+                    // simulate application flush.
+                    mediaExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+                    mediaCodec.flush();
+
+                    completed.set(runDecodeTillFirstOutput(mediaCodec, mediaExtractor));
+                } catch (IOException e) {
+                    throw new RuntimeException("error setting up decoding", e);
+                } finally {
+                    if (mediaCodec != null) {
+                        mediaCodec.stop();
+                        mediaCodec.release();
+                    }
+                    if (mediaExtractor != null) {
+                        mediaExtractor.release();
+                    }
+                    if (outputSurface != null) {
+                        outputSurface.release();
+                    }
+                }
+            }
+        });
+        videoDecodingThread.start();
+        videoDecodingThread.join(DECODING_TIMEOUT_MS);
+        // In case it's timed out, need to stop the thread and have all resources released.
+        videoDecodingThread.interrupt();
+        if (!completed.get()) {
+            throw new RuntimeException("timed out decoding to end-of-stream");
+        }
+    }
+
+    // Run the decoder till it generates an output buffer.
+    // Return true when that output buffer is not empty, false otherwise.
+    private static boolean runDecodeTillFirstOutput(
+            MediaCodec mediaCodec, MediaExtractor mediaExtractor) {
+        final int TIME_OUT_US = 10000;
+
+        assertTrue("Wrong test stream which has no data.",
+                mediaExtractor.getSampleTrackIndex() != -1);
+        boolean signaledEos = false;
+        MediaCodec.BufferInfo outputBufferInfo = new MediaCodec.BufferInfo();
+        while (!Thread.interrupted()) {
+            // Try to feed more data into the codec.
+            if (!signaledEos) {
+                int bufferIndex = mediaCodec.dequeueInputBuffer(TIME_OUT_US /* timeoutUs */);
+                if (bufferIndex != -1) {
+                    ByteBuffer buffer = mediaCodec.getInputBuffer(bufferIndex);
+                    int size = mediaExtractor.readSampleData(buffer, 0 /* offset */);
+                    long timestampUs = mediaExtractor.getSampleTime();
+                    mediaExtractor.advance();
+                    signaledEos = mediaExtractor.getSampleTrackIndex() == -1;
+                    mediaCodec.queueInputBuffer(bufferIndex,
+                            0 /* offset */,
+                            size,
+                            timestampUs,
+                            signaledEos ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+                    Log.i("DEBUG", "queue with " + signaledEos);
+                }
+            }
+
+            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(
+                    outputBufferInfo, TIME_OUT_US /* timeoutUs */);
+
+            if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED
+                    || outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED
+                    || outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                continue;
+            }
+            assertTrue("Wrong output buffer index", outputBufferIndex >= 0);
+
+            mediaCodec.releaseOutputBuffer(outputBufferIndex, false /* render */);
+            boolean eos = (outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+            Log.i("DEBUG", "Got a frame with eos=" + eos);
+            if (eos && outputBufferInfo.size == 0) {
+                return false;
+            } else {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     /**
      * Tests whether decoding a short group-of-pictures succeeds. The test queues a few video frames
      * then signals end-of-stream. The test fails if the decoder doesn't output the queued frames.
diff --git a/tests/tests/media/src/android/media/cts/MediaControllerTest.java b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
index a153c4d..caf2a94 100644
--- a/tests/tests/media/src/android/media/cts/MediaControllerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
@@ -21,6 +21,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState.CustomAction;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -182,6 +183,14 @@
             assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
 
             mCallback.reset();
+            final Uri uri = Uri.parse("content://test/popcorn.mod");
+            controls.playFromUri(uri, extras);
+            mWaitLock.wait(TIME_OUT_MS);
+            assertTrue(mCallback.mOnPlayFromUriCalled);
+            assertEquals(uri, mCallback.mUri);
+            assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+            mCallback.reset();
             final String action = "test-action";
             controls.sendCustomAction(action, extras);
             mWaitLock.wait(TIME_OUT_MS);
@@ -209,30 +218,32 @@
     }
 
     private class MediaSessionCallback extends MediaSession.Callback {
-        private volatile long mSeekPosition;
-        private volatile long mQueueItemId;
-        private volatile Rating mRating;
-        private volatile String mMediaId;
-        private volatile String mQuery;
-        private volatile String mAction;
-        private volatile String mCommand;
-        private volatile Bundle mExtras;
-        private volatile ResultReceiver mCommandCallback;
+        private long mSeekPosition;
+        private long mQueueItemId;
+        private Rating mRating;
+        private String mMediaId;
+        private String mQuery;
+        private Uri mUri;
+        private String mAction;
+        private String mCommand;
+        private Bundle mExtras;
+        private ResultReceiver mCommandCallback;
 
-        private volatile boolean mOnPlayCalled;
-        private volatile boolean mOnPauseCalled;
-        private volatile boolean mOnStopCalled;
-        private volatile boolean mOnFastForwardCalled;
-        private volatile boolean mOnRewindCalled;
-        private volatile boolean mOnSkipToPreviousCalled;
-        private volatile boolean mOnSkipToNextCalled;
-        private volatile boolean mOnSeekToCalled;
-        private volatile boolean mOnSkipToQueueItemCalled;
-        private volatile boolean mOnSetRatingCalled;
-        private volatile boolean mOnPlayFromMediaIdCalled;
-        private volatile boolean mOnPlayFromSearchCalled;
-        private volatile boolean mOnCustomActionCalled;
-        private volatile boolean mOnCommandCalled;
+        private boolean mOnPlayCalled;
+        private boolean mOnPauseCalled;
+        private boolean mOnStopCalled;
+        private boolean mOnFastForwardCalled;
+        private boolean mOnRewindCalled;
+        private boolean mOnSkipToPreviousCalled;
+        private boolean mOnSkipToNextCalled;
+        private boolean mOnSeekToCalled;
+        private boolean mOnSkipToQueueItemCalled;
+        private boolean mOnSetRatingCalled;
+        private boolean mOnPlayFromMediaIdCalled;
+        private boolean mOnPlayFromSearchCalled;
+        private boolean mOnPlayFromUriCalled;
+        private boolean mOnCustomActionCalled;
+        private boolean mOnCommandCalled;
 
         public void reset() {
             mSeekPosition = -1;
@@ -240,6 +251,7 @@
             mRating = null;
             mMediaId = null;
             mQuery = null;
+            mUri = null;
             mAction = null;
             mExtras = null;
             mCommand = null;
@@ -257,6 +269,7 @@
             mOnSetRatingCalled = false;
             mOnPlayFromMediaIdCalled = false;
             mOnPlayFromSearchCalled = false;
+            mOnPlayFromUriCalled = false;
             mOnCustomActionCalled = false;
             mOnCommandCalled = false;
         }
@@ -356,6 +369,16 @@
         }
 
         @Override
+        public void onPlayFromUri(Uri uri, Bundle extras) {
+            synchronized (mWaitLock) {
+                mOnPlayFromUriCalled = true;
+                mUri = uri;
+                mExtras = extras;
+                mWaitLock.notify();
+            }
+        }
+
+        @Override
         public void onCustomAction(String action, Bundle extras) {
             synchronized (mWaitLock) {
                 mOnCustomActionCalled= true;
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 562656b..54e6ef1 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -20,10 +20,13 @@
 
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
+import android.cts.util.MediaUtils;
 import android.media.MediaDataSource;
 import android.media.MediaMetadataRetriever;
 import android.test.AndroidTestCase;
 
+import java.io.IOException;
+
 public class MediaMetadataRetrieverTest extends AndroidTestCase {
     protected Resources mResources;
     protected MediaMetadataRetriever mRetriever;
@@ -149,4 +152,49 @@
         dataSource.returnFromReadAt(-2);
         assertTrue(mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) == null);
     }
+
+    private void testThumbnail(int resId) {
+        if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")) {
+            MediaUtils.skipTest("no video codecs for resource");
+            return;
+        }
+
+        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+        Resources resources = getContext().getResources();
+        AssetFileDescriptor afd = resources.openRawResourceFd(resId);
+
+        retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+
+        try {
+            afd.close();
+        } catch (IOException e) {
+            fail("Unable to open file");
+        }
+
+        assertNotNull(retriever.getFrameAtTime(-1 /* timeUs (any) */));
+    }
+
+    public void testThumbnailH264() {
+        testThumbnail(R.raw.video_1280x720_mp4_h264_8192kbps_30fps_aac_stereo_128kbps_44100hz);
+    }
+
+    public void testThumbnailH263() {
+        testThumbnail(R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_11025hz);
+    }
+
+    public void testThumbnailMPEG4() {
+        testThumbnail(R.raw.video_1280x720_mp4_mpeg4_1000kbps_25fps_aac_stereo_128kbps_44100hz);
+    }
+
+    public void testThumbnailVP8() {
+        testThumbnail(R.raw.video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_48000hz);
+    }
+
+    public void testThumbnailVP9() {
+        testThumbnail(R.raw.video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz);
+    }
+
+    public void testThumbnailHEVC() {
+        testThumbnail(R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz);
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
index 35e88f0..b5ba715 100644
--- a/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
+++ b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
@@ -46,7 +46,8 @@
 
     /* package private */ static MediaSession sSession;
     private Bundle mExtras;
-    private Result<List<MediaItem>> mPendingResult;
+    private Result<List<MediaItem>> mPendingLoadChildrenResult;
+    private Result<MediaItem> mPendingLoadItemResult;
 
     @Override
     public void onCreate() {
@@ -73,16 +74,43 @@
             }
             result.sendResult(mediaItems);
         } else if (MEDIA_ID_CHILDREN_DELAYED.equals(parentMediaId)) {
-            Assert.assertNull(mPendingResult);
-            mPendingResult = result;
+            Assert.assertNull(mPendingLoadChildrenResult);
+            mPendingLoadChildrenResult = result;
             result.detach();
         }
     }
 
+    @Override
+    public void onLoadItem(String itemId, Result<MediaItem> result) {
+        if (MEDIA_ID_CHILDREN_DELAYED.equals(itemId)) {
+            mPendingLoadItemResult = result;
+            result.detach();
+            return;
+        }
+
+        for (String id : MEDIA_ID_CHILDREN) {
+            if (id.equals(itemId)) {
+                result.sendResult(new MediaItem(new MediaDescription.Builder()
+                        .setMediaId(id).build(), MediaItem.FLAG_BROWSABLE));
+                return;
+            }
+        }
+
+        super.onLoadItem(itemId, result);
+    }
+
     public void sendDelayedNotifyChildrenChanged() {
-        if (mPendingResult != null) {
-            mPendingResult.sendResult(Collections.<MediaItem>emptyList());
-            mPendingResult = null;
+        if (mPendingLoadChildrenResult != null) {
+            mPendingLoadChildrenResult.sendResult(Collections.<MediaItem>emptyList());
+            mPendingLoadChildrenResult = null;
+        }
+    }
+
+    public void sendDelayedItemLoaded() {
+        if (mPendingLoadItemResult != null) {
+            mPendingLoadItemResult.sendResult(new MediaItem(new MediaDescription.Builder()
+                    .setMediaId(MEDIA_ID_CHILDREN_DELAYED).build(), MediaItem.FLAG_BROWSABLE));
+            mPendingLoadItemResult = null;
         }
     }
 }
diff --git a/tests/tests/midi/AndroidManifest.xml b/tests/tests/midi/AndroidManifest.xml
index 2cdd211..971f4fb 100755
--- a/tests/tests/midi/AndroidManifest.xml
+++ b/tests/tests/midi/AndroidManifest.xml
@@ -25,7 +25,8 @@
     <application>
         <uses-library android:name="android.test.runner" />
 
-        <service android:name="MidiEchoTestService">
+        <service android:name="MidiEchoTestService"
+                android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
             <intent-filter>
                 <action android:name="android.media.midi.MidiDeviceService" />
             </intent-filter>
diff --git a/tests/tests/os/src/android/os/cts/DebugTest.java b/tests/tests/os/src/android/os/cts/DebugTest.java
index d8f5e87..8301cfc 100644
--- a/tests/tests/os/src/android/os/cts/DebugTest.java
+++ b/tests/tests/os/src/android/os/cts/DebugTest.java
@@ -300,4 +300,53 @@
         checkHistogram(gc_count_rate_histogram);
         checkHistogram(blocking_gc_count_rate_histogram);
     }
+
+    public void testGetMemoryStat() throws Exception {
+        Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
+        Debug.getMemoryInfo(memoryInfo);
+
+        String summary_java_heap = memoryInfo.getMemoryStat("summary.java-heap");
+        String summary_native_heap = memoryInfo.getMemoryStat("summary.native-heap");
+        String summary_code = memoryInfo.getMemoryStat("summary.code");
+        String summary_stack = memoryInfo.getMemoryStat("summary.stack");
+        String summary_graphics = memoryInfo.getMemoryStat("summary.graphics");
+        String summary_private_other = memoryInfo.getMemoryStat("summary.private-other");
+        String summary_system = memoryInfo.getMemoryStat("summary.system");
+        String summary_total_pss = memoryInfo.getMemoryStat("summary.total-pss");
+        String summary_total_swap = memoryInfo.getMemoryStat("summary.total-swap");
+        checkNumber(summary_java_heap);
+        checkNumber(summary_native_heap);
+        checkNumber(summary_code);
+        checkNumber(summary_stack);
+        checkNumber(summary_graphics);
+        checkNumber(summary_private_other);
+        checkNumber(summary_system);
+        checkNumber(summary_total_pss);
+        checkNumber(summary_total_swap);
+    }
+
+    public void testGetMemoryStats() throws Exception {
+        Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
+        Debug.getMemoryInfo(memoryInfo);
+
+        Map<String, String> map = memoryInfo.getMemoryStats();
+        String summary_java_heap = map.get("summary.java-heap");
+        String summary_native_heap = map.get("summary.native-heap");
+        String summary_code = map.get("summary.code");
+        String summary_stack = map.get("summary.stack");
+        String summary_graphics = map.get("summary.graphics");
+        String summary_private_other = map.get("summary.private-other");
+        String summary_system = map.get("summary.system");
+        String summary_total_pss = map.get("summary.total-pss");
+        String summary_total_swap = map.get("summary.total-swap");
+        checkNumber(summary_java_heap);
+        checkNumber(summary_native_heap);
+        checkNumber(summary_code);
+        checkNumber(summary_stack);
+        checkNumber(summary_graphics);
+        checkNumber(summary_private_other);
+        checkNumber(summary_system);
+        checkNumber(summary_total_pss);
+        checkNumber(summary_total_swap);
+    }
 }
diff --git a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
index 2287660..3bcc4b9 100644
--- a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
@@ -358,6 +358,15 @@
         }
     }
 
+    // http://b/21578056
+    public void testFileNamesWithNonBmpChars() throws Exception {
+        final File file = File.createTempFile("treble_clef_\ud834\udd1e", ".tmp");
+        final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
+                ParcelFileDescriptor.MODE_READ_ONLY);
+        assertNotNull(pfd);
+        pfd.close();
+    }
+
     static ParcelFileDescriptor makeParcelFileDescriptor(Context con) throws Exception {
         final String fileName = "testParcelFileDescriptor";
 
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 4050f53..04b8554 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -158,7 +158,6 @@
         assertFalse(f.canRead());
         assertFalse(f.canWrite());
         assertFalse(f.canExecute());
-        assertFalse(f.exists());
     }
 
     @MediumTest
@@ -179,7 +178,6 @@
         assertFalse(f.canRead());
         assertFalse(f.canWrite());
         assertFalse(f.canExecute());
-        assertFalse(f.exists());
     }
 
     @MediumTest
diff --git a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
index 105fb81..2dd5892 100644
--- a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
@@ -16,7 +16,6 @@
 
 package android.permission.cts;
 
-import android.provider.Browser;
 import android.provider.CallLog;
 import android.provider.Contacts;
 import android.test.AndroidTestCase;
@@ -77,45 +76,4 @@
         assertWritingContentUriRequiresPermission(android.provider.Settings.System.CONTENT_URI,
                 android.Manifest.permission.WRITE_SETTINGS);
     }
-
-    /**
-     * Verify that read and write to browser bookmarks requires permissions.
-     * <p>Tests Permission:
-     *   {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
-     */
-    public void testReadBookmarks() {
-        assertReadingContentUriRequiresPermission(Browser.BOOKMARKS_URI,
-                android.Manifest.permission.READ_HISTORY_BOOKMARKS);
-    }
-
-    /**
-     * Verify that read and write to browser bookmarks requires permissions.
-     * <p>Tests Permission:
-         {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
-     */
-    public void testWriteBookmarks() {
-        assertWritingContentUriRequiresPermission(Browser.BOOKMARKS_URI,
-                android.Manifest.permission.WRITE_HISTORY_BOOKMARKS);
-    }
-
-    /**
-     * Verify that read and write to browser history requires permissions.
-     * <p>Tests Permission:
-     *   {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
-     */
-    public void testReadBrowserHistory() {
-        assertReadingContentUriRequiresPermission(Browser.SEARCHES_URI,
-                android.Manifest.permission.READ_HISTORY_BOOKMARKS);
-    }
-
-    /**
-     * Verify that read and write to browser history requires permissions.
-     * <p>Tests Permission:
-         {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
-     */
-    public void testWriteBrowserHistory() {
-        assertWritingContentUriRequiresPermission(Browser.SEARCHES_URI,
-                android.Manifest.permission.WRITE_HISTORY_BOOKMARKS);
-    }
 }
-
diff --git a/tests/tests/provider/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 855c4ce..5f18635 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -27,8 +27,6 @@
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
-    <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
-    <uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" />
     <uses-permission android:name="android.permission.WRITE_CALENDAR" />
     <uses-permission android:name="android.permission.READ_CALENDAR" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/tests/tests/provider/src/android/provider/cts/BrowserTest.java b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
deleted file mode 100644
index 9f96412..0000000
--- a/tests/tests/provider/src/android/provider/cts/BrowserTest.java
+++ /dev/null
@@ -1,697 +0,0 @@
-/*
- * Copyright (C) 2008 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.provider.cts;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.Browser;
-import android.provider.Browser.BookmarkColumns;
-import android.provider.Browser.SearchColumns;
-import android.test.ActivityInstrumentationTestCase2;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-public class BrowserTest extends ActivityInstrumentationTestCase2<BrowserStubActivity> {
-    public BrowserTest() {
-        super("com.android.cts.provider", BrowserStubActivity.class);
-    }
-
-    private Context mContext;
-    private ContentResolver mContentResolver;
-    private ContentProviderClient mProvider;
-    private BrowserStubActivity mActivity;
-    private boolean mMasterSyncEnabled;
-
-    // the backup for the 2 tables which we will modify in test cases
-    private ArrayList<ContentValues> mBookmarksBackup;
-    private ArrayList<ContentValues> mSearchesBackup;
-
-    private static final String ADD_BOOKMARK_CLASS_NAME =
-            "com.android.browser.AddBookmarkPage";
-    private static final String COMPOSE_MESSAGE_CLASS_NAME =
-            "com.android.mms.ui.ComposeMessageActivity";
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mContext = getInstrumentation().getTargetContext();
-        mContentResolver = mContext.getContentResolver();
-        mProvider = mContentResolver.acquireContentProviderClient(
-                Browser.BOOKMARKS_URI.getAuthority());
-        mBookmarksBackup = new ArrayList<ContentValues>();
-        mSearchesBackup = new ArrayList<ContentValues>();
-
-        // Disable sync
-        mMasterSyncEnabled = ContentResolver.getMasterSyncAutomatically();
-        ContentResolver.setMasterSyncAutomatically(false);
-
-        // backup the current contents in database
-        Cursor cursor = mProvider.query(Browser.BOOKMARKS_URI, null, null, null, null, null);
-        while (cursor.moveToNext()) {
-            String[] colNames = cursor.getColumnNames();
-            ContentValues value = new ContentValues();
-
-            for (int i = 0; i < colNames.length; i++) {
-                switch (cursor.getType(i)) {
-                case Cursor.FIELD_TYPE_BLOB:
-                    value.put(colNames[i], cursor.getBlob(i));
-                    break;
-                case Cursor.FIELD_TYPE_FLOAT:
-                    value.put(colNames[i], cursor.getFloat(i));
-                    break;
-                case Cursor.FIELD_TYPE_INTEGER:
-                    if (!"_ID".equalsIgnoreCase(colNames[i])) {
-                        value.put(colNames[i], cursor.getLong(i));
-                    }
-                    break;
-                case Cursor.FIELD_TYPE_STRING:
-                    value.put(colNames[i], cursor.getString(i));
-                    break;
-                }
-            }
-            mBookmarksBackup.add(value);
-        }
-
-        cursor.close();
-
-        cursor = mProvider.query(Browser.SEARCHES_URI, null, null, null, null, null);
-        if (cursor.moveToFirst()) {
-            while (!cursor.isAfterLast()) {
-                ContentValues value = new ContentValues();
-
-                value.put(SearchColumns._ID, cursor.getInt(0));
-                value.put(SearchColumns.SEARCH, cursor.getString(1));
-                value.put(SearchColumns.DATE, cursor.getLong(2));
-                mSearchesBackup.add(value);
-
-                cursor.moveToNext();
-            };
-        }
-        cursor.close();
-
-        mProvider.delete(Browser.BOOKMARKS_URI, null, null);
-        mProvider.delete(Browser.SEARCHES_URI, null, null);
-
-        mActivity = getActivity();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        try {
-            // clear all new contents added in test cases.
-            mProvider.delete(Browser.BOOKMARKS_URI, null, null);
-            mProvider.delete(Browser.SEARCHES_URI, null, null);
-
-            // recover the old backup contents
-            for (ContentValues value : mBookmarksBackup) {
-                mProvider.insert(Browser.BOOKMARKS_URI, value);
-            }
-
-            for (ContentValues value : mSearchesBackup) {
-                mProvider.insert(Browser.SEARCHES_URI, value);
-            }
-
-            // Re-enable sync
-            ContentResolver.setMasterSyncAutomatically(mMasterSyncEnabled);
-        } finally {
-            super.tearDown();
-        }
-    }
-
-    public void testAccessSearches() {
-        final String searchString = "search string";
-        final String searchStringAnother = "another search string";
-        Cursor cursor;
-
-        try {
-            Browser.addSearchUrl(mContentResolver, searchString);
-            cursor = mProvider.query(
-                    Browser.SEARCHES_URI,
-                    Browser.SEARCHES_PROJECTION,
-                    null, null, null, null);
-            assertEquals(1, cursor.getCount());
-            cursor.moveToFirst();
-            assertEquals(searchString,
-                    cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
-            long oldDate = cursor.getLong(Browser.SEARCHES_PROJECTION_DATE_INDEX);
-            cursor.close();
-
-            Browser.addSearchUrl(mContentResolver, searchString);
-            cursor = mProvider.query(Browser.SEARCHES_URI,
-                    Browser.SEARCHES_PROJECTION,
-                    null, null, null, null);
-            assertEquals(1, cursor.getCount());
-            cursor.moveToFirst();
-            long date = cursor.getLong(Browser.SEARCHES_PROJECTION_DATE_INDEX);
-            assertTrue(date > oldDate);
-            assertEquals(searchString,
-                    cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
-            cursor.close();
-
-            Browser.addSearchUrl(mContentResolver, searchStringAnother);
-            cursor = mProvider.query(Browser.SEARCHES_URI,
-                    Browser.SEARCHES_PROJECTION,
-                    null, null, null, null);
-            assertEquals(2, cursor.getCount());
-            cursor.moveToFirst();
-            assertEquals(searchString,
-                    cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
-            cursor.moveToNext();
-            assertEquals(searchStringAnother,
-                    cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
-            cursor.close();
-
-            Browser.clearSearches(mContentResolver);
-            cursor = mProvider.query(
-                    Browser.SEARCHES_URI,
-                    Browser.SEARCHES_PROJECTION,
-                    null, null, null, null);
-            assertEquals(0, cursor.getCount());
-        } catch (RemoteException e) {
-            fail("Unexpected RemoteException");
-        }
-    }
-
-    public void testGetAllBookmarks() {
-        Cursor cursor;
-        final String bookmarkUrl1 = "www.bookmark1.com";
-        final String bookmarkUrl2 = "www.bookmark2.com";
-        final String historyUrl = "www.history.com";
-
-        try {
-            cursor = Browser.getAllBookmarks(mContentResolver);
-            assertEquals(0, cursor.getCount());
-            cursor.close();
-
-            ContentValues value = new ContentValues();
-            value.put(BookmarkColumns.URL, bookmarkUrl1);
-            value.put(BookmarkColumns.BOOKMARK, 1);
-            mProvider.insert(Browser.BOOKMARKS_URI, value);
-            value.put(BookmarkColumns.URL, bookmarkUrl2);
-            value.put(BookmarkColumns.BOOKMARK, 1);
-            mProvider.insert(Browser.BOOKMARKS_URI, value);
-            value.put(BookmarkColumns.URL, historyUrl);
-            value.put(BookmarkColumns.BOOKMARK, 0);
-            mProvider.insert(Browser.BOOKMARKS_URI, value);
-            cursor = Browser.getAllBookmarks(mContentResolver);
-            assertEquals(2, cursor.getCount());
-            cursor.moveToFirst();
-            assertEquals(bookmarkUrl1, cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals(bookmarkUrl2, cursor.getString(0));
-        } catch (RemoteException e) {
-            fail("unexpected RemoteException");
-        }
-    }
-
-    public void testGetAllVisitedUrls() {
-        Cursor cursor;
-        final String visitedUrl1 = "www.visited1.com";
-        final String visitedUrl2 = "www.visited2.com";
-        final String visitedUrl3 = "www.visited3.com";
-
-        try {
-            cursor = Browser.getAllVisitedUrls(mContentResolver);
-            assertEquals(0, cursor.getCount());
-            cursor.close();
-
-            ContentValues value = new ContentValues();
-            value.put(BookmarkColumns.URL, visitedUrl1);
-            value.put(BookmarkColumns.BOOKMARK, 1);
-            value.put(BookmarkColumns.VISITS, 1);
-            mProvider.insert(Browser.BOOKMARKS_URI, value);
-            value.put(BookmarkColumns.URL, visitedUrl2);
-            value.put(BookmarkColumns.BOOKMARK, 0);
-            value.put(BookmarkColumns.VISITS, 5);
-            mProvider.insert(Browser.BOOKMARKS_URI, value);
-            value.put(BookmarkColumns.URL, visitedUrl3);
-            value.put(BookmarkColumns.BOOKMARK, 1);
-            value.put(BookmarkColumns.VISITS, 0);
-            mProvider.insert(Browser.BOOKMARKS_URI, value);
-            cursor = Browser.getAllVisitedUrls(mContentResolver);
-            assertEquals(3, cursor.getCount());
-            cursor.moveToFirst();
-            assertEquals(visitedUrl1, cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals(visitedUrl2, cursor.getString(0));
-            cursor.moveToNext();
-            assertEquals(visitedUrl3, cursor.getString(0));
-        } catch (RemoteException e) {
-            fail("unexpected RemoteException");
-        }
-    }
-
-    public void testUpdateVisitedHistory() {
-        Cursor cursor;
-        final String visitedHistoryUrl = "www.visited-history.com";
-        long oldTime = 0;
-
-        try {
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    null, null, null, null);
-            assertEquals(0, cursor.getCount());
-            cursor.close();
-            Browser.updateVisitedHistory(mContentResolver, visitedHistoryUrl, true);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    null, null, null, null);
-            assertEquals(1, cursor.getCount());
-            cursor.moveToFirst();
-            assertEquals(visitedHistoryUrl, cursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
-            assertEquals(visitedHistoryUrl,
-                    cursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
-            assertEquals(0, cursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX));
-            assertEquals(1, cursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX));
-            oldTime = cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX);
-            cursor.close();
-
-            Browser.updateVisitedHistory(mContentResolver, visitedHistoryUrl, true);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    null, null, null, null);
-            assertEquals(1, cursor.getCount());
-            cursor.moveToFirst();
-            assertEquals(visitedHistoryUrl, cursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
-            assertEquals(visitedHistoryUrl,
-                    cursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
-            assertEquals(0, cursor.getInt(Browser.HISTORY_PROJECTION_BOOKMARK_INDEX));
-            assertEquals(2, cursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX));
-            assertTrue(oldTime < cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
-            cursor.close();
-        } catch (RemoteException e1) {
-            fail("unexpected RemoteException");
-        }
-    }
-
-    public void testAccessHistory() {
-        Cursor cursor;
-        // NOTE: this value must keep same with the Browser.MAX_HISTORY_COUNT.
-        final int MAX_HISTORY_COUNT = 250;
-        final String bookmarkUrl = "www.visited-bookmark.com";
-        final String historyUrlPrefix = "www.visited-history";
-        final String historyUrlPostfix = ".com";
-        final long TIME_BASE = new Date().getTime() - 1000;
-            try {
-            assertFalse(Browser.canClearHistory(mContentResolver));
-            Browser.clearHistory(mContentResolver);
-            assertFalse(Browser.canClearHistory(mContentResolver));
-
-            // The total number of the history table's rows: MAX_HISTORY_COUNT,
-            // but there is 1 row is a bookmark.
-            // The date of the history is from 1 ~ (MAX_HISTORY_COUNT - 1).
-            ContentValues value = new ContentValues();
-            for (int i = 1; i <= MAX_HISTORY_COUNT - 1; i++) {
-                value.put(BookmarkColumns.URL, historyUrlPrefix + i + historyUrlPostfix);
-                value.put(BookmarkColumns.BOOKMARK, 0);
-                value.put(BookmarkColumns.DATE, i + TIME_BASE);
-                mProvider.insert(Browser.BOOKMARKS_URI, value);
-            }
-            value.put(BookmarkColumns.URL, bookmarkUrl);
-            value.put(BookmarkColumns.BOOKMARK, 1);
-            value.put(BookmarkColumns.VISITS, 5);
-            value.put(BookmarkColumns.DATE, new Date().getTime());
-            mProvider.insert(Browser.BOOKMARKS_URI, value);
-            value.clear();
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    null, null, null, null);
-            assertEquals(MAX_HISTORY_COUNT, cursor.getCount());
-            cursor.close();
-            Browser.truncateHistory(mContentResolver);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    null, null, null, null);
-            assertEquals(MAX_HISTORY_COUNT, cursor.getCount());
-            cursor.close();
-
-            // Add more one history which is not a bookmark,
-            // then the history rows except bookmark is MAX_HISTORY_COUNT.
-            value.put(BookmarkColumns.URL, historyUrlPrefix
-                    + MAX_HISTORY_COUNT + historyUrlPostfix);
-            value.put(BookmarkColumns.BOOKMARK, 0);
-            value.put(BookmarkColumns.DATE, MAX_HISTORY_COUNT + TIME_BASE);
-            mProvider.insert(Browser.BOOKMARKS_URI, value);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    null, null, null, null);
-            assertEquals(MAX_HISTORY_COUNT + 1, cursor.getCount());
-            cursor.close();
-            Browser.truncateHistory(mContentResolver);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    null, null, null, null);
-            assertEquals(MAX_HISTORY_COUNT + 1 - Browser.TRUNCATE_N_OLDEST, cursor.getCount());
-            cursor.moveToFirst();
-            assertEquals(Browser.TRUNCATE_N_OLDEST + 1 + TIME_BASE,
-                    cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
-            cursor.close();
-
-            // Delete specified history
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    BookmarkColumns.BOOKMARK + " = 0",
-                    null, BookmarkColumns.DATE, null);
-            int historyCountBeforeDelete = cursor.getCount();
-            cursor.moveToLast();
-            assertEquals(MAX_HISTORY_COUNT + TIME_BASE,
-                    cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
-            cursor.close();
-            Browser.deleteFromHistory(mContentResolver,
-                    historyUrlPrefix + MAX_HISTORY_COUNT + historyUrlPostfix);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    BookmarkColumns.BOOKMARK + " = 0",
-                    null, BookmarkColumns.DATE, null);
-            int historyCountAfterDelete = cursor.getCount();
-            assertEquals(historyCountBeforeDelete - 1, historyCountAfterDelete);
-            cursor.moveToLast();
-            assertEquals(MAX_HISTORY_COUNT - 1 + TIME_BASE,
-                    cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
-            cursor.close();
-
-            // Specify a url which is not existed in current table.
-            historyCountBeforeDelete = historyCountAfterDelete;
-            Browser.deleteFromHistory(mContentResolver, "delete a not-existed url");
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    BookmarkColumns.BOOKMARK + " = 0",
-                    null, BookmarkColumns.DATE, null);
-            historyCountAfterDelete = cursor.getCount();
-            assertEquals(historyCountBeforeDelete, historyCountAfterDelete);
-            cursor.close();
-
-            // Specify the history in a time frame to be deleted
-            historyCountBeforeDelete = historyCountAfterDelete;
-            long begin = 6 + TIME_BASE;
-            long end = 20 + TIME_BASE;
-            Browser.deleteHistoryTimeFrame(mContentResolver, begin, end);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    BookmarkColumns.BOOKMARK + " = 0",
-                    null, BookmarkColumns.DATE, null);
-            historyCountAfterDelete = cursor.getCount();
-            assertEquals(historyCountBeforeDelete - (end - begin), historyCountAfterDelete);
-            cursor.moveToFirst();
-            assertEquals(end, cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
-            cursor.close();
-
-            // Specify the history in a time frame (not specify begin) to be deleted.
-            historyCountBeforeDelete = historyCountAfterDelete;
-            long firstDate = end;
-            begin = -1;
-            end = 34 + TIME_BASE;
-            Browser.deleteHistoryTimeFrame(mContentResolver, begin, end);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    BookmarkColumns.BOOKMARK + " = 0",
-                    null, BookmarkColumns.DATE, null);
-            historyCountAfterDelete = cursor.getCount();
-            assertEquals(historyCountBeforeDelete - (end - firstDate), historyCountAfterDelete);
-            cursor.moveToFirst();
-            assertEquals(end, cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
-            cursor.moveToLast();
-            long lastDate = cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX);
-            cursor.close();
-
-            // Specify the history in a time frame (not specify end) to be deleted.
-            historyCountBeforeDelete = historyCountAfterDelete;
-            begin = MAX_HISTORY_COUNT - 10 + TIME_BASE;
-            end = -1;
-            Browser.deleteHistoryTimeFrame(mContentResolver, begin, end);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    BookmarkColumns.BOOKMARK + " = 0",
-                    null, BookmarkColumns.DATE, null);
-            historyCountAfterDelete = cursor.getCount();
-            assertEquals(historyCountBeforeDelete - (lastDate - begin + 1),
-                    historyCountAfterDelete);
-            cursor.moveToLast();
-            assertEquals(begin - 1, cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
-            cursor.close();
-
-            // Clear all history.
-            assertTrue(Browser.canClearHistory(mContentResolver));
-            Browser.clearHistory(mContentResolver);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION,
-                    null, null, BookmarkColumns.DATE, null);
-            assertEquals(1, cursor.getCount());
-            cursor.moveToFirst();
-            assertEquals(bookmarkUrl, cursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX));
-            assertEquals(0, cursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX));
-            assertEquals(0, cursor.getLong(Browser.HISTORY_PROJECTION_DATE_INDEX));
-            cursor.close();
-            assertFalse(Browser.canClearHistory(mContentResolver));
-        } catch (RemoteException e) {
-            fail("unexpected RemoteException");
-        }
-    }
-
-    public void testRequestAllIcons() {
-        Browser.requestAllIcons(mContentResolver, null, null);
-    }
-
-    public void testSaveBookmark() {
-        // TODO: send KEYCODE_DPAD_CENTER to skip the resolve page, but no effect.
-//        assertFalse(isRunning(ADD_BOOKMARK_CLASS_NAME));
-//        Browser.saveBookmark(mActivity, "bookmark title", "www.bookmark.com");
-//        try {
-//            Thread.sleep(2000);
-//        } catch (InterruptedException e) {
-//            e.printStackTrace();
-//        }
-//        getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
-//        assertStarted(ADD_BOOKMARK_CLASS_NAME, 6000);
-
-        // TODO: how to finish the activity.
-    }
-
-    public void testSendString() {
-        // assertFalse(isRunning(COMPOSE_MESSAGE_CLASS_NAME));
-        // Browser.sendString(mActivity, "string to be sent");
-        // assertStarted(COMPOSE_MESSAGE_CLASS_NAME, 5000);
-
-        // TODO: how to finish the activity.
-    }
-
-    private boolean isRunning(String className) {
-        ActivityManager activityManager = (ActivityManager) mActivity
-                .getSystemService(Context.ACTIVITY_SERVICE);
-        List<RunningTaskInfo> list = activityManager.getRunningTasks(1);
-        RunningTaskInfo info = list.get(0);
-        if (null == info || null == info.topActivity) {
-            return false;
-        }
-
-        if (className.equals(info.topActivity.getClassName())) {
-            return true;
-        }
-
-        return false;
-    }
-
-    private void assertStarted(String className, long timeout) {
-        final long timeSlice = 200;
-        while (timeout > 0) {
-            try {
-                Thread.sleep(timeSlice);
-            } catch (InterruptedException e) {
-                fail("unexpected InterruptedException");
-            }
-            if (isRunning(className)) {
-                return;
-            }
-            timeout -= timeSlice;
-        }
-        fail("has not started BrowserActivity yet");
-    }
-
-    /**
-     * Test case just for the actual content provider behavior on Bookmarks table.
-     * It does not test any APIs in android.provider.Browser.java, so we cannot add
-     * annotation for it.
-     */
-    public void testBookmarksTable() {
-        final String[] BOOKMARKS_PROJECTION = new String[] {
-                BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
-                BookmarkColumns.DATE, BookmarkColumns.CREATED, BookmarkColumns.BOOKMARK,
-                BookmarkColumns.TITLE, BookmarkColumns.FAVICON };
-        final int ID_INDEX = 0;
-        final int URL_INDEX = 1;
-        final int VISITS_INDEX = 2;
-        final int DATE_INDEX = 3;
-        final int CREATED_INDEX = 4;
-        final int BOOKMARK_INDEX = 5;
-        final int TITLE_INDEX = 6;
-        final int FAVICON_INDEX = 7;
-
-        String insertBookmarkTitle = "bookmark_insert";
-        String insertBookmarkUrl = "www.bookmark_insert.com";
-
-        String updateBookmarkTitle = "bookmark_update";
-        String updateBookmarkUrl = "www.bookmark_update.com";
-        try {
-            // Test: insert
-            ContentValues value = new ContentValues();
-            long createDate = new Date().getTime();
-            value.put(BookmarkColumns.TITLE, insertBookmarkTitle);
-            value.put(BookmarkColumns.URL, insertBookmarkUrl);
-            value.put(BookmarkColumns.VISITS, 0);
-            value.put(BookmarkColumns.DATE, createDate);
-            value.put(BookmarkColumns.CREATED, createDate);
-            value.put(BookmarkColumns.BOOKMARK, 0);
-
-            Uri insertUri = mProvider.insert(Browser.BOOKMARKS_URI, value);
-            Cursor cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    BOOKMARKS_PROJECTION,
-                    BookmarkColumns.TITLE + " = ?",
-                    new String[] {insertBookmarkTitle},
-                    BookmarkColumns.DATE, null);
-            assertTrue(cursor.moveToNext());
-            assertEquals(insertBookmarkTitle, cursor.getString(TITLE_INDEX));
-            assertEquals(insertBookmarkUrl,cursor.getString(URL_INDEX));
-            assertEquals(0,cursor.getInt(VISITS_INDEX));
-            assertEquals(createDate, cursor.getLong(DATE_INDEX));
-            assertEquals(createDate, cursor.getLong(CREATED_INDEX));
-            assertEquals(0, cursor.getInt(BOOKMARK_INDEX));
-            assertNull(cursor.getBlob(FAVICON_INDEX));
-            int Id = cursor.getInt(ID_INDEX);
-            cursor.close();
-
-            // Test: update
-            value.clear();
-            long updateDate = new Date().getTime();
-            value.put(BookmarkColumns.TITLE, updateBookmarkTitle);
-            value.put(BookmarkColumns.URL, updateBookmarkUrl);
-            value.put(BookmarkColumns.VISITS, 1);
-            value.put(BookmarkColumns.DATE, updateDate);
-
-            mProvider.update(Browser.BOOKMARKS_URI, value,
-                    BookmarkColumns.TITLE + " = ?",
-                    new String[] {insertBookmarkTitle});
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    BOOKMARKS_PROJECTION,
-                    BookmarkColumns._ID + " = " + Id,
-                    null, null, null);
-            assertTrue(cursor.moveToNext());
-            assertEquals(updateBookmarkTitle, cursor.getString(TITLE_INDEX));
-            assertEquals(updateBookmarkUrl,cursor.getString(URL_INDEX));
-            assertEquals(1,cursor.getInt(VISITS_INDEX));
-            assertEquals(updateDate, cursor.getLong(DATE_INDEX));
-            assertEquals(createDate, cursor.getLong(CREATED_INDEX));
-            assertEquals(0, cursor.getInt(BOOKMARK_INDEX));
-            assertNull(cursor.getBlob(FAVICON_INDEX));
-            assertEquals(Id, cursor.getInt(ID_INDEX));
-
-            // Test: delete
-            mProvider.delete(insertUri, null, null);
-            cursor = mProvider.query(
-                    Browser.BOOKMARKS_URI,
-                    BOOKMARKS_PROJECTION,
-                    BookmarkColumns._ID + " = " + Id,
-                    null, null, null);
-            assertEquals(0, cursor.getCount());
-        } catch (RemoteException e) {
-            fail("Unexpected RemoteException");
-        }
-    }
-
-    /**
-     * Test case just for the actual content provider behavior on Searches table.
-     * It does not test any APIs in android.provider.Browser.java, so we cannot add
-     * annotation for it.
-     */
-    public void testSearchesTable() {
-        final int ID_INDEX = 0;
-        String insertSearch = "search_insert";
-        String updateSearch = "search_update";
-
-        try {
-            // Test: insert
-            ContentValues value = new ContentValues();
-            long createDate = new Date().getTime();
-            value.put(SearchColumns.SEARCH, insertSearch);
-            value.put(SearchColumns.DATE, createDate);
-
-            Uri insertUri = mProvider.insert(Browser.SEARCHES_URI, value);
-            Cursor cursor = mProvider.query(Browser.SEARCHES_URI,
-                    Browser.SEARCHES_PROJECTION, SearchColumns.SEARCH + " = ?",
-                    new String[] {insertSearch}, null, null);
-            assertTrue(cursor.moveToNext());
-            assertEquals(insertSearch,
-                    cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
-            assertEquals(createDate,
-                    cursor.getLong(Browser.SEARCHES_PROJECTION_DATE_INDEX));
-            int Id = cursor.getInt(ID_INDEX);
-            cursor.close();
-
-            // Test: update
-            value.clear();
-            long updateDate = new Date().getTime();
-            value.put(SearchColumns.SEARCH, updateSearch);
-            value.put(SearchColumns.DATE, updateDate);
-
-            mProvider.update(Browser.SEARCHES_URI, value,
-                    SearchColumns._ID + " = " + Id, null);
-            cursor = mProvider.query(Browser.SEARCHES_URI,
-                    Browser.SEARCHES_PROJECTION,
-                    SearchColumns._ID + " = " + Id, null, null, null);
-            assertTrue(cursor.moveToNext());
-            assertEquals(updateSearch,
-                    cursor.getString(Browser.SEARCHES_PROJECTION_SEARCH_INDEX));
-            assertEquals(updateDate,
-                    cursor.getLong(Browser.SEARCHES_PROJECTION_DATE_INDEX));
-            assertEquals(Id, cursor.getInt(ID_INDEX));
-
-            // Test: delete
-            mProvider.delete(insertUri, null, null);
-            cursor = mProvider.query(Browser.SEARCHES_URI,
-                    Browser.SEARCHES_PROJECTION,
-                    SearchColumns._ID + " = " + Id, null, null, null);
-            assertEquals(0, cursor.getCount());
-        } catch (RemoteException e) {
-            fail("Unexpected RemoteException");
-        }
-    }
-}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_AggregationSuggestionsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_AggregationSuggestionsTest.java
index 1aa2b64..64c1a66 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_AggregationSuggestionsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_AggregationSuggestionsTest.java
@@ -191,5 +191,23 @@
         return new long[] {rawContact1.getContactId(),
                 rawContact2.getContactId(), rawContact3.getContactId()};
     }
+
+    public void testAggregationSuggestionsQueryBuilderWithContactId() throws Exception {
+        Uri uri = new AggregationSuggestions.Builder().setContactId(12).setLimit(7).build();
+        assertEquals("content://com.android.contacts/contacts/12/suggestions?limit=7",
+                uri.toString());
+    }
+
+    public void testAggregationSuggestionsQueryBuilderWithValues() throws Exception {
+        Uri uri = new AggregationSuggestions.Builder()
+                .addNameParameter("name1")
+                .addNameParameter("name2")
+                .setLimit(7)
+                .build();
+        assertEquals("content://com.android.contacts/contacts/0/suggestions?"
+                + "limit=7"
+                + "&query=name%3Aname1"
+                + "&query=name%3Aname2", uri.toString());
+    }
 }
 
diff --git a/tests/tests/renderscript/Android.mk b/tests/tests/renderscript/Android.mk
index 1ea800e..7b30859 100644
--- a/tests/tests/renderscript/Android.mk
+++ b/tests/tests/renderscript/Android.mk
@@ -33,6 +33,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
 
+LOCAL_RENDERSCRIPT_FLAGS := -Wno-error=deprecated-declarations
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
index f9d62ac..8ec15fd 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ScriptGroupTest.java
@@ -386,21 +386,14 @@
         ScriptGroup.Input unbound = builder.addInput();
 
         ScriptGroup.Closure c = null;
-        ScriptGroup.Future f = null;
-        int stride;
-        for (stride = ARRAY_SIZE / 2; stride >= 1; stride >>= 1) {
+        ScriptGroup.Binding b2 = new ScriptGroup.Binding(s.getFieldID_a(), unbound);
+        for (int stride = ARRAY_SIZE / 2; stride >= 1; stride >>= 1) {
             ScriptGroup.Binding b1 = new ScriptGroup.Binding(s.getFieldID_reduction_stride(),
                                                              stride);
-            ScriptGroup.Binding b2;
-            if (f == null) {
-                b2 = new ScriptGroup.Binding(s.getFieldID_a_in(), unbound);
-            } else {
-                b2 = new ScriptGroup.Binding(s.getFieldID_a_in(), f);
-            }
             c = builder.addKernel(s.getKernelID_add(),
                                   Type.createX(mRS, Element.I32_4(mRS), stride),
                                   b1, b2);
-            f = c.getReturn();
+            b2 = new ScriptGroup.Binding(s.getFieldID_a(), c.getReturn());
         }
 
         if (c == null) {
@@ -427,6 +420,64 @@
     }
 
     /**
+     * Tests that the kernel output to a global can be used as a future
+     */
+    public void testBuilder2KernelOutputToGlobal() {
+        ScriptC_reduction s = new ScriptC_reduction(mRS);
+
+        int[] array = new int[ARRAY_SIZE * 4];
+
+        for (int i = 0; i < ARRAY_SIZE; i++) {
+            array[i*4] = i;
+            array[i*4 + 1] = i;
+            array[i*4 + 2] = i;
+            array[i*4 + 3] = i;
+        }
+
+        Allocation input = Allocation.createSized(mRS, Element.I32_4(mRS), ARRAY_SIZE);
+        input.copyFrom(array);
+        Allocation input1 = Allocation.createSized(mRS, Element.I32_4(mRS), ARRAY_SIZE);
+
+        ScriptGroup.Builder2 builder = new ScriptGroup.Builder2(mRS);
+
+        ScriptGroup.Input unbound = builder.addInput();
+
+        ScriptGroup.Closure c = null;
+        ScriptGroup.Binding b2 = new ScriptGroup.Binding(s.getFieldID_a(), unbound);
+        for (int stride = ARRAY_SIZE / 2; stride >= 1; stride >>= 1) {
+            ScriptGroup.Binding b1 = new ScriptGroup.Binding(s.getFieldID_reduction_stride(),
+                                                             stride);
+            c = builder.addKernel(s.getKernelID_add2(),
+                                  Type.createX(mRS, Element.I32_4(mRS), stride),
+                                  b1, b2);
+            b2 = new ScriptGroup.Binding(s.getFieldID_a(),
+                                         c.getGlobal(s.getFieldID_a()));
+        }
+
+        if (c == null) {
+            return;
+        }
+
+        ScriptGroup group = builder.create("Summation", c.getGlobal(s.getFieldID_a()));
+
+        int[] a = new int[4 * ARRAY_SIZE];
+        ((Allocation)group.execute(input, input1)[0]).copyTo(a);
+
+        mRS.finish();
+
+        boolean failed = false;
+        for (int i = 0; i < 4; i++) {
+            if (failed == false && a[i] != ARRAY_SIZE * (ARRAY_SIZE - 1) / 2) {
+                Log.e(TAG,
+                      "a["+i+"]="+a[i]+", should be "+ (ARRAY_SIZE * (ARRAY_SIZE - 1) / 2));
+                failed = true;
+            }
+        }
+
+        assertTrue(!failed);
+    }
+
+    /**
      * Tests that invoke-to-kernel dependency is handled correctly
      */
     public void testBuilder2InvokeToKernelDependency() {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/reduction.rs b/tests/tests/renderscript/src/android/renderscript/cts/reduction.rs
index 860e42d..19de6fe 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/reduction.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/reduction.rs
@@ -17,10 +17,19 @@
 #pragma version(1)
 #pragma rs java_package_name(android.renderscript.cts)
 
-rs_allocation a_in;
+rs_allocation a;
 int reduction_stride;
 
 int4 __attribute__((kernel)) add(uint x)
 {
-  return rsGetElementAt_int4(a_in, x) + rsGetElementAt_int4(a_in, x + reduction_stride);
+  return rsGetElementAt_int4(a, x) + rsGetElementAt_int4(a, x + reduction_stride);
+}
+
+int4 __attribute__((kernel)) add2(uint x)
+{
+  rsSetElementAt_int4(a,
+                      rsGetElementAt_int4(a, x) +
+                      rsGetElementAt_int4(a, x + reduction_stride),
+                      x);
+  return 0;
 }
diff --git a/tests/tests/rscpp/librscpptest/shared.rsh b/tests/tests/rscpp/librscpptest/shared.rsh
index 3bc52f2..c5599d7 100644
--- a/tests/tests/rscpp/librscpptest/shared.rsh
+++ b/tests/tests/rscpp/librscpptest/shared.rsh
@@ -3,11 +3,11 @@
 
 static int64_t g_time;
 
-static void start(void) {
+static inline void start(void) {
     g_time = rsUptimeMillis();
 }
 
-static float end(void) {
+static inline float end(void) {
     int64_t t = rsUptimeMillis() - g_time;
     return ((float)t) / 1000.f;
 }
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index 7106385..6bef886 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -28,7 +28,6 @@
 		android_security_cts_LinuxRngTest.cpp \
 		android_security_cts_LoadEffectLibraryTest.cpp \
 		android_security_cts_NativeCodeTest.cpp \
-		android_security_cts_SeccompDeathTestService.cpp \
 		android_security_cts_SELinuxTest.cpp \
 		android_security_cts_MMapExecutableTest.cpp \
 		android_security_cts_AudioPolicyBinderTest.cpp
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index 424dbaf..1051a82 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -22,7 +22,6 @@
 extern int register_android_security_cts_LinuxRngTest(JNIEnv*);
 extern int register_android_security_cts_NativeCodeTest(JNIEnv*);
 extern int register_android_security_cts_LoadEffectLibraryTest(JNIEnv*);
-extern int register_android_security_cts_SeccompDeathTestService(JNIEnv*);
 extern int register_android_security_cts_SELinuxTest(JNIEnv*);
 extern int register_android_security_cts_MMapExecutableTest(JNIEnv* env);
 extern int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env);
@@ -50,10 +49,6 @@
         return JNI_ERR;
     }
 
-    if (register_android_security_cts_SeccompDeathTestService(env)) {
-        return JNI_ERR;
-    }
-
     if (register_android_security_cts_SELinuxTest(env)) {
         return JNI_ERR;
     }
diff --git a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
index 00765c6..350309b 100644
--- a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
@@ -34,6 +34,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <linux/sysctl.h>
+#include <arpa/inet.h>
 
 /*
  * Returns true iff this device is vulnerable to CVE-2013-2094.
@@ -227,6 +228,28 @@
     return !vulnerable;
 }
 
+static jboolean android_security_cts_NativeCodeTest_doPingPongRootTest(JNIEnv*, jobject)
+{
+    int icmp_sock;
+    struct sockaddr sock_addr;
+
+    memset(&sock_addr, 0, sizeof(sock_addr));
+    icmp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+    sock_addr.sa_family = AF_INET;
+
+    /* first connect */
+    connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+    /* disconnect */
+    sock_addr.sa_family = AF_UNSPEC;
+    connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+    /* second disconnect -> crash */
+    sock_addr.sa_family = AF_UNSPEC;
+    connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+    return true;
+}
 
 static JNINativeMethod gMethods[] = {
     {  "doPerfEventTest", "()Z",
@@ -241,6 +264,8 @@
             (void *) android_security_cts_NativeCodeTest_doFutexTest },
     {  "doNvmapIocFromIdTest", "()Z",
             (void *) android_security_cts_NativeCodeTest_doNvmapIocFromIdTest },
+    {  "doPingPongRootTest", "()Z",
+            (void *) android_security_cts_NativeCodeTest_doPingPongRootTest },
 };
 
 int register_android_security_cts_NativeCodeTest(JNIEnv* env)
@@ -249,3 +274,4 @@
     return env->RegisterNatives(clazz, gMethods,
             sizeof(gMethods) / sizeof(JNINativeMethod));
 }
+
diff --git a/tests/tests/security/jni/android_security_cts_SeccompDeathTestService.cpp b/tests/tests/security/jni/android_security_cts_SeccompDeathTestService.cpp
deleted file mode 100644
index eb32521..0000000
--- a/tests/tests/security/jni/android_security_cts_SeccompDeathTestService.cpp
+++ /dev/null
@@ -1,34 +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.
- */
-
-#include <jni.h>
-#include <signal.h>
-#include <unistd.h>
-
-void android_security_cts_SeccompDeathTestService_testSigSysSelf(JNIEnv* env, jobject thiz)
-{
-    kill(getpid(), SIGSYS);
-}
-
-static JNINativeMethod methods[] = {
-    { "testSigSysSelf", "()V",
-        (void *)android_security_cts_SeccompDeathTestService_testSigSysSelf }
-};
-
-int register_android_security_cts_SeccompDeathTestService(JNIEnv* env) {
-    jclass clazz = env->FindClass("android/security/cts/SeccompDeathTestService");
-    return env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(JNINativeMethod));
-}
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index a2f8c09..ab41b4f 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -58,6 +58,12 @@
                    doNvmapIocFromIdTest());
     }
 
+    public void testPingPongRoot() throws Exception {
+        assertTrue("Device is vulnerable to CVE-2015-3636, a vulnerability in the ping "
+                   + "socket implementation. Please apply the security patch at "
+                   + "https://github.com/torvalds/linux/commit/a134f083e79f",
+                   doPingPongRootTest());
+    }
     /**
      * Returns true iff this device is vulnerable to CVE-2013-2094.
      * A patch for CVE-2013-2094 can be found at
@@ -120,4 +126,18 @@
      * false if the device is vulnerable.
      */
     private static native boolean doCVE20141710Test();
+
+    /**
+     * CVE-2015-3636
+     *
+     * Returns true if the patch is applied, crashes the system otherwise.
+     *
+     * Detects if the following patch is present.
+     * https://github.com/torvalds/linux/commit/a134f083e79f
+     *
+     * Credit: Wen Xu and wushi of KeenTeam.
+     * http://seclists.org/oss-sec/2015/q2/333
+     */
+    private static native boolean doPingPongRootTest();
+
 }
diff --git a/tests/tests/security/src/android/security/cts/SeccompBpfTest.java b/tests/tests/security/src/android/security/cts/SeccompBpfTest.java
deleted file mode 100644
index b7d8f2e..0000000
--- a/tests/tests/security/src/android/security/cts/SeccompBpfTest.java
+++ /dev/null
@@ -1,207 +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.security.cts;
-
-import android.test.AndroidTestCase;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * Test for seccomp-bpf sandboxing technology. This makes use of the
- * SeccompDeathTestService to run sandboxing death tests out-of-process.
- */
-public class SeccompBpfTest extends AndroidTestCase implements ServiceConnection,
-       IBinder.DeathRecipient {
-    static final String TAG = "SeccompBpfTest";
-
-    /**
-     * Message sent from the SeccompDeathTestService before it runs a test.
-     */
-    static final int MSG_TEST_STARTED = 1;
-    /**
-     * Message sent from the SeccompDeathTestService after a test exits cleanly.
-     */
-    static final int MSG_TEST_ENDED_CLEAN = 2;
-
-    /**
-     * Dedicated thread used to receive messages from the SeccompDeathTestService.
-     */
-    final private HandlerThread mHandlerThread = new HandlerThread("SeccompBpfTest handler");
-    /**
-     * Messenger that runs on mHandlerThread.
-     */
-    private Messenger mMessenger;
-
-    /**
-     * Condition that blocks the test/instrumentation thread that runs the
-     * test cases, while the SeccompDeathTestService runs the test out-of-process.
-     */
-    final private ConditionVariable mCondition = new ConditionVariable();
-
-    /**
-     * The SeccompDeathTestService number to run.
-     */
-    private int mTestNumber = -1;
-
-    /**
-     * If the test has started.
-     */
-    private boolean mTestStarted = false;
-    /**
-     * If the test ended (either cleanly or with death).
-     */
-    private boolean mTestEnded = false;
-    /**
-     * If the test ended cleanly or died.
-     */
-    private boolean mTestDied = false;
-
-    public void testDeathTest() {
-        runDeathTest(SeccompDeathTestService.TEST_DEATH_TEST);
-        assertTrue(mTestDied);
-    }
-
-    public void testCleanTest() {
-        runDeathTest(SeccompDeathTestService.TEST_CLEAN_TEST);
-        assertFalse(mTestDied);
-    }
-
-    public void testSigSysSelf() {
-        runDeathTest(SeccompDeathTestService.TEST_SIGSYS_SELF);
-        assertTrue(mTestDied);
-    }
-
-    /**
-     * Runs a death test by its test number, which needs to match a value in
-     * SeccompDeathTestService.
-     *
-     * This blocks until the completion of the test, after which the test body
-     * can use mTestEnded/mTestDied to see if the test died.
-     */
-    public void runDeathTest(final int testNumber) {
-        mTestStarted = false;
-        mTestEnded = false;
-        mTestDied = false;
-
-        mTestNumber = testNumber;
-
-        Log.d(TAG, "Starting runDeathTest");
-        launchDeathTestService();
-        mCondition.block();
-
-        assertTrue(mTestStarted);
-        assertTrue(mTestEnded);
-    }
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        mHandlerThread.start();
-        mMessenger = new Messenger(new Handler(mHandlerThread.getLooper()) {
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                    case MSG_TEST_STARTED:
-                        onTestStarted();
-                        break;
-                    case MSG_TEST_ENDED_CLEAN:
-                        onTestEnded(false);
-                        break;
-                    default:
-                        super.handleMessage(msg);
-                }
-            }
-        });
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        try {
-            mHandlerThread.quitSafely();
-        } finally {
-            super.tearDown();
-        }
-    }
-
-    private void launchDeathTestService() {
-        Log.d(TAG, "launchDeathTestService");
-        mCondition.close();
-
-        Intent intent = new Intent();
-        intent.setComponent(new ComponentName("com.android.cts.security", "android.security.cts.SeccompDeathTestService"));
-
-        if (!getContext().bindService(intent, this, Context.BIND_AUTO_CREATE)) {
-            mCondition.open();
-            fail("Failed to start DeathTestService");
-        }
-    }
-
-    @Override
-    public void onServiceConnected(ComponentName name, IBinder service) {
-        Log.d(TAG, "onServiceConnected");
-
-        Messenger remoteMessenger = new Messenger(service);
-        Message msg = Message.obtain(null, SeccompDeathTestService.MSG_RUN_TEST);
-        msg.getData().putBinder(SeccompDeathTestService.REPLY_BINDER_NAME, mMessenger.getBinder());
-        msg.getData().putInt(SeccompDeathTestService.RUN_TEST_IDENTIFIER, mTestNumber);
-
-        try {
-            service.linkToDeath(this, 0);
-            remoteMessenger.send(msg);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error setting up SeccompDeathTestService: " + e.getMessage());
-        }
-        Log.d(TAG, "Send MSG_TEST_START");
-    }
-
-    private void onTestStarted() {
-        Log.d(TAG, "onTestStarted");
-        mTestStarted = true;
-    }
-
-    @Override
-    public void onServiceDisconnected(ComponentName name) {
-        Log.d(TAG, "onServiceDisconnected");
-    }
-
-    @Override
-    public void binderDied() {
-        Log.d(TAG, "binderDied");
-        if (mTestEnded)
-            return;
-        onTestEnded(true);
-    }
-
-    private void onTestEnded(boolean died) {
-        Log.d(TAG, "onTestEnded, died=" + died);
-        mTestEnded = true;
-        mTestDied = died;
-        getContext().unbindService(this);
-        mCondition.open();
-    }
-}
diff --git a/tests/tests/security/src/android/security/cts/SeccompDeathTestService.java b/tests/tests/security/src/android/security/cts/SeccompDeathTestService.java
deleted file mode 100644
index c78ea35..0000000
--- a/tests/tests/security/src/android/security/cts/SeccompDeathTestService.java
+++ /dev/null
@@ -1,122 +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.security.cts;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * Service used to run tests for seccomp-bpf sandboxing. Since sandbox violations
- * result in process termination, they cannot be run from within the test case
- * itself. The SeccompBpfTest starts this service to run code out-of-process and
- * then observes when the Binder channel dies. If the test does not die, the
- * service reports back to the test that it exited cleanly.
- */
-public class SeccompDeathTestService extends Service {
-    static final String TAG = SeccompBpfTest.TAG;
-
-    static {
-        System.loadLibrary("ctssecurity_jni");
-    }
-
-    /**
-     * Message sent from SeccompBpfTest to run a test.
-     */
-    final static int MSG_RUN_TEST = 100;
-    /**
-     * In MSG_RUN_TEST, the test number to run.
-     */
-    final static String RUN_TEST_IDENTIFIER = "android.security.cts.SeccompDeathTestService.testID";
-    /**
-     * In MSG_RUN_TEST, the Binder on which to report clean death.
-     */
-    static final String REPLY_BINDER_NAME = "android.security.cts.SeccompBpfTest";
-
-    // Test numbers that map to test methods in this service.
-    final static int TEST_DEATH_TEST = 1;
-    final static int TEST_CLEAN_TEST = 2;
-    final static int TEST_SIGSYS_SELF = 3;
-
-    final private Messenger mMessenger = new Messenger(new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_RUN_TEST:
-                    runTest(msg);
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    });
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        Log.d(TAG, "onBind");
-        return mMessenger.getBinder();
-    }
-
-    private void runTest(Message msg) {
-        Log.d(TAG, "runTest");
-        IBinder harnessBinder = msg.getData().getBinder(REPLY_BINDER_NAME);
-        Messenger harness = new Messenger(harnessBinder);
-
-        try {
-            Log.d(TAG, "Send MSG_TEST_STARTED");
-            harness.send(Message.obtain(null, SeccompBpfTest.MSG_TEST_STARTED));
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to MSG_TEST_STARTED: " + e.getMessage());
-        }
-
-        demuxTest(msg.getData().getInt(RUN_TEST_IDENTIFIER));
-
-        try {
-            Log.d(TAG, "Send MSG_TEST_ENDED_CLEAN");
-            harness.send(Message.obtain(null, SeccompBpfTest.MSG_TEST_ENDED_CLEAN));
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to MSG_TEST_ENDED_CLEAN: " + e.getMessage());
-        }
-    }
-
-    private void demuxTest(int testNumber) {
-        switch (testNumber) {
-            case TEST_DEATH_TEST:
-                testDeath();
-                break;
-            case TEST_CLEAN_TEST:
-                break;
-            case TEST_SIGSYS_SELF:
-                testSigSysSelf();
-                break;
-            default:
-                throw new RuntimeException("Unknown test number " + testNumber);
-        }
-    }
-
-    public void testDeath() {
-        String s = null;
-        s.hashCode();
-    }
-
-    public native void testSigSysSelf();
-}
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index a569058..61fe2e9 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -31,6 +31,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
 import android.telecom.cts.MockConnectionService.ConnectionServiceCallbacks;
 import android.telecom.cts.MockInCallService.InCallServiceCallbacks;
 import android.test.InstrumentationTestCase;
@@ -52,7 +53,8 @@
             TEST_PHONE_ACCOUNT_HANDLE, LABEL)
             .setAddress(Uri.parse("tel:555-TEST"))
             .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
-            .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+            .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
+                    PhoneAccount.CAPABILITY_VIDEO_CALLING)
             .setHighlightColor(Color.RED)
             .setShortDescription(LABEL)
             .setSupportedUriSchemes(Arrays.asList("tel"))
@@ -84,6 +86,7 @@
     @Override
     protected void tearDown() throws Exception {
         if (shouldTestTelecom(mContext)) {
+            cleanupCalls();
             if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
                 TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
             }
@@ -160,9 +163,27 @@
     /**
      *  Puts Telecom in a state where there is an active call provided by the
      *  {@link MockConnectionService} which can be tested.
+     *
+     *  @param videoState the video state of the call.
+     */
+    void placeAndVerifyCall(int videoState) {
+        placeAndVerifyCall(null, videoState);
+    }
+
+    /**
+     *  Puts Telecom in a state where there is an active call provided by the
+     *  {@link MockConnectionService} which can be tested.
      */
     void placeAndVerifyCall(Bundle extras) {
-        placeNewCallWithPhoneAccount(extras);
+        placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    /**
+     *  Puts Telecom in a state where there is an active call provided by the
+     *  {@link MockConnectionService} which can be tested.
+     */
+    void placeAndVerifyCall(Bundle extras, int videoState) {
+        placeNewCallWithPhoneAccount(extras, videoState);
 
         try {
             if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
@@ -221,34 +242,28 @@
     }
 
     /**
-     * Disconnect the created test call, verify that Telecom has cleared all calls and has
-     * unbound from the {@link ConnectionService}.
+     * Disconnect the created test call and verify that Telecom has cleared all calls.
      */
-    void cleanupAndVerifyUnbind() {
+    void cleanupCalls() {
         if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
-            mInCallCallbacks.prepareForUnbind();
-
             mInCallCallbacks.getService().disconnectLastCall();
             assertNumCalls(mInCallCallbacks.getService(), 0);
-
-            try {
-                if (!mInCallCallbacks.unbindLock.tryAcquire(3, TimeUnit.SECONDS)) {
-                    fail("Telecom did not unbind from InCallService after all calls removed.");
-                }
-            } catch (InterruptedException e) {
-                Log.i(TAG, "Test interrupted!");
-            }
         }
     }
 
     /**
      * Place a new outgoing call via the {@link MockConnectionService}
      */
-    private void placeNewCallWithPhoneAccount(Bundle extras) {
+    private void placeNewCallWithPhoneAccount(Bundle extras, int videoState) {
         if (extras == null) {
             extras = new Bundle();
         }
         extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEST_PHONE_ACCOUNT_HANDLE);
+
+        if (!VideoProfile.isAudioOnly(videoState)) {
+            extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
+        }
+
         mTelecomManager.placeCall(getTestNumber(), extras);
     }
 
@@ -416,8 +431,33 @@
         assertEquals(description, condition.expected(), condition.actual());
     }
 
-    private interface Condition {
+    /**
+     * Performs some work, and waits for the condition to be met.  If the condition is not met in
+     * each step of the loop, the work is performed again.
+     *
+     * @param work The work to perform.
+     * @param condition The condition.
+     * @param timeout The timeout.
+     * @param description Description of the work being performed.
+     */
+    void doWorkAndWaitUntilConditionIsTrueOrTimeout(Work work, Condition condition, long timeout,
+            String description) {
+        final long start = System.currentTimeMillis();
+        work.doWork();
+        while (!condition.expected().equals(condition.actual())
+                && System.currentTimeMillis() - start < timeout) {
+            sleep(50);
+            work.doWork();
+        }
+        assertEquals(description, condition.expected(), condition.actual());
+    }
+
+    protected interface Condition {
         Object expected();
         Object actual();
     }
+
+    protected interface Work {
+        void doWork();
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallTest.java b/tests/tests/telecom/src/android/telecom/cts/CallTest.java
new file mode 100644
index 0000000..efbb69f
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/CallTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.telecom.cts;
+
+import static android.telecom.Call.Details.*;
+
+import android.telecom.Call;
+import android.test.AndroidTestCase;
+
+public class CallTest extends AndroidTestCase {
+
+    public void testCapabilitiesCan() {
+        assertTrue(Call.Details.can(CAPABILITY_HOLD, CAPABILITY_HOLD));
+        assertTrue(Call.Details.can(CAPABILITY_MUTE, CAPABILITY_MUTE));
+        assertTrue(Call.Details.can(CAPABILITY_HOLD | CAPABILITY_DISCONNECT_FROM_CONFERENCE,
+                CAPABILITY_HOLD));
+        assertTrue(Call.Details.can(CAPABILITY_MERGE_CONFERENCE
+                | CAPABILITY_DISCONNECT_FROM_CONFERENCE | CAPABILITY_MUTE,
+                CAPABILITY_MUTE));
+
+        assertFalse(Call.Details.can(CAPABILITY_MUTE, CAPABILITY_HOLD));
+        assertFalse(Call.Details.can(CAPABILITY_MERGE_CONFERENCE
+                | CAPABILITY_DISCONNECT_FROM_CONFERENCE | CAPABILITY_MUTE,
+                CAPABILITY_HOLD));
+    }
+
+    public void testCapabilitiesToString() {
+        assertEquals("[Capabilities: CAPABILITY_HOLD]",
+                Call.Details.capabilitiesToString(CAPABILITY_HOLD));
+        assertEquals("[Capabilities: CAPABILITY_SUPPORT_HOLD]",
+                Call.Details.capabilitiesToString(CAPABILITY_SUPPORT_HOLD));
+        assertEquals("[Capabilities: CAPABILITY_MERGE_CONFERENCE]",
+                Call.Details.capabilitiesToString(CAPABILITY_MERGE_CONFERENCE));
+        assertEquals("[Capabilities: CAPABILITY_SWAP_CONFERENCE]",
+                Call.Details.capabilitiesToString(CAPABILITY_SWAP_CONFERENCE));
+        assertEquals("[Capabilities: CAPABILITY_RESPOND_VIA_TEXT]",
+                Call.Details.capabilitiesToString(CAPABILITY_RESPOND_VIA_TEXT));
+        assertEquals("[Capabilities: CAPABILITY_MUTE]",
+                Call.Details.capabilitiesToString(CAPABILITY_MUTE));
+        assertEquals("[Capabilities: CAPABILITY_MANAGE_CONFERENCE]",
+                Call.Details.capabilitiesToString(CAPABILITY_MANAGE_CONFERENCE));
+
+        final int capabilities = CAPABILITY_SUPPORTS_VT_LOCAL_RX
+                | CAPABILITY_SUPPORTS_VT_LOCAL_TX
+                | CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL
+                | CAPABILITY_SUPPORTS_VT_REMOTE_RX
+                | CAPABILITY_SUPPORTS_VT_REMOTE_TX
+                | CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL
+                | CAPABILITY_CAN_PAUSE_VIDEO;
+        assertEquals("[Capabilities: "
+                + "CAPABILITY_SUPPORTS_VT_LOCAL_RX "
+                + "CAPABILITY_SUPPORTS_VT_LOCAL_TX "
+                + "CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL "
+                + "CAPABILITY_SUPPORTS_VT_REMOTE_RX "
+                + "CAPABILITY_SUPPORTS_VT_REMOTE_TX "
+                + "CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL "
+                + "CAPABILITY_CAN_PAUSE_VIDEO"
+                + "]",
+                Call.Details.capabilitiesToString(capabilities));
+    }
+
+    public void testHasProperty() {
+        assertTrue(Call.Details.hasProperty(PROPERTY_WIFI, PROPERTY_WIFI));
+        assertTrue(Call.Details.hasProperty(PROPERTY_HIGH_DEF_AUDIO, PROPERTY_HIGH_DEF_AUDIO));
+        assertTrue(Call.Details.hasProperty(PROPERTY_HIGH_DEF_AUDIO | PROPERTY_CONFERENCE
+                | PROPERTY_WIFI, PROPERTY_CONFERENCE));
+
+        assertFalse(Call.Details.hasProperty(PROPERTY_WIFI, PROPERTY_CONFERENCE));
+        assertFalse(Call.Details.hasProperty(PROPERTY_HIGH_DEF_AUDIO | PROPERTY_CONFERENCE
+                | PROPERTY_WIFI, PROPERTY_GENERIC_CONFERENCE));
+    }
+
+    public void testPropertiesToString() {
+        assertEquals("[Properties: PROPERTY_CONFERENCE]",
+                Call.Details.propertiesToString(PROPERTY_CONFERENCE));
+        assertEquals("[Properties: PROPERTY_GENERIC_CONFERENCE]",
+                Call.Details.propertiesToString(PROPERTY_GENERIC_CONFERENCE));
+        assertEquals("[Properties: PROPERTY_EMERGENCY_CALLBACK_MODE]",
+                Call.Details.propertiesToString(PROPERTY_EMERGENCY_CALLBACK_MODE));
+        assertEquals("[Properties: PROPERTY_WIFI]",
+                Call.Details.propertiesToString(PROPERTY_WIFI));
+        assertEquals("[Properties: PROPERTY_HIGH_DEF_AUDIO]",
+                Call.Details.propertiesToString(PROPERTY_HIGH_DEF_AUDIO));
+
+        final int properties = PROPERTY_CONFERENCE
+                | PROPERTY_WIFI
+                | PROPERTY_HIGH_DEF_AUDIO;
+        assertEquals("[Properties: "
+                + "PROPERTY_CONFERENCE "
+                + "PROPERTY_WIFI "
+                + "PROPERTY_HIGH_DEF_AUDIO"
+                + "]",
+                Call.Details.propertiesToString(properties));
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java b/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
index 5685bf8..b574a96 100644
--- a/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/DefaultDialerOperationsTest.java
@@ -16,8 +16,10 @@
 
 package android.telecom.cts;
 
+import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.provider.VoicemailContract.Voicemails;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -35,12 +37,23 @@
     private TelecomManager mTelecomManager;
     private PhoneAccountHandle mPhoneAccountHandle;
     private String mPreviousDefaultDialer = null;
+    private String mSystemDialer = null;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mContext = getInstrumentation().getContext();
+
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
         mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
+        // Reset the current dialer to the system dialer, to ensure that we start each test
+        // without being the default dialer.
+        mSystemDialer = TestUtils.getSystemDialer(getInstrumentation());
+        if (!TextUtils.isEmpty(mSystemDialer)) {
+            TestUtils.setDefaultDialer(getInstrumentation(), mSystemDialer);
+        }
         mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
         final List<PhoneAccountHandle> accounts = mTelecomManager.getCallCapablePhoneAccounts();
         if (accounts != null && !accounts.isEmpty()) {
@@ -51,12 +64,26 @@
     @Override
     protected void tearDown() throws Exception {
         if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
+            // Restore the default dialer to whatever the default dialer was before the tests
+            // were started. This may or may not be the system dialer.
             TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
         }
         super.tearDown();
     }
 
-    public void testVoicemailReadWrite_correctlyThrowsSecurityException() throws Exception {
+    public void testGetDefaultDialerPackage() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        assertEquals(mSystemDialer, mTelecomManager.getDefaultDialerPackage());
+        TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+        assertEquals(TestUtils.PACKAGE, mTelecomManager.getDefaultDialerPackage());
+    }
+
+    public void testVoicemailReadWritePermissions() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
         try {
             mContext.getContentResolver().query(Voicemails.CONTENT_URI, null, null, null, null);
             fail("Reading voicemails should throw SecurityException if not default Dialer");
@@ -87,7 +114,10 @@
                 Voicemails._ID + "=999 AND 1=2", null);
     }
 
-    public void testSilenceRinger_correctlyThrowsSecurityException() throws Exception {
+    public void testSilenceRingerPermissions() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
         try {
             mTelecomManager.silenceRinger();
             fail("TelecomManager.silenceRinger should throw SecurityException if not default "
@@ -100,8 +130,11 @@
         mTelecomManager.silenceRinger();
     }
 
-    public void testCancelMissedCallsNotification_correctlyThrowsSecurityException()
+    public void testCancelMissedCallsNotificationPermissions()
             throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
         try {
             mTelecomManager.cancelMissedCallsNotification();
             fail("TelecomManager.cancelMissedCallsNotification should throw SecurityException if "
@@ -114,8 +147,11 @@
         mTelecomManager.cancelMissedCallsNotification();
     }
 
-    public void testHandlePinMmi_correctlyThrowsSecurityException()
+    public void testHandlePinMmPermissions()
             throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
         try {
             mTelecomManager.handleMmi("0");
             fail("TelecomManager.handleMmi should throw SecurityException if not default dialer");
@@ -134,7 +170,10 @@
         mTelecomManager.handleMmi("0", mPhoneAccountHandle);
     }
 
-    public void testGetAdnForPhoneAccount_correctlyThrowsSecurityException() throws Exception {
+    public void testGetAdnForPhoneAccountPermissions() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
         try {
             mTelecomManager.getAdnUriForPhoneAccount(mPhoneAccountHandle);
             fail("TelecomManager.getAdnUriForPhoneAccount should throw SecurityException if "
@@ -146,4 +185,32 @@
         // No exception if the calling package is the default dialer.
         mTelecomManager.getAdnUriForPhoneAccount(mPhoneAccountHandle);
     }
+
+    public void testSetDefaultDialerNoDialIntent_notSupported() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        final PackageManager pm = mContext.getPackageManager();
+        final ComponentName name = new ComponentName(mContext,
+                "android.telecom.cts.MockDialerActivity");
+        try {
+            pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP);
+
+            final String result =
+                    TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+            assertNotSame(result, TestUtils.PACKAGE);
+            assertTrue("Expected failure indicating that this was not an installed dialer app",
+                    result.contains("is not an installed Dialer app"));
+        } finally {
+            pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                    PackageManager.DONT_KILL_APP);
+        }
+
+        // Now that the activity is present again in the package manager, this should succeed.
+        final String result = TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+        assertTrue("Expected success message indicating that " + TestUtils.PACKAGE + " was set as "
+                + "default dialer.", result.contains("set as default dialer"));
+        assertEquals(TestUtils.PACKAGE, TestUtils.getDefaultDialer(getInstrumentation()));
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index c4ec5c4..97a9a53 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -23,45 +23,36 @@
 import android.telecom.Connection;
 import android.telecom.ConnectionService;
 import android.telecom.InCallService;
+import android.telecom.VideoProfile;
 
 /**
  * Extended suite of tests that use {@link MockConnectionService} and {@link MockInCallService} to
  * verify the functionality of the Telecom service.
  */
 public class ExtendedInCallServiceTest extends BaseTelecomTestWithMockServices {
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        if (shouldTestTelecom(mContext)) {
-            placeAndVerifyCall();
-            verifyConnectionForOutgoingCall();
-        }
-    }
 
-    @Override
-    protected void tearDown() throws Exception {
-        if (shouldTestTelecom(mContext)) {
-            cleanupAndVerifyUnbind();
-        }
-        super.tearDown();
-    }
-
-    public void testWithMockConnection_AddNewOutgoingCallAndThenDisconnect() {
+    public void testAddNewOutgoingCallAndThenDisconnect() {
         if (!shouldTestTelecom(mContext)) {
             return;
         }
 
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+
         final MockInCallService inCallService = mInCallCallbacks.getService();
         inCallService.disconnectLastCall();
 
         assertNumCalls(inCallService, 0);
     }
 
-    public void testWithMockConnection_MuteAndUnmutePhone() {
+    public void testMuteAndUnmutePhone() {
         if (!shouldTestTelecom(mContext)) {
             return;
         }
 
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+
         final MockInCallService inCallService = mInCallCallbacks.getService();
 
         final Call call = inCallService.getLastCall();
@@ -81,11 +72,14 @@
         assertMuteState(inCallService, false);
     }
 
-    public void testWithMockConnection_SwitchAudioRoutes() {
+    public void testSwitchAudioRoutes() {
         if (!shouldTestTelecom(mContext)) {
             return;
         }
 
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+
         final MockInCallService inCallService = mInCallCallbacks.getService();
         final MockConnection connection = mConnectionCallbacks.outgoingConnection;
 
@@ -107,12 +101,18 @@
     /**
      * Tests that DTMF Tones are sent from the {@link InCallService} to the
      * {@link ConnectionService} in the correct sequence.
+     *
+     * @see {@link Call#playDtmfTone(char)}
+     * @see {@link Call#stopDtmfTone()}
      */
-    public void testWithMockConnection_DtmfTones() {
+    public void testPlayAndStopDtmfTones() {
         if (!shouldTestTelecom(mContext)) {
             return;
         }
 
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+
         final MockInCallService inCallService = mInCallCallbacks.getService();
         final MockConnection connection = mConnectionCallbacks.outgoingConnection;
 
@@ -127,17 +127,26 @@
         call.playDtmfTone('2');
         assertDtmfString(connection, "12");
 
+        call.stopDtmfTone();
+        assertDtmfString(connection, "12.");
+
         call.playDtmfTone('3');
         call.playDtmfTone('4');
         call.playDtmfTone('5');
-        assertDtmfString(connection, "12345");
+        assertDtmfString(connection, "12.345");
+
+        call.stopDtmfTone();
+        assertDtmfString(connection, "12.345.");
     }
 
-    public void testWithMockConnection_HoldAndUnholdCall() {
+    public void testHoldAndUnholdCall() {
         if (!shouldTestTelecom(mContext)) {
             return;
         }
 
+        placeAndVerifyCall();
+        verifyConnectionForOutgoingCall();
+
         final MockInCallService inCallService = mInCallCallbacks.getService();
         final MockConnection connection = mConnectionCallbacks.outgoingConnection;
 
@@ -153,4 +162,60 @@
         assertCallState(call, Call.STATE_ACTIVE);
         assertEquals(Connection.STATE_ACTIVE, connection.getState());
     }
+
+    public void testAnswerIncomingCallAudioOnly() {
+        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        verifyConnectionForIncomingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final MockConnection connection = mConnectionCallbacks.incomingConnection;
+
+        final Call call = inCallService.getLastCall();
+
+        assertCallState(call, Call.STATE_RINGING);
+        assertConnectionState(connection, Connection.STATE_RINGING);
+
+        call.answer(VideoProfile.STATE_AUDIO_ONLY);
+
+        assertCallState(call, Call.STATE_ACTIVE);
+        assertConnectionState(connection, Connection.STATE_ACTIVE);
+    }
+
+    public void testAnswerIncomingCallAsVideo_SendsCorrectVideoState() {
+        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        verifyConnectionForIncomingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final MockConnection connection = mConnectionCallbacks.incomingConnection;
+
+        final Call call = inCallService.getLastCall();
+
+        assertCallState(call, Call.STATE_RINGING);
+        assertConnectionState(connection, Connection.STATE_RINGING);
+
+        call.answer(VideoProfile.STATE_BIDIRECTIONAL);
+
+        assertCallState(call, Call.STATE_ACTIVE);
+        assertConnectionState(connection, Connection.STATE_ACTIVE);
+        assertEquals("Connection did not receive VideoState for answered call",
+                VideoProfile.STATE_BIDIRECTIONAL, connection.videoState);
+    }
+
+    public void testRejectIncomingCall() {
+        addAndVerifyNewIncomingCall(getTestNumber(), null);
+        verifyConnectionForIncomingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final MockConnection connection = mConnectionCallbacks.incomingConnection;
+
+        final Call call = inCallService.getLastCall();
+
+        assertCallState(call, Call.STATE_RINGING);
+        assertConnectionState(connection, Connection.STATE_RINGING);
+
+        call.reject(false, null);
+
+        assertCallState(call, Call.STATE_DISCONNECTED);
+        assertConnectionState(connection, Connection.STATE_DISCONNECTED);
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
index 67a0fe0..bf9d730 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
@@ -20,6 +20,7 @@
 import android.telecom.CallAudioState;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
+import android.telecom.VideoProfile;
 import android.util.Log;
 
 /**
@@ -31,10 +32,18 @@
     private CallAudioState mCallAudioState =
             new CallAudioState(false, CallAudioState.ROUTE_EARPIECE, ROUTE_EARPIECE | ROUTE_SPEAKER);
     private int mState = STATE_NEW;
+    public int videoState = VideoProfile.STATE_AUDIO_ONLY;
     private String mDtmfString = "";
+    private MockVideoProvider mMockVideoProvider;
 
     @Override
     public void onAnswer() {
+        onAnswer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    @Override
+    public void onAnswer(int videoState) {
+        this.videoState = videoState;
         setActive();
     }
 
@@ -69,6 +78,11 @@
     }
 
     @Override
+    public void onStopDtmfTone() {
+        mDtmfString += ".";
+    }
+
+    @Override
     public void onCallAudioStateChanged(CallAudioState state) {
         mCallAudioState = state;
     }
@@ -89,4 +103,45 @@
     public String getDtmfString() {
         return mDtmfString;
     }
+
+    /**
+     * Creates a mock video provider for this connection.
+     */
+    public void createMockVideoProvider() {
+        final MockVideoProvider mockVideoProvider = new MockVideoProvider(this);
+        mMockVideoProvider = mockVideoProvider;
+        setVideoProvider(mockVideoProvider);
+    }
+
+    public void sendMockVideoQuality(int videoQuality) {
+        if (mMockVideoProvider == null) {
+            return;
+        }
+        mMockVideoProvider.sendMockVideoQuality(videoQuality);
+    }
+
+    public void sendMockCallSessionEvent(int event) {
+        if (mMockVideoProvider == null) {
+            return;
+        }
+        mMockVideoProvider.sendMockCallSessionEvent(event);
+    }
+
+    public void sendMockPeerWidth(int width) {
+        if (mMockVideoProvider == null) {
+            return;
+        }
+        mMockVideoProvider.sendMockPeerWidth(width);
+    }
+
+    public void sendMockSessionModifyRequest(VideoProfile request) {
+        if (mMockVideoProvider == null) {
+            return;
+        }
+        mMockVideoProvider.sendMockSessionModifyRequest(request);
+    }
+
+    public MockVideoProvider getMockVideoProvider() {
+        return mMockVideoProvider;
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
index 2b54f32..6dbea7c 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnectionService.java
@@ -19,13 +19,30 @@
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
 import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 
 import java.util.concurrent.Semaphore;
 
+/**
+ * This is the official ConnectionService for Telecom's CTS App. Since telecom requires that a
+ * CS be registered in the AndroidManifest.xml file, we have to have a single implementation
+ * of a CS and this is it. To test specific CS behavior, tests will implement their own CS and
+ * tell MockConnectionService to forward any method invocations to that test's implementation.
+ * This is set up using {@link #setUp} and should be cleaned up before the end of the test using
+ * {@link #tearDown}.
+ */
 public class MockConnectionService extends ConnectionService {
     private static ConnectionServiceCallbacks sCallbacks;
+    private static ConnectionService sConnectionService;
+
+    /**
+     * Used to control whether the {@link MockVideoProvider} will be created when connections are
+     * created.  Used by {@link VideoCallTest#testVideoCallDelayProvider()} to test scenario where
+     * the {@link MockVideoProvider} is not created immediately when the Connection is created.
+     */
+    private static boolean sCreateVideoProvider = true;
     private static Object sLock = new Object();
 
     public static abstract class ConnectionServiceCallbacks {
@@ -48,11 +65,40 @@
         }
     }
 
+    public static PhoneAccount setUp(PhoneAccount phoneAccount, ConnectionService connectionService)
+            throws Exception {
+        synchronized(sLock) {
+            if (sConnectionService != null) {
+                throw new Exception("Mock ConnectionService exists.  Failed to call tearDown().");
+            }
+            sConnectionService = connectionService;
+            return phoneAccount;
+        }
+    }
+
+    public static void tearDown() {
+        synchronized(sLock) {
+            sConnectionService = null;
+        }
+    }
+
     @Override
     public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
+        synchronized(sLock) {
+            if (sConnectionService != null) {
+                return sConnectionService.onCreateOutgoingConnection(
+                        connectionManagerPhoneAccount, request);
+            }
+        }
         final MockConnection connection = new MockConnection();
         connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
+        if (sCreateVideoProvider) {
+            connection.createMockVideoProvider();
+        } else {
+            sCreateVideoProvider = true;
+        }
+        connection.setVideoState(request.getVideoState());
 
         final ConnectionServiceCallbacks callbacks = getCallbacks();
         if (callbacks != null) {
@@ -66,8 +112,17 @@
     @Override
     public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
+        synchronized(sLock) {
+            if (sConnectionService != null) {
+                return sConnectionService.onCreateIncomingConnection(
+                        connectionManagerPhoneAccount, request);
+            }
+        }
+
         final MockConnection connection = new MockConnection();
         connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED);
+        connection.createMockVideoProvider();
+        connection.setVideoState(request.getVideoState());
 
         final ConnectionServiceCallbacks callbacks = getCallbacks();
         if (callbacks != null) {
@@ -92,4 +147,8 @@
             return sCallbacks;
         }
     }
+
+    public static void setCreateVideoProvider(boolean createVideoProvider) {
+        sCreateVideoProvider = createVideoProvider;
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index 3c48ddd..f45d4c3 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -17,22 +17,27 @@
 package android.telecom.cts;
 
 import android.content.Intent;
+import android.os.Binder;
 import android.telecom.Call;
 import android.telecom.InCallService;
+import android.util.ArrayMap;
+import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Map;
 import java.util.concurrent.Semaphore;
 
 public class MockInCallService extends InCallService {
     private ArrayList<Call> mCalls = new ArrayList<>();
     private static InCallServiceCallbacks sCallbacks;
+    private Map<Call, MockVideoCallCallback> mVideoCallCallbacks =
+            new ArrayMap<Call, MockVideoCallCallback>();
 
     private static final Object sLock = new Object();
 
     public static abstract class InCallServiceCallbacks {
         private MockInCallService mService;
         public Semaphore lock = new Semaphore(0);
-        public Semaphore unbindLock = null;
 
         public void onCallAdded(Call call, int numCalls) {};
         public void onCallRemoved(Call call, int numCalls) {};
@@ -45,10 +50,6 @@
         final public void setService(MockInCallService service) {
             mService = service;
         }
-
-        final public void prepareForUnbind() {
-            unbindLock = new Semaphore(0);
-        }
     }
 
     private Call.Callback mCallCallback = new Call.Callback() {
@@ -58,8 +59,25 @@
                 getCallbacks().onCallStateChanged(call, state);
             }
         }
+
+        @Override
+        public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {
+            saveVideoCall(call, videoCall);
+        }
     };
 
+    private void saveVideoCall(Call call, VideoCall videoCall) {
+        if (videoCall != null) {
+            if (!mVideoCallCallbacks.containsKey(call)) {
+                MockVideoCallCallback listener = new MockVideoCallCallback(call);
+                videoCall.registerCallback(listener);
+                mVideoCallCallbacks.put(call, listener);
+            }
+        } else {
+            mVideoCallCallbacks.remove(call);
+        }
+    }
+
     @Override
     public android.os.IBinder onBind(android.content.Intent intent) {
         if (getCallbacks() != null) {
@@ -69,18 +87,15 @@
     }
 
     @Override
-    public boolean onUnbind(Intent intent) {
-        if (getCallbacks() != null && getCallbacks().unbindLock != null) {
-            getCallbacks().unbindLock.release();
-        }
-        return super.onUnbind(intent);
-    }
-
-    @Override
     public void onCallAdded(Call call) {
         if (!mCalls.contains(call)) {
             mCalls.add(call);
             call.registerCallback(mCallCallback);
+
+            VideoCall videoCall = call.getVideoCall();
+            if (videoCall != null) {
+                saveVideoCall(call, videoCall);
+            }
         }
         if (getCallbacks() != null) {
             getCallbacks().onCallAdded(call, mCalls.size());
@@ -92,6 +107,7 @@
         mCalls.remove(call);
         if (getCallbacks() != null) {
             getCallbacks().onCallRemoved(call, mCalls.size());
+            saveVideoCall(call, null /* remove videoCall */);
         }
     }
 
@@ -133,4 +149,23 @@
             return sCallbacks;
         }
     }
+
+    /**
+     * Determines if a video callback has been registered for the passed in call.
+     *
+     * @param call The call.
+     * @return {@code true} if a video callback has been registered.
+     */
+    public boolean isVideoCallbackRegistered(Call call) {
+        return mVideoCallCallbacks.containsKey(call);
+    }
+
+    /**
+     * Retrieves the video callbacks associated with a call.
+     * @param call The call.
+     * @return The {@link MockVideoCallCallback} instance associated with the call.
+     */
+    public MockVideoCallCallback getVideoCallCallback(Call call) {
+        return mVideoCallCallbacks.get(call);
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockVideoCallCallback.java b/tests/tests/telecom/src/android/telecom/cts/MockVideoCallCallback.java
new file mode 100644
index 0000000..07e159c
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/MockVideoCallCallback.java
@@ -0,0 +1,183 @@
+/*
+ * 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.telecom.cts;
+
+import android.telecom.Call;
+import android.telecom.InCallService;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+
+/**
+ * Mock video call Callback class.
+ */
+public class MockVideoCallCallback extends InCallService.VideoCall.Callback {
+    private Call mCall;
+    private CameraCapabilities mCameraCapabilities;
+    private long mDataUsage = MockVideoProvider.DATA_USAGE_UNDEFINED;
+    private int mVideoQuality = MockVideoProvider.VIDEO_QUALITY_UNDEFINED;
+    private int mCallSessionEvent = MockVideoProvider.SESSION_EVENT_UNDEFINED;
+    private int mPeerWidth = MockVideoProvider.PEER_WIDTH_UNDEFINED;
+    private VideoProfile mResponseProfile = null;
+    private VideoProfile mRequestProfile = null;
+
+    public MockVideoCallCallback(Call call) {
+        mCall = call;
+    }
+
+    /**
+     * Store incoming session modify request so tests can inspect it.
+     *
+     * @param videoProfile The requested video profile.
+     */
+    @Override
+    public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
+        mRequestProfile = videoProfile;
+    }
+
+    /**
+     * Store incoming session modify response so tests can inspect it.
+     *
+     * @param status Status of the session modify request.
+     * @param requestedProfile The original request which was sent to the peer device.
+     * @param responseProfile The actual profile changes made by the peer device.
+     */
+    @Override
+    public void onSessionModifyResponseReceived(int status, VideoProfile requestedProfile,
+            VideoProfile responseProfile) {
+        mResponseProfile = responseProfile;
+    }
+
+    /**
+     * Store incoming session event so tests can inspect it.
+     *
+     * @param event The event.
+     */
+    @Override
+    public void onCallSessionEvent(int event) {
+        mCallSessionEvent = event;
+    }
+
+    /**
+     * Store incoming peer dimensions so tests can inspect them.
+     *
+     * @param width  The updated peer video width.
+     * @param height The updated peer video height.
+     */
+    @Override
+    public void onPeerDimensionsChanged(int width, int height) {
+        mPeerWidth = width;
+    }
+
+    /**
+     * Store incoming video quality so tests can inspect them.
+     *
+     * @param videoQuality  The updated peer video quality.  Valid values:
+     *      {@link VideoProfile#QUALITY_HIGH},
+     *      {@link VideoProfile#QUALITY_MEDIUM},
+     *      {@link VideoProfile#QUALITY_LOW},
+     */
+    @Override
+    public void onVideoQualityChanged(int videoQuality) {
+        mVideoQuality = videoQuality;
+    }
+
+    /**
+     * Store incoming call data usage so tests can inspect it.
+     *
+     * @param dataUsage The updated data usage (in bytes).
+     */
+    @Override
+    public void onCallDataUsageChanged(long dataUsage) {
+        mDataUsage = dataUsage;
+    }
+
+    /**
+     * Store incoming camera capabilities so tests can inspect them.
+     *
+     * @param cameraCapabilities The changed camera capabilities.
+     */
+    @Override
+    public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) {
+        mCameraCapabilities = cameraCapabilities;
+    }
+
+    /**
+     * Returns the last received {@link CameraCapabilities}.
+     *
+     * @return The {@link CameraCapabilities}.
+     */
+    public CameraCapabilities getCameraCapabilities() {
+        return mCameraCapabilities;
+    }
+
+    /**
+     * Returns the last received data usage.
+     *
+     * @return The data usage.
+     */
+    public long getDataUsage() {
+        return mDataUsage;
+    }
+
+    /**
+     * Returns the last received video quality.
+     *
+     * @return The video quality.
+     */
+    public int getVideoQuality()
+    {
+        return mVideoQuality;
+    }
+
+    /**
+     * Returns the last received call session event.
+     *
+     * @return The call session event.
+     */
+    public int getCallSessionEvent()
+    {
+        return mCallSessionEvent;
+    }
+
+    /**
+     * Returns the last received peer width.
+     *
+     * @return The call session event.
+     */
+    public int getPeerWidth()
+    {
+        return mPeerWidth;
+    }
+
+    /**
+     * Returns the last received response video profile.
+     *
+     * @return The video profile.
+     */
+    public VideoProfile getResponseProfile() {
+        return mResponseProfile;
+    }
+
+    /**
+     * Returns the last requested video profile.
+     *
+     * @return The video profile.
+     */
+    public VideoProfile getRequestProfile() {
+        return mRequestProfile;
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java b/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
new file mode 100644
index 0000000..8ed422f
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
@@ -0,0 +1,192 @@
+/*
+ * 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.telecom.cts;
+
+import android.net.Uri;
+import android.telecom.Connection;
+import android.telecom.VideoProfile;
+import android.view.Surface;
+
+import android.telecom.Connection.VideoProvider;
+
+/**
+ * Implements a mock video provider implementation.
+ */
+public class MockVideoProvider extends VideoProvider {
+    public static final String CAMERA_NONE = "none";
+    public static final String CAMERA_FRONT = "front";
+    public static final String CAMERA_BACK = "back";
+    public static final int CAMERA_FRONT_DIMENSIONS = 1024;
+    public static final int CAMERA_BACK_DIMENSIONS = 2048;
+    public static final long DATA_USAGE = 1024;
+    public static final long DATA_USAGE_UNDEFINED = -1;
+    public static final int VIDEO_QUALITY_UNDEFINED = -1;
+    public static final int SESSION_EVENT_UNDEFINED = -1;
+    public static final int PEER_WIDTH_UNDEFINED = -1;
+    public static final int DEVICE_ORIENTATION_UNDEFINED = -1;
+    public static final float ZOOM_UNDEFINED = -1.0f;
+
+    private Uri mPauseImageUri;
+    private String mCameraId = CAMERA_NONE;
+    private MockConnection mMockConnection;
+    private int mDeviceOrientation = DEVICE_ORIENTATION_UNDEFINED;
+    private float mZoom = ZOOM_UNDEFINED;
+    private Surface mPreviewSurface = null;
+    private Surface mDisplaySurface = null;
+    private VideoProfile mSessionModifyResponse = null;
+
+    public MockVideoProvider(MockConnection mockConnection) {
+        mMockConnection = mockConnection;
+    }
+
+    @Override
+    public void onSetCamera(String cameraId) {
+        handleCameraChange(cameraId);
+    }
+
+    @Override
+    public void onSetPreviewSurface(Surface surface) {
+        mPreviewSurface = surface;
+    }
+
+    @Override
+    public void onSetDisplaySurface(Surface surface) {
+        mDisplaySurface = surface;
+    }
+
+    @Override
+    public void onSetDeviceOrientation(int rotation) {
+        mDeviceOrientation = rotation;
+    }
+
+    @Override
+    public void onSetZoom(float value) {
+        mZoom = value;
+    }
+
+    /**
+     * Handles a session modification request from the {@link MockInCallService}. Assumes the peer
+     * has accepted the proposed video profile.
+     *
+     * @param fromProfile The video properties prior to the request.
+     * @param toProfile The video properties with the requested changes made.
+     */
+    @Override
+    public void onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+        receiveSessionModifyResponse(Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS,
+                toProfile, toProfile);
+        mMockConnection.setVideoState(toProfile.getVideoState());
+    }
+
+    @Override
+    public void onSendSessionModifyResponse(VideoProfile responseProfile) {
+        mSessionModifyResponse = responseProfile;
+    }
+
+    /**
+     * Responds with the current camera capabilities.
+     */
+    @Override
+    public void onRequestCameraCapabilities() {
+        handleCameraChange(mCameraId);
+    }
+
+    /**
+     * Handles requests to retrieve the connection data usage by returning a fixed usage amount of
+     * {@code 1024} bytes.
+     */
+    @Override
+    public void onRequestConnectionDataUsage() {
+        setCallDataUsage(DATA_USAGE);
+    }
+
+    @Override
+    public void onSetPauseImage(Uri uri) {
+        mPauseImageUri = uri;
+    }
+
+    /**
+     * Handles a change to the current camera selection.  Responds by reporting the capabilities of
+     * the camera.
+     */
+    private void handleCameraChange(String cameraId) {
+        mCameraId = cameraId;
+        if (CAMERA_FRONT.equals(mCameraId)) {
+            changeCameraCapabilities(new VideoProfile.CameraCapabilities(CAMERA_FRONT_DIMENSIONS,
+                    CAMERA_FRONT_DIMENSIONS));
+        } else if (CAMERA_BACK.equals(mCameraId)) {
+            changeCameraCapabilities(new VideoProfile.CameraCapabilities(CAMERA_BACK_DIMENSIONS,
+                    CAMERA_BACK_DIMENSIONS));
+        }
+    }
+
+    /**
+     * Sends a mock video quality value from the provider.
+     *
+     * @param videoQuality The video quality.
+     */
+    public void sendMockVideoQuality(int videoQuality) {
+        changeVideoQuality(videoQuality);
+    }
+
+    /**
+     * Sends a mock call session event from the provider.
+     *
+     * @param event The call session event.
+     */
+    public void sendMockCallSessionEvent(int event) {
+        handleCallSessionEvent(event);
+    }
+
+    /**
+     * Sends a mock peer width from the provider.
+     *
+     * @param width The peer width.
+     */
+    public void sendMockPeerWidth(int width) {
+        changePeerDimensions(width, width);
+    }
+
+    /**
+     * Sends a mock session modify request from the provider.
+     *
+     * @param request The requested profile.
+     */
+    public void sendMockSessionModifyRequest(VideoProfile request) {
+        receiveSessionModifyRequest(request);
+    }
+
+    public int getDeviceOrientation() {
+        return mDeviceOrientation;
+    }
+
+    public float getZoom() {
+        return mZoom;
+    }
+
+    public Surface getPreviewSurface() {
+        return mPreviewSurface;
+    }
+
+    public Surface getDisplaySurface() {
+        return mDisplaySurface;
+    }
+
+    public VideoProfile getSessionModifyResponse() {
+        return mSessionModifyResponse;
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java b/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
new file mode 100644
index 0000000..f0df70a
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.telecom.cts;
+
+import static android.telecom.cts.TestUtils.shouldTestTelecom;
+
+import android.content.Context;
+import android.net.Uri;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+
+/**
+ * Tests that certain numbers make their way through to the connection service.
+ */
+public class NumberDialingTest extends SimpleTelecomTest {
+
+    private Context mContext;
+
+    /**
+     * Amount of time to wait for an asynchronous method invocation to ConnectionService.
+     */
+    private static final int CS_WAIT_MILLIS = 2000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getContext();
+    }
+
+    public void testEndInPound() throws Exception {
+        if (!shouldTestTelecom(mContext)) {
+            return;
+        }
+
+        final Object[] res = new Object[1];
+        Uri address = Uri.fromParts("tel", "*1234#", null);
+
+        PhoneAccount account = setupConnectionService("testEndInPound",
+                new ConnectionService() {
+                    @Override
+                    public Connection onCreateOutgoingConnection(
+                            PhoneAccountHandle connectionManagerPhoneAccount,
+                            ConnectionRequest request) {
+                        res[0] = request.getAddress();
+                        synchronized(res) {
+                            res.notify();
+                        }
+                        return null;  // do not actually place the call.
+                    }
+
+                }, FLAG_REGISTER | FLAG_ENABLE);
+
+        startCallTo(address, account.getAccountHandle());
+        synchronized(res) {
+            res.wait(CS_WAIT_MILLIS);
+        }
+        assertEquals(address, res[0]);
+
+        tearDownConnectionService(account);
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
index 5895267..c12b508 100644
--- a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -27,14 +27,6 @@
  */
 public class OutgoingCallTest extends BaseTelecomTestWithMockServices {
 
-    @Override
-    protected void tearDown() throws Exception {
-        if (shouldTestTelecom(mContext)) {
-            cleanupAndVerifyUnbind();
-        }
-        super.tearDown();
-    }
-
     // TODO: Need to send some commands to the UserManager via adb to do setup
     public void testDisallowOutgoingCallsForSecondaryUser() {
 
diff --git a/tests/tests/telecom/src/android/telecom/cts/SimpleTelecomTest.java b/tests/tests/telecom/src/android/telecom/cts/SimpleTelecomTest.java
new file mode 100644
index 0000000..377a999
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/SimpleTelecomTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.telecom.cts;
+
+import static android.telecom.cts.TestUtils.shouldTestTelecom;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.telecom.Connection;
+import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Scaffolding for a simple telecom test.  Provides helper methods for registers a phone account
+ * and making calls.
+ */
+public class SimpleTelecomTest extends InstrumentationTestCase {
+
+    private Context mContext;
+    private TelecomManager mTelecomManager;
+
+    public static final int FLAG_REGISTER = 0x1;
+    public static final int FLAG_ENABLE = 0x2;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getContext();
+        mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        MockConnectionService.tearDown();
+        super.tearDown();
+    }
+
+    protected PhoneAccount setupConnectionService(
+            String testTag, ConnectionService connectionService, int flags) throws Exception {
+
+        PhoneAccount.Builder builder = new PhoneAccount.Builder(
+                new PhoneAccountHandle(
+                        new ComponentName("com.android.cts.telecom",
+                            "android.telecom.cts.MockConnectionService"),
+                        testTag),
+                "TestPA " + testTag)
+            .setAddress(Uri.fromParts("tel:", "5417705", null))
+            .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+            .setShortDescription("CTS Test Account with ID " + testTag)
+            .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+            .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL);
+
+        // register and enable the phone account
+        PhoneAccount account = builder.build();
+        MockConnectionService.setUp(account, connectionService);
+
+        if ((flags & FLAG_REGISTER) != 0) {
+            mTelecomManager.registerPhoneAccount(account);
+        }
+        if ((flags & FLAG_ENABLE) != 0) {
+            TestUtils.enablePhoneAccount(getInstrumentation(), account.getAccountHandle());
+        }
+
+        return account;
+    }
+
+    protected void tearDownConnectionService(PhoneAccount account) throws Exception {
+        mTelecomManager.unregisterPhoneAccount(account.getAccountHandle());
+    }
+
+    protected void startCallTo(Uri address, PhoneAccountHandle accountHandle) {
+        final Intent intent = new Intent(Intent.ACTION_CALL, address);
+        if (accountHandle != null) {
+            intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
+        }
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index 8cca04c..ddc85a6 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -34,7 +34,9 @@
     static final boolean HAS_TELECOM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
     static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000;
 
-    public static final String PACKAGE = "com.android.cts.telecom";
+    // Non-final to allow modification by tests not in this package (e.g. permission-related
+    // tests in the Telecom2 test package.
+    public static String PACKAGE = "com.android.cts.telecom";
     public static final String COMPONENT = "android.telecom.cts.MockConnectionService";
     public static final String ACCOUNT_ID = "xtstest_CALL_PROVIDER_ID";
 
@@ -44,6 +46,8 @@
 
     private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer";
 
+    private static final String COMMAND_GET_SYSTEM_DIALER = "telecom get-system-dialer";
+
     private static final String COMMAND_ENABLE = "telecom set-phone-account-enabled ";
 
     public static boolean shouldTestTelecom(Context context) {
@@ -54,15 +58,19 @@
         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
     }
 
-    public static void setDefaultDialer(Instrumentation instrumentation, String packageName)
+    public static String setDefaultDialer(Instrumentation instrumentation, String packageName)
             throws Exception {
-        executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName);
+        return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName);
     }
 
     public static String getDefaultDialer(Instrumentation instrumentation) throws Exception {
         return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER);
     }
 
+    public static String getSystemDialer(Instrumentation instrumentation) throws Exception {
+        return executeShellCommand(instrumentation, COMMAND_GET_SYSTEM_DIALER);
+    }
+
     public static void enablePhoneAccount(Instrumentation instrumentation,
             PhoneAccountHandle handle) throws Exception {
         final ComponentName component = handle.getComponentName();
diff --git a/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java b/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java
new file mode 100644
index 0000000..b3ceb4d
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java
@@ -0,0 +1,777 @@
+/*
+ * 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.telecom.cts;
+
+import android.graphics.SurfaceTexture;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telecom.InCallService;
+import android.telecom.VideoProfile;
+import android.util.Log;
+import android.view.Surface;
+import android.view.TextureView;
+
+import static android.telecom.cts.TestUtils.shouldTestTelecom;
+
+/**
+ * Suites of tests that use {@link MockVideoProvider} and {@link MockVideoCallCallback} to verify
+ * the functionality of the video APIs.
+ *
+ * Note: You'll notice the use of {@code work}, and
+ * {@code doWorkAndWaitUntilConditionIsTrueOrTimeout} here.  The problem is the
+ * {@link MockVideoProvider} is running using a Handler.  To get it to emit mock data that is
+ * in sync with the setup operations performed on the handler, we'd need access to its handler.
+ * The handler of the {@link Connection.VideoProvider} is, however, not public.  As a workaround
+ * we will call local methods on the MockVideoProvider.  This means there is a chance the
+ * VideoProvider will emit the data we're interested in before the callbacks (on the handler)
+ * are even set up.  Consequently, the callbacks we're depending on in our test may not get
+ * called.  To compensate we will call the test methods on the provider repeatedly until we
+ * hear back via our callback.  Suboptimal, but it works.
+ */
+public class VideoCallTest extends BaseTelecomTestWithMockServices {
+    /**
+     * Tests ability to start a 2-way video call and retrieve its video state.
+     */
+    public void testMakeTwoWayVideoCall() {
+
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+
+        assertCallState(call, Call.STATE_ACTIVE);
+        assertVideoState(call, VideoProfile.STATE_BIDIRECTIONAL);
+        assertVideoCallbackRegistered(inCallService, call, true);
+    }
+
+    /**
+     * Tests ability to start a 1-way video call and retrieve its video state.
+     */
+    public void testMakeOneWayVideoCall() {
+        placeAndVerifyCall(VideoProfile.STATE_TX_ENABLED);
+        verifyConnectionForOutgoingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+
+        assertVideoState(call, VideoProfile.STATE_TX_ENABLED);
+        assertVideoCallbackRegistered(inCallService, call, true);
+    }
+
+    /**
+     * Tests ability to upgrade an audio-only call to a video call.
+     */
+    public void testUpgradeToVideo() {
+        placeAndVerifyCall(VideoProfile.STATE_AUDIO_ONLY);
+        verifyConnectionForOutgoingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoState(call, VideoProfile.STATE_AUDIO_ONLY);
+        assertVideoCallbackRegistered(inCallService, call, true);
+
+        // Send request to upgrade to video.
+        InCallService.VideoCall videoCall = call.getVideoCall();
+        videoCall.sendSessionModifyRequest(new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL));
+        assertVideoState(call, VideoProfile.STATE_BIDIRECTIONAL);
+        assertResponseVideoProfileReceived(inCallService.getVideoCallCallback(call),
+                VideoProfile.STATE_BIDIRECTIONAL);
+    }
+
+    /**
+     * Tests ability to receive a session modification request.
+     */
+    public void testReceiveSessionModifyRequest() {
+        placeAndVerifyCall(VideoProfile.STATE_AUDIO_ONLY);
+        verifyConnectionForOutgoingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        assertVideoState(call, VideoProfile.STATE_AUDIO_ONLY);
+        assertVideoCallbackRegistered(inCallService, call, true);
+
+        // Have the video profile mock reception of a request.
+        assertRequestVideoProfileReceived(inCallService.getVideoCallCallback(call),
+                VideoProfile.STATE_BIDIRECTIONAL,
+                new Work() {
+                    @Override
+                    public void doWork() {
+                        connection.sendMockSessionModifyRequest(
+                                new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL));
+                    }
+                });
+    }
+
+    /**
+     * Tests ability to send a session modification response.
+     */
+    public void testSendSessionModifyResponse() {
+        placeAndVerifyCall(VideoProfile.STATE_AUDIO_ONLY);
+        verifyConnectionForOutgoingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+        assertVideoState(call, VideoProfile.STATE_AUDIO_ONLY);
+        assertVideoCallbackRegistered(inCallService, call, true);
+
+        InCallService.VideoCall videoCall = call.getVideoCall();
+        videoCall.sendSessionModifyResponse(new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL));
+        assertSessionModifyResponse(mockVideoProvider, VideoProfile.STATE_BIDIRECTIONAL);
+    }
+
+    /**
+     * Tests ability to start a video call, delaying the creation of the provider until after
+     * the call has been initiated (rather than immediately when the call is created).  This more
+     * closely mimics the lifespan of a {@code VideoProvider} instance as it is reasonable to
+     * expect there will be some overhead associated with configuring the camera at the start of
+     * the call.
+     */
+    public void testVideoCallDelayProvider() {
+        // Don't create video provider when call is created initially; we will do this later.
+        try {
+            MockConnectionService.setCreateVideoProvider(false);
+            placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+            verifyConnectionForOutgoingCall();
+
+            final MockInCallService inCallService = mInCallCallbacks.getService();
+            final Call call = inCallService.getLastCall();
+            final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+
+            assertVideoState(call, VideoProfile.STATE_BIDIRECTIONAL);
+            // After initial connection creation there should not be a video provider or callbacks
+            // registered.
+            assertVideoCallbackRegistered(inCallService, call, false);
+
+            // Trigger delayed creation of video provider and registration of callbacks and assert that
+            // it happened.
+            connection.createMockVideoProvider();
+            assertVideoCallbackRegistered(inCallService, call, true);
+
+            // Ensure video providers are created in the future.
+        } finally {
+            MockConnectionService.setCreateVideoProvider(true);
+        }
+    }
+
+
+    /**
+     * Tests ability to change the current camera.  Ensures that the camera capabilities are sent
+     * back in response.
+     */
+    public void testChangeCamera() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+        final InCallService.VideoCall videoCall = call.getVideoCall();
+
+        videoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
+        assertCameraCapabilitiesReceived(inCallService.getVideoCallCallback(call),
+                MockVideoProvider.CAMERA_FRONT_DIMENSIONS);
+
+        videoCall.setCamera(MockVideoProvider.CAMERA_BACK);
+        assertCameraCapabilitiesReceived(inCallService.getVideoCallCallback(call),
+                MockVideoProvider.CAMERA_BACK_DIMENSIONS);
+    }
+
+    /**
+     * Tests ability to request the camera capabilities from the video provider.
+     */
+    public void testRequestCameraCapabilities() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+        final InCallService.VideoCall videoCall = call.getVideoCall();
+
+        // First, set the camera.
+        videoCall.setCamera(MockVideoProvider.CAMERA_FRONT);
+        // Retrieve the camera capabilities that are automatically send when the camera is set --
+        // ensures the cached value is cleared first.
+        inCallService.getVideoCallCallback(call).getCameraCapabilities();
+
+        // Now, request capabilities.
+        videoCall.requestCameraCapabilities();
+        assertCameraCapabilitiesReceived(inCallService.getVideoCallCallback(call),
+                MockVideoProvider.CAMERA_FRONT_DIMENSIONS);
+    }
+
+    /**
+     * Tests ability to request data usage from the video provider.
+     */
+    public void testRequestDataUsage() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+        final InCallService.VideoCall videoCall = call.getVideoCall();
+
+        videoCall.requestCallDataUsage();
+        assertCallDataUsageReceived(inCallService.getVideoCallCallback(call),
+                MockVideoProvider.DATA_USAGE);
+    }
+
+    /**
+     * Tests ability to receive changes to the video quality from the video provider.
+     */
+    public void testReceiveVideoQuality() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+
+        assertVideoQualityReceived(inCallService.getVideoCallCallback(call),
+                VideoProfile.QUALITY_HIGH,
+                new Work() {
+                    @Override
+                    public void doWork() {
+                        connection
+                                .sendMockVideoQuality(VideoProfile.QUALITY_HIGH);
+                    }
+                });
+
+        assertVideoQualityReceived(inCallService.getVideoCallCallback(call),
+                VideoProfile.QUALITY_MEDIUM,
+                new Work() {
+                    @Override
+                    public void doWork() {
+                        connection
+                                .sendMockVideoQuality(VideoProfile.QUALITY_MEDIUM);
+                    }
+                });
+    }
+
+    /**
+     * Tests ability to receive call session events from the video provider.
+     */
+    public void testReceiveCallSessionEvent() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+
+        assertCallSessionEventReceived(inCallService.getVideoCallCallback(call),
+                Connection.VideoProvider.SESSION_EVENT_CAMERA_READY,
+                new Work() {
+                    @Override
+                    public void doWork() {
+                        connection.sendMockCallSessionEvent(
+                                Connection.VideoProvider.SESSION_EVENT_CAMERA_READY);
+                    }
+                });
+    }
+
+    /**
+     * Tests ability to receive changes to the peer dimensions from the video provider.
+     */
+    public void testReceivePeerDimensionChange() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+
+        assertPeerWidthChanged(inCallService.getVideoCallCallback(call),
+                MockVideoProvider.CAMERA_BACK_DIMENSIONS,
+                new Work() {
+                    @Override
+                    public void doWork() {
+                        connection.sendMockPeerWidth(MockVideoProvider.CAMERA_BACK_DIMENSIONS);
+                    }
+                });
+    }
+
+    /**
+     * Tests ability to set the device orientation via the provider.
+     */
+    public void testSetDeviceOrientation() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+        final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+        final InCallService.VideoCall videoCall = call.getVideoCall();
+
+        // Set device orientation and ensure provider knows about it.
+        videoCall.setDeviceOrientation(90);
+        assertDeviceOrientationChanged(mockVideoProvider, 90);
+    }
+
+    /**
+     * Tests ability to set the preview surface via the provider.
+     */
+    public void testSetPreviewSurface() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+        final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+        final InCallService.VideoCall videoCall = call.getVideoCall();
+
+        Surface surface = new Surface(new SurfaceTexture(1));
+        // Set a surface
+        videoCall.setPreviewSurface(surface);
+        assertPreviewSurfaceChanged(mockVideoProvider, true);
+
+        // Clear the surface
+        videoCall.setPreviewSurface(null);
+        assertPreviewSurfaceChanged(mockVideoProvider, false);
+    }
+
+    /**
+     * Tests ability to set the display surface via the provider.
+     */
+    public void testSetDisplaySurface() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+        final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+        final InCallService.VideoCall videoCall = call.getVideoCall();
+
+        // Set a surface
+        Surface surface = new Surface(new SurfaceTexture(1));
+        videoCall.setDisplaySurface(surface);
+        assertDisplaySurfaceChanged(mockVideoProvider, true);
+
+        // Clear the surface
+        videoCall.setDisplaySurface(null);
+        assertDisplaySurfaceChanged(mockVideoProvider, false);
+    }
+
+    /**
+     * Tests ability to set the camera zoom via the provider.
+     */
+    public void testSetZoom() {
+        placeAndVerifyCall(VideoProfile.STATE_BIDIRECTIONAL);
+        verifyConnectionForOutgoingCall();
+
+        final MockConnection connection = mConnectionCallbacks.outgoingConnection;
+        final MockInCallService inCallService = mInCallCallbacks.getService();
+        final Call call = inCallService.getLastCall();
+        assertVideoCallbackRegistered(inCallService, call, true);
+        final MockVideoProvider mockVideoProvider = connection.getMockVideoProvider();
+        final InCallService.VideoCall videoCall = call.getVideoCall();
+
+        videoCall.setZoom(0.0f);
+        assertZoomChanged(mockVideoProvider, 0.0f);
+
+        videoCall.setZoom(10.0f);
+        assertZoomChanged(mockVideoProvider, 10.0f);
+
+        call.disconnect();
+    }
+
+    /**
+     * Asserts that a call video state is as expected.
+     *
+     * @param call The call.
+     * @param videoState The expected video state.
+     */
+    private void assertVideoState(final Call call, final int videoState) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return videoState;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return call.getDetails().getVideoState();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Call should be in videoState " + videoState
+        );
+    }
+
+    /**
+     * Asserts whether the InCallService has registered a video call back (and hence a video call)
+     * for a call.
+     *
+     * @param inCallService The incall service.
+     * @param call The call.
+     * @param isRegistered The expected registration state.
+     */
+    private void assertVideoCallbackRegistered(final MockInCallService inCallService,
+            final Call call, final Boolean isRegistered) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return isRegistered;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return inCallService.isVideoCallbackRegistered(call);
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Video callback registration state should be " + isRegistered
+        );
+    }
+
+    /**
+     * Asserts whether the camera capabilities have changed to an expected value.  Compares the
+     * camera height only (the {@link MockVideoProvider} sets height and width to be the same.
+     *
+     * @param videoCallCallback The video call callback.
+     * @param expectedCameraWidthHeight The expected width and height.
+     */
+    private void assertCameraCapabilitiesReceived(final MockVideoCallCallback videoCallCallback,
+            final int expectedCameraWidthHeight) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expectedCameraWidthHeight;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        VideoProfile.CameraCapabilities cameraCapabilities =
+                                videoCallCallback.getCameraCapabilities();
+                        return cameraCapabilities == null ? 0 : cameraCapabilities.getHeight();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Camera width and height should be " + expectedCameraWidthHeight
+        );
+    }
+
+    /**
+     * Asserts whether the call data usage has changed to the expected value.
+     *
+     * @param videoCallCallback The video call callback.
+     * @param expectedDataUsage The expected data usage.
+     */
+    private void assertCallDataUsageReceived(final MockVideoCallCallback videoCallCallback,
+            final long expectedDataUsage) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expectedDataUsage;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return videoCallCallback.getDataUsage();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Data usage should be " + expectedDataUsage
+        );
+    }
+
+    /**
+     * Asserts whether the video quality has changed to the expected value.
+     *
+     * @param videoCallCallback The video call callback.
+     * @param expectedVideoQuality The expected video quality.
+     * @param work The work to perform to have the provider emit the video quality.
+     */
+    private void assertVideoQualityReceived(final MockVideoCallCallback videoCallCallback,
+            final int expectedVideoQuality, final Work work) {
+        doWorkAndWaitUntilConditionIsTrueOrTimeout(
+                work,
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expectedVideoQuality;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return videoCallCallback.getVideoQuality();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Video quality should be " + expectedVideoQuality
+        );
+    }
+
+    /**
+     * Asserts whether the call session event has changed to the expected value.
+     *
+     * @param videoCallCallback The video call callback.
+     * @param expectedEvent The expected event.
+     * @param work The work to be performed to send the call session event from the provider.
+     */
+    private void assertCallSessionEventReceived(final MockVideoCallCallback videoCallCallback,
+            final int expectedEvent, final Work work) {
+        doWorkAndWaitUntilConditionIsTrueOrTimeout(
+                work,
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expectedEvent;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return videoCallCallback.getCallSessionEvent();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Call session event should be " + expectedEvent
+        );
+    }
+
+    /**
+     * Asserts whether the peer width has changed to the expected value.
+     *
+     * @param videoCallCallback The video call callback.
+     * @param expectedWidth The expected width.
+     * @param work The work to be performed to send the peer width from the provider.
+     */
+    private void assertPeerWidthChanged(final MockVideoCallCallback videoCallCallback,
+            final int expectedWidth, final Work work) {
+        doWorkAndWaitUntilConditionIsTrueOrTimeout(
+                work,
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expectedWidth;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return videoCallCallback.getPeerWidth();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Peer width should be " + expectedWidth
+        );
+    }
+
+    /**
+     * Asserts whether the device orientation has changed to the expected value.
+     *
+     * @param mockVideoProvider The mock video provider.
+     * @param expected The expected device orientation.
+     */
+    private void assertDeviceOrientationChanged(final MockVideoProvider mockVideoProvider,
+            final int expected) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expected;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return mockVideoProvider.getDeviceOrientation();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Orientation should be " + expected
+        );
+    }
+
+    /**
+     * Asserts whether the preview surface has been set or not.
+     *
+     * @param mockVideoProvider The mock video provider.
+     * @param expected {@code true} if it is expected the preview surface is not null, {@code false}
+     *                             if it is expected the preview surface is null.
+     */
+    private void assertPreviewSurfaceChanged(final MockVideoProvider mockVideoProvider,
+            final boolean expected) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expected;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return mockVideoProvider.getPreviewSurface() != null;
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Preview should be set? " + expected
+        );
+    }
+
+    /**
+     * Asserts whether the display surface has been set or not.
+     *
+     * @param mockVideoProvider The mock video provider.
+     * @param expected {@code true} if it is expected the display surface is not null, {@code false}
+     *                             if it is expected the display surface is null.
+     */
+    private void assertDisplaySurfaceChanged(final MockVideoProvider mockVideoProvider,
+            final boolean expected) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expected;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        return mockVideoProvider.getDisplaySurface() != null;
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Display should be set? " + expected
+        );
+    }
+
+    /**
+     * Asserts whether the zoom has changed to the expected value.  Note: To make comparisons easier
+     * the floats are cast to ints, so ensure only whole values are used.
+     *
+     * @param mockVideoProvider The mock video provider.
+     * @param expected The expected zoom.
+     */
+    private void assertZoomChanged(final MockVideoProvider mockVideoProvider,
+            final float expected) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        // Cast to int so we're not doing float equality
+                        return (int)expected;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        // Cast to int so we're not doing float equality
+                        return (int)mockVideoProvider.getZoom();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Zoom should be " + expected
+        );
+    }
+
+    /**
+     * Asserts whether a response video profile has been received
+     *
+     * @param videoCallCallback The video call callback.
+     * @param expected The expected video state.
+     */
+    private void assertResponseVideoProfileReceived(final MockVideoCallCallback videoCallCallback,
+            final int expected) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expected;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        VideoProfile videoProfile = videoCallCallback.getResponseProfile();
+                        return videoProfile == null ? -1 : videoProfile.getVideoState();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Video state should be " + expected
+        );
+    }
+
+    /**
+     * Asserts whether a session modification request has been received.
+     *
+     * @param videoCallCallback The video call callback.
+     * @param expected The expected video state.
+     * @param work The work to be performed to cause the session modification request to be emit
+     *             from the provider.
+     */
+    private void assertRequestVideoProfileReceived(final MockVideoCallCallback videoCallCallback,
+            final int expected, final Work work) {
+        doWorkAndWaitUntilConditionIsTrueOrTimeout(
+                work,
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expected;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        VideoProfile videoProfile = videoCallCallback.getRequestProfile();
+                        return videoProfile == null ? -1 : videoProfile.getVideoState();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Video state should be " + expected
+        );
+    }
+
+    /**
+     * Asserts whether the provider got a session modify response with the expected value.
+     *
+     * @param mockVideoProvider The mock video provider.
+     * @param expected The expected video state of the session modify response.
+     */
+    private void assertSessionModifyResponse(final MockVideoProvider mockVideoProvider,
+            final int expected) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return expected;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        VideoProfile responseProfile = mockVideoProvider.getSessionModifyResponse();
+                        return responseProfile == null ? -1 : responseProfile.getVideoState();
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Session modify response video state should be " + expected
+        );
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/VideoProfileTest.java b/tests/tests/telecom/src/android/telecom/cts/VideoProfileTest.java
new file mode 100644
index 0000000..6c16abb
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/VideoProfileTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.telecom.cts;
+
+import android.telecom.VideoProfile;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests helper methods in the {@link VideoProfile} class.
+ */
+public class VideoProfileTest extends AndroidTestCase {
+    public void testIsAudioOnly() {
+        assertTrue(VideoProfile.isAudioOnly(VideoProfile.STATE_AUDIO_ONLY));
+        assertTrue(VideoProfile.isAudioOnly(VideoProfile.STATE_PAUSED));
+
+        assertFalse(VideoProfile.isAudioOnly(VideoProfile.STATE_BIDIRECTIONAL));
+        assertFalse(VideoProfile.isAudioOnly(VideoProfile.STATE_TX_ENABLED));
+        assertFalse(VideoProfile.isAudioOnly(VideoProfile.STATE_RX_ENABLED));
+        assertFalse(VideoProfile
+                .isAudioOnly(VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+        assertFalse(VideoProfile
+                .isAudioOnly(VideoProfile.STATE_TX_ENABLED | VideoProfile.STATE_PAUSED));
+        assertFalse(VideoProfile
+                .isAudioOnly(VideoProfile.STATE_RX_ENABLED | VideoProfile.STATE_PAUSED));
+    }
+
+    public void testIsVideo() {
+        assertTrue(VideoProfile.isVideo(VideoProfile.STATE_BIDIRECTIONAL));
+        assertTrue(VideoProfile.isVideo(VideoProfile.STATE_RX_ENABLED));
+        assertTrue(VideoProfile.isVideo(VideoProfile.STATE_TX_ENABLED));
+        assertTrue(VideoProfile.isVideo(VideoProfile.STATE_BIDIRECTIONAL |
+                VideoProfile.STATE_PAUSED));
+        assertTrue(VideoProfile.isVideo(VideoProfile.STATE_RX_ENABLED | VideoProfile.STATE_PAUSED));
+        assertTrue(VideoProfile.isVideo(VideoProfile.STATE_TX_ENABLED | VideoProfile.STATE_PAUSED));
+
+        assertFalse(VideoProfile.isVideo(VideoProfile.STATE_AUDIO_ONLY));
+        assertFalse(VideoProfile.isVideo(VideoProfile.STATE_PAUSED));
+    }
+
+    public void testIsBidirectional() {
+        assertTrue(VideoProfile.isBidirectional(VideoProfile.STATE_BIDIRECTIONAL));
+        assertTrue(VideoProfile.isBidirectional(VideoProfile.STATE_BIDIRECTIONAL |
+                VideoProfile.STATE_PAUSED));
+
+        assertFalse(VideoProfile.isBidirectional(VideoProfile.STATE_TX_ENABLED));
+        assertFalse(VideoProfile.isBidirectional(VideoProfile.STATE_TX_ENABLED |
+                VideoProfile.STATE_PAUSED));
+        assertFalse(VideoProfile.isBidirectional(VideoProfile.STATE_RX_ENABLED));
+        assertFalse(VideoProfile.isBidirectional(VideoProfile.STATE_RX_ENABLED |
+                VideoProfile.STATE_PAUSED));
+    }
+
+    public void testIsPaused() {
+        assertTrue(VideoProfile.isPaused(VideoProfile.STATE_PAUSED));
+        assertTrue(VideoProfile.isPaused(VideoProfile.STATE_BIDIRECTIONAL |
+                VideoProfile.STATE_PAUSED));
+        assertTrue(VideoProfile.isPaused(VideoProfile.STATE_TX_ENABLED |
+                VideoProfile.STATE_PAUSED));
+        assertTrue(VideoProfile.isPaused(VideoProfile.STATE_RX_ENABLED |
+                VideoProfile.STATE_PAUSED));
+
+        assertFalse(VideoProfile.isPaused(VideoProfile.STATE_AUDIO_ONLY));
+        assertFalse(VideoProfile.isPaused(VideoProfile.STATE_TX_ENABLED));
+        assertFalse(VideoProfile.isPaused(VideoProfile.STATE_RX_ENABLED));
+        assertFalse(VideoProfile.isPaused(VideoProfile.STATE_BIDIRECTIONAL));
+    }
+
+    public void testIsReceptionEnabled() {
+        assertTrue(VideoProfile.isReceptionEnabled(VideoProfile.STATE_RX_ENABLED));
+        assertTrue(VideoProfile.isReceptionEnabled(VideoProfile.STATE_BIDIRECTIONAL));
+        assertTrue(VideoProfile.isReceptionEnabled(VideoProfile.STATE_RX_ENABLED |
+                VideoProfile.STATE_PAUSED));
+        assertTrue(VideoProfile.isReceptionEnabled(VideoProfile.STATE_BIDIRECTIONAL |
+                VideoProfile.STATE_PAUSED));
+
+        assertFalse(VideoProfile.isReceptionEnabled(VideoProfile.STATE_AUDIO_ONLY));
+        assertFalse(VideoProfile.isReceptionEnabled(VideoProfile.STATE_TX_ENABLED));
+        assertFalse(VideoProfile.isReceptionEnabled(VideoProfile.STATE_PAUSED));
+    }
+
+    public void testIsTransmissionEnabled() {
+        assertTrue(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_TX_ENABLED));
+        assertTrue(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_BIDIRECTIONAL));
+        assertTrue(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_TX_ENABLED |
+                VideoProfile.STATE_PAUSED));
+        assertTrue(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_BIDIRECTIONAL |
+                VideoProfile.STATE_PAUSED));
+
+        assertFalse(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_AUDIO_ONLY));
+        assertFalse(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_RX_ENABLED));
+        assertFalse(VideoProfile.isTransmissionEnabled(VideoProfile.STATE_PAUSED));
+    }
+}
diff --git a/tests/tests/telecom2/Android.mk b/tests/tests/telecom2/Android.mk
new file mode 100644
index 0000000..fa23492
--- /dev/null
+++ b/tests/tests/telecom2/Android.mk
@@ -0,0 +1,36 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsTelecomTestCases2
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+src_dirs := src \
+    ../telecom/src
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/telecom2/AndroidManifest.xml b/tests/tests/telecom2/AndroidManifest.xml
new file mode 100644
index 0000000..e618768
--- /dev/null
+++ b/tests/tests/telecom2/AndroidManifest.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.telecom2">
+    <uses-sdk android:minSdkVersion="21" />
+
+    <!--
+        This app contains tests to verify Telecom's behavior when the app is missing certain
+        permissions.
+    -->
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <service android:name="android.telecom.cts.MockConnectionService"
+            android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" >
+            <intent-filter>
+                <action android:name="android.telecom.ConnectionService" />
+            </intent-filter>
+        </service>
+
+        <service android:name="android.telecom.cts.MockInCallService"
+            android:permission="android.permission.BIND_INCALL_SERVICE" >
+            <intent-filter>
+                <action android:name="android.telecom.InCallService"/>
+            </intent-filter>
+        </service>
+
+        <activity android:name="android.telecom.cts.MockDialerActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:mimeType="vnd.android.cursor.item/phone" />
+                <data android:mimeType="vnd.android.cursor.item/person" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="voicemail" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <action android:name="android.intent.action.DIAL" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="tel" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.telecom2"
+                     android:label="CTS tests for android.telecom package">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/telecom2/src/android/telecom/cts/DefaultDialerOperationsNoPermissionsTest.java b/tests/tests/telecom2/src/android/telecom/cts/DefaultDialerOperationsNoPermissionsTest.java
new file mode 100644
index 0000000..4ca6bcd
--- /dev/null
+++ b/tests/tests/telecom2/src/android/telecom/cts/DefaultDialerOperationsNoPermissionsTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.telecom.cts;
+
+import android.content.Context;
+import android.telecom.TelecomManager;
+import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
+
+/**
+ * Verifies that certain privileged operations can only be performed by the default dialer.
+ */
+public class DefaultDialerOperationsNoPermissionsTest extends InstrumentationTestCase {
+    private Context mContext;
+    private TelecomManager mTelecomManager;
+    private String mPreviousDefaultDialer = null;
+    private String mSystemDialer = null;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getContext();
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        TestUtils.PACKAGE = mContext.getPackageName();
+        mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
+        // Reset the current dialer to the system dialer, to ensure that we start each test
+        // without being the default dialer.
+        mSystemDialer = TestUtils.getSystemDialer(getInstrumentation());
+        if (!TextUtils.isEmpty(mSystemDialer)) {
+            TestUtils.setDefaultDialer(getInstrumentation(), mSystemDialer);
+        }
+        mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
+            // Restore the default dialer to whatever the default dialer was before the tests
+            // were started. This may or may not be the system dialer.
+            TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
+        }
+        super.tearDown();
+    }
+
+    public void testShowInCallScreenPermissions() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+            @Override
+            public void run() {
+                mTelecomManager.showInCallScreen(false);
+            }
+        }, "showInCallScreen");
+    }
+
+    public void testGetCallCapableAccountsPermissions() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+            @Override
+            public void run() {
+                mTelecomManager.getCallCapablePhoneAccounts();
+            }
+        }, "getCallCapableAccounts");
+    }
+
+    public void testGetDefaultOutgoingPhoneAccount() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+            @Override
+            public void run() {
+                mTelecomManager.getDefaultOutgoingPhoneAccount("tel");
+            }
+        }, "getDefaultOutgoingPhoneAccount");
+    }
+
+    public void testGetLine1Number() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+            @Override
+            public void run() {
+                mTelecomManager.getLine1Number(null);
+            }
+        }, "getLine1Number");
+    }
+
+    public void testGetVoicemailNumber() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+            @Override
+            public void run() {
+                mTelecomManager.getVoiceMailNumber(null);
+            }
+        }, "getVoiceMailNumber");
+    }
+
+    public void testIsVoicemailNumber() throws Exception {
+        verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+            @Override
+            public void run() {
+                mTelecomManager.isVoiceMailNumber(null, null);
+            }
+        }, "isVoiceMailNumber");
+    }
+
+    public void testIsInCall() throws Exception {
+        if (!TestUtils.shouldTestTelecom(mContext)) {
+            return;
+        }
+        verifyForReadPhoneStateOrDefaultDialer(new Runnable() {
+            @Override
+            public void run() {
+                mTelecomManager.isInCall();
+            }
+        }, "isInCall");
+    }
+
+    private void verifyForReadPhoneStateOrDefaultDialer(Runnable runnable, String methodName)
+            throws Exception{
+        try {
+            runnable.run();
+            fail("TelecomManager." + methodName + " should throw SecurityException if no "
+                    + "READ_PHONE_STATE permission");
+        } catch (SecurityException e) {
+        }
+
+        TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+        runnable.run();
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/CarrierServiceTest.java b/tests/tests/telephony/src/android/telephony/cts/CarrierServiceTest.java
new file mode 100644
index 0000000..492f8ff
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/cts/CarrierServiceTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.telephony.cts;
+
+import android.content.Intent;
+import android.os.PersistableBundle;
+import android.service.carrier.CarrierIdentifier;
+import android.service.carrier.CarrierService;
+import android.test.ServiceTestCase;
+
+public class CarrierServiceTest extends ServiceTestCase<CarrierServiceTest.TestCarrierService> {
+    public CarrierServiceTest() { super(TestCarrierService.class); }
+
+    public void testNotifyCarrierNetworkChange_true() {
+        notifyCarrierNetworkChange(true);
+    }
+
+    public void testNotifyCarrierNetworkChange_false() {
+        notifyCarrierNetworkChange(false);
+    }
+
+    private void notifyCarrierNetworkChange(boolean active) {
+        Intent intent = new Intent(getContext(), TestCarrierService.class);
+        startService(intent);
+
+        try {
+            getService().notifyCarrierNetworkChange(active);
+            fail("Expected SecurityException for notifyCarrierNetworkChange(" + active + ")");
+        } catch (SecurityException e) { /* Expected */ }
+    }
+
+    public static class TestCarrierService extends CarrierService {
+        @Override
+        public PersistableBundle onLoadConfig(CarrierIdentifier id) { return null; }
+    }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
index f0f977a..18aa23f 100644
--- a/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -22,6 +22,7 @@
 import android.telephony.CellLocation;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.net.ConnectivityManager;
 import android.test.InstrumentationTestCase;
@@ -40,6 +41,7 @@
     private boolean mOnMessageWaitingIndicatorChangedCalled;
     private boolean mOnServiceStateChangedCalled;
     private boolean mOnSignalStrengthChangedCalled;
+    private SignalStrength mSignalStrength;
     private TelephonyManager mTelephonyManager;
     private PhoneStateListener mListener;
     private final Object mLock = new Object();
@@ -152,6 +154,54 @@
         assertTrue(mOnSignalStrengthChangedCalled);
     }
 
+    public void testOnSignalStrengthsChanged() throws Throwable {
+        if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+            Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+            return;
+        }
+
+        TestThread t = new TestThread(new Runnable() {
+            public void run() {
+                Looper.prepare();
+
+                mListener = new PhoneStateListener() {
+                    @Override
+                    public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+                        synchronized(mLock) {
+                            mSignalStrength = signalStrength;
+                            mLock.notify();
+                        }
+                    }
+                };
+                mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+
+                Looper.loop();
+            }
+        });
+
+        assertTrue(mSignalStrength == null);
+        t.start();
+
+        synchronized (mLock) {
+            while(mSignalStrength == null) {
+                mLock.wait();
+            }
+        }
+        t.checkException();
+        assertTrue(mSignalStrength != null);
+
+        // Call SignalStrength methods to make sure they do not throw any exceptions
+        mSignalStrength.getCdmaDbm();
+        mSignalStrength.getCdmaEcio();
+        mSignalStrength.getEvdoDbm();
+        mSignalStrength.getEvdoEcio();
+        mSignalStrength.getEvdoSnr();
+        mSignalStrength.getGsmBitErrorRate();
+        mSignalStrength.getGsmSignalStrength();
+        mSignalStrength.isGsm();
+        mSignalStrength.getLevel();
+    }
+
     public void testOnMessageWaitingIndicatorChanged() throws Throwable {
         if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
             Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
diff --git a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
index 3279abe..20810a8 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
@@ -22,8 +22,7 @@
 import android.test.AndroidTestCase;
 
 public class SimRestrictedApisTest extends AndroidTestCase {
-    private static final byte[] TEST_PDU = {
-            0, 0 };
+    private static final byte[] TEST_PDU = { 0, 0 };
     private TelephonyManager mTelephonyManager;
 
     @Override
@@ -266,24 +265,4 @@
         } catch (SecurityException expected) {
         }
     }
-
-    /**
-     * Tests the TelephonyManager.notifyCarrierNetworkChange() API to make sure a
-     * SecurityException is thrown since the test APK is not signed by a certificate on the SIM.
-     */
-    public void testNotifyCarrierNetworkChange() {
-        try {
-            if (isSimCardPresent()) {
-                TelephonyManager.getDefault().notifyCarrierNetworkChange(true /* active */);
-                fail("Expected SecurityException for notifyCarrierNetworkChange(true)");
-            }
-        } catch (SecurityException expected) {}
-
-        try {
-            if (isSimCardPresent()) {
-                TelephonyManager.getDefault().notifyCarrierNetworkChange(false /* active */);
-                fail("Expected SecurityException for notifyCarrierNetworkChange(false)");
-            }
-        } catch (SecurityException expected) {}
-    }
 }
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
index 145cc84..0527e9a 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
@@ -299,7 +299,7 @@
             new ShortCodeTest("it", "112", CATEGORY_NOT_SHORT_CODE),
             new ShortCodeTest("it", "116117", CATEGORY_FREE_SHORT_CODE),
             new ShortCodeTest("it", "4567", CATEGORY_NOT_SHORT_CODE),
-            new ShortCodeTest("it", "48000", CATEGORY_FREE_SHORT_CODE),
+            new ShortCodeTest("it", "48000", CATEGORY_PREMIUM_SHORT_CODE),
             new ShortCodeTest("it", "45678", CATEGORY_PREMIUM_SHORT_CODE),
             new ShortCodeTest("it", "56789", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
             new ShortCodeTest("it", "456789", CATEGORY_NOT_SHORT_CODE),
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 2be1dcb..ce3fe78 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -160,7 +160,28 @@
         mTelephonyManager.getNeighboringCellInfo();
         mTelephonyManager.isNetworkRoaming();
         mTelephonyManager.getDeviceId();
+        mTelephonyManager.getDeviceId(mTelephonyManager.getDefaultSim());
         mTelephonyManager.getDeviceSoftwareVersion();
+        mTelephonyManager.getPhoneCount();
+    }
+
+    /**
+     * Tests that the phone count returned is valid.
+     */
+    public void testGetPhoneCount() {
+        int phoneCount = mTelephonyManager.getPhoneCount();
+        int phoneType = mTelephonyManager.getPhoneType();
+        switch (phoneType) {
+            case TelephonyManager.PHONE_TYPE_GSM:
+            case TelephonyManager.PHONE_TYPE_CDMA:
+                assertTrue("Phone count should be > 0", phoneCount > 0);
+                break;
+            case TelephonyManager.PHONE_TYPE_NONE:
+                assertTrue("Phone count should be 0", phoneCount == 0);
+                break;
+            default:
+                throw new IllegalArgumentException("Did you add a new phone type? " + phoneType);
+        }
     }
 
     /**
@@ -170,6 +191,24 @@
      */
     public void testGetDeviceId() {
         String deviceId = mTelephonyManager.getDeviceId();
+        verifyDeviceId(deviceId);
+    }
+
+    /**
+     * Tests that the device properly reports either a valid IMEI if
+     * GSM, a valid MEID or ESN if CDMA, or a valid MAC address if
+     * only a WiFi device.
+     */
+    public void testGetDeviceIdForSlotId() {
+        String deviceId = mTelephonyManager.getDeviceId(mTelephonyManager.getDefaultSim());
+        verifyDeviceId(deviceId);
+        // Also verify that no exception is thrown for any slot id (including invalid ones)
+        for (int i = -1; i <= mTelephonyManager.getPhoneCount(); i++) {
+            mTelephonyManager.getDeviceId(i);
+        }
+    }
+
+    private void verifyDeviceId(String deviceId) {
         int phoneType = mTelephonyManager.getPhoneType();
         switch (phoneType) {
             case TelephonyManager.PHONE_TYPE_GSM:
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
index 4895ca9..36b081c 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
@@ -18,10 +18,12 @@
 
 
 import android.test.AndroidTestCase;
+import android.text.Editable;
 import android.text.InputFilter;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
+import android.text.TextWatcher;
 import android.text.style.StrikethroughSpan;
 import android.text.style.TabStopSpan;
 import android.text.style.UnderlineSpan;
@@ -596,4 +598,46 @@
             // expected exception
         }
     }
+
+    private static class MockTextWatcher implements TextWatcher {
+        private int mDepth = 0;
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+            SpannableStringBuilder builder = (SpannableStringBuilder)s;
+            mDepth++;
+            assertEquals(mDepth, builder.getTextWatcherDepth());
+            mDepth--;
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+            SpannableStringBuilder builder = (SpannableStringBuilder)s;
+            mDepth++;
+            assertEquals(mDepth, builder.getTextWatcherDepth());
+            mDepth--;
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+            SpannableStringBuilder builder = (SpannableStringBuilder)s;
+            mDepth++;
+            assertEquals(mDepth, builder.getTextWatcherDepth());
+            if (mDepth <= builder.length()) {
+                // This will recursively call afterTextChanged.
+                builder.replace(mDepth - 1, mDepth, "a");
+            }
+            mDepth--;
+        }
+    }
+
+    public void testGetTextWatcherDepth() {
+        SpannableStringBuilder builder = new SpannableStringBuilder("hello");
+        builder.setSpan(new MockTextWatcher(), 0, builder.length(), 0);
+        assertEquals(0, builder.getTextWatcherDepth());
+        builder.replace(0, 1, "H");
+        assertEquals(0, builder.getTextWatcherDepth());
+        // MockTextWatcher replaces each character with 'a'.
+        assertEquals("aaaaa", builder.toString());
+    }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
index 878d5ae..2061023 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
@@ -27,23 +27,28 @@
 public class ShadowTests extends ActivityTestBase {
     @SmallTest
     public void testShadowLayout() {
+        int shadowColorValue = 0xDB;
+        // Android TV theme overrides shadow opacity to be darker.
+        if (getActivity().getOnTv()) {
+            shadowColorValue = 0xBB;
+        }
+        SamplePointVerifier verifier = new SamplePointVerifier(
+                new Point[] {
+                        // view area
+                        new Point(25, 64),
+                        new Point(64, 64),
+                        // shadow area
+                        new Point(25, 65),
+                        new Point(64, 65)
+                },
+                new int[] {
+                        Color.WHITE,
+                        Color.WHITE,
+                        Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
+                        Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
+                });
         createTest()
                 .addLayout(R.layout.simple_shadow_layout, null, true/* HW only */)
-                .runWithVerifier(
-                new SamplePointVerifier(
-                        new Point[] {
-                                // view area
-                                new Point(25, 64),
-                                new Point(64, 64),
-                                // shadow area
-                                new Point(25, 65),
-                                new Point(64, 65)
-                        },
-                        new int[] {
-                                Color.WHITE,
-                                Color.WHITE,
-                                Color.rgb(222, 222, 222),
-                                Color.rgb(222, 222, 222),
-                        }));
+                .runWithVerifier(verifier);
     }
 }
\ No newline at end of file
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 4bd513a..041fcdd 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.content.res.Configuration;
 import android.graphics.Point;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,12 +39,19 @@
 
     private Handler mHandler;
     private View mView;
+    private boolean mOnTv;
 
     public void onCreate(Bundle bundle){
         super.onCreate(bundle);
         getWindow().getDecorView().setSystemUiVisibility(
                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
         mHandler = new RenderSpecHandler();
+        int uiMode = getResources().getConfiguration().uiMode;
+        mOnTv = (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) == Configuration.UI_MODE_TYPE_TELEVISION;
+    }
+
+    public boolean getOnTv() {
+        return mOnTv;
     }
 
     public Point enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, String webViewUrl,
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index a36e6fd..9ccbeb4 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -159,6 +159,10 @@
                 android:resource="@xml/merge" />
         </activity>
 
+        <activity android:name="android.view.cts.ActionModeCtsActivity"
+            android:label="ActionModeCtsActivity">
+        </activity>
+
         <activity android:name="android.view.cts.ViewGroupCtsActivity"
             android:label="WidgetViewGroupCtsActivity">
             <intent-filter>
diff --git a/tests/tests/view/res/xml/keyboard.xml b/tests/tests/view/res/xml/keyboard.xml
new file mode 100644
index 0000000..dd64cc0
--- /dev/null
+++ b/tests/tests/view/res/xml/keyboard.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="10px"
+    >
+
+    <Row>
+        <Key android:codes="-1" android:keyLabel="Sticky!"
+                android:isModifier="true" android:isSticky="true" />
+        <Key android:codes="120" android:keyLabel="x" />
+    </Row>
+</Keyboard>
diff --git a/tests/tests/view/src/android/view/cts/ActionModeCtsActivity.java b/tests/tests/view/src/android/view/cts/ActionModeCtsActivity.java
new file mode 100644
index 0000000..c03db59
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ActionModeCtsActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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.view.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class ActionModeCtsActivity extends Activity {
+
+    public View contentView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        contentView = new FrameLayout(this);
+        setContentView(contentView);
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/ActionModeTest.java b/tests/tests/view/src/android/view/cts/ActionModeTest.java
index 61df9fe..6898d3c 100644
--- a/tests/tests/view/src/android/view/cts/ActionModeTest.java
+++ b/tests/tests/view/src/android/view/cts/ActionModeTest.java
@@ -16,13 +16,20 @@
 
 package android.view.cts;
 
-import android.test.AndroidTestCase;
+import android.graphics.Rect;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
 import android.view.ActionMode;
 import android.view.Menu;
 import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 
-public class ActionModeTest extends AndroidTestCase {
+public class ActionModeTest extends ActivityInstrumentationTestCase2<ActionModeCtsActivity> {
+
+    public ActionModeTest() {
+        super(ActionModeCtsActivity.class);
+    }
 
     public void testSetType() {
         ActionMode actionMode = new MockActionMode();
@@ -43,6 +50,51 @@
         assertFalse(actionMode.mInvalidateWasCalled);
     }
 
+    public void testInvalidateContentRectOnFloatingCallsCallback() {
+        final View view = getActivity().contentView;
+        final MockActionModeCallback2 callback = new MockActionModeCallback2();
+
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ActionMode mode = view.startActionMode(callback, ActionMode.TYPE_FLOATING);
+                assertNotNull(mode);
+                mode.invalidateContentRect();
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+
+        assertTrue(callback.mIsOnGetContentRectCalled);
+    }
+
+    private static class MockActionModeCallback2 extends ActionMode.Callback2 {
+        boolean mIsOnGetContentRectCalled = false;
+
+        @Override
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            return false;
+        }
+
+        @Override
+        public void onDestroyActionMode(ActionMode mode) {}
+
+        @Override
+        public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+            mIsOnGetContentRectCalled = true;
+            super.onGetContentRect(mode, view, outRect);
+        }
+    }
+
     private static class MockActionMode extends ActionMode {
         boolean mInvalidateWasCalled = false;
 
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/KeyboardTest.java b/tests/tests/view/src/android/view/inputmethod/cts/KeyboardTest.java
new file mode 100644
index 0000000..ce7f9d7
--- /dev/null
+++ b/tests/tests/view/src/android/view/inputmethod/cts/KeyboardTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import com.android.cts.view.R;
+
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.test.AndroidTestCase;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.Keyboard.Key;
+
+import java.util.List;
+
+public class KeyboardTest extends AndroidTestCase {
+
+    public void testKeyOnPressedAndReleased() {
+        Key nonStickyKey = null;
+        Key stickyKey = null;
+        // Indirectly instantiate Keyboard.Key with XML resources.
+        final Keyboard keyboard = new Keyboard(getContext(), R.xml.keyboard);
+        for (final Key key : keyboard.getKeys()) {
+            if (!key.sticky) {
+                nonStickyKey = key;
+                break;
+            }
+        }
+        for (final Key key : keyboard.getModifierKeys()) {
+            if (key.sticky) {
+                stickyKey = key;
+                break;
+            }
+        }
+
+        // Asserting existences of following keys is not the goal of this test, but this should work
+        // anyway.
+        assertNotNull(nonStickyKey);
+        assertNotNull(stickyKey);
+
+        // At first, both "pressed" and "on" must be false.
+        assertFalse(nonStickyKey.pressed);
+        assertFalse(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertFalse(stickyKey.on);
+
+        // Pressing the key must flip the "pressed" state only.
+        nonStickyKey.onPressed();
+        stickyKey.onPressed();
+        assertTrue(nonStickyKey.pressed);
+        assertTrue(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertFalse(stickyKey.on);
+
+        // Releasing the key inside the key area must flip the "pressed" state and toggle the "on"
+        // state if the key is marked as sticky.
+        nonStickyKey.onReleased(true /* inside */);
+        stickyKey.onReleased(true /* inside */);
+        assertFalse(nonStickyKey.pressed);
+        assertFalse(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertTrue(stickyKey.on);   // The key state is toggled.
+
+        // Pressing the key again must flip the "pressed" state only.
+        nonStickyKey.onPressed();
+        stickyKey.onPressed();
+        assertTrue(nonStickyKey.pressed);
+        assertTrue(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertTrue(stickyKey.on);
+
+        // Releasing the key inside the key area must flip the "pressed" state and toggle the "on"
+        // state if the key is marked as sticky hence we will be back to the initial state.
+        nonStickyKey.onReleased(true /* inside */);
+        stickyKey.onReleased(true /* inside */);
+        assertFalse(nonStickyKey.pressed);
+        assertFalse(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertFalse(stickyKey.on);
+
+        // Pressing then releasing the key outside the key area must not affect the "on" state.
+        nonStickyKey.onPressed();
+        stickyKey.onPressed();
+        nonStickyKey.onReleased(false /* inside */);
+        stickyKey.onReleased(false /* inside */);
+        assertFalse(nonStickyKey.pressed);
+        assertFalse(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertFalse(stickyKey.on);
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index dba2243..ee6f4ec 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -415,43 +415,17 @@
     }
 
     @UiThreadTest
-    public void testSetScrollBarStyle() {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
-        assertFalse(mWebView.overlayHorizontalScrollbar());
-        assertFalse(mWebView.overlayVerticalScrollbar());
-
-        mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
-        assertTrue(mWebView.overlayHorizontalScrollbar());
-        assertTrue(mWebView.overlayVerticalScrollbar());
-
-        mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
-        assertFalse(mWebView.overlayHorizontalScrollbar());
-        assertFalse(mWebView.overlayVerticalScrollbar());
-
-        mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
-        assertTrue(mWebView.overlayHorizontalScrollbar());
-        assertTrue(mWebView.overlayVerticalScrollbar());
-    }
-
-    @UiThreadTest
     public void testScrollBarOverlay() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
 
+        // These functions have no effect; just verify they don't crash
         mWebView.setHorizontalScrollbarOverlay(true);
         mWebView.setVerticalScrollbarOverlay(false);
+
         assertTrue(mWebView.overlayHorizontalScrollbar());
         assertFalse(mWebView.overlayVerticalScrollbar());
-
-        mWebView.setHorizontalScrollbarOverlay(false);
-        mWebView.setVerticalScrollbarOverlay(true);
-        assertFalse(mWebView.overlayHorizontalScrollbar());
-        assertTrue(mWebView.overlayVerticalScrollbar());
     }
 
     @UiThreadTest
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index 4fadafc..81a1a4b 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -383,34 +383,6 @@
         assertEquals(1, mAdapterView.getSelectedItemId());
         assertEquals(1, mAdapterView.getSelectedItemPosition());
         assertEquals(FRUIT[1], mAdapterView.getSelectedItem());
-
-        // Ensure getSelectedItemZzz() synchronizes after data set change.
-        ArrayAdapter<String> adapter = new ArrayAdapter<>(
-                getActivity(), android.R.layout.simple_list_item_1);
-        adapter.add(FRUIT[0]);
-        adapter.add(FRUIT[1]);
-
-        ListAdapter previousAdapter = mAdapterView.getAdapter();
-        mAdapterView.setAdapter(adapter);
-        mAdapterView.setSelection(1);
-        assertEquals("Initial getSelectedItemId() is correct",
-                1, mAdapterView.getSelectedItemId());
-        assertEquals("Initial getSelectedItemPosition() is correct",
-                1, mAdapterView.getSelectedItemPosition());
-
-        adapter.remove(FRUIT[0]);
-        assertEquals("Synchronized getSelectedItemId() after data set invalidation",
-                0, mAdapterView.getSelectedItemId());
-        assertEquals("Synchronized getSelectedItemPosition() after data set invalidation",
-                0, mAdapterView.getSelectedItemPosition());
-
-        adapter.clear();
-        assertEquals("Synchronized getSelectedItemId() after data set cleared",
-                AdapterView.INVALID_ROW_ID, mAdapterView.getSelectedItemId());
-        assertEquals("Synchronized getSelectedItemPosition() after data set cleared",
-                AdapterView.INVALID_POSITION, mAdapterView.getSelectedItemPosition());
-
-        mAdapterView.setAdapter(previousAdapter);
     }
 
     /*
diff --git a/tests/tests/widget/src/android/widget/cts/ListViewTest.java b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
index 5f0967a..60db540 100644
--- a/tests/tests/widget/src/android/widget/cts/ListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
@@ -32,15 +32,18 @@
 import android.test.UiThreadTest;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.LayoutAnimationController;
+import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.TextView;
 
@@ -361,6 +364,53 @@
         assertEquals(2, mListView.getHeaderViewsCount());
     }
 
+    public void testHeaderFooterType() throws Throwable {
+        final TextView headerView = new TextView(getActivity());
+        final List<Pair<View, View>> mismatch = new ArrayList<Pair<View, View>>();
+        final ArrayAdapter adapter = new ArrayAdapter<String>(mActivity,
+                android.R.layout.simple_list_item_1, mNameList) {
+            @Override
+            public int getItemViewType(int position) {
+                return position == 0 ? AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER :
+                        super.getItemViewType(position - 1);
+            }
+
+            @Override
+            public View getView(int position, View convertView, ViewGroup parent) {
+                if (position == 0) {
+                    if (convertView != null && convertView != headerView) {
+                        mismatch.add(new Pair<View, View>(headerView, convertView));
+                    }
+                    return headerView;
+                } else {
+                    return super.getView(position - 1, convertView, parent);
+                }
+            }
+
+            @Override
+            public int getCount() {
+                return super.getCount() + 1;
+            }
+        };
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setAdapter(adapter);
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                adapter.notifyDataSetChanged();
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+
+        assertEquals(0, mismatch.size());
+    }
+
     public void testAccessDivider() {
         mInstrumentation.runOnMainSync(new Runnable() {
             public void run() {
diff --git a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
new file mode 100644
index 0000000..2dff4cb
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.widget.cts;
+
+import com.android.cts.widget.R;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.Gravity;
+import android.widget.PopupMenu;
+
+
+public class PopupMenuTest extends
+        ActivityInstrumentationTestCase2<MockPopupWindowCtsActivity> {
+    private Instrumentation mInstrumentation;
+    private Activity mActivity;
+
+    public PopupMenuTest() {
+        super("com.android.cts.widget", MockPopupWindowCtsActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mInstrumentation = getInstrumentation();
+        mActivity = getActivity();
+    }
+
+    public void testAccessGravity() {
+        PopupMenu popupMenu = new PopupMenu(mActivity,
+                mActivity.findViewById(R.id.anchor_middle_left));
+        assertEquals(Gravity.NO_GRAVITY, popupMenu.getGravity());
+        popupMenu.setGravity(Gravity.TOP);
+        assertEquals(Gravity.TOP, popupMenu.getGravity());
+    }
+
+    public void testOnDismissListener() {
+        final PopupMenu popupMenu = new PopupMenu(mActivity,
+                mActivity.findViewById(R.id.anchor_middle_left));
+        TestPopupDismissListener listener = new TestPopupDismissListener();
+        popupMenu.setOnDismissListener(listener);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                popupMenu.show();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(0, listener.getDismissCount());
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                popupMenu.dismiss();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(1, listener.getDismissCount());
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                popupMenu.dismiss();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertEquals(1, listener.getDismissCount());
+    }
+
+    private class TestPopupDismissListener implements PopupMenu.OnDismissListener {
+        int mDismissCount;
+
+        @Override
+        public void onDismiss(PopupMenu menu) {
+            mDismissCount++;
+        }
+
+        int getDismissCount() {
+            return mDismissCount;
+        }
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 706ad8d..1131e02 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -29,6 +29,8 @@
 import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
+import android.transition.Transition;
+import android.transition.TransitionValues;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -334,6 +336,41 @@
         dismissPopup();
     }
 
+    public void testOverlapAnchor() {
+        int[] anchorXY = new int[2];
+        int[] viewOnScreenXY = new int[2];
+        int[] viewInWindowXY = new int[2];
+
+        mPopupWindow = createPopupWindow(createPopupContent());
+        final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
+        upperAnchor.getLocationOnScreen(anchorXY);
+
+        assertFalse(mPopupWindow.getOverlapAnchor());
+        mPopupWindow.setOverlapAnchor(true);
+        assertTrue(mPopupWindow.getOverlapAnchor());
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                mPopupWindow.showAsDropDown(upperAnchor, 0, 0);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY);
+        mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
+        assertEquals(anchorXY[0] + viewInWindowXY[0], viewOnScreenXY[0]);
+        assertEquals(anchorXY[1] + viewInWindowXY[1], viewOnScreenXY[1]);
+    }
+
+    public void testAccessWindowLayoutType() {
+        mPopupWindow = createPopupWindow(createPopupContent());
+        assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
+                mPopupWindow.getWindowLayoutType());
+        mPopupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+        assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
+                mPopupWindow.getWindowLayoutType());
+    }
+
     public void testGetMaxAvailableHeight() {
         mPopupWindow = createPopupWindow(createPopupContent());
 
@@ -447,6 +484,36 @@
                 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM & p.flags);
     }
 
+    public void testEnterExitTransition() {
+        mPopupWindow = createPopupWindow(createPopupContent());
+        final View anchorView = mActivity.findViewById(R.id.anchor_upper);
+
+        final MockTransition enterTransition = new MockTransition();
+        final MockTransition exitTransition = new MockTransition();
+        mPopupWindow.setEnterTransition(enterTransition);
+        mPopupWindow.setExitTransition(exitTransition);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                mPopupWindow.showAsDropDown(anchorView, 0, 0);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(1, enterTransition.getTransitionCount());
+        assertEquals(0, exitTransition.getTransitionCount());
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                mPopupWindow.dismiss();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(1, enterTransition.getTransitionCount());
+        assertEquals(1, exitTransition.getTransitionCount());
+    }
+
     public void testUpdatePositionAndDimension() {
         int[] fstXY = new int[2];
         int[] sndXY = new int[2];
@@ -823,6 +890,28 @@
         }
     }
 
+    private static class MockTransition extends Transition {
+        private int mTransitionCount;
+
+        private MockTransition() {
+            addListener(new Transition.TransitionListenerAdapter() {
+                public void onTransitionEnd(Transition transition) {
+                    mTransitionCount++;
+                }
+            });
+        }
+
+        public void captureStartValues(TransitionValues transitionValues) {
+        }
+
+        public void captureEndValues(TransitionValues transitionValues) {
+        }
+
+        int getTransitionCount() {
+            return mTransitionCount;
+        }
+    }
+
     private View createPopupContent() {
         View popupView = new View(mActivity);
         popupView.setLayoutParams(new ViewGroup.LayoutParams(50, 50));
diff --git a/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java b/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java
new file mode 100644
index 0000000..cfd61a2
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.widget.cts;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.test.InstrumentationTestCase;
+import android.test.UiThreadTest;
+import android.widget.QuickContactBadge;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class QuickContactBadgeTest extends InstrumentationTestCase {
+
+    @UiThreadTest
+    public void testPrioritizedMimetype() throws InterruptedException {
+        final String plainMimeType = "text/plain";
+        final Uri nonExistentContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 0);
+        final CountDownLatch latch = new CountDownLatch(1);
+        Context context = new ContextWrapper(getInstrumentation().getContext()) {
+            @Override
+            public void startActivity(Intent intent) {
+                testCallback(intent);
+            }
+
+            @Override
+            public void startActivityAsUser(Intent intent, UserHandle user) {
+                testCallback(intent);
+            }
+
+            @Override
+            public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+                testCallback(intent);
+            }
+
+            private void testCallback(Intent intent) {
+                assertEquals(plainMimeType, intent.getStringExtra(
+                        ContactsContract.QuickContact.EXTRA_PRIORITIZED_MIMETYPE));
+                latch.countDown();
+            }
+        };
+
+        // Execute: create QuickContactBadge with a prioritized mimetype and click on it
+        QuickContactBadge badge = new QuickContactBadge(context);
+        badge.setPrioritizedMimeType(plainMimeType);
+        badge.assignContactUri(nonExistentContactUri);
+        badge.onClick(badge);
+
+        // Verify: the QuickContactBadge attempts to start an activity, and sets the
+        // prioritized mimetype. We don't know which method will be used to start the activity,
+        // so we check all options.
+        assertTrue(latch.await(1, TimeUnit.SECONDS));
+    }
+}
+
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 7d6d33b..291e5c2 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -42,6 +42,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.MoreAsserts;
 import android.test.TouchUtils;
 import android.test.UiThreadTest;
 import android.text.Editable;
@@ -1667,6 +1668,96 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    public void testCopyAndPaste() {
+        initTextViewForTyping();
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mTextView.setText("abcd", BufferType.EDITABLE);
+                mTextView.setSelected(true);
+
+                // Copy "bc".
+                Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
+                mTextView.onTextContextMenuItem(android.R.id.copy);
+
+                // Paste "bc" between "b" and "c".
+                Selection.setSelection((Spannable) mTextView.getText(), 2, 2);
+                mTextView.onTextContextMenuItem(android.R.id.paste);
+                assertEquals("abbccd", mTextView.getText().toString());
+
+                // Select entire text and paste "bc".
+                Selection.selectAll((Spannable) mTextView.getText());
+                mTextView.onTextContextMenuItem(android.R.id.paste);
+                assertEquals("bc", mTextView.getText().toString());
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+
+    public void testCutAndPaste() {
+        initTextViewForTyping();
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mTextView.setText("abcd", BufferType.EDITABLE);
+                mTextView.setSelected(true);
+
+                // Cut "bc".
+                Selection.setSelection((Spannable) mTextView.getText(), 1, 3);
+                mTextView.onTextContextMenuItem(android.R.id.cut);
+                assertEquals("ad", mTextView.getText().toString());
+
+                // Cut "ad".
+                Selection.setSelection((Spannable) mTextView.getText(), 0, 2);
+                mTextView.onTextContextMenuItem(android.R.id.cut);
+                assertEquals("", mTextView.getText().toString());
+
+                // Paste "ad".
+                mTextView.onTextContextMenuItem(android.R.id.paste);
+                assertEquals("ad", mTextView.getText().toString());
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+
+    private static boolean hasSpansAtMiddleOfText(final TextView textView, final Class<?> type) {
+        final Spannable spannable = (Spannable)textView.getText();
+        final int at = spannable.length() / 2;
+        return spannable.getSpans(at, at, type).length > 0;
+    }
+
+    public void testCutAndPaste_withAndWithoutStyle() {
+        initTextViewForTyping();
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mTextView.setText("example", BufferType.EDITABLE);
+                mTextView.setSelected(true);
+
+                // Set URLSpan.
+                final Spannable spannable = (Spannable) mTextView.getText();
+                spannable.setSpan(new URLSpan("http://example.com"), 0, spannable.length(), 0);
+                assertTrue(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
+
+                // Cut entire text.
+                Selection.selectAll((Spannable) mTextView.getText());
+                mTextView.onTextContextMenuItem(android.R.id.cut);
+                assertEquals("", mTextView.getText().toString());
+
+                // Paste without style.
+                mTextView.onTextContextMenuItem(android.R.id.pasteAsPlainText);
+                assertEquals("example", mTextView.getText().toString());
+                // Check that the text doesn't have URLSpan.
+                assertFalse(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
+
+                // Paste with style.
+                Selection.selectAll((Spannable) mTextView.getText());
+                mTextView.onTextContextMenuItem(android.R.id.paste);
+                assertEquals("example", mTextView.getText().toString());
+                // Check that the text has URLSpan.
+                assertTrue(hasSpansAtMiddleOfText(mTextView, URLSpan.class));
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+
     @UiThreadTest
     public void testSetText() {
         TextView tv = findTextView(R.id.textview_text);
@@ -3505,6 +3596,12 @@
 
         tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
         assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getRawTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getRawTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getRawTextDirection());
     }
 
     @UiThreadTest
@@ -3531,6 +3628,12 @@
 
         tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
         assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection());
     }
 
     @UiThreadTest
@@ -3559,6 +3662,12 @@
 
         tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
         assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection());
     }
 
     @UiThreadTest
@@ -3585,6 +3694,12 @@
 
         tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
         assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection());
     }
 
     @UiThreadTest
@@ -3614,6 +3729,12 @@
         tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
         assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection());
 
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection());
+
         // Force to RTL text direction on the layout
         ll.setTextDirection(View.TEXT_DIRECTION_RTL);
 
@@ -3634,6 +3755,12 @@
 
         tv.setTextDirection(View.TEXT_DIRECTION_LOCALE);
         assertEquals(View.TEXT_DIRECTION_LOCALE, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection());
     }
 
     @UiThreadTest
@@ -3654,6 +3781,106 @@
         assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection());
     }
 
+    @UiThreadTest
+    public void testTextDirectionFirstStrongLtr() {
+        {
+            // The first directional character is LTR, the paragraph direction is LTR.
+            LinearLayout ll = new LinearLayout(mActivity);
+
+            TextView tv = new TextView(mActivity);
+            tv.setText("this is a test");
+            ll.addView(tv);
+
+            tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+            assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection());
+
+            tv.onPreDraw();  // For freezing layout.
+            Layout layout = tv.getLayout();
+            assertEquals(Layout.DIR_LEFT_TO_RIGHT, layout.getParagraphDirection(0));
+        }
+        {
+            // The first directional character is RTL, the paragraph direction is RTL.
+            LinearLayout ll = new LinearLayout(mActivity);
+
+            TextView tv = new TextView(mActivity);
+            tv.setText("\u05DD\u05DE"); // Hebrew
+            ll.addView(tv);
+
+            tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+            assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection());
+
+            tv.onPreDraw();  // For freezing layout.
+            Layout layout = tv.getLayout();
+            assertEquals(Layout.DIR_RIGHT_TO_LEFT, layout.getParagraphDirection(0));
+        }
+        {
+            // The first directional character is not a strong directional character, the paragraph
+            // direction is LTR.
+            LinearLayout ll = new LinearLayout(mActivity);
+
+            TextView tv = new TextView(mActivity);
+            tv.setText("\uFFFD");  // REPLACEMENT CHARACTER. Neutral direction.
+            ll.addView(tv);
+
+            tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_LTR);
+            assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_LTR, tv.getTextDirection());
+
+            tv.onPreDraw();  // For freezing layout.
+            Layout layout = tv.getLayout();
+            assertEquals(Layout.DIR_LEFT_TO_RIGHT, layout.getParagraphDirection(0));
+        }
+    }
+
+    @UiThreadTest
+    public void testTextDirectionFirstStrongRtl() {
+        {
+            // The first directional character is LTR, the paragraph direction is LTR.
+            LinearLayout ll = new LinearLayout(mActivity);
+
+            TextView tv = new TextView(mActivity);
+            tv.setText("this is a test");
+            ll.addView(tv);
+
+            tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+            assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection());
+
+            tv.onPreDraw();  // For freezing layout.
+            Layout layout = tv.getLayout();
+            assertEquals(Layout.DIR_LEFT_TO_RIGHT, layout.getParagraphDirection(0));
+        }
+        {
+            // The first directional character is RTL, the paragraph direction is RTL.
+            LinearLayout ll = new LinearLayout(mActivity);
+
+            TextView tv = new TextView(mActivity);
+            tv.setText("\u05DD\u05DE"); // Hebrew
+            ll.addView(tv);
+
+            tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+            assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection());
+
+            tv.onPreDraw();  // For freezing layout.
+            Layout layout = tv.getLayout();
+            assertEquals(Layout.DIR_RIGHT_TO_LEFT, layout.getParagraphDirection(0));
+        }
+        {
+            // The first directional character is not a strong directional character, the paragraph
+            // direction is RTL.
+            LinearLayout ll = new LinearLayout(mActivity);
+
+            TextView tv = new TextView(mActivity);
+            tv.setText("\uFFFD");  // REPLACEMENT CHARACTER. Neutral direction.
+            ll.addView(tv);
+
+            tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG_RTL);
+            assertEquals(View.TEXT_DIRECTION_FIRST_STRONG_RTL, tv.getTextDirection());
+
+            tv.onPreDraw();  // For freezing layout.
+            Layout layout = tv.getLayout();
+            assertEquals(Layout.DIR_RIGHT_TO_LEFT, layout.getParagraphDirection(0));
+        }
+    }
+
     public void testAllCapsLocalization() {
         String testString = "abcdefghijklmnopqrstuvwxyz";
 
@@ -4027,6 +4254,53 @@
         assertNull(drawables[BOTTOM]);
     }
 
+    public void testSetGetBreakStrategy() {
+        TextView tv = new TextView(mActivity);
+
+        // The default value is from the theme, here the default is BREAK_STRATEGY_HIGH_QUALITY for
+        // TextView.
+        assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, tv.getBreakStrategy());
+
+        tv.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
+        assertEquals(Layout.BREAK_STRATEGY_SIMPLE, tv.getBreakStrategy());
+
+        tv.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY);
+        assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, tv.getBreakStrategy());
+
+        tv.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+        assertEquals(Layout.BREAK_STRATEGY_BALANCED, tv.getBreakStrategy());
+
+        EditText et = new EditText(mActivity);
+
+        // The default value is from the theme, here the default is BREAK_STRATEGY_SIMPLE for
+        // EditText.
+        assertEquals(Layout.BREAK_STRATEGY_SIMPLE, et.getBreakStrategy());
+
+        et.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
+        assertEquals(Layout.BREAK_STRATEGY_SIMPLE, et.getBreakStrategy());
+
+        et.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY);
+        assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, et.getBreakStrategy());
+
+        et.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
+        assertEquals(Layout.BREAK_STRATEGY_BALANCED, et.getBreakStrategy());
+    }
+
+    public void testSetGetHyphenationFrequency() {
+        TextView tv = new TextView(mActivity);
+
+        assertEquals(Layout.HYPHENATION_FREQUENCY_NORMAL, tv.getHyphenationFrequency());
+
+        tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
+        assertEquals(Layout.HYPHENATION_FREQUENCY_NONE, tv.getHyphenationFrequency());
+
+        tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
+        assertEquals(Layout.HYPHENATION_FREQUENCY_NORMAL, tv.getHyphenationFrequency());
+
+        tv.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
+        assertEquals(Layout.HYPHENATION_FREQUENCY_FULL, tv.getHyphenationFrequency());
+    }
+
     private static class MockOnEditorActionListener implements OnEditorActionListener {
         private boolean isOnEditorActionCalled;
 
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerTest.java b/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
index fcf787a..1ce2844 100644
--- a/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
@@ -167,6 +167,37 @@
         assertEquals(Integer.valueOf(23), mTimePicker.getCurrentHour());
     }
 
+    public void testAccessHour() {
+        mTimePicker = new TimePicker(mContext);
+
+        // AM/PM mode
+        mTimePicker.setIs24HourView(false);
+
+        mTimePicker.setHour(0);
+        assertEquals(0, mTimePicker.getHour());
+
+        mTimePicker.setHour(12);
+        assertEquals(12, mTimePicker.getHour());
+
+        mTimePicker.setHour(13);
+        assertEquals(13, mTimePicker.getHour());
+
+        mTimePicker.setHour(23);
+        assertEquals(23, mTimePicker.getHour());
+
+        // for 24 hour mode
+        mTimePicker.setIs24HourView(true);
+
+        mTimePicker.setHour(0);
+        assertEquals(0, mTimePicker.getHour());
+
+        mTimePicker.setHour(13);
+        assertEquals(13, mTimePicker.getHour());
+
+        mTimePicker.setHour(23);
+        assertEquals(23, mTimePicker.getHour());
+    }
+
     public void testAccessIs24HourView() {
         mTimePicker = new TimePicker(mContext);
         assertFalse(mTimePicker.is24HourView());
@@ -194,6 +225,22 @@
         assertEquals(Integer.valueOf(59), mTimePicker.getCurrentMinute());
     }
 
+    public void testAccessMinute() {
+        mTimePicker = new TimePicker(mContext);
+
+        mTimePicker.setMinute(0);
+        assertEquals(0, mTimePicker.getMinute());
+
+        mTimePicker.setMinute(12);
+        assertEquals(12, mTimePicker.getMinute());
+
+        mTimePicker.setMinute(33);
+        assertEquals(33, mTimePicker.getMinute());
+
+        mTimePicker.setMinute(59);
+        assertEquals(59, mTimePicker.getMinute());
+    }
+
     public void testGetBaseline() {
         mTimePicker = new TimePicker(mContext);
         assertEquals(-1, mTimePicker.getBaseline());