Merge "Report errors from CtsTestServer for improved debugging" into mnc-dev
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 03f8ff9..5475a59 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -369,6 +369,28 @@
     rgb.reshape(w*h*3)[:] = flt.reshape(w*h*3)[:]
     return rgb.astype(numpy.float32) / 255.0
 
+def load_rgb_image(fname):
+    """Load a standard image file (JPG, PNG, etc.).
+
+    Args:
+        fname: The path of the file to load.
+
+    Returns:
+        RGB float-3 image array, with pixel values in [0.0, 1.0].
+    """
+    img = Image.open(fname)
+    w = img.size[0]
+    h = img.size[1]
+    a = numpy.array(img)
+    if len(a.shape) == 3 and a.shape[2] == 3:
+        # RGB
+        return a.reshape(h,w,3) / 255.0
+    elif len(a.shape) == 2 or len(a.shape) == 3 and a.shape[2] == 1:
+        # Greyscale; convert to RGB
+        return a.reshape(h*w).repeat(3).reshape(h,w,3) / 255.0
+    else:
+        raise its.error.Error('Unsupported image type')
+
 def load_yuv420_to_rgb_image(yuv_fname,
                              w, h,
                              ccm_yuv_to_rgb=DEFAULT_YUV_TO_RGB_CCM,
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 98e8acd..a914e7c 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -971,6 +971,17 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.camera.any" />
         </activity>
 
+        <activity android:name=".camera.flashlight.CameraFlashlightActivity"
+                  android:label="@string/camera_flashlight_test"
+                  android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_camera" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.camera.flash" />
+        </activity>
+
         <activity android:name=".usb.UsbAccessoryTestActivity"
                 android:label="@string/usb_accessory_test"
                 android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/apps/CtsVerifier/res/layout/camera_flashlight.xml b/apps/CtsVerifier/res/layout/camera_flashlight.xml
new file mode 100644
index 0000000..2d4378c
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/camera_flashlight.xml
@@ -0,0 +1,65 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="bottom"
+    android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/flash_instruction_text"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="2"
+            android:gravity="center"
+            android:text="@string/camera_flashlight_start_text" />
+
+        <Button
+            android:id="@+id/flash_instruction_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="100dp"
+            android:layout_marginRight="100dp"
+            android:text="@string/camera_flashlight_start_button" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginLeft="50dp"
+            android:layout_marginRight="50dp"
+            android:layout_marginBottom="50dp"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/flash_on_button"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:text="@string/camera_flashlight_on_button" />
+
+            <Button
+                android:id="@+id/flash_off_button"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:text="@string/camera_flashlight_off_button" />
+
+        </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons"/>
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 7e67e9f..926a993 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -842,6 +842,25 @@
     <string name="its_test_passed">All Camera ITS tests passed.  Pass button enabled!</string>
     <string name="its_test_failed">Some Camera ITS tests failed.</string>
 
+    <!-- Strings for the Camera Flashlight test activity -->
+    <string name="camera_flashlight_test">Camera Flashlight</string>
+    <string name="camera_flashlight_info">
+        This test checks the flashlight functionality. It will turn on and off the flashlight of
+        each camera device that has a flash unit. Follow the instructions on screen and observe the
+        flashlight status changing.
+    </string>
+    <string name="camera_flashlight_start_button">Start</string>
+    <string name="camera_flashlight_next_button">Next</string>
+    <string name="camera_flashlight_done_button">Done</string>
+    <string name="camera_flashlight_on_button">On</string>
+    <string name="camera_flashlight_off_button">Off</string>
+    <string name="camera_flashlight_start_text">Press Start to start flashlight test.</string>
+    <string name="camera_flashlight_question_text">Is Camera %1$s flashlight on or off?</string>
+    <string name="camera_flashlight_next_text">Ok. Press next.</string>
+    <string name="camera_flashlight_failed_text">Test failed. Press Done or Fail button.</string>
+    <string name="camera_flashlight_passed_text">All tests passed. Press Done or Pass button.
+    </string>
+
     <!-- Strings for StreamingVideoActivity -->
     <string name="streaming_video">Streaming Video Quality Verifier</string>
     <string name="streaming_video_info">This is a test for assessing the quality of streaming videos.  Play each stream and verify that the video is smooth and in sync with the audio, and that there are no quality problems.</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/flashlight/CameraFlashlightActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/flashlight/CameraFlashlightActivity.java
new file mode 100644
index 0000000..4d37b3e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/flashlight/CameraFlashlightActivity.java
@@ -0,0 +1,275 @@
+/*
+ * 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 com.android.cts.verifier.camera.flashlight;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraCharacteristics;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.util.HashSet;
+import java.util.HashMap;
+
+/**
+ * This test checks the flashlight functionality by turning on and off the flashlight. After it
+ * turns on or off the flashlight, it asks for user input to verify the flashlight status. The
+ * test will pass when the user input is correct for all camera devices with a flash unit.
+ */
+public class CameraFlashlightActivity extends PassFailButtons.Activity {
+
+    private static final String TAG = "CameraFlashlight";
+
+    private CameraManager mCameraManager;
+    private TestState mTestState;
+    private final HashSet<String> mPendingCameraIds = new HashSet<>();
+    private String mCurrentCameraId;
+
+    private Button mInstructionButton;
+    private Button mOnButton;
+    private Button mOffButton;
+    private TextView mInstructionTextView;
+    private final HashSet<View> mAllButtons = new HashSet<>();
+    // TestState -> enabled buttons
+    private final HashMap<TestState, HashSet<View>> mStateButtonsMap = new HashMap<>();
+
+    private enum TestState {
+        NOT_STARTED,
+        TESTING_ON,
+        WAITING_ON_CALLBACK_ON,
+        RESPONDED_ON_CORRECTLY,
+        WAITING_ON_CALLBACK_OFF,
+        TESTING_OFF,
+        RESPONDED_OFF_CORRECTLY,
+        ALL_PASSED,
+        FAILED
+    }
+
+    private final View.OnClickListener mInstructionButtonListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            switch (mTestState) {
+                case NOT_STARTED:
+                    // Start testing turning on the first camera's flashlight.
+                    // Fall through.
+                case RESPONDED_OFF_CORRECTLY:
+                    // Current camera passed. Start testing turning on next camera's flashlight.
+                    if (mPendingCameraIds.size() == 0) {
+                        // Passed
+                        mTestState = TestState.ALL_PASSED;
+                        updateButtonsAndInstructionLocked();
+                        return;
+                    }
+
+                    mCurrentCameraId = (String)mPendingCameraIds.toArray()[0];
+                    mPendingCameraIds.remove(mCurrentCameraId);
+
+                    try {
+                        mCameraManager.setTorchMode(mCurrentCameraId, true);
+                        mTestState = TestState.WAITING_ON_CALLBACK_ON;
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        mTestState = TestState.FAILED;
+                    }
+                    break;
+
+                case RESPONDED_ON_CORRECTLY:
+                    // Flashlight is on and user responded correctly.
+                    // Turning off the flashlight.
+                    try {
+                        mCameraManager.setTorchMode(mCurrentCameraId, false);
+                        mTestState = TestState.WAITING_ON_CALLBACK_OFF;
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        mTestState = TestState.FAILED;
+                    }
+                    break;
+
+                case FAILED:
+                    // The test failed, report failure.
+                    if (mCurrentCameraId != null) {
+                        try {
+                            mCameraManager.setTorchMode(mCurrentCameraId, false);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            Log.e(TAG, "Test failed but cannot turn off the torch");
+                        }
+                    }
+                    setTestResultAndFinish(false);
+                    break;
+
+                case ALL_PASSED:
+                    // The test passed, report pass.
+                    setTestResultAndFinish(true);
+                    break;
+            }
+
+            updateButtonsAndInstructionLocked();
+        }
+    };
+
+    private final View.OnClickListener mOnButtonListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            // Check if user responded correctly.
+            if (mTestState == TestState.TESTING_ON) {
+                mTestState = TestState.RESPONDED_ON_CORRECTLY;
+            } else {
+                mTestState = TestState.FAILED;
+            }
+            updateButtonsAndInstructionLocked();
+        }
+    };
+
+    private final View.OnClickListener mOffButtonListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            // Check if user responded correctly.
+            if (mTestState == TestState.TESTING_OFF) {
+                mTestState = TestState.RESPONDED_OFF_CORRECTLY;
+            } else {
+                mTestState = TestState.FAILED;
+            }
+            updateButtonsAndInstructionLocked();
+        }
+    };
+
+    private final CameraManager.TorchCallback mTorchCallback = new CameraManager.TorchCallback() {
+        @Override
+        public void onTorchModeChanged(String cameraId, boolean enabled) {
+            if (!cameraId.equals(mCurrentCameraId)) {
+                return;
+            }
+
+            // Move to next state after receiving the expected callback.
+            if (mTestState == TestState.WAITING_ON_CALLBACK_ON && enabled) {
+                mTestState = TestState.TESTING_ON;
+            } else if (mTestState == TestState.WAITING_ON_CALLBACK_OFF && !enabled) {
+                mTestState = TestState.TESTING_OFF;
+            }
+            updateButtonsAndInstructionLocked();
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // initialize state -> buttons map
+        for (TestState state : TestState.values()) {
+            mStateButtonsMap.put(state, new HashSet<View>());
+        }
+
+        mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
+
+        try {
+            String[] cameraIds = mCameraManager.getCameraIdList();
+            for (String id : cameraIds) {
+                CameraCharacteristics info = mCameraManager.getCameraCharacteristics(id);
+                if (info.get(CameraCharacteristics.FLASH_INFO_AVAILABLE).booleanValue() ==
+                        true) {
+                    mPendingCameraIds.add(id);
+                }
+            }
+            mCameraManager.registerTorchCallback(mTorchCallback, new Handler());
+        } catch (Exception e) {
+            e.printStackTrace();
+            mTestState = TestState.FAILED;
+            updateButtonsAndInstructionLocked();
+            return;
+        }
+
+        // Setup the UI.
+        setContentView(R.layout.camera_flashlight);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.camera_flashlight_test, R.string.camera_flashlight_info, -1);
+
+        mInstructionTextView = (TextView) findViewById(R.id.flash_instruction_text);
+
+        // Get the buttons and attach the listener.
+        mInstructionButton = (Button) findViewById(R.id.flash_instruction_button);
+        mInstructionButton.setOnClickListener(mInstructionButtonListener);
+        mStateButtonsMap.get(TestState.NOT_STARTED).add(mInstructionButton);
+        mStateButtonsMap.get(TestState.RESPONDED_ON_CORRECTLY).add(mInstructionButton);
+        mStateButtonsMap.get(TestState.RESPONDED_OFF_CORRECTLY).add(mInstructionButton);
+        mStateButtonsMap.get(TestState.ALL_PASSED).add(mInstructionButton);
+        mStateButtonsMap.get(TestState.FAILED).add(mInstructionButton);
+        mAllButtons.add(mInstructionButton);
+
+        mOnButton = (Button) findViewById(R.id.flash_on_button);
+        mOnButton.setOnClickListener(mOnButtonListener);
+        mStateButtonsMap.get(TestState.TESTING_ON).add(mOnButton);
+        mStateButtonsMap.get(TestState.TESTING_OFF).add(mOnButton);
+        mAllButtons.add(mOnButton);
+
+        mOffButton = (Button) findViewById(R.id.flash_off_button);
+        mOffButton.setOnClickListener(mOffButtonListener);
+        mStateButtonsMap.get(TestState.TESTING_ON).add(mOffButton);
+        mStateButtonsMap.get(TestState.TESTING_OFF).add(mOffButton);
+        mAllButtons.add(mOffButton);
+
+        View passButton = getPassButton();
+        mStateButtonsMap.get(TestState.ALL_PASSED).add(passButton);
+        mAllButtons.add(passButton);
+
+        mTestState = TestState.NOT_STARTED;
+        updateButtonsAndInstructionLocked();
+    }
+
+
+    private void updateButtonsAndInstructionLocked() {
+        for (View v : mAllButtons) {
+            v.setEnabled(false);
+        }
+
+        // Only enable the buttons for this state.
+        HashSet<View> views = mStateButtonsMap.get(mTestState);
+        for (View v : views) {
+            v.setEnabled(true);
+        }
+
+        switch (mTestState) {
+            case TESTING_ON:
+            case TESTING_OFF:
+                mInstructionTextView.setText(String.format(
+                        getString(R.string.camera_flashlight_question_text), mCurrentCameraId));
+                break;
+            case RESPONDED_ON_CORRECTLY:
+            case RESPONDED_OFF_CORRECTLY:
+                mInstructionTextView.setText(R.string.camera_flashlight_next_text);
+                mInstructionButton.setText(R.string.camera_flashlight_next_button);
+                break;
+            case FAILED:
+                mInstructionTextView.setText(R.string.camera_flashlight_failed_text);
+                mInstructionButton.setText(R.string.camera_flashlight_done_button);
+                break;
+            case ALL_PASSED:
+                mInstructionTextView.setText(R.string.camera_flashlight_passed_text);
+                mInstructionButton.setText(R.string.camera_flashlight_done_button);
+                break;
+            default:
+                break;
+        }
+    }
+}
diff --git a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
index d0a3a37..efb53d5 100644
--- a/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
+++ b/common/host-side/xml-plan-generator/src/com/android/compatibility/common/xmlgenerator/XmlPlanGenerator.java
@@ -22,9 +22,9 @@
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
-import vogar.Expectation;
 import vogar.ExpectationStore;
 import vogar.ModeId;
+import vogar.Result;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -161,7 +161,7 @@
     }
 
     public static boolean isKnownFailure(ExpectationStore store, String fullname) {
-        return store != null && store.get(fullname) != Expectation.SUCCESS;
+        return store != null && store.get(fullname).getResult() != Result.SUCCESS;
     }
 
     public static void main(String[] args) throws Exception {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
index e4f5134..dcb5bbc 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
@@ -19,6 +19,9 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Process;
 import android.test.AndroidTestCase;
 
 /**
@@ -32,6 +35,14 @@
 public class BaseDeviceOwnerTest extends AndroidTestCase {
 
     public static class BasicAdminReceiver extends DeviceAdminReceiver {
+        @Override
+        public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri,
+                String suggestedAlias) {
+            if (uid != Process.myUid() || uri == null) {
+                return null;
+            }
+            return uri.getQueryParameter("alias");
+        }
     }
 
     public static final String PACKAGE_NAME = BaseDeviceOwnerTest.class.getPackage().getName();
@@ -44,11 +55,15 @@
 
         mDevicePolicyManager = (DevicePolicyManager)
                 mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        assertTrue(mDevicePolicyManager.isAdminActive(getWho()));
-        assertTrue(mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME));
+        assertDeviceOwner(mDevicePolicyManager);
     }
 
-    public static ComponentName getWho() {
+    static void assertDeviceOwner(DevicePolicyManager dpm) {
+        assertTrue(dpm.isAdminActive(getWho()));
+        assertTrue(dpm.isDeviceOwnerApp(PACKAGE_NAME));
+    }
+
+    protected static ComponentName getWho() {
         return new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName());
     }
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
index 23d108f..a608794 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
@@ -15,11 +15,21 @@
  */
 package com.android.cts.deviceowner;
 
+import static com.android.cts.deviceowner.BaseDeviceOwnerTest.getWho;
 import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1;
 
+import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
+import android.security.KeyChainException;
+import android.test.ActivityInstrumentationTestCase2;
 
 import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.Certificate;
@@ -28,12 +38,31 @@
 import java.security.PrivateKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
-public class KeyManagementTest extends BaseDeviceOwnerTest {
+import android.content.ComponentName;
+import android.content.Context;
+
+public class KeyManagementTest extends
+        ActivityInstrumentationTestCase2<ExampleIntentReceivingActivity1> {
+
+    private static final int KEYCHAIN_TIMEOUT_MS = 8000;
+    private DevicePolicyManager mDevicePolicyManager;
+
+    public KeyManagementTest() {
+        super(ExampleIntentReceivingActivity1.class);
+    }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+
+        // Confirm our DeviceOwner is set up
+        mDevicePolicyManager = (DevicePolicyManager)
+                getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
+        BaseDeviceOwnerTest.assertDeviceOwner(mDevicePolicyManager);
+
         // Enable credential storage by setting a nonempty password.
         assertTrue(mDevicePolicyManager.resetPassword("test", 0));
     }
@@ -49,27 +78,32 @@
     }
 
     public void testCanInstallValidRsaKeypair()
-            throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException {
+            throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException,
+                    KeyChainException, InterruptedException, UnsupportedEncodingException {
         final String alias = "com.android.test.valid-rsa-key-1";
         final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA");
         final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
         assertTrue(mDevicePolicyManager.installKeyPair(getWho(), privKey, cert, alias));
+
+        assertEquals(alias, new KeyChainAliasFuture(alias).get());
+        final PrivateKey retrievedKey = KeyChain.getPrivateKey(getActivity(), alias);
+        assertEquals(retrievedKey.getAlgorithm(), "RSA");
     }
 
-    public void testNullKeyParamsFailGracefully()
+    public void testNullKeyParamsFailPredictably()
             throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException {
         final String alias = "com.android.test.null-key-1";
         final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA");
         final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
         try {
-            assertFalse(mDevicePolicyManager.installKeyPair(getWho(), null, cert, alias));
-        } catch (NullPointerException accept) {
-            // Accept either false return value or NPE
+            mDevicePolicyManager.installKeyPair(getWho(), null, cert, alias);
+            fail("Exception should have been thrown for null PrivateKey");
+        } catch (NullPointerException expected) {
         }
         try {
-            assertFalse(mDevicePolicyManager.installKeyPair(getWho(), privKey, null, alias));
-        } catch (NullPointerException accept) {
-            // Accept either false return value or NPE
+            mDevicePolicyManager.installKeyPair(getWho(), privKey, null, alias);
+            fail("Exception should have been thrown for null Certificate");
+        } catch (NullPointerException expected) {
         }
     }
 
@@ -79,21 +113,46 @@
         final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey, "RSA");
         final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
         try {
-            assertFalse(mDevicePolicyManager.installKeyPair(null, privKey, cert, alias));
+            mDevicePolicyManager.installKeyPair(null, privKey, cert, alias);
             fail("Exception should have been thrown for null ComponentName");
-        } catch (SecurityException | NullPointerException expected) {
+        } catch (SecurityException expected) {
         }
     }
 
-    PrivateKey getPrivateKey(final byte[] key, String type)
+    private static PrivateKey getPrivateKey(final byte[] key, String type)
             throws NoSuchAlgorithmException, InvalidKeySpecException {
         return KeyFactory.getInstance(type).generatePrivate(
                 new PKCS8EncodedKeySpec(key));
     }
 
-    Certificate getCertificate(byte[] cert) throws CertificateException {
+    private static Certificate getCertificate(byte[] cert) throws CertificateException {
         return CertificateFactory.getInstance("X.509").generateCertificate(
                 new ByteArrayInputStream(cert));
     }
 
+    private class KeyChainAliasFuture implements KeyChainAliasCallback {
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+        private String mChosenAlias = null;
+
+        @Override
+        public void alias(final String chosenAlias) {
+            mChosenAlias = chosenAlias;
+            mLatch.countDown();
+        }
+
+        public KeyChainAliasFuture(String alias) throws UnsupportedEncodingException {
+            /* Pass the alias as a GET to an imaginary server instead of explicitly asking for it,
+             * to make sure the DPC actually has to do some work to grant the cert.
+             */
+            final Uri uri =
+                    Uri.parse("https://example.org/?alias=" + URLEncoder.encode(alias, "UTF-8"));
+            KeyChain.choosePrivateKeyAlias(getActivity(), this,
+                    null /* keyTypes */, null /* issuers */, uri, null /* alias */);
+        }
+
+        public String get() throws InterruptedException {
+            assertTrue("Chooser timeout", mLatch.await(KEYCHAIN_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            return mChosenAlias;
+        }
+    };
 }
diff --git a/libs/vogar-expect/src/vogar/Expectation.java b/libs/vogar-expect/src/vogar/Expectation.java
index f065f42..ddbc233 100644
--- a/libs/vogar-expect/src/vogar/Expectation.java
+++ b/libs/vogar-expect/src/vogar/Expectation.java
@@ -16,7 +16,6 @@
 
 package vogar;
 
-import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -37,14 +36,6 @@
  */
 public final class Expectation {
 
-    /** The pattern to use when no expected output is specified */
-    public static final Pattern MATCH_ALL_PATTERN
-            = Pattern.compile(".*", Pattern.MULTILINE | Pattern.DOTALL);
-
-    /** The expectation of a general successful run. */
-    public static final Expectation SUCCESS = new Expectation(Result.SUCCESS, MATCH_ALL_PATTERN,
-            Collections.<String>emptySet(), "", -1);
-
     /** Justification for this expectation */
     private final String description;
 
diff --git a/libs/vogar-expect/src/vogar/ExpectationStore.java b/libs/vogar-expect/src/vogar/ExpectationStore.java
index cfa20e9..1818889 100644
--- a/libs/vogar-expect/src/vogar/ExpectationStore.java
+++ b/libs/vogar-expect/src/vogar/ExpectationStore.java
@@ -26,6 +26,7 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -50,7 +51,17 @@
  * expectation, the outcome expectation will be returned.
  */
 public final class ExpectationStore {
+
+    /** The pattern to use when no expected output is specified */
+    private static final Pattern MATCH_ALL_PATTERN
+            = Pattern.compile(".*", Pattern.MULTILINE | Pattern.DOTALL);
+
+    /** The expectation of a general successful run. */
+    private static final Expectation SUCCESS = new Expectation(Result.SUCCESS, MATCH_ALL_PATTERN,
+            Collections.<String>emptySet(), "", -1);
+
     private static final int PATTERN_FLAGS = Pattern.MULTILINE | Pattern.DOTALL;
+
     private final Map<String, Expectation> outcomes = new LinkedHashMap<String, Expectation>();
     private final Map<String, Expectation> failures = new LinkedHashMap<String, Expectation>();
 
@@ -62,7 +73,7 @@
      */
     public Expectation get(String name) {
         Expectation byName = getByNameOrPackage(name);
-        return byName != null ? byName : Expectation.SUCCESS;
+        return byName != null ? byName : SUCCESS;
     }
 
     /**
@@ -87,7 +98,7 @@
         }
 
         Expectation byName = getByNameOrPackage(outcome.getName());
-        return byName != null ? byName : Expectation.SUCCESS;
+        return byName != null ? byName : SUCCESS;
     }
 
     private Expectation getByNameOrPackage(String name) {
@@ -142,7 +153,7 @@
     private void readExpectation(JsonReader reader, ModeId mode) throws IOException {
         boolean isFailure = false;
         Result result = Result.SUCCESS;
-        Pattern pattern = Expectation.MATCH_ALL_PATTERN;
+        Pattern pattern = MATCH_ALL_PATTERN;
         Set<String> names = new LinkedHashSet<String>();
         Set<String> tags = new LinkedHashSet<String>();
         Set<ModeId> modes = null;
diff --git a/tests/tests/accessibility/AndroidManifest.xml b/tests/tests/accessibility/AndroidManifest.xml
index 319fb49..b3bcbc8 100644
--- a/tests/tests/accessibility/AndroidManifest.xml
+++ b/tests/tests/accessibility/AndroidManifest.xml
@@ -17,18 +17,19 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="android.view.cts.accessibility">
+          package="android.view.cts.accessibility">
 
-  <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-  <application android:theme="@android:style/Theme.Holo.NoActionBar" >
-      <uses-library android:name="android.test.runner"/>
-  </application>
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
 
-  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                   android:targetPackage="android.view.cts.accessibility"
-                   android:label="Tests for the accessibility APIs.">
+    <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.view.cts.accessibility"
+                     android:label="Tests for the accessibility APIs.">
         <meta-data android:name="listener"
-            android:value="com.android.cts.runner.CtsTestRunListener" />
+                   android:value="com.android.cts.runner.CtsTestRunListener" />
     </instrumentation>
 
 </manifest>
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
new file mode 100644
index 0000000..53eb215
--- /dev/null
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.accessibility.cts;
+
+import android.app.UiAutomation;
+import android.os.ParcelFileDescriptor;
+import android.test.InstrumentationTestCase;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+
+/**
+ * Tests whether the CaptioningManager APIs are functional.
+ */
+public class CaptioningManagerTest extends InstrumentationTestCase {
+    private CaptioningManager mManager;
+    private UiAutomation mUiAutomation;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mManager = getInstrumentation().getTargetContext().getSystemService(
+                CaptioningManager.class);
+
+        assertNotNull("Obtained captioning manager", mManager);
+
+        mUiAutomation = getInstrumentation().getUiAutomation();
+    }
+
+    /**
+     * Tests whether a client can observe changes in caption properties.
+     */
+    public void testChangeListener() {
+        putSecureSetting("accessibility_captioning_enabled","0");
+        putSecureSetting("accessibility_captioning_preset", "1");
+        putSecureSetting("accessibility_captioning_locale", "en_US");
+        putSecureSetting("accessibility_captioning_font_scale", "1.0");
+
+        MockCaptioningChangeListener listener = new MockCaptioningChangeListener();
+        mManager.addCaptioningChangeListener(listener);
+
+        putSecureSetting("accessibility_captioning_enabled", "1");
+        assertTrue("Observed enabled change", listener.wasEnabledChangedCalled);
+
+        putSecureSetting("accessibility_captioning_preset", "-1");
+        assertTrue("Observed user style change", listener.wasUserStyleChangedCalled);
+
+        putSecureSetting("accessibility_captioning_locale", "ja_JP");
+        assertTrue("Observed locale change", listener.wasLocaleChangedCalled);
+
+        putSecureSetting("accessibility_captioning_font_scale", "2.0");
+        assertTrue("Observed font scale change", listener.wasFontScaleChangedCalled);
+
+        mManager.removeCaptioningChangeListener(listener);
+
+        listener.reset();
+
+        putSecureSetting("accessibility_captioning_enabled","0");
+        assertFalse("Did not observe enabled change", listener.wasEnabledChangedCalled);
+
+        try {
+            mManager.removeCaptioningChangeListener(listener);
+        } catch (Exception e) {
+            throw new AssertionError("Fails silently when removing listener twice", e);
+        }
+    }
+
+    public void testProperties() {
+        putSecureSetting("accessibility_captioning_font_scale", "2.0");
+        putSecureSetting("accessibility_captioning_locale", "ja_JP");
+        putSecureSetting("accessibility_captioning_enabled", "1");
+
+        assertEquals("Test runner set font scale to 2.0", 2.0f, mManager.getFontScale());
+        assertEquals("Test runner set locale to Japanese", Locale.JAPAN, mManager.getLocale());
+        assertEquals("Test runner set enabled to true", true, mManager.isEnabled());
+    }
+
+    public void testUserStyle() {
+        putSecureSetting("accessibility_captioning_preset", "-1");
+        putSecureSetting("accessibility_captioning_foreground_color", "511");
+        putSecureSetting("accessibility_captioning_background_color", "511");
+        putSecureSetting("accessibility_captioning_window_color", "511");
+        putSecureSetting("accessibility_captioning_edge_color", "511");
+        putSecureSetting("accessibility_captioning_edge_type", "-1");
+        deleteSecureSetting("accessibility_captioning_typeface");
+
+        CaptionStyle userStyle = mManager.getUserStyle();
+        assertNotNull("Default user style is not null", userStyle);
+        assertFalse("Default user style has no edge type", userStyle.hasEdgeType());
+        assertFalse("Default user style has no edge color", userStyle.hasEdgeColor());
+        assertFalse("Default user style has no foreground color", userStyle.hasForegroundColor());
+        assertFalse("Default user style has no background color", userStyle.hasBackgroundColor());
+        assertFalse("Default user style has no window color", userStyle.hasWindowColor());
+        assertNull("Default user style has no typeface", userStyle.getTypeface());
+    }
+
+    private void deleteSecureSetting(String name) {
+        execShellCommand("settings delete secure " + name);
+    }
+
+    private void putSecureSetting(String name, String value) {
+        execShellCommand("settings put secure " + name + " " + value);
+    }
+
+    private void execShellCommand(String cmd) {
+        ParcelFileDescriptor pfd = mUiAutomation.executeShellCommand(cmd);
+        InputStream is = new FileInputStream(pfd.getFileDescriptor());
+        try {
+            final byte[] buffer = new byte[8192];
+            while ((is.read(buffer)) != -1);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to exec: " + cmd);
+        }
+    }
+
+    private static class MockCaptioningChangeListener extends CaptioningChangeListener {
+        boolean wasEnabledChangedCalled = false;
+        boolean wasUserStyleChangedCalled = false;
+        boolean wasLocaleChangedCalled = false;
+        boolean wasFontScaleChangedCalled = false;
+
+        @Override
+        public void onEnabledChanged(boolean enabled) {
+            super.onEnabledChanged(enabled);
+            wasEnabledChangedCalled = true;
+        }
+
+        @Override
+        public void onUserStyleChanged(CaptionStyle userStyle) {
+            super.onUserStyleChanged(userStyle);
+            wasUserStyleChangedCalled = true;
+        }
+
+        @Override
+        public void onLocaleChanged(Locale locale) {
+            super.onLocaleChanged(locale);
+            wasLocaleChangedCalled = true;
+        }
+
+        @Override
+        public void onFontScaleChanged(float fontScale) {
+            super.onFontScaleChanged(fontScale);
+            wasFontScaleChangedCalled = true;
+        }
+
+        public void reset() {
+            wasEnabledChangedCalled = false;
+            wasUserStyleChangedCalled = false;
+            wasLocaleChangedCalled = false;
+            wasFontScaleChangedCalled = false;
+        }
+    }
+}
diff --git a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
index a68d860..2c87b78 100644
--- a/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -96,6 +96,82 @@
         }
     }
 
+    public void testDownloadManagerSupportsHttp() throws Exception {
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            long id = mDownloadManager.enqueue(new Request(getGoodUrl()));
+
+            assertEquals(1, getTotalNumberDownloads());
+
+            assertDownloadQueryableById(id);
+
+            receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    public void testDownloadManagerSupportsHttpWithExternalWebServer() throws Exception {
+        // As a result of testDownloadManagerSupportsHttpsWithExternalWebServer relying on an
+        // external resource https://www.example.com this test uses http://www.example.com to help
+        // disambiguate errors from testDownloadManagerSupportsHttpsWithExternalWebServer.
+
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            long id = mDownloadManager.enqueue(new Request(Uri.parse("http://www.example.com")));
+
+            assertEquals(1, getTotalNumberDownloads());
+
+            assertDownloadQueryableById(id);
+
+            receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    public void testDownloadManagerSupportsHttpsWithExternalWebServer() throws Exception {
+        // For HTTPS, DownloadManager trusts only SSL server certs issued by CAs trusted by the
+        // system. Unfortunately, this means that it cannot trust the mock web server's SSL cert.
+        // Until this is resolved (e.g., by making it possible to specify additional CA certs to
+        // trust for a particular download), this test relies on https://www.example.com being
+        // operational and reachable from the Android under test.
+
+        final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+        try {
+            IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+            mContext.registerReceiver(receiver, intentFilter);
+
+            long id = mDownloadManager.enqueue(new Request(Uri.parse("https://www.example.com")));
+
+            assertEquals(1, getTotalNumberDownloads());
+
+            assertDownloadQueryableById(id);
+
+            receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+
+            assertDownloadQueryableByStatus(DownloadManager.STATUS_SUCCESSFUL);
+
+            assertRemoveDownload(id, 0);
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
     public void testMinimumDownload() throws Exception {
         final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
         try {
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index 8d96d91..9f2086d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -809,7 +809,6 @@
     }
 
     public void testReset() {
-
         Paint p  = new Paint();
         ColorFilter c = new ColorFilter();
         MaskFilter m  = new MaskFilter();
@@ -845,7 +844,6 @@
         assertEquals(null, p.getShader());
         assertEquals(null, p.getTypeface());
         assertEquals(null, p.getXfermode());
-
     }
 
     public void testSetLinearText() {
@@ -1184,6 +1182,51 @@
         }
     }
 
+    public void testGetRunAdvance_nonzeroIndex() {
+        Paint p = new Paint();
+        final String text = "Android powers hundreds of millions of mobile " +
+                "devices in more than 190 countries around the world. It's" +
+                "the largest installed base of any mobile platform and" +
+                "growing fast—every day another million users power up their" +
+                "Android devices for the first time and start looking for" +
+                "apps, games, and other digital content.";
+        // Test offset index does not affect width.
+        final float widthAndroidFirst = p.getRunAdvance(
+                text, 0, 7, 0, text.length(), false, 7);
+        final float widthAndroidSecond = p.getRunAdvance(
+                text, 215, 222, 0, text.length(), false, 222);
+        assertTrue(Math.abs(widthAndroidFirst - widthAndroidSecond) < 1);
+    }
+
+    public void testGetRunAdvance_glyphDependingContext() {
+        Paint p = new Paint();
+        // Test the context change the character shape.
+        // First character should be isolated form because the context ends at index 1.
+        final float isolatedFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 1, true, 1);
+        // First character should be initial form because the context ends at index 2.
+        final float initialFormWidth = p.getRunAdvance("\u0644\u0644", 0, 1, 0, 2, true, 1);
+        assertTrue(isolatedFormWidth > initialFormWidth);
+    }
+
+    public void testGetRunAdvance_arabic() {
+        Paint p = new Paint();
+        // Test total width is equals to sum of each character's width.
+        // "What is Unicode?" in Arabic.
+        final String text =
+                "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" +
+                "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" +
+                "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" +
+                "\u062F\u061F";
+        final float totalWidth = p.getRunAdvance(
+                text, 0, text.length(), 0, text.length(), true, text.length());
+        float sumOfCharactersWidth = 0;
+        for (int i = 0; i < text.length(); i++) {
+            sumOfCharactersWidth += p.getRunAdvance(
+                    text, i, i + 1, 0, text.length(), true, i + 1);
+        }
+        assertTrue(Math.abs(totalWidth - sumOfCharactersWidth) < 1);
+    }
+
     public void testGetOffsetForAdvance() {
         Paint p = new Paint();
         {
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 457c688..88e7acb 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -164,21 +164,6 @@
 
     }
 
-    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 0d3c22f..79d2a1d 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
@@ -184,25 +184,6 @@
         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();
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 608805a..a91353f 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
@@ -546,13 +546,6 @@
         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();
 
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 9d9f52f..eeda22c 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -189,16 +189,6 @@
         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 f24d6e2..a2f9ddf 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
@@ -502,19 +502,6 @@
         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();
@@ -1467,11 +1454,6 @@
             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 2c7209b..720397c 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
@@ -197,16 +197,6 @@
         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());
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 186010b..5b3234e 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
@@ -332,18 +332,6 @@
         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/AndroidManifest.xml b/tests/tests/hardware/AndroidManifest.xml
index 7b15b61..1d1e3a8 100644
--- a/tests/tests/hardware/AndroidManifest.xml
+++ b/tests/tests/hardware/AndroidManifest.xml
@@ -71,6 +71,8 @@
             android:process=":camera2ActivityProcess">
         </activity>
 
+        <activity android:name="android.hardware.input.cts.InputCtsActivity"
+            android:label="InputCtsActivity" />
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/hardware/res/raw/gamepad_press_a.json b/tests/tests/hardware/res/raw/gamepad_press_a.json
new file mode 100644
index 0000000..ff3ca4f
--- /dev/null
+++ b/tests/tests/hardware/res/raw/gamepad_press_a.json
@@ -0,0 +1,39 @@
+{
+    "id": 1,
+    "command": "register",
+    "name": "Odie (Test)",
+    "vid": 0x18d1,
+    "pid": 0x2c40,
+    "descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x09, 0x0a, 0x01, 0x00,
+        0x0a, 0x02, 0x00, 0x0a, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x0a, 0x07, 0x00, 0x0a, 0x08, 0x00,
+        0x0a, 0x0e, 0x00, 0x0a, 0x0f, 0x00, 0x0a, 0x0d, 0x00, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x0a,
+        0x23, 0x02, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0b, 0x81, 0x02, 0x75, 0x01, 0x95,
+        0x01, 0x81, 0x03, 0x05, 0x01, 0x75, 0x04, 0x95, 0x01, 0x25, 0x07, 0x46, 0x3b, 0x01, 0x66,
+        0x14, 0x00, 0x09, 0x39, 0x81, 0x42, 0x66, 0x00, 0x00, 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30,
+        0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x05, 0x02, 0x09, 0xc5, 0x09, 0xc4, 0x15, 0x00, 0x26,
+        0xff, 0x00, 0x35, 0x00, 0x46, 0xff, 0x00, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0xc0, 0x85,
+        0x02, 0x05, 0x08, 0x0a, 0x01, 0x00, 0x0a, 0x02, 0x00, 0x0a, 0x03, 0x00, 0x0a, 0x04, 0x00,
+        0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x04, 0x91, 0x02, 0x75, 0x04, 0x95, 0x01, 0x91,
+        0x03, 0xc0, 0x05, 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x85, 0x03, 0x05, 0x01, 0x09, 0x06, 0xa1,
+        0x02, 0x05, 0x06, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x81,
+        0x02, 0x06, 0xbc, 0xff, 0x0a, 0xad, 0xbd, 0x75, 0x08, 0x95, 0x06, 0x81, 0x02, 0xc0, 0xc0],
+    "report": [0x01, 0x00, 0x80, 0x90, 0x80, 0x7f, 0x73, 0x00, 0x00]
+}
+
+{
+    "id": 1,
+    "command": "report",
+    "report": [0x01, 0x01, 0x80, 0x90, 0x80, 0x7f, 0x73, 0x00, 0x00]
+}
+
+{
+    "id": 1,
+    "command": "delay",
+    "duration": 10
+}
+
+{
+    "id": 1,
+    "command": "report",
+    "report": [0x01, 0x00, 0x80, 0x90, 0x80, 0x7f, 0x73, 0x00, 0x00]
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index 6f2eaf9..b744686 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -1677,7 +1677,9 @@
         // Validate capture result vs. request
         Size resultThumbnailSize = captureResult.get(CaptureResult.JPEG_THUMBNAIL_SIZE);
         int orientationTested = expectedExifData.jpegOrientation;
-        if ((orientationTested == 90 || orientationTested == 270)) {
+        // Legacy shim always doesn't rotate thumbnail size
+        if ((orientationTested == 90 || orientationTested == 270) &&
+                staticInfo.isHardwareLevelLimitedOrBetter()) {
             int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                     /*defaultValue*/-1);
             if (exifOrientation == ExifInterface.ORIENTATION_UNDEFINED) {
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 8184226..0da0ce7 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -115,19 +115,7 @@
                     continue;
                 }
 
-                Size[] targetCaptureSizes =
-                        mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
-                                StaticMetadata.StreamDirection.Output);
-
-                assertTrue("No capture sizes available for RAW format!",
-                        targetCaptureSizes.length != 0);
-                Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
-                Size activeArraySize = new Size(activeArray.width(), activeArray.height());
-                assertTrue("Missing ActiveArraySize", activeArray.width() > 0 &&
-                        activeArray.height() > 0);
-                // TODO: Allow PixelArraySize also.
-                assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
-                        targetCaptureSizes, activeArraySize);
+                Size activeArraySize = mStaticInfo.getRawDimensChecked();
 
                 // Create capture image reader
                 CameraTestUtils.SimpleImageReaderListener captureListener
@@ -202,19 +190,7 @@
                     continue;
                 }
 
-                Size[] targetCaptureSizes =
-                        mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
-                                StaticMetadata.StreamDirection.Output);
-
-                assertTrue("No capture sizes available for RAW format!",
-                        targetCaptureSizes.length != 0);
-                Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
-                Size activeArraySize = new Size(activeArray.width(), activeArray.height());
-                assertTrue("Missing ActiveArraySize", activeArray.width() > 0 &&
-                        activeArray.height() > 0);
-                // TODO: Allow PixelArraySize also.
-                assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
-                        targetCaptureSizes, activeArraySize);
+                Size activeArraySize = mStaticInfo.getRawDimensChecked();
 
                 Size[] targetPreviewSizes =
                         mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888,
@@ -322,19 +298,7 @@
                     continue;
                 }
 
-                Size[] targetCaptureSizes =
-                        mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
-                                StaticMetadata.StreamDirection.Output);
-
-                assertTrue("No capture sizes available for RAW format!",
-                        targetCaptureSizes.length != 0);
-                Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
-                Size activeArraySize = new Size(activeArray.width(), activeArray.height());
-                assertTrue("Active array has invalid size!", activeArray.width() > 0 &&
-                        activeArray.height() > 0);
-                // TODO: Allow PixelArraySize also.
-                assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
-                        targetCaptureSizes, activeArraySize);
+                Size activeArraySize = mStaticInfo.getRawDimensChecked();
 
                 // Get largest jpeg size
                 Size[] targetJpegSizes =
@@ -369,8 +333,8 @@
                         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));
+                assertTrue("Raw bitmap size must be equal to either pre-correction active array" +
+                        " size or pixel array size.", rawBitmapSize.equals(activeArraySize));
 
                 byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
 
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 aa1c3fc..695df10 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -395,6 +395,15 @@
             // Ensure that max YUV size matches max JPEG size
             Size maxYuvSize = CameraTestUtils.getMaxSize(
                     config.getOutputSizes(ImageFormat.YUV_420_888));
+            Size maxFastYuvSize = maxYuvSize;
+
+            Size[] slowYuvSizes = config.getHighResolutionOutputSizes(ImageFormat.YUV_420_888);
+            assertTrue("Null slow YUV size array not allowed with BURST_CAPTURE capability!",
+                    slowYuvSizes != null);
+            if (slowYuvSizes.length > 0) {
+                Size maxSlowYuvSize = CameraTestUtils.getMaxSize(slowYuvSizes);
+                maxYuvSize = CameraTestUtils.getMaxSize(new Size[]{maxYuvSize, maxSlowYuvSize});
+            }
             Size maxJpegSize = CameraTestUtils.getMaxSize(config.getOutputSizes(ImageFormat.JPEG));
 
             boolean haveMaxYuv = maxYuvSize != null ?
@@ -413,19 +422,29 @@
             boolean haveAwbLock = CameraTestUtils.getValueNotNull(
                     c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
 
-            // Ensure that YUV output is fast enough - needs to be at least 20 fps
+            // Ensure that max YUV output is fast enough - needs to be at least 10 fps
 
             long maxYuvRate =
                 config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
-            final long MIN_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps
+            final long MIN_MAXSIZE_DURATION_BOUND_NS = 100000000; // 100 ms, 10 fps
+            boolean haveMaxYuvRate = maxYuvRate <= MIN_MAXSIZE_DURATION_BOUND_NS;
 
-            boolean haveMaxYuvRate = maxYuvRate <= MIN_DURATION_BOUND_NS;
+            // Ensure that some >=8MP YUV output is fast enough - needs to be at least 20 fps
+
+            long maxFastYuvRate =
+                    config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxFastYuvSize);
+            final long MIN_8MP_DURATION_BOUND_NS = 200000000; // 50 ms, 20 fps
+            boolean haveFastYuvRate = maxFastYuvRate <= MIN_8MP_DURATION_BOUND_NS;
+
+            final int SIZE_8MP_BOUND = 8000000;
+            boolean havefast8MPYuv = (maxFastYuvSize.getWidth() * maxFastYuvSize.getHeight()) >
+                    SIZE_8MP_BOUND;
 
             // Ensure that there's an FPS range that's fast enough to capture at above
-            // minFrameDuration, for full-auto bursts
+            // minFrameDuration, for full-auto bursts at the fast resolutions
             Range[] fpsRanges = CameraTestUtils.getValueNotNull(
                     c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
-            float minYuvFps = 1.f / maxYuvRate;
+            float minYuvFps = 1.f / maxFastYuvRate;
 
             boolean haveFastAeTargetFps = false;
             for (Range<Integer> r : fpsRanges) {
@@ -454,11 +473,18 @@
                                 mIds[counter]),
                         haveMaxYuv);
                 assertTrue(
-                        String.format("BURST-capable camera device %s YUV frame rate is too slow" +
+                        String.format("BURST-capable camera device %s max-resolution " +
+                                "YUV frame rate is too slow" +
                                 "(%d ns min frame duration reported, less than %d ns expected)",
-                                mIds[counter], maxYuvRate, MIN_DURATION_BOUND_NS),
+                                mIds[counter], maxYuvRate, MIN_MAXSIZE_DURATION_BOUND_NS),
                         haveMaxYuvRate);
                 assertTrue(
+                        String.format("BURST-capable camera device %s >= 8MP YUV output " +
+                                "frame rate is too slow" +
+                                "(%d ns min frame duration reported, less than %d ns expected)",
+                                mIds[counter], maxYuvRate, MIN_8MP_DURATION_BOUND_NS),
+                        haveFastYuvRate);
+                assertTrue(
                         String.format("BURST-capable camera device %s does not list an AE target " +
                                 " FPS range with min FPS >= %f, for full-AUTO bursts",
                                 mIds[counter], minYuvFps),
@@ -553,6 +579,7 @@
                     // Verify camera can output the reprocess input formats and sizes.
                     Size[] inputSizes = configs.getInputSizes(input);
                     Size[] outputSizes = configs.getOutputSizes(input);
+                    Size[] highResOutputSizes = configs.getHighResolutionOutputSizes(input);
                     assertTrue("no input size supported for format " + input,
                             inputSizes.length > 0);
                     assertTrue("no output size supported for format " + input,
@@ -560,7 +587,9 @@
 
                     for (Size inputSize : inputSizes) {
                         assertTrue("Camera must be able to output the supported reprocessing " +
-                            "input size", arrayContains(outputSizes, inputSize));
+                                "input size",
+                                arrayContains(outputSizes, inputSize) ||
+                                arrayContains(highResOutputSizes, inputSize));
                     }
                 }
             }
@@ -618,6 +647,13 @@
                         config.isOutputSupportedFor(format));
                 List<Size> supportedSizes = CameraTestUtils.getAscendingOrderSizes(
                         Arrays.asList(config.getOutputSizes(format)), /*ascending*/true);
+                if (arrayContains(actualCapabilities,
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
+                    supportedSizes.addAll(
+                        Arrays.asList(config.getHighResolutionOutputSizes(format)));
+                    supportedSizes = CameraTestUtils.getAscendingOrderSizes(
+                        supportedSizes, /*ascending*/true);
+                }
                 assertTrue("Supported format " + format + " has no sizes listed",
                         supportedSizes.size() > 0);
                 for (int i = 0; i < supportedSizes.size(); i++) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index d75550a..a078ad4 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -198,22 +198,18 @@
         sanityCheckConfigurationTables(TABLES);
 
         for (String id : mCameraIds) {
+            openDevice(id);
 
             // Find the concrete max sizes for each format/resolution combination
+            MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id);
 
-            CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
-
-            MaxStreamSizes maxSizes = new MaxStreamSizes(cc, id);
-
-            final StaticMetadata staticInfo = new StaticMetadata(cc);
             String streamConfigurationMapString =
-                    cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).toString();
+                    mStaticInfo.getCharacteristics().get(
+                            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).toString();
             if (VERBOSE) {
                 Log.v(TAG, "StreamConfigurationMap: " + streamConfigurationMapString);
             }
 
-            openDevice(id);
-
             // Always run legacy-level tests
 
             for (int[] config : LEGACY_COMBINATIONS) {
@@ -222,7 +218,7 @@
 
             // Then run higher-level tests if applicable
 
-            if (!staticInfo.isHardwareLevelLegacy()) {
+            if (!mStaticInfo.isHardwareLevelLegacy()) {
 
                 // If not legacy, at least limited, so run limited-level tests
 
@@ -232,20 +228,20 @@
 
                 // Check for BURST_CAPTURE, FULL and RAW and run those if appropriate
 
-                if (staticInfo.isCapabilitySupported(
+                if (mStaticInfo.isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
                     for (int[] config : BURST_COMBINATIONS) {
                         testOutputCombination(id, config, maxSizes);
                     }
                 }
 
-                if (staticInfo.isHardwareLevelFull()) {
+                if (mStaticInfo.isHardwareLevelFull()) {
                     for (int[] config : FULL_COMBINATIONS) {
                         testOutputCombination(id, config, maxSizes);
                     }
                 }
 
-                if (staticInfo.isCapabilitySupported(
+                if (mStaticInfo.isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
                     for (int[] config : RAW_COMBINATIONS) {
                         testOutputCombination(id, config, maxSizes);
@@ -316,8 +312,8 @@
 
         for (String id : mCameraIds) {
             CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
-            MaxStreamSizes maxSizes = new MaxStreamSizes(cc, id);
             StaticMetadata staticInfo = new StaticMetadata(cc);
+            MaxStreamSizes maxSizes = new MaxStreamSizes(staticInfo, id);
 
             // Skip the test for legacy devices.
             if (staticInfo.isHardwareLevelLegacy()) {
@@ -396,15 +392,15 @@
         static final int VGA = 3;
         static final int RESOLUTION_COUNT = 4;
 
-        public MaxStreamSizes(CameraCharacteristics cc, String cameraId) {
-            StreamConfigurationMap configs =
-                    cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-            Size[] privSizes = configs.getOutputSizes(SurfaceTexture.class);
-            Size[] yuvSizes = configs.getOutputSizes(ImageFormat.YUV_420_888);
-            Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
-            Size[] rawSizes = configs.getOutputSizes(ImageFormat.RAW_SENSOR);
+        public MaxStreamSizes(StaticMetadata sm, String cameraId) {
+            Size[] privSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.PRIVATE,
+                    StaticMetadata.StreamDirection.Output);
+            Size[] yuvSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888,
+                    StaticMetadata.StreamDirection.Output);
+            Size[] jpegSizes = sm.getJpegOutputSizesChecked();
+            Size[] rawSizes = sm.getRawOutputSizesChecked();
 
-            maxRawSize = (rawSizes != null) ? CameraTestUtils.getMaxSize(rawSizes) : null;
+            maxRawSize = (rawSizes.length != 0) ? CameraTestUtils.getMaxSize(rawSizes) : null;
 
             maxPrivSizes[PREVIEW] = getMaxSize(privSizes, PREVIEW_SIZE_BOUND);
             maxYuvSizes[PREVIEW]  = getMaxSize(yuvSizes, PREVIEW_SIZE_BOUND);
@@ -418,10 +414,14 @@
             maxYuvSizes[MAXIMUM] = CameraTestUtils.getMaxSize(yuvSizes);
             maxJpegSizes[MAXIMUM] = CameraTestUtils.getMaxSize(jpegSizes);
 
-            maxInputPrivSize = CameraTestUtils.getMaxSize(
-                    configs.getInputSizes(ImageFormat.PRIVATE));
-            maxInputYuvSize = CameraTestUtils.getMaxSize(
-                    configs.getInputSizes(ImageFormat.YUV_420_888));
+            StreamConfigurationMap configs = sm.getCharacteristics().get(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            Size[] privInputSizes = configs.getInputSizes(ImageFormat.PRIVATE);
+            maxInputPrivSize = privInputSizes != null ?
+                    CameraTestUtils.getMaxSize(privInputSizes) : null;
+            Size[] yuvInputSizes = configs.getInputSizes(ImageFormat.YUV_420_888);
+            maxInputYuvSize = yuvInputSizes != null ?
+                    CameraTestUtils.getMaxSize(yuvInputSizes) : null;
 
             // Must always be supported, add unconditionally
             final Size vgaSize = new Size(640, 480);
@@ -561,16 +561,17 @@
         boolean supportOpaqueReprocess = staticInfo.isCapabilitySupported(
                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
 
+        // Skip the configuration if the format is not supported for reprocessing.
+        if ((reprocessConfig[0] == YUV && !supportYuvReprocess) ||
+                (reprocessConfig[0] == PRIV && !supportOpaqueReprocess)) {
+            return;
+        }
+
         try {
             // reprocessConfig[0:1] is input
             InputConfiguration inputConfig = getInputConfig(
                     Arrays.copyOfRange(reprocessConfig, 0, 2), maxSizes);
 
-            // Skip the configuration if the format is not supported for reprocessing.
-            if ((inputConfig.getFormat() == ImageFormat.YUV_420_888 && !supportYuvReprocess) ||
-                    (inputConfig.getFormat() == ImageFormat.PRIVATE && !supportOpaqueReprocess)) {
-                return;
-            }
 
             inputReader = ImageReader.newInstance(inputConfig.getWidth(), inputConfig.getHeight(),
                     inputConfig.getFormat(), NUM_REPROCESS_CAPTURES);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
index 0dba61e..e876d1b 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -102,7 +102,7 @@
             }
 
             // Max jpeg resolution must be very close to  sensor resolution
-            Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
+            Size[] jpegSizes = mStaticInfo.getJpegOutputSizesChecked();
             Size maxJpegSize = CameraTestUtils.getMaxSize(jpegSizes);
             mCollector.expectSizesAreSimilar(
                     "Active array size and max JPEG size should be similar",
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 73cfaf7..ee4ddd9 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -663,16 +663,7 @@
      */
     private void rawCaptureTestByCamera() throws Exception {
         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
-        Size[] rawSizes = mStaticInfo.getRawOutputSizesChecked();
-
-        assertTrue("No capture sizes available for RAW format!",
-                rawSizes.length != 0);
-        Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
-        Size size = new Size(activeArray.width(), activeArray.height());
-        assertTrue("Missing ActiveArraySize", activeArray.width() > 0 &&
-                activeArray.height() > 0);
-        assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
-                rawSizes, size);
+        Size size = mStaticInfo.getRawDimensChecked();
 
         // Prepare raw capture and start preview.
         CaptureRequest.Builder previewBuilder =
@@ -711,20 +702,12 @@
     private void fullRawCaptureTestByCamera() throws Exception {
         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
         Size maxStillSz = mOrderedStillSizes.get(0);
-        Size[] rawSizes = mStaticInfo.getRawOutputSizesChecked();
 
         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
         SimpleImageReaderListener jpegListener = new SimpleImageReaderListener();
         SimpleImageReaderListener rawListener = new SimpleImageReaderListener();
 
-        assertTrue("No capture sizes available for RAW format!",
-                rawSizes.length != 0);
-        Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
-        Size size = new Size(activeArray.width(), activeArray.height());
-        assertTrue("Missing ActiveArraySize", activeArray.width() > 0 &&
-                activeArray.height() > 0);
-        assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
-                rawSizes, size);
+        Size size = mStaticInfo.getRawDimensChecked();
 
         if (VERBOSE) {
             Log.v(TAG, "Testing multi capture with size " + size.toString()
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 3890b33..a828371 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
@@ -41,6 +41,8 @@
 import java.util.List;
 import java.util.Set;
 
+import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+
 /**
  * Helpers to get common static info out of the camera.
  *
@@ -743,6 +745,26 @@
     }
 
     /**
+     * Get and check pre-correction active array size.
+     */
+    public Rect getPreCorrectedActiveArraySizeChecked() {
+        Key<Rect> key = CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE;
+        Rect activeArray = getValueFromKeyNonNull(key);
+
+        if (activeArray == null) {
+            return new Rect(0, 0, 0, 0);
+        }
+
+        Size pixelArraySize = getPixelArraySizeChecked();
+        checkTrueForKey(key, "values left/top are invalid", activeArray.left >= 0 && activeArray.top >= 0);
+        checkTrueForKey(key, "values width/height are invalid",
+                activeArray.width() <= pixelArraySize.getWidth() &&
+                activeArray.height() <= pixelArraySize.getHeight());
+
+        return activeArray;
+    }
+
+    /**
      * Get and check active array size.
      */
     public Rect getActiveArraySizeChecked() {
@@ -763,6 +785,29 @@
     }
 
     /**
+     * Get the dimensions to use for RAW16 buffers.
+     */
+    public Size getRawDimensChecked() throws Exception {
+        Size[] targetCaptureSizes = getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+                        StaticMetadata.StreamDirection.Output);
+        Assert.assertTrue("No capture sizes available for RAW format!",
+                targetCaptureSizes.length != 0);
+        Rect activeArray = getPreCorrectedActiveArraySizeChecked();
+        Size preCorrectionActiveArraySize =
+                new Size(activeArray.width(), activeArray.height());
+        Size pixelArraySize = getPixelArraySizeChecked();
+        Assert.assertTrue("Missing pre-correction active array size", activeArray.width() > 0 &&
+                activeArray.height() > 0);
+        Assert.assertTrue("Missing pixel array size", pixelArraySize.getWidth() > 0 &&
+                pixelArraySize.getHeight() > 0);
+        Size[] allowedArraySizes = new Size[] { preCorrectionActiveArraySize,
+                pixelArraySize };
+        return assertArrayContainsAnyOf("Available sizes for RAW format" +
+                " must include either the pre-corrected active array size, or the full " +
+                "pixel array size", targetCaptureSizes, allowedArraySizes);
+    }
+
+    /**
      * Get the sensitivity value and clamp to the range if needed.
      *
      * @param sensitivity Input sensitivity value to check.
@@ -1168,7 +1213,7 @@
      *
      * @return Empty size array if jpeg output is not supported
      */
-    public Size[] getJpegOutputSizeChecked() {
+    public Size[] getJpegOutputSizesChecked() {
         return getAvailableSizesForFormatChecked(ImageFormat.JPEG,
                 StreamDirection.Output);
     }
@@ -1236,6 +1281,22 @@
      * @return The sizes of the given format, empty array if no available size is found.
      */
     public Size[] getAvailableSizesForFormatChecked(int format, StreamDirection direction) {
+        return getAvailableSizesForFormatChecked(format, direction,
+                /*fastSizes*/true, /*slowSizes*/true);
+    }
+
+    /**
+     * Get available sizes for given format and direction, and whether to limit to slow or fast
+     * resolutions.
+     *
+     * @param format The format for the requested size array.
+     * @param direction The stream direction, input or output.
+     * @param fastSizes whether to include getOutputSizes() sizes (generally faster)
+     * @param slowSizes whether to include getHighResolutionOutputSizes() sizes (generally slower)
+     * @return The sizes of the given format, empty array if no available size is found.
+     */
+    public Size[] getAvailableSizesForFormatChecked(int format, StreamDirection direction,
+            boolean fastSizes, boolean slowSizes) {
         Key<StreamConfigurationMap> key =
                 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
         StreamConfigurationMap config = getValueFromKeyNonNull(key);
@@ -1244,11 +1305,27 @@
             return new Size[0];
         }
 
-        Size[] sizes;
+        Size[] sizes = null;
 
         switch (direction) {
             case Output:
-                sizes = config.getOutputSizes(format);
+                Size[] fastSizeList = null;
+                Size[] slowSizeList = null;
+                if (fastSizes) {
+                    fastSizeList = config.getOutputSizes(format);
+                }
+                if (slowSizes) {
+                    slowSizeList = config.getHighResolutionOutputSizes(format);
+                }
+                if (fastSizeList != null && slowSizeList != null) {
+                    sizes = new Size[slowSizeList.length + fastSizeList.length];
+                    System.arraycopy(fastSizeList, 0, sizes, 0, fastSizeList.length);
+                    System.arraycopy(slowSizeList, 0, sizes, fastSizeList.length, slowSizeList.length);
+                } else if (fastSizeList != null) {
+                    sizes = fastSizeList;
+                } else if (slowSizeList != null) {
+                    sizes = slowSizeList;
+                }
                 break;
             case Input:
                 sizes = config.getInputSizes(format);
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/InputCallback.java b/tests/tests/hardware/src/android/hardware/input/cts/InputCallback.java
new file mode 100644
index 0000000..accdcaf
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/input/cts/InputCallback.java
@@ -0,0 +1,25 @@
+/*
+ * 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.hardware.input.cts;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public interface InputCallback {
+    public void onKeyEvent(KeyEvent ev);
+    public void onMotionEvent(MotionEvent ev);
+}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java b/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java
new file mode 100644
index 0000000..b16cadb
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java
@@ -0,0 +1,64 @@
+/*
+ * 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.hardware.input.cts;
+
+import android.app.Activity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InputCtsActivity extends Activity {
+    private InputCallback mInputCallback;
+
+    @Override
+    public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+        if (mInputCallback != null) {
+            mInputCallback.onMotionEvent(ev);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (mInputCallback != null) {
+            mInputCallback.onMotionEvent(ev);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean dispatchTrackballEvent(MotionEvent ev) {
+        if (mInputCallback != null) {
+            mInputCallback.onMotionEvent(ev);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent ev) {
+        if (mInputCallback != null) {
+            mInputCallback.onKeyEvent(ev);
+        }
+        return true;
+    }
+
+    public void setInputCallback(InputCallback callback) {
+        mInputCallback = callback;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/GamepadTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/GamepadTestCase.java
new file mode 100644
index 0000000..92fba12
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/GamepadTestCase.java
@@ -0,0 +1,36 @@
+/*
+ * 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.hardware.input.cts.tests;
+
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.io.Writer;
+import java.util.List;
+
+import com.android.cts.hardware.R;
+
+public class GamepadTestCase extends InputTestCase {
+    private static final String TAG = "GamepadTests";
+
+    public void testButtonA() throws Exception {
+        sendHidCommands(R.raw.gamepad_press_a);
+        assertReceivedKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BUTTON_A);
+        assertReceivedKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BUTTON_A);
+        assertNoMoreEvents();
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
new file mode 100644
index 0000000..fba5f51
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
@@ -0,0 +1,221 @@
+/*
+ * 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.hardware.input.cts.tests;
+
+import android.app.UiAutomation;
+import android.hardware.input.cts.InputCtsActivity;
+import android.hardware.input.cts.InputCallback;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.List;
+import java.util.UUID;
+
+public class InputTestCase extends ActivityInstrumentationTestCase2<InputCtsActivity> {
+    private static final String TAG = "InputTestCase";
+    private static final String HID_EXECUTABLE = "hid";
+    private static final int SHELL_UID = 2000;
+    private static final String[] KEY_ACTIONS = {"DOWN", "UP", "MULTIPLE"};
+
+    private File mFifo;
+    private Writer mWriter;
+
+    private BlockingQueue<KeyEvent> mKeys;
+    private BlockingQueue<MotionEvent> mMotions;
+    private InputListener mInputListener;
+
+    public InputTestCase() {
+        super(InputCtsActivity.class);
+        mKeys = new LinkedBlockingQueue<KeyEvent>();
+        mMotions = new LinkedBlockingQueue<MotionEvent>();
+        mInputListener = new InputListener();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mFifo = setupFifo();
+        clearKeys();
+        clearMotions();
+        getActivity().setInputCallback(mInputListener);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mFifo != null) {
+            mFifo.delete();
+            mFifo = null;
+        }
+        closeQuietly(mWriter);
+        mWriter = null;
+        super.tearDown();
+    }
+
+    /**
+     * Sends the HID commands designated by the given resource id.
+     * The commands must be in the format expected by the `hid` shell command.
+     *
+     * @param id The resource id from which to load the HID commands. This must be a "raw"
+     * resource.
+     */
+    public void sendHidCommands(int id) {
+        try {
+            Writer w = getWriter();
+            w.write(getEvents(id));
+            w.flush();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Asserts that the application received a {@link android.view.KeyEvent} with the given action
+     * and keycode.
+     *
+     * If other KeyEvents are received by the application prior to the expected KeyEvent, or no
+     * KeyEvents are received within a reasonable amount of time, then this will throw an
+     * AssertionFailedError.
+     *
+     * @param action The action to expect on the next KeyEvent
+     * (e.g. {@link android.view.KeyEvent#ACTION_DOWN}).
+     * @param keyCode The expected key code of the next KeyEvent.
+     */
+    public void assertReceivedKeyEvent(int action, int keyCode) {
+        KeyEvent k = waitForKey();
+        if (k == null) {
+            fail("Timed out waiting for " + KeyEvent.keyCodeToString(keyCode)
+                    + " with action " + KEY_ACTIONS[action]);
+            return;
+        }
+        assertEquals(action, k.getAction());
+        assertEquals(keyCode, k.getKeyCode());
+    }
+
+    /**
+     * Asserts that no more events have been received by the application.
+     *
+     * If any more events have been received by the application, this throws an
+     * AssertionFailedError.
+     */
+    public void assertNoMoreEvents() {
+        KeyEvent key;
+        MotionEvent motion;
+        if ((key = mKeys.poll()) != null) {
+            fail("Extraneous key events generated: " + key);
+        }
+        if ((motion = mMotions.poll()) != null) {
+            fail("Extraneous motion events generated: " + motion);
+        }
+    }
+
+    private KeyEvent waitForKey() {
+        try {
+            return mKeys.poll(1, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            return null;
+        }
+    }
+
+    private void clearKeys() {
+        mKeys.clear();
+    }
+
+    private void clearMotions() {
+        mMotions.clear();
+    }
+
+    private File setupFifo() throws ErrnoException {
+        File dir = getActivity().getCacheDir();
+        String filename = dir.getAbsolutePath() + File.separator +  UUID.randomUUID().toString();
+        Os.mkfifo(filename, 0666);
+        File f = new File(filename);
+        return f;
+    }
+
+    private Writer getWriter() throws IOException {
+        if (mWriter == null) {
+            UiAutomation ui = getInstrumentation().getUiAutomation();
+            ui.executeShellCommand("hid " + mFifo.getAbsolutePath());
+            mWriter = new FileWriter(mFifo);
+        }
+        return mWriter;
+    }
+
+    private String getEvents(int id) throws IOException {
+        InputStream is =
+            getInstrumentation().getTargetContext().getResources().openRawResource(id);
+        return readFully(is);
+    }
+
+
+    private static void closeQuietly(AutoCloseable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
+            } catch (Exception ignored) { }
+        }
+    }
+
+    private static String readFully(InputStream is) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        int read = 0;
+        byte[] buffer = new byte[1024];
+        while ((read = is.read(buffer)) >= 0) {
+            baos.write(buffer, 0, read);
+        }
+        return baos.toString();
+    }
+
+    private class InputListener implements InputCallback {
+        public void onKeyEvent(KeyEvent ev) {
+            boolean done = false;
+            do {
+                try {
+                    mKeys.put(new KeyEvent(ev));
+                    done = true;
+                } catch (InterruptedException ignore) { }
+            } while (!done);
+        }
+
+        public void onMotionEvent(MotionEvent ev) {
+            boolean done = false;
+            do {
+                try {
+                    mMotions.put(MotionEvent.obtain(ev));
+                    done = true;
+                } catch (InterruptedException ignore) { }
+            } while (!done);
+        }
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
index 2dbcbbf..db7f539 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
@@ -91,6 +91,8 @@
     @SuppressWarnings("deprecation")
     private static final Date NOW_PLUS_10_YEARS = new Date(NOW.getYear() + 10, 0, 1);
 
+    private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+
     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
@@ -721,6 +723,55 @@
     // currently be tested here because CTS does not require that secure lock screen is set up and
     // that at least one fingerprint is enrolled.
 
+    public void testGenerate_EC_ModernSpec_KeyNotYetValid() throws Exception {
+        KeyPairGenerator generator = getEcGenerator();
+        Date validityStart = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setKeySize(256)
+                .setDigests(KeyProperties.DIGEST_SHA256)
+                .setKeyValidityStart(validityStart)
+                .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());
+        assertEquals(validityStart, keyInfo.getKeyValidityStart());
+    }
+
+    public void testGenerate_RSA_ModernSpec_KeyExpiredForOrigination() throws Exception {
+        KeyPairGenerator generator = getRsaGenerator();
+        Date originationEnd = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        generator.initialize(new KeyGenParameterSpec.Builder(
+                TEST_ALIAS_1,
+                KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+                .setKeySize(1024)
+                .setDigests(KeyProperties.DIGEST_SHA256)
+                .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                .setKeyValidityForOriginationEnd(originationEnd)
+                .build());
+        KeyPair keyPair = generator.generateKeyPair();
+        assertGeneratedKeyPairAndSelfSignedCertificate(
+                keyPair,
+                TEST_ALIAS_1,
+                "RSA",
+                1024,
+                DEFAULT_CERT_SUBJECT,
+                DEFAULT_CERT_SERIAL_NUMBER,
+                DEFAULT_CERT_NOT_BEFORE,
+                DEFAULT_CERT_NOT_AFTER);
+        KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+        assertEquals(originationEnd, keyInfo.getKeyValidityForOriginationEnd());
+    }
+
     public void testGenerate_EC_ModernSpec_SupportedSizes() throws Exception {
         assertKeyGenUsingECSizeOnlyUsesCorrectCurve(224, ECCurves.NIST_P_224_SPEC);
         assertKeyGenUsingECSizeOnlyUsesCorrectCurve(256, ECCurves.NIST_P_256_SPEC);
diff --git a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
index f5d024a..34d8b1b 100644
--- a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
@@ -692,14 +692,11 @@
                 TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
 
         good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-
-        assertInitSignThrowsInvalidKeyException("SHA224withRSA", good.build());
         assertInitSignSucceeds("SHA224withRSA", good.build());
         assertInitSignThrowsInvalidKeyException("SHA224withRSA",
                 TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
 
         good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
-        assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS", good.build());
         assertInitSignSucceeds("SHA224withRSA/PSS", good.build());
         assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS",
                 TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
@@ -715,14 +712,11 @@
                 TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
 
         good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-
-        assertInitSignThrowsInvalidKeyException("SHA224withRSA", good.build());
         assertInitSignSucceeds("SHA224withRSA", good.build());
         assertInitSignThrowsInvalidKeyException("SHA224withRSA",
                 TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
 
         good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
-        assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS", good.build());
         assertInitSignSucceeds("SHA224withRSA/PSS", good.build());
         assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS",
                 TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
diff --git a/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java
deleted file mode 100644
index eaf6fdf..0000000
--- a/tests/tests/permission2/src/android/permission2/cts/ReadSocialStreamPermissionTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.permission2.cts;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.StreamItems;
-import android.test.AndroidTestCase;
-
-public class ReadSocialStreamPermissionTest extends AndroidTestCase {
-
-    private ContentResolver mResolver;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mResolver = mContext.getContentResolver();
-    }
-
-    public void testReadSocialStreamPermission_byRawContactId() throws Exception {
-        try {
-            mResolver.query(Uri.withAppendedPath(
-                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, 1337),
-                    Contacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null);
-            fail("Expected a READ_SOCIAL_STREAM exception");
-        } catch (SecurityException e) {
-            // Expect a READ_SOCIAL_STREAM exception.
-        }
-    }
-
-    public void testReadSocialStreamPermission_byContactId() throws Exception {
-        try {
-            mResolver.query(Uri.withAppendedPath(
-                    ContentUris.withAppendedId(Contacts.CONTENT_URI, 1337),
-                    Contacts.StreamItems.CONTENT_DIRECTORY), null, null, null, null);
-            fail("Expected a READ_SOCIAL_STREAM exception");
-        } catch (SecurityException e) {
-            // Expect a READ_SOCIAL_STREAM exception.
-        }
-    }
-
-    public void testReadSocialStreamPermission_byLookUpKey() throws Exception {
-        try {
-            mResolver.query(Contacts.CONTENT_LOOKUP_URI.buildUpon()
-                    .appendPath("lookDownKey")
-                    .appendPath(Contacts.StreamItems.CONTENT_DIRECTORY)
-                    .build(), null, null, null, null);
-            fail("Expected a READ_SOCIAL_STREAM exception");
-        } catch (SecurityException e) {
-            // Expect a READ_SOCIAL_STREAM exception.
-        }
-    }
-
-    public void testReadSocialStreamPermission_byStreamItemId() throws Exception {
-        try {
-            mResolver.query(ContentUris.withAppendedId(StreamItems.CONTENT_URI, 1337),
-                    null, null, null, null);
-            fail("Expected a READ_SOCIAL_STREAM exception");
-        } catch (SecurityException e) {
-            // Expect a READ_SOCIAL_STREAM exception.
-        }
-    }
-}
diff --git a/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java
deleted file mode 100644
index 262fcfa..0000000
--- a/tests/tests/permission2/src/android/permission2/cts/WriteSocialStreamPermissionTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.permission2.cts;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.net.Uri;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.StreamItems;
-import android.test.AndroidTestCase;
-
-public class WriteSocialStreamPermissionTest extends AndroidTestCase {
-
-    private ContentResolver mResolver;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mResolver = mContext.getContentResolver();
-    }
-
-    public void testWriteSocialStreamPermission_byContentDirectory() throws Exception {
-        try {
-            ContentValues values = new ContentValues();
-            mResolver.insert(Uri.withAppendedPath(
-                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, 1337),
-                    RawContacts.StreamItems.CONTENT_DIRECTORY), values);
-            fail("Expected a WRITE_SOCIAL_STREAM exception");
-        } catch (SecurityException e) {
-            // Expect a WRITE_SOCIAL_STREAM exception.
-        }
-    }
-
-    public void testWriteSocialStreamPermission_byContentUri() throws Exception {
-        try {
-            ContentValues values = new ContentValues();
-            mResolver.insert(StreamItems.CONTENT_URI, values);
-            fail("Expected a WRITE_SOCIAL_STREAM exception");
-        } catch (SecurityException e) {
-            // Expect a WRITE_SOCIAL_STREAM exception.
-        }
-    }
-}
diff --git a/tests/tests/provider/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 5f18635..b1a7130 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -37,10 +37,8 @@
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
     <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
-    <uses-permission android:name="android.permission.WRITE_SOCIAL_STREAM" />
     <uses-permission android:name="android.permission.READ_CALL_LOG" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
-    <uses-permission android:name="android.permission.READ_SOCIAL_STREAM" />
 
     <application>
         <uses-library android:name="android.test.runner"/>
diff --git a/tests/tests/telecom/res/drawable/ic_phone_24dp.png b/tests/tests/telecom/res/drawable/ic_phone_24dp.png
new file mode 100644
index 0000000..b0e0205
--- /dev/null
+++ b/tests/tests/telecom/res/drawable/ic_phone_24dp.png
Binary files differ
diff --git a/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java b/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java
new file mode 100644
index 0000000..88babef
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java
@@ -0,0 +1,272 @@
+/*
+ * 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.*;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.media.ToneGenerator;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.telecom.CallAudioState;
+import android.telecom.ConnectionRequest;
+import android.telecom.DisconnectCause;
+import android.telecom.GatewayInfo;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.StatusHints;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.test.InstrumentationTestCase;
+
+import com.android.cts.telecom.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Verifies the parcelable interface of all the telecom objects.
+ */
+public class DataObjectUnitTests extends InstrumentationTestCase {
+
+
+    /**
+     * Tests the PhoneAccount object creation and recreation from a Parcel.
+     */
+    public void testPhoneAccount() throws Exception {
+        Context context = getInstrumentation().getContext();
+        PhoneAccountHandle accountHandle = new PhoneAccountHandle(
+                new ComponentName(PACKAGE, COMPONENT),
+                ACCOUNT_ID);
+        Icon phoneIcon = Icon.createWithResource(context, R.drawable.ic_phone_24dp);
+        Uri tel = Uri.parse("tel:555-TEST");
+        PhoneAccount account = PhoneAccount.builder(
+                accountHandle, LABEL)
+                .setAddress(tel)
+                .setSubscriptionAddress(tel)
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setHighlightColor(Color.RED)
+                .setShortDescription(LABEL)
+                .setSupportedUriSchemes(Arrays.asList("tel"))
+                .setIcon(phoneIcon)
+                .build();
+        assertNotNull(account);
+        assertEquals(accountHandle, account.getAccountHandle());
+        assertEquals(tel, account.getAddress());
+        assertEquals(tel, account.getSubscriptionAddress());
+        assertEquals(PhoneAccount.CAPABILITY_CALL_PROVIDER, account.getCapabilities());
+        assertEquals(Color.RED, account.getHighlightColor());
+        assertEquals(LABEL, account.getShortDescription());
+        assertEquals(Arrays.asList("tel"), account.getSupportedUriSchemes());
+        assertEquals(phoneIcon.toString(), account.getIcon().toString());
+        assertEquals(0, account.describeContents());
+
+        // Create a parcel of the object and recreate the object back
+        // from the parcel.
+        Parcel p = Parcel.obtain();
+        account.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        PhoneAccount parcelAccount = PhoneAccount.CREATOR.createFromParcel(p);
+        assertNotNull(parcelAccount);
+        assertEquals(accountHandle, parcelAccount.getAccountHandle());
+        assertEquals(tel, parcelAccount.getAddress());
+        assertEquals(tel, parcelAccount.getSubscriptionAddress());
+        assertEquals(PhoneAccount.CAPABILITY_CALL_PROVIDER, parcelAccount.getCapabilities());
+        assertEquals(Color.RED, parcelAccount.getHighlightColor());
+        assertEquals(LABEL, parcelAccount.getShortDescription());
+        assertEquals(Arrays.asList("tel"), parcelAccount.getSupportedUriSchemes());
+        assertEquals(phoneIcon.toString(), parcelAccount.getIcon().toString());
+        assertEquals(0, parcelAccount.describeContents());
+        p.recycle();
+    }
+
+    /**
+     * Tests the ConnectionRequest object creation and recreation from a Parcel.
+     */
+    public void testConnectionRequest() throws Exception {
+        PhoneAccountHandle accountHandle = new PhoneAccountHandle(
+                new ComponentName(PACKAGE, COMPONENT),
+                ACCOUNT_ID);
+        Bundle extras = new Bundle();
+        extras.putString(
+                TelecomManager.GATEWAY_PROVIDER_PACKAGE,
+                PACKAGE);
+        ConnectionRequest request = new ConnectionRequest(
+                accountHandle,
+                Uri.parse("tel:555-TEST"),
+                extras,
+                VideoProfile.STATE_AUDIO_ONLY);
+        assertEquals(accountHandle, request.getAccountHandle());
+        assertEquals(Uri.parse("tel:555-TEST"), request.getAddress());
+        assertEquals(extras.getString(
+                TelecomManager.GATEWAY_PROVIDER_PACKAGE),
+                request.getExtras().getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE));
+        assertEquals(VideoProfile.STATE_AUDIO_ONLY, request.getVideoState());
+        assertEquals(0, request.describeContents());
+
+        // Create a parcel of the object and recreate the object back
+        // from the parcel.
+        Parcel p = Parcel.obtain();
+        request.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        ConnectionRequest parcelRequest = ConnectionRequest.CREATOR.createFromParcel(p);
+        assertEquals(accountHandle, parcelRequest.getAccountHandle());
+        assertEquals(Uri.parse("tel:555-TEST"), parcelRequest.getAddress());
+        assertEquals(
+                extras.getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE),
+                parcelRequest.getExtras().getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE));
+        assertEquals(VideoProfile.STATE_AUDIO_ONLY, parcelRequest.getVideoState());
+        assertEquals(0, parcelRequest.describeContents());
+        p.recycle();
+    }
+
+    /**
+     * Tests the DisconnectCause object creation and recreation from a Parcel.
+     */
+    public void testDisconnectCause() throws Exception {
+        Context context = getInstrumentation().getContext();
+        final CharSequence label = "Out of service area";
+        final CharSequence description = "Mobile network not available";
+        final String reason = "CTS Testing";
+        DisconnectCause cause = new DisconnectCause(
+                DisconnectCause.ERROR,
+                label,
+                description,
+                reason,
+                ToneGenerator.TONE_CDMA_CALLDROP_LITE);
+        assertEquals(DisconnectCause.ERROR, cause.getCode());
+        assertEquals(label, cause.getLabel());
+        assertEquals(description, cause.getDescription());
+        assertEquals(reason, cause.getReason());
+        assertEquals(ToneGenerator.TONE_CDMA_CALLDROP_LITE, cause.getTone());
+        assertEquals(0, cause.describeContents());
+
+        // Create a parcel of the object and recreate the object back
+        // from the parcel.
+        Parcel p = Parcel.obtain();
+        cause.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        DisconnectCause parcelCause = DisconnectCause.CREATOR.createFromParcel(p);
+        assertEquals(DisconnectCause.ERROR, parcelCause.getCode());
+        assertEquals(label, parcelCause.getLabel());
+        assertEquals(description, parcelCause.getDescription());
+        assertEquals(reason, parcelCause.getReason());
+        assertEquals(ToneGenerator.TONE_CDMA_CALLDROP_LITE, parcelCause.getTone());
+        assertEquals(0, parcelCause.describeContents());
+        assertEquals(cause, parcelCause);
+        p.recycle();
+    }
+
+    /**
+     * Tests the StatusHints object creation and recreation from a Parcel.
+     */
+    public void testStatusHints() throws Exception {
+        Context context = getInstrumentation().getContext();
+        final CharSequence label = "Wi-Fi call";
+        Bundle extras = new Bundle();
+        extras.putString(
+                TelecomManager.GATEWAY_PROVIDER_PACKAGE,
+                PACKAGE);
+        Icon icon = Icon.createWithResource(context, R.drawable.ic_phone_24dp);
+        StatusHints hints = new StatusHints(
+                label,
+                icon,
+                extras);
+        assertEquals(label, hints.getLabel());
+        assertEquals(icon.toString(), hints.getIcon().toString());
+        assertEquals(extras.getString(
+                TelecomManager.GATEWAY_PROVIDER_PACKAGE),
+                hints.getExtras().getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE));
+        assertEquals(0, hints.describeContents());
+
+        // Create a parcel of the object and recreate the object back
+        // from the parcel.
+        Parcel p = Parcel.obtain();
+        hints.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        StatusHints parcelHints = StatusHints.CREATOR.createFromParcel(p);
+        assertEquals(label, parcelHints.getLabel());
+        assertEquals(icon.toString(), parcelHints.getIcon().toString());
+        assertEquals(
+                extras.getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE),
+                parcelHints.getExtras().getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE));
+        assertEquals(0, parcelHints.describeContents());
+        // This fails because Bundle does not have a equals implementation.
+        // assertEquals(hints, parcelHints);
+        p.recycle();
+    }
+
+    /**
+     * Tests the GatewayInfo object creation and recreation from a Parcel.
+     */
+    public void testGatewayInfo() throws Exception {
+        final CharSequence label = "Wi-Fi call";
+        Uri originalAddress = Uri.parse("http://www.google.com");
+        Uri gatewayAddress = Uri.parse("http://www.google.com");
+        GatewayInfo info = new GatewayInfo(
+                PACKAGE,
+                gatewayAddress,
+                originalAddress);
+        assertEquals(PACKAGE, info.getGatewayProviderPackageName());
+        assertEquals(gatewayAddress, info.getGatewayAddress());
+        assertEquals(originalAddress, info.getOriginalAddress());
+        assertEquals(0, info.describeContents());
+
+        // Create a parcel of the object and recreate the object back
+        // from the parcel.
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        GatewayInfo parcelInfo = GatewayInfo.CREATOR.createFromParcel(p);
+        assertEquals(PACKAGE, parcelInfo.getGatewayProviderPackageName());
+        assertEquals(gatewayAddress, parcelInfo.getGatewayAddress());
+        assertEquals(originalAddress, parcelInfo.getOriginalAddress());
+        assertEquals(0, parcelInfo.describeContents());
+        p.recycle();
+    }
+
+    /**
+     * Tests the CallAudioState object creation and recreation from a Parcel.
+     */
+    public void testCallAudioState() throws Exception {
+        CallAudioState audioState = new CallAudioState(
+                true,
+                CallAudioState.ROUTE_EARPIECE,
+                CallAudioState.ROUTE_WIRED_OR_EARPIECE);
+        assertEquals(true, audioState.isMuted());
+        assertEquals(CallAudioState.ROUTE_EARPIECE, audioState.getRoute());
+        assertEquals(CallAudioState.ROUTE_WIRED_OR_EARPIECE, audioState.getSupportedRouteMask());
+        assertEquals(0, audioState.describeContents());
+
+        // Create a parcel of the object and recreate the object back
+        // from the parcel.
+        Parcel p = Parcel.obtain();
+        audioState.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        CallAudioState parcelAudioState = CallAudioState.CREATOR.createFromParcel(p);
+        assertEquals(true, parcelAudioState.isMuted());
+        assertEquals(CallAudioState.ROUTE_EARPIECE, parcelAudioState.getRoute());
+        assertEquals(CallAudioState.ROUTE_WIRED_OR_EARPIECE, parcelAudioState.getSupportedRouteMask());
+        assertEquals(0, parcelAudioState.describeContents());
+        assertEquals(audioState, parcelAudioState);
+        p.recycle();
+    }
+}
diff --git a/tests/tests/telecom2/Android.mk b/tests/tests/telecom2/Android.mk
index fa23492..71edb7b 100644
--- a/tests/tests/telecom2/Android.mk
+++ b/tests/tests/telecom2/Android.mk
@@ -29,8 +29,18 @@
 src_dirs := src \
     ../telecom/src
 
+res_dirs := res \
+    ../telecom/res
+
 LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
 
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_AAPT_FLAGS := \
+    --auto-add-overlay \
+    --extra-packages com.android.cts.telecom \
+    --rename-manifest-package com.android.cts.telecom2 \
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/telecom2/res/.gitignore b/tests/tests/telecom2/res/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/tests/telecom2/res/.gitignore
diff --git a/tests/tests/uirendering/res/layout/circle_clipped_webview.xml b/tests/tests/uirendering/res/layout/circle_clipped_webview.xml
new file mode 100644
index 0000000..44b65be
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/circle_clipped_webview.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+<android.uirendering.cts.testclasses.view.CircleClipFrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height">
+    <WebView
+        android:id="@+id/webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</android.uirendering.cts.testclasses.view.CircleClipFrameLayout>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 866736d..12ae2a3 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -14,14 +14,15 @@
 import android.uirendering.cts.testinfrastructure.ViewInitializer;
 import android.view.View;
 import android.view.ViewGroup;
+import android.webkit.WebView;
 import com.android.cts.uirendering.R;
 
 public class PathClippingTests extends ActivityTestBase {
     // draw circle with hole in it, with stroked circle
-    static final CanvasClient sCircleDrawCanvasClient = new CanvasClient() {
+    static final CanvasClient sTorusDrawCanvasClient = new CanvasClient() {
         @Override
         public String getDebugString() {
-            return "StrokedCircleDraw";
+            return "TorusDraw";
         }
 
         @Override
@@ -36,10 +37,10 @@
     };
 
     // draw circle with hole in it, by path operations + path clipping
-    static final CanvasClient sCircleClipCanvasClient = new CanvasClient() {
+    static final CanvasClient sTorusClipCanvasClient = new CanvasClient() {
         @Override
         public String getDebugString() {
-            return "CircleClipDraw";
+            return "TorusClipDraw";
         }
 
         @Override
@@ -60,15 +61,15 @@
     @SmallTest
     public void testCircleWithCircle() {
         createTest()
-                .addCanvasClient(sCircleDrawCanvasClient, false)
-                .addCanvasClient(sCircleClipCanvasClient)
+                .addCanvasClient(sTorusDrawCanvasClient, false)
+                .addCanvasClient(sTorusClipCanvasClient)
                 .runWithComparer(new MSSIMComparer(0.90));
     }
 
     @SmallTest
     public void testCircleWithPoints() {
         createTest()
-                .addCanvasClient(sCircleClipCanvasClient)
+                .addCanvasClient(sTorusClipCanvasClient)
                 .runWithVerifier(new SamplePointVerifier(
                         new Point[] {
                                 // inside of circle
@@ -143,4 +144,29 @@
                 })
                 .runWithComparer(new MSSIMComparer(0.90));
     }
+
+    @SmallTest
+    public void testWebViewClipWithCircle() {
+        createTest()
+                // golden client - draw a simple non-AA circle
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        Paint paint = new Paint();
+                        paint.setAntiAlias(false);
+                        paint.setColor(Color.BLUE);
+                        canvas.drawOval(0, 0, width, height, paint);
+                    }
+                }, false)
+                // verify against solid color webview, clipped to its parent oval
+                .addLayout(R.layout.circle_clipped_webview, new ViewInitializer() {
+                    @Override
+                    public void initializeView(View view) {
+                        WebView webview = (WebView)view.findViewById(R.id.webview);
+                        assertNotNull(webview);
+                        webview.loadData("<body style=\"background-color:blue\">", null, null);
+                    }
+                })
+                .runWithComparer(new MSSIMComparer(0.95));
+    }
 }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/CircleClipFrameLayout.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/CircleClipFrameLayout.java
new file mode 100644
index 0000000..4329c60
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/CircleClipFrameLayout.java
@@ -0,0 +1,54 @@
+/*
+ * 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.uirendering.cts.testclasses.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class CircleClipFrameLayout extends FrameLayout {
+    final Path mClipPath = new Path();
+    public CircleClipFrameLayout(Context context) {
+        this(context, null);
+    }
+
+    public CircleClipFrameLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CircleClipFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public CircleClipFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        canvas.save();
+
+        mClipPath.reset();
+        mClipPath.addOval(0, 0, getWidth(), getHeight(), Path.Direction.CW);
+        canvas.clipPath(mClipPath);
+        super.dispatchDraw(canvas);
+
+        canvas.restore();
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
index b8935fb..900e14d 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
@@ -1,3 +1,19 @@
+/*
+ * 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.uirendering.cts.testclasses.view;
 
 import android.content.Context;
diff --git a/tests/tests/view/res/layout/view_layout.xml b/tests/tests/view/res/layout/view_layout.xml
index e6e1550..b501a4d 100644
--- a/tests/tests/view/res/layout/view_layout.xml
+++ b/tests/tests/view/res/layout/view_layout.xml
@@ -21,7 +21,8 @@
     android:id="@+id/viewlayout_root"
     android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:scrollIndicators="left|right">
 
     <android.view.cts.MockView
         android:id="@+id/mock_view"
diff --git a/tests/tests/view/src/android/view/cts/ContextThemeWrapperTest.java b/tests/tests/view/src/android/view/cts/ContextThemeWrapperTest.java
index c40f095..012a13d 100644
--- a/tests/tests/view/src/android/view/cts/ContextThemeWrapperTest.java
+++ b/tests/tests/view/src/android/view/cts/ContextThemeWrapperTest.java
@@ -43,11 +43,11 @@
     }
 
     public void testConstructor() {
-        // new the ContextThemeWrapper instance
         new ContextThemeWrapper();
 
-        // new the ContextThemeWrapper instance
         new ContextThemeWrapper(getContext(), R.style.TextAppearance);
+
+        new ContextThemeWrapper(getContext(), getContext().getTheme());
     }
 
     public void testAccessTheme() {
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 34e4d09..fe3bd60 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -250,6 +250,31 @@
         assertSame(parent, view.getParent());
     }
 
+    public void testAccessScrollIndicators() {
+        View view = mActivity.findViewById(R.id.viewlayout_root);
+
+        assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT,
+                view.getScrollIndicators());
+    }
+
+    public void testSetScrollIndicators() {
+        View view = new View(mActivity);
+
+        view.setScrollIndicators(0);
+        assertEquals(0, view.getScrollIndicators());
+
+        view.setScrollIndicators(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT);
+        assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT,
+                view.getScrollIndicators());
+
+        view.setScrollIndicators(View.SCROLL_INDICATOR_TOP, View.SCROLL_INDICATOR_TOP);
+        assertEquals(View.SCROLL_INDICATOR_LEFT | View.SCROLL_INDICATOR_RIGHT
+                        | View.SCROLL_INDICATOR_TOP, view.getScrollIndicators());
+
+        view.setScrollIndicators(0, view.getScrollIndicators());
+        assertEquals(0, view.getScrollIndicators());
+    }
+
     public void testFindViewById() {
         View parent = mActivity.findViewById(R.id.viewlayout_root);
         assertSame(parent, parent.findViewById(R.id.viewlayout_root));
diff --git a/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java b/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
index bf5382a..2269e00 100644
--- a/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
@@ -188,6 +188,7 @@
         // set null drawable
         compoundButton = new MockCompoundButton(mContext);
         compoundButton.setButtonDrawable(null);
+        assertNull(compoundButton.getButtonDrawable());
 
         // set drawable when checkedTextView is GONE
         compoundButton = new MockCompoundButton(mContext);
@@ -197,6 +198,7 @@
         assertEquals(StateSet.WILD_CARD, firstDrawable.getState());
 
         compoundButton.setButtonDrawable(firstDrawable);
+        assertSame(firstDrawable, compoundButton.getButtonDrawable());
         assertFalse(firstDrawable.isVisible());
 
         // update drawable when checkedTextView is VISIBLE
@@ -206,6 +208,7 @@
         assertEquals(StateSet.WILD_CARD, secondDrawable.getState());
 
         compoundButton.setButtonDrawable(secondDrawable);
+        assertSame(secondDrawable, compoundButton.getButtonDrawable());
         assertTrue(secondDrawable.isVisible());
         // the firstDrawable is not active.
         assertFalse(firstDrawable.isVisible());
diff --git a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
new file mode 100644
index 0000000..0c021eb
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.app.AlertDialog;
+import android.app.DatePickerDialog;
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.UiThreadTest;
+
+/**
+ * Test {@link DatePickerDialog}.
+ */
+public class DatePickerDialogTest extends AndroidTestCase {
+    private Context mContext;
+
+    @Override
+    public void setUp() {
+        mContext = getContext();
+    }
+
+    @UiThreadTest
+    @SuppressWarnings("deprecation")
+    public void testConstructor() {
+        new DatePickerDialog(mContext, null, 1970, 1, 1);
+
+        new DatePickerDialog(mContext, AlertDialog.THEME_TRADITIONAL, null, 1970, 1, 1);
+
+        new DatePickerDialog(mContext, AlertDialog.THEME_HOLO_DARK, null, 1970, 1, 1);
+
+        new DatePickerDialog(mContext,
+                android.R.style.Theme_Material_Dialog_Alert, null, 1970, 1, 1);
+
+        try {
+            new DatePickerDialog(null, null, 1970, 1, 1);
+            fail("should throw NullPointerException");
+        } catch (Exception e) {
+        }
+    }
+
+    @UiThreadTest
+    public void testShow() {
+        DatePickerDialog d = createDatePickerDialog();
+
+        d.show();
+        assertTrue("Showing date picker", d.isShowing());
+
+        d.show();
+        assertTrue("Date picker still showing", d.isShowing());
+
+        d.dismiss();
+        assertFalse("Dismissed date picker", d.isShowing());
+
+        d.dismiss();
+        assertTrue("Date picker still dismissed", d.isShowing());
+    }
+
+    private MockDatePickerDialog createDatePickerDialog() {
+        return new MockDatePickerDialog(mContext, null, 1970, 1, 1);
+    }
+
+    private class MockDatePickerDialog extends DatePickerDialog {
+        public MockDatePickerDialog(Context context, OnDateSetListener callBack,
+                int year, int monthOfYear, int dayOfMonth) {
+            super(context, callBack, year, monthOfYear, dayOfMonth);
+        }
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
new file mode 100644
index 0000000..fa4e8063
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -0,0 +1,323 @@
+/*
+ * 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.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ListPopupWindow;
+import android.widget.PopupWindow;
+import android.widget.PopupWindow.OnDismissListener;
+
+public class ListPopupWindowTest extends
+        ActivityInstrumentationTestCase2<MockPopupWindowCtsActivity> {
+    private Instrumentation mInstrumentation;
+    private Activity mActivity;
+
+    /** The list popup window. */
+    private ListPopupWindow mPopupWindow;
+
+    /**
+     * Instantiates a new popup window test.
+     */
+    public ListPopupWindowTest() {
+        super(MockPopupWindowCtsActivity.class);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see android.test.ActivityInstrumentationTestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mInstrumentation = getInstrumentation();
+        mActivity = getActivity();
+    }
+
+    public void testConstructor() {
+        new ListPopupWindow(mActivity);
+
+        new ListPopupWindow(mActivity, null);
+
+        new ListPopupWindow(mActivity, null, android.R.attr.popupWindowStyle);
+
+        new ListPopupWindow(mActivity, null, 0, android.R.style.Widget_Material_ListPopupWindow);
+    }
+
+    public void testAccessBackground() {
+        mPopupWindow = new ListPopupWindow(mActivity);
+
+        Drawable drawable = new ColorDrawable();
+        mPopupWindow.setBackgroundDrawable(drawable);
+        assertSame(drawable, mPopupWindow.getBackground());
+
+        mPopupWindow.setBackgroundDrawable(null);
+        assertNull(mPopupWindow.getBackground());
+    }
+
+    public void testAccessAnimationStyle() {
+        mPopupWindow = new ListPopupWindow(mActivity);
+        assertEquals(0, mPopupWindow.getAnimationStyle());
+
+        mPopupWindow.setAnimationStyle(android.R.style.Animation_Toast);
+        assertEquals(android.R.style.Animation_Toast, mPopupWindow.getAnimationStyle());
+
+        // abnormal values
+        mPopupWindow.setAnimationStyle(-100);
+        assertEquals(-100, mPopupWindow.getAnimationStyle());
+    }
+
+    public void testAccessHeight() {
+        mPopupWindow = new ListPopupWindow(mActivity);
+        assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getHeight());
+
+        int height = getDisplay().getHeight() / 2;
+        mPopupWindow.setHeight(height);
+        assertEquals(height, mPopupWindow.getHeight());
+
+        height = getDisplay().getHeight();
+        mPopupWindow.setHeight(height);
+        assertEquals(height, mPopupWindow.getHeight());
+
+        mPopupWindow.setHeight(0);
+        assertEquals(0, mPopupWindow.getHeight());
+
+        height = getDisplay().getHeight() * 2;
+        mPopupWindow.setHeight(height);
+        assertEquals(height, mPopupWindow.getHeight());
+
+        height = -getDisplay().getHeight() / 2;
+        mPopupWindow.setHeight(height);
+        assertEquals(height, mPopupWindow.getHeight());
+    }
+
+    /**
+     * Gets the display.
+     *
+     * @return the display
+     */
+    private Display getDisplay() {
+        WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
+        return wm.getDefaultDisplay();
+    }
+
+    public void testAccessWidth() {
+        mPopupWindow = new ListPopupWindow(mActivity);
+        assertEquals(WindowManager.LayoutParams.WRAP_CONTENT, mPopupWindow.getWidth());
+
+        int width = getDisplay().getWidth() / 2;
+        mPopupWindow.setWidth(width);
+        assertEquals(width, mPopupWindow.getWidth());
+
+        width = getDisplay().getWidth();
+        mPopupWindow.setWidth(width);
+        assertEquals(width, mPopupWindow.getWidth());
+
+        mPopupWindow.setWidth(0);
+        assertEquals(0, mPopupWindow.getWidth());
+
+        width = getDisplay().getWidth() * 2;
+        mPopupWindow.setWidth(width);
+        assertEquals(width, mPopupWindow.getWidth());
+
+        width = - getDisplay().getWidth() / 2;
+        mPopupWindow.setWidth(width);
+        assertEquals(width, mPopupWindow.getWidth());
+    }
+
+    public void testShow() {
+        int[] anchorXY = new int[2];
+        int[] viewOnScreenXY = new int[2];
+        int[] viewInWindowXY = new int[2];
+
+        mPopupWindow = new ListPopupWindow(mActivity);
+
+        final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
+        mPopupWindow.setAnchorView(upperAnchor);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                mPopupWindow.show();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(mPopupWindow.isShowing());
+
+        mPopupWindow.getListView().getLocationOnScreen(viewOnScreenXY);
+        upperAnchor.getLocationOnScreen(anchorXY);
+        mPopupWindow.getListView().getLocationInWindow(viewInWindowXY);
+        assertEquals(anchorXY[0] + viewInWindowXY[0], viewOnScreenXY[0]);
+        assertEquals(anchorXY[1] + viewInWindowXY[1] + upperAnchor.getHeight(), viewOnScreenXY[1]);
+
+        dismissPopup();
+    }
+
+    public void testSetWindowLayoutType() {
+        mPopupWindow = new ListPopupWindow(mActivity);
+
+        final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
+        mPopupWindow.setAnchorView(upperAnchor);
+        mPopupWindow.setWindowLayoutType(
+                WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
+
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                mPopupWindow.show();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(mPopupWindow.isShowing());
+
+        WindowManager.LayoutParams p = (WindowManager.LayoutParams)
+                mPopupWindow.getListView().getRootView().getLayoutParams();
+        assertEquals(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL, p.type);
+
+        dismissPopup();
+    }
+
+    @UiThreadTest
+    public void testDismiss() {
+        mPopupWindow = new ListPopupWindow(mActivity);
+        assertFalse(mPopupWindow.isShowing());
+        View anchorView = mActivity.findViewById(R.id.anchor_upper);
+        mPopupWindow.setAnchorView(anchorView);
+        mPopupWindow.show();
+
+        mPopupWindow.dismiss();
+        assertFalse(mPopupWindow.isShowing());
+
+        mPopupWindow.dismiss();
+        assertFalse(mPopupWindow.isShowing());
+    }
+
+    public void testSetOnDismissListener() {
+        mPopupWindow = new ListPopupWindow(mActivity);
+        mPopupWindow.setOnDismissListener(null);
+
+        MockOnDismissListener onDismissListener = new MockOnDismissListener();
+        mPopupWindow.setOnDismissListener(onDismissListener);
+        showPopup();
+        dismissPopup();
+        assertEquals(1, onDismissListener.getOnDismissCalledCount());
+
+        showPopup();
+        dismissPopup();
+        assertEquals(2, onDismissListener.getOnDismissCalledCount());
+
+        mPopupWindow.setOnDismissListener(null);
+        showPopup();
+        dismissPopup();
+        assertEquals(2, onDismissListener.getOnDismissCalledCount());
+    }
+
+    public void testAccessInputMethodMode() {
+        mPopupWindow = new ListPopupWindow(mActivity);
+        assertEquals(PopupWindow.INPUT_METHOD_NEEDED, mPopupWindow.getInputMethodMode());
+
+        mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE);
+        assertEquals(PopupWindow.INPUT_METHOD_FROM_FOCUSABLE, mPopupWindow.getInputMethodMode());
+
+        mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+        assertEquals(PopupWindow.INPUT_METHOD_NEEDED, mPopupWindow.getInputMethodMode());
+
+        mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+        assertEquals(PopupWindow.INPUT_METHOD_NOT_NEEDED, mPopupWindow.getInputMethodMode());
+
+        mPopupWindow.setInputMethodMode(-1);
+        assertEquals(-1, mPopupWindow.getInputMethodMode());
+    }
+
+    /**
+     * The listener interface for receiving OnDismiss events. The class that is
+     * interested in processing a OnDismiss event implements this interface, and
+     * the object created with that class is registered with a component using
+     * the component's <code>setOnDismissListener<code> method. When
+     * the OnDismiss event occurs, that object's appropriate
+     * method is invoked.
+     */
+    private static class MockOnDismissListener implements OnDismissListener {
+
+        /** The Ondismiss called count. */
+        private int mOnDismissCalledCount;
+
+        /**
+         * Gets the onDismiss() called count.
+         *
+         * @return the on dismiss called count
+         */
+        public int getOnDismissCalledCount() {
+            return mOnDismissCalledCount;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see android.widget.PopupWindow.OnDismissListener#onDismiss()
+         */
+        public void onDismiss() {
+            mOnDismissCalledCount++;
+        }
+
+    }
+
+    /**
+     * Show PopupWindow.
+     */
+    // FIXME: logcat info complains that there is window leakage due to that mPopupWindow is not
+    // clean up. Need to fix it.
+    private void showPopup() {
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                if (mPopupWindow == null || mPopupWindow.isShowing()) {
+                    return;
+                }
+                View anchor = mActivity.findViewById(R.id.anchor_upper);
+                mPopupWindow.setAnchorView(anchor);
+                mPopupWindow.show();
+                assertTrue(mPopupWindow.isShowing());
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+
+    /**
+     * Dismiss PopupWindow.
+     */
+    private void dismissPopup() {
+        mInstrumentation.runOnMainSync(new Runnable() {
+            public void run() {
+                if (mPopupWindow == null || !mPopupWindow.isShowing())
+                    return;
+                mPopupWindow.dismiss();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java b/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
index b5ce5c9..a8d7f54 100644
--- a/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RelativeLayoutTest.java
@@ -318,6 +318,18 @@
         assertFalse(myRelativeLayout.checkLayoutParams(p3));
     }
 
+    public void testGetRule() {
+        RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(0, 0);
+        p.addRule(RelativeLayout.LEFT_OF, R.id.abslistview_root);
+        p.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
+
+        assertEquals("Get resource ID rule", R.id.abslistview_root,
+                p.getRule(RelativeLayout.LEFT_OF));
+        assertEquals("Get boolean rule", RelativeLayout.TRUE,
+                p.getRule(RelativeLayout.CENTER_IN_PARENT));
+        assertEquals("Get missing rule", 0, p.getRule(RelativeLayout.ABOVE));
+    }
+
     private class MyRelativeLayout extends RelativeLayout {
         public MyRelativeLayout(Context context) {
             super(context);
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 62c4732..ec098f8 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -78,12 +78,16 @@
 import android.util.TypedValue;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.ActionMode;
 import android.view.Gravity;
 import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnCreateContextMenuListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
@@ -2615,6 +2619,88 @@
         assertTrue(mTextView.getHeight() <= maxLines * mTextView.getLineHeight());
     }
 
+    public int calculateTextWidth(String text) {
+        mTextView = findTextView(R.id.textview_text);
+
+        // Set the TextView width as the half of the whole text.
+        float[] widths = new float[text.length()];
+        mTextView.getPaint().getTextWidths(text, widths);
+        float textfieldWidth = 0.0f;
+        for (int i = 0; i < text.length(); ++i) {
+            textfieldWidth += widths[i];
+        }
+        return (int)textfieldWidth;
+
+    }
+
+    @UiThreadTest
+    public void testHyphenationNotHappen_frequencyNone() {
+        final int[] BREAK_STRATEGIES = {
+            Layout.BREAK_STRATEGY_SIMPLE, Layout.BREAK_STRATEGY_HIGH_QUALITY,
+            Layout.BREAK_STRATEGY_BALANCED };
+
+        mTextView = findTextView(R.id.textview_text);
+
+        for (int breakStrategy : BREAK_STRATEGIES) {
+            for (int charWidth = 10; charWidth < 120; charWidth += 5) {
+                // Change the text view's width to charWidth width.
+                mTextView.setWidth(calculateTextWidth(LONG_TEXT.substring(0, charWidth)));
+
+                mTextView.setText(LONG_TEXT);
+                mTextView.setBreakStrategy(breakStrategy);
+
+                mTextView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
+
+                mTextView.requestLayout();
+                mTextView.onPreDraw();  // For freezing the layout.
+                Layout layout = mTextView.getLayout();
+
+                final int lineCount = layout.getLineCount();
+                for (int line = 0; line < lineCount; ++line) {
+                    final int lineEnd = layout.getLineEnd(line);
+                    // In any width, any break strategy, hyphenation should not happen if
+                    // HYPHENATION_FREQUENCY_NONE is specified.
+                    assertTrue(lineEnd == LONG_TEXT.length() ||
+                            Character.isWhitespace(LONG_TEXT.charAt(lineEnd - 1)));
+                }
+            }
+        }
+    }
+
+    @UiThreadTest
+    public void testHyphenationNotHappen_breakStrategySimple() {
+        final int[] HYPHENATION_FREQUENCIES = {
+            Layout.HYPHENATION_FREQUENCY_NORMAL, Layout.HYPHENATION_FREQUENCY_FULL,
+            Layout.HYPHENATION_FREQUENCY_NONE };
+
+        mTextView = findTextView(R.id.textview_text);
+
+        for (int hyphenationFrequency: HYPHENATION_FREQUENCIES) {
+            for (int charWidth = 10; charWidth < 120; charWidth += 5) {
+                // Change the text view's width to charWidth width.
+                mTextView.setWidth(calculateTextWidth(LONG_TEXT.substring(0, charWidth)));
+
+                mTextView.setText(LONG_TEXT);
+                mTextView.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
+
+                mTextView.setHyphenationFrequency(hyphenationFrequency);
+
+                mTextView.requestLayout();
+                mTextView.onPreDraw();  // For freezing the layout.
+                Layout layout = mTextView.getLayout();
+
+                final int lineCount = layout.getLineCount();
+                for (int line = 0; line < lineCount; ++line) {
+                    final int lineEnd = layout.getLineEnd(line);
+                    // In any width, any hyphenation frequency, hyphenation should not happen if
+                    // BREAK_STRATEGY_SIMPLE is specified.
+                    assertTrue(lineEnd == LONG_TEXT.length() ||
+                            Character.isWhitespace(LONG_TEXT.charAt(lineEnd - 1)));
+                }
+            }
+        }
+    }
+
     @UiThreadTest
     public void testSetMaxLinesException() {
         mTextView = new TextView(mActivity);
@@ -4341,6 +4427,135 @@
         assertEquals(Layout.HYPHENATION_FREQUENCY_FULL, tv.getHyphenationFrequency());
     }
 
+    public void testSetAndGetCustomSelectionActionModeCallback() {
+        final String text = "abcde";
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mTextView = new EditText(mActivity);
+                mActivity.setContentView(mTextView);
+                mTextView.setText(text, BufferType.SPANNABLE);
+                mTextView.setTextIsSelectable(true);
+                mTextView.requestFocus();
+                mTextView.setSelected(true);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Check default value.
+        assertNull(mTextView.getCustomSelectionActionModeCallback());
+
+        MockActionModeCallback callbackBlockActionMode = new MockActionModeCallback(false);
+        mTextView.setCustomSelectionActionModeCallback(callbackBlockActionMode);
+        assertEquals(callbackBlockActionMode,
+                mTextView.getCustomSelectionActionModeCallback());
+
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                // Set selection and try to start action mode.
+                final Bundle args = new Bundle();
+                args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+                args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length());
+                mTextView.performAccessibilityActionInternal(
+                        AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(1, callbackBlockActionMode.getCreateCount());
+
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                // Remove selection and stop action mode.
+                mTextView.onTextContextMenuItem(android.R.id.copy);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Action mode was blocked.
+        assertEquals(0, callbackBlockActionMode.getDestroyCount());
+
+        // Overwrite callback.
+        MockActionModeCallback callbackStartActionMode = new MockActionModeCallback(true);
+        mTextView.setCustomSelectionActionModeCallback(callbackStartActionMode);
+        assertEquals(callbackStartActionMode, mTextView.getCustomSelectionActionModeCallback());
+
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                // Set selection and try to start action mode.
+                final Bundle args = new Bundle();
+                args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0);
+                args.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length());
+                mTextView.performAccessibilityActionInternal(
+                        AccessibilityNodeInfo.ACTION_SET_SELECTION, args);
+
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(1, callbackStartActionMode.getCreateCount());
+
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                // Remove selection and stop action mode.
+                mTextView.onTextContextMenuItem(android.R.id.copy);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Action mode was started
+        assertEquals(1, callbackStartActionMode.getDestroyCount());
+    }
+
+    public void testSetAndGetCustomInseltionActionMode() {
+        initTextViewForTyping();
+        // Check default value.
+        assertNull(mTextView.getCustomInsertionActionModeCallback());
+
+        MockActionModeCallback callback = new MockActionModeCallback(false);
+        mTextView.setCustomInsertionActionModeCallback(callback);
+        assertEquals(callback, mTextView.getCustomInsertionActionModeCallback());
+        // TODO(Bug: 22033189): Tests the set callback is actually used.
+    }
+
+    private static class MockActionModeCallback implements ActionMode.Callback {
+        private int mCreateCount = 0;
+        private int mDestroyCount = 0;
+        private final boolean mAllowToStartActionMode;
+
+        public MockActionModeCallback(boolean allowToStartActionMode) {
+            mAllowToStartActionMode = allowToStartActionMode;
+        }
+
+        public int getCreateCount() {
+            return mCreateCount;
+        }
+
+        public int getDestroyCount() {
+            return mDestroyCount;
+        }
+
+        @Override
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return false;
+        }
+
+        @Override
+        public void onDestroyActionMode(ActionMode mode) {
+            mDestroyCount++;
+        }
+
+        @Override
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            mCreateCount++;
+            return mAllowToStartActionMode;
+        }
+
+        @Override
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            return false;
+        }
+    };
+
     private static class MockOnEditorActionListener implements OnEditorActionListener {
         private boolean isOnEditorActionCalled;
 
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
index 833bf69..328b855 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
@@ -20,6 +20,7 @@
 
 import vogar.Expectation;
 import vogar.ExpectationStore;
+import vogar.Result;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -228,7 +229,8 @@
     }
 
     public static boolean isKnownFailure(ExpectationStore expectationStore, String testName) {
-        return expectationStore != null && expectationStore.get(testName) != Expectation.SUCCESS;
+        return expectationStore != null
+            && expectationStore.get(testName).getResult() != Result.SUCCESS;
     }
 
     // Returns the list of ABIs supported by this TestCase on this architecture.
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index 95b77f2..83c451e 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -447,6 +447,9 @@
                                                                     expectations,
                                                                     testClassName,
                                                                     testName);
+        int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectations,
+                                                           testClassName,
+                                                           testName);
         TestClass testClass;
         if (testCases.containsKey(testClassName)) {
             testClass = testCases.get(testClassName);
@@ -456,7 +459,7 @@
         }
 
         testClass.mCases.add(new TestMethod(testName, "", "", supportedAbis,
-              knownFailure, false, false));
+              knownFailure, false, false, timeoutInMinutes));
     }
 
     private static boolean isJunit3Test(Class<?> klass) {
diff --git a/tools/utils/DescriptionGenerator.java b/tools/utils/DescriptionGenerator.java
index 09e1118..1e2542f 100644
--- a/tools/utils/DescriptionGenerator.java
+++ b/tools/utils/DescriptionGenerator.java
@@ -86,6 +86,7 @@
     static final String ATTRIBUTE_NAME = "name";
     static final String ATTRIBUTE_ABIS = "abis";
     static final String ATTRIBUTE_HOST_CONTROLLER = "HostController";
+    static final String ATTRIBUTE_TIMEOUT = "timeout";
 
     static final String XML_OUTPUT_PATH = "./description.xml";
 
@@ -438,6 +439,10 @@
                     if ((caze.mController != null) && (caze.mController.length() != 0)) {
                         setAttribute(caseNode, ATTRIBUTE_HOST_CONTROLLER, caze.mController);
                     }
+                    if (caze.mTimeoutInMinutes != 0) {
+                        setAttribute(caseNode, ATTRIBUTE_TIMEOUT,
+                                     Integer.toString(caze.mTimeoutInMinutes));
+                    }
 
                     if (caze.mDescription != null && !caze.mDescription.equals("")) {
                         caseNode.appendChild(mDoc.createElement(TAG_DESCRIPTION))
@@ -573,9 +578,10 @@
                             VogarUtils.buildFullTestName(clazz.toString(), name));
                     Set<String> supportedAbis =
                             VogarUtils.extractSupportedAbis(architecture, expectation);
+                    int timeoutInMinutes = VogarUtils.timeoutInMinutes(expectation);
                     cases.add(new TestMethod(
                             name, method.commentText(), controller, supportedAbis,
-                                    knownFailure, isBroken, isSuppressed));
+                                    knownFailure, isBroken, isSuppressed, timeoutInMinutes));
                 }
             }
 
@@ -635,6 +641,7 @@
         String mKnownFailure;
         boolean mIsBroken;
         boolean mIsSuppressed;
+        int mTimeoutInMinutes;  // zero to use default timeout.
 
         /**
          * Construct an test case object.
@@ -644,7 +651,10 @@
          * @param knownFailure The reason of known failure.
          */
         TestMethod(String name, String description, String controller, Set<String> abis,
-                String knownFailure, boolean isBroken, boolean isSuppressed) {
+                String knownFailure, boolean isBroken, boolean isSuppressed, int timeoutInMinutes) {
+            if (timeoutInMinutes < 0) {
+                throw new IllegalArgumentException("timeoutInMinutes < 0: " + timeoutInMinutes);
+            }
             mName = name;
             mDescription = description;
             mController = controller;
@@ -652,6 +662,7 @@
             mKnownFailure = knownFailure;
             mIsBroken = isBroken;
             mIsSuppressed = isSuppressed;
+            mTimeoutInMinutes = timeoutInMinutes;
         }
     }
 }
diff --git a/tools/utils/VogarUtils.java b/tools/utils/VogarUtils.java
index 5e8b944..8e77e7c 100644
--- a/tools/utils/VogarUtils.java
+++ b/tools/utils/VogarUtils.java
@@ -19,6 +19,7 @@
 import vogar.Expectation;
 import vogar.ExpectationStore;
 import vogar.ModeId;
+import vogar.Result;
 
 import java.io.File;
 import java.io.FilenameFilter;
@@ -52,14 +53,14 @@
         }
         String fullTestName = buildFullTestName(testClassName, testMethodName);
         Expectation expectation = expectationStore.get(fullTestName);
-        if (expectation == Expectation.SUCCESS) {
+        if (expectation.getResult() == Result.SUCCESS) {
             return false;
         }
 
         String description = expectation.getDescription();
         boolean foundAbi = AbiUtils.parseAbiList(description).size() > 0;
 
-        return expectation != Expectation.SUCCESS && !foundAbi;
+        return expectation.getResult() != Result.SUCCESS && !foundAbi;
     }
 
     public static ExpectationStore provideExpectationStore(String dir) throws IOException {
@@ -119,7 +120,7 @@
                                                    String className,
                                                    String testName) {
 
-        String fullTestName = VogarUtils.buildFullTestName(className, testName);
+        String fullTestName = buildFullTestName(className, testName);
         Set<String> supportedAbiSet = AbiUtils.getAbisForArch(architecture);
         for (ExpectationStore expectationStore : expectationStores) {
             Expectation expectation = expectationStore.get(fullTestName);
@@ -128,4 +129,44 @@
 
         return supportedAbiSet;
     }
+
+    /**
+     * Returns the greatest timeout in minutes for the test in all
+     * expectation stores, or 0 if no timeout was found.
+     */
+    public static int timeoutInMinutes(ExpectationStore[] expectationStores,
+            final String testClassName,
+            final String testMethodName) {
+        int timeoutInMinutes = 0;
+        for (ExpectationStore expectationStore : expectationStores) {
+            timeoutInMinutes = Math.max(timeoutInMinutes,
+                                        timeoutInMinutes(expectationStore,
+                                                         testClassName,
+                                                         testMethodName));
+        }
+        return timeoutInMinutes;
+    }
+
+    /**
+     * Returns the timeout in minutes for the test in the expectation
+     * stores, or 0 if no timeout was found.
+     */
+    public static int timeoutInMinutes(ExpectationStore expectationStore,
+            final String testClassName,
+            final String testMethodName) {
+        if (expectationStore == null) {
+            return 0;
+        }
+        String fullTestName = buildFullTestName(testClassName, testMethodName);
+        return timeoutInMinutes(expectationStore.get(fullTestName));
+    }
+
+    /**
+     * Returns the timeout in minutes for the expectation. Currently a
+     * tag of large results in a 60 minute timeout, otherwise 0 is
+     * returned to indicate a default timeout should be used.
+     */
+    public static int timeoutInMinutes(Expectation expectation) {
+        return expectation.getTags().contains("large") ? 60 : 0;
+    }
 }