Merge "Make AlmostFullTest no longer overfill the disk." into nougat-mr1-cts-dev
diff --git a/OldCtsTestCaseList.mk b/OldCtsTestCaseList.mk
index c54d283..a1a7d5a16 100644
--- a/OldCtsTestCaseList.mk
+++ b/OldCtsTestCaseList.mk
@@ -291,6 +291,7 @@
 cts_native_tests := \
     CtsNativeMediaSlTestCases \
     CtsNativeMediaXaTestCases \
+    CtsNativeNetTestCases \
 
 ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
 cts_native_tests += CtsBionicTestCases
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index e5fbba5..00fb1d6 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -86,8 +86,8 @@
         pixel_pitch_w = (sensor_size["width"] / fmts[0]["width"] * 1E3)
         print "Assert pixel_pitch WxH: %.2f um, %.2f um" % (pixel_pitch_w,
                                                             pixel_pitch_h)
-        assert 1.0 <= pixel_pitch_w <= 10
-        assert 1.0 <= pixel_pitch_h <= 10
+        assert 0.9 <= pixel_pitch_w <= 10
+        assert 0.9 <= pixel_pitch_h <= 10
         assert 0.333 <= pixel_pitch_w/pixel_pitch_h <= 3.0
 
         diag = math.sqrt(sensor_size["height"] ** 2 +
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 81532c8..ecd35f3 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.cts.verifier"
       android:versionCode="5"
-      android:versionName="7.1_r7">
+      android:versionName="7.1_r8">
 
     <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25"/>
 
diff --git a/apps/CtsVerifier/assets/report/compatibility_result.xsl b/apps/CtsVerifier/assets/report/compatibility_result.xsl
index b8c1245..1e9ec31 100644
--- a/apps/CtsVerifier/assets/report/compatibility_result.xsl
+++ b/apps/CtsVerifier/assets/report/compatibility_result.xsl
@@ -82,12 +82,6 @@
                             </td>
                         </tr>
                         <tr>
-                            <td class="rowtitle">Tests Not Executed</td>
-                            <td>
-                                <xsl:value-of select="Result/Summary/@not_executed"/>
-                            </td>
-                        </tr>
-                        <tr>
                             <td class="rowtitle">Modules Done</td>
                             <td>
                                 <xsl:value-of select="Result/Summary/@modules_done"/>
@@ -134,7 +128,6 @@
                             <th>Module</th>
                             <th>Passed</th>
                             <th>Failed</th>
-                            <th>Not Executed</th>
                             <th>Total Tests</th>
                         </tr>
                         <xsl:for-each select="Result/Module">
@@ -150,10 +143,7 @@
                                     <xsl:value-of select="count(TestCase/Test[@result = 'fail'])"/>
                                 </td>
                                 <td>
-                                    <xsl:value-of select="@not_executed"/>
-                                </td>
-                                <td>
-                                    <xsl:value-of select="count(TestCase/Test) + @not_executed"/>
+                                    <xsl:value-of select="count(TestCase/Test)"/>
                                 </td>
                             </tr>
                         </xsl:for-each> <!-- end Module -->
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 79f364d..d2dd9ef 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -64,6 +64,8 @@
     private ComponentName mAdminReceiverComponent;
     private KeyguardManager mKeyguardManager;
 
+    private ByodFlowTestHelper mByodFlowTestHelper;
+
     private DialogTestListItem mProfileOwnerInstalled;
     private DialogTestListItem mDiskEncryptionTest;
     private DialogTestListItem mProfileAccountVisibleTest;
@@ -114,10 +116,12 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mByodFlowTestHelper = new ByodFlowTestHelper(this);
         mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
         mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
 
-        enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+        mByodFlowTestHelper.setup();
+
         mPrepareTestButton.setText(R.string.provisioning_byod_start);
         mPrepareTestButton.setOnClickListener(new OnClickListener() {
             @Override
@@ -186,8 +190,7 @@
     public void finish() {
         // Pass and fail buttons are known to call finish() when clicked, and this is when we want to
         // clean up the provisioned profile.
-        Utils.requestDeleteManagedProfile(this);
-        enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+        mByodFlowTestHelper.tearDown();
         super.finish();
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
new file mode 100644
index 0000000..fb92248
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
@@ -0,0 +1,50 @@
+package com.android.cts.verifier.managedprovisioning;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+public class ByodFlowTestHelper {
+    private Context mContext;
+    private PackageManager mPackageManager;
+
+    public ByodFlowTestHelper(Context context) {
+        this.mContext = context;
+        this.mPackageManager = mContext.getPackageManager();
+    }
+
+    public void setup() {
+        setComponentsEnabledState(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+    }
+
+    /**
+     * Clean up things. This has to be working even it is called multiple times.
+     */
+    public void tearDown() {
+        Utils.requestDeleteManagedProfile(mContext);
+        setComponentsEnabledState(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+    }
+
+    /**
+     * Disable or enable app components in the current profile. When they are disabled only the
+     * counterpart in the other profile can respond (via cross-profile intent filter).
+     *
+     * @param enabledState {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED} or
+     *                     {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
+     */
+    private void setComponentsEnabledState(final int enabledState) {
+        final String[] components = {
+                ByodHelperActivity.class.getName(),
+                WorkStatusTestActivity.class.getName(),
+                PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+                AuthenticationBoundKeyTestActivity.class.getName(),
+                VpnTestActivity.class.getName(),
+                CommandReceiverActivity.class.getName(),
+                SetSupportMessageActivity.class.getName()
+        };
+        for (String component : components) {
+            mPackageManager.setComponentEnabledSetting(new ComponentName(mContext, component),
+                    enabledState, PackageManager.DONT_KILL_APP);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 755d185..2663364 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -75,6 +75,10 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        // Tidy up in case previous run crashed.
+        new ByodFlowTestHelper(this).tearDown();
+
         if (ACTION_CHECK_DEVICE_OWNER.equals(getIntent().getAction())) {
             DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
                     Context.DEVICE_POLICY_SERVICE);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
index 1d23175..7ab8e7e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
@@ -80,6 +80,9 @@
             return;
         }
 
+        // Tidy up in case previous run crashed.
+        new ByodFlowTestHelper(this).tearDown();
+
         setContentView(R.layout.requesting_bugreport_device_owner);
         setInfoResources(R.string.device_owner_requesting_bugreport_tests,
                 R.string.device_owner_requesting_bugreport_tests_info, 0);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
index e00d353..e74f07e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
@@ -154,6 +154,8 @@
                 return pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS);
             case PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE:
                 return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
+            case PolicyTransparencyTestActivity.TEST_CHECK_LOCK_SCREEN_INFO:
+                return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
             default:
                 return true;
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
index db275f2..2ea5cf9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
@@ -58,9 +58,8 @@
     static void requestDeleteManagedProfile(Context context) {
         try {
             Intent intent = new Intent(ByodHelperActivity.ACTION_REMOVE_MANAGED_PROFILE);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             context.startActivity(intent);
-            String message = context.getString(R.string.provisioning_byod_delete_profile);
-            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
         }
         catch (ActivityNotFoundException e) {
             Log.d(TAG, "requestDeleteProfileOwner: ActivityNotFoundException", e);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index c49340e..52dc79b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -224,6 +224,10 @@
             // Verify they can't poke at each other
             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testUserIsolation", owner);
             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testUserIsolation", secondary);
+
+            // Verify they can't access other users' content using media provider
+            runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testMediaProviderUserIsolation", owner);
+            runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testMediaProviderUserIsolation", secondary);
         } finally {
             getDevice().uninstallPackage(MULTIUSER_PKG);
             removeUsersForTest(users);
diff --git a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/src/com/android/cts/multiuserstorageapp/MultiUserStorageTest.java b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/src/com/android/cts/multiuserstorageapp/MultiUserStorageTest.java
index d9f00d2..3cf1443 100644
--- a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/src/com/android/cts/multiuserstorageapp/MultiUserStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/src/com/android/cts/multiuserstorageapp/MultiUserStorageTest.java
@@ -17,15 +17,20 @@
 package com.android.cts.multiuserstorageapp;
 
 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileNoAccess;
 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPathsExceptObb;
 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.writeInt;
 
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
 import android.os.Environment;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 
 /**
  * Test multi-user emulated storage environment, ensuring that each user has
@@ -138,6 +143,36 @@
         }
     }
 
+    /**
+     * Verify that files cannot be accessed through media provider.
+     */
+    public void testMediaProviderUserIsolation() throws Exception {
+        final File myPath = Environment.getExternalStorageDirectory();
+        final int myId = android.os.Process.myUid() / 100000;
+        assertEquals(String.valueOf(myId), myPath.getName());
+
+        Log.d(TAG, "My path is " + myPath + " user id " + myId);
+        final File basePath = myPath.getParentFile();
+        for (int i = 0; i < 128; i++) {
+            if (i == myId) continue;
+            final File otherPath = new File(basePath,i + "/" + FILE_SINGLETON);
+            assertFileNoAccess(otherPath);
+
+            final String URI_MEDIA_STRING = "content://media/external/audio/media/";
+            ContentResolver contentResolver = mContext.getContentResolver();
+            ContentValues cv = new ContentValues();
+            cv.put("_data", otherPath.getAbsolutePath());
+            contentResolver.insert(Uri.parse(URI_MEDIA_STRING), cv);
+
+            try {
+                mContext.getContentResolver().openInputStream(Uri.parse(URI_MEDIA_STRING));
+                fail("Accessing through media provider should not be allowed. Path " + myPath);
+            } catch (FileNotFoundException expected) {
+                // OK
+            }
+        }
+    }
+
     private File buildApiObbPath(String file) {
         return new File(getContext().getObbDir(), file);
     }
diff --git a/hostsidetests/theme/assets/tvdpi.zip b/hostsidetests/theme/assets/tvdpi.zip
new file mode 100755
index 0000000..553657f
--- /dev/null
+++ b/hostsidetests/theme/assets/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java b/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
index a8871a3..7cea5e5 100644
--- a/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
+++ b/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
@@ -99,8 +99,9 @@
             getDevice().executeShellCommand(SLEEP_COMMAND);
             // Start the APK and wait for it to complete.
             getDevice().executeShellCommand(START_COMMAND);
-            // Give the activity some time to start
-            Thread.sleep(500);
+            // Adding delay for OEM specific features which could delay the time of printing the
+            // test log. Please refer to b/62075150 for additional details.
+            Thread.sleep(1000);
             // Dump logcat.
             String logs = getDevice().executeAdbCommand(
                     "logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
diff --git a/tests/camera/AndroidTest.xml b/tests/camera/AndroidTest.xml
index 0f53255..34b116f 100644
--- a/tests/camera/AndroidTest.xml
+++ b/tests/camera/AndroidTest.xml
@@ -21,7 +21,7 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.camera.cts" />
         <option name="runtime-hint" value="12m7s" />
-        <!-- test-timeout unit is ms, value = 60 min -->
-        <option name="test-timeout" value="3600000" />
+        <!-- test-timeout unit is ms, value = 200 min -->
+        <option name="test-timeout" value="12000000" />
     </test>
 </configuration>
diff --git a/tests/leanbackjank/app/Android.mk b/tests/leanbackjank/app/Android.mk
index e2cbf89..dbd28f4 100644
--- a/tests/leanbackjank/app/Android.mk
+++ b/tests/leanbackjank/app/Android.mk
@@ -47,4 +47,6 @@
         --extra-packages android.support.v17.leanback \
         --extra-packages android.support.v7.recyclerview
 
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/leanbackjank/app/proguard.flags b/tests/leanbackjank/app/proguard.flags
new file mode 100644
index 0000000..0674e77
--- /dev/null
+++ b/tests/leanbackjank/app/proguard.flags
@@ -0,0 +1 @@
+-dontobfuscate
\ No newline at end of file
diff --git a/tests/leanbackjank/app/res/values/dimens.xml b/tests/leanbackjank/app/res/values/dimens.xml
new file mode 100644
index 0000000..30792fd
--- /dev/null
+++ b/tests/leanbackjank/app/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+
+<resources>
+    <dimen name="shadow_unfocused_z">0dp</dimen>
+    <dimen name="shadow_focused_z">10dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
index bedbc5d..4e6b55d 100644
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
+++ b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
@@ -17,11 +17,15 @@
 package android.leanbackjank.app.presenter;
 
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.support.v17.leanback.widget.ImageCardView;
 import android.support.v17.leanback.widget.Presenter;
 import android.view.ViewGroup;
 
 import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.target.Target;
 import android.leanbackjank.app.R;
 import android.leanbackjank.app.model.Movie;
 
@@ -46,25 +50,17 @@
         ImageCardView cardView = new ImageCardView(parent.getContext()) {
             @Override
             public void setSelected(boolean selected) {
-                updateCardBackgroundColor(this, selected);
+                findViewById(R.id.info_field).setBackgroundColor(
+                        selected ? sSelectedBackgroundColor : sDefaultBackgroundColor);
                 super.setSelected(selected);
             }
         };
 
         cardView.setFocusable(true);
         cardView.setFocusableInTouchMode(true);
-        updateCardBackgroundColor(cardView, false);
         return new ViewHolder(cardView);
     }
 
-    private static void updateCardBackgroundColor(ImageCardView view, boolean selected) {
-        int color = selected ? sSelectedBackgroundColor : sDefaultBackgroundColor;
-        // Both background colors should be set because the view's background is temporarily visible
-        // during animations.
-        view.setBackgroundColor(color);
-        view.findViewById(R.id.info_field).setBackgroundColor(color);
-    }
-
     @Override
     public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
         Movie movie = (Movie) item;
@@ -73,10 +69,27 @@
         cardView.setTitleText(movie.getTitle());
         cardView.setContentText(movie.getStudio());
         cardView.setMainImageDimensions(CARD_WIDTH, CARD_HEIGHT);
+        cardView.setBackgroundColor(sDefaultBackgroundColor);
+
         Glide.with(viewHolder.view.getContext())
                 .load(R.drawable.gradation)
                 .centerCrop()
                 .error(mDefaultCardImage)
+                .listener(new RequestListener<Integer, GlideDrawable>() {
+                    @Override
+                    public boolean onException(Exception e, Integer i, Target<GlideDrawable> target,
+                            boolean b) {
+                        return false;
+                    }
+
+                    @Override
+                    public boolean onResourceReady(GlideDrawable glideDrawable, Integer i,
+                            Target<GlideDrawable> target, boolean b, boolean b1) {
+                        // Remove the background color to reduce overdraw.
+                        cardView.setBackground(null);
+                        return false;
+                    }
+                })
                 .into(cardView.getMainImageView());
     }
 
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java b/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
index 399681c..ee04924 100644
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
+++ b/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
@@ -15,6 +15,7 @@
 package android.leanbackjank.app.ui;
 
 import android.content.Intent;
+import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.leanbackjank.app.IntentKeys;
 import android.leanbackjank.app.R;
@@ -32,6 +33,7 @@
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.ShadowOverlayHelper;
 import android.util.DisplayMetrics;
 import android.view.View;
 
@@ -166,7 +168,17 @@
     }
 
     public void buildRowAdapterItems(HashMap<String, List<Movie>> data) {
-        mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+        ListRowPresenter listRowPresenter = new ListRowPresenter() {
+            @Override
+            protected ShadowOverlayHelper.Options createShadowOverlayOptions() {
+                Resources res = getResources();
+                ShadowOverlayHelper.Options options = new ShadowOverlayHelper.Options();
+                options.dynamicShadowZ(res.getDimension(R.dimen.shadow_unfocused_z),
+                        res.getDimension(R.dimen.shadow_focused_z));
+                return options;
+            }
+        };
+        mRowsAdapter = new ArrayObjectAdapter(listRowPresenter);
         CardPresenter cardPresenter = new CardPresenter();
 
         int i = 0;
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
index 33b99a4..1183c11 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
@@ -48,6 +48,7 @@
 
     private AnimationDrawable mAnimationDrawable;
     private Resources mResources;
+    private boolean mInitialOneShotValue;
 
     public AnimationDrawableTest() {
         super("android.graphics.cts", ImageViewCtsActivity.class);
@@ -64,6 +65,7 @@
                     ImageView imageView = (ImageView) activity.findViewById(R.id.imageview);
                     imageView.setBackgroundResource(R.drawable.animationdrawable);
                     mAnimationDrawable = (AnimationDrawable) imageView.getBackground();
+                    mInitialOneShotValue = mAnimationDrawable.isOneShot();
                 }
             });
         } catch (Throwable t) {
@@ -71,6 +73,12 @@
         }
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        mAnimationDrawable.setOneShot(mInitialOneShotValue);
+        super.tearDown();
+    }
+
     public void testConstructor() {
         mAnimationDrawable = new AnimationDrawable();
         // Check the values set in the constructor
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index eda78e8..66dcef6 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -46,7 +46,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil ctsdeviceutil compatibility-device-util ctstestserver ctstestrunner ndkaudio
 
-LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni libaudio_jni libnativehelper_compat_libc++ libndkaudioLib
+LOCAL_JNI_SHARED_LIBRARIES := libctsmediadrm_jni libctsmediacodec_jni libaudio_jni libnativehelper_compat_libc++ libndkaudioLib
 
 # do not compress VP9 video files
 LOCAL_AAPT_FLAGS := -0 .vp9
diff --git a/tests/tests/media/libmediandkjni/AMediaObjects.h b/tests/tests/media/libmediandkjni/AMediaObjects.h
new file mode 100644
index 0000000..c4d5397
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/AMediaObjects.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef AMEDIAOBJECTS_H_
+#define AMEDIAOBJECTS_H_
+
+#include <utils/Log.h>
+
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+
+namespace {
+
+// Simple class to manage deletion of AMedia objects
+class AMediaObjects {
+ public:
+    AMediaObjects();
+    virtual ~AMediaObjects();
+
+    void setCrypto(AMediaCrypto* const theCrypto) {
+        mCrypto = theCrypto;
+    }
+    void setDrm(AMediaDrm* const theDrm) {
+        mDrm = theDrm;
+    }
+    void setVideoExtractor(AMediaExtractor* const theExtractor) {
+        mVideoExtractor = theExtractor;
+    }
+    void setAudioExtractor(AMediaExtractor* const theExtractor) {
+        mAudioExtractor = theExtractor;
+    }
+
+    AMediaCrypto* getCrypto() const { return mCrypto; }
+    AMediaDrm* getDrm() const { return mDrm; }
+    AMediaExtractor* getAudioExtractor() const { return mAudioExtractor; }
+    AMediaExtractor* getVideoExtractor() const { return mVideoExtractor; }
+
+ private:
+    AMediaCrypto *mCrypto;
+    AMediaDrm* mDrm;
+    AMediaExtractor* mAudioExtractor;
+    AMediaExtractor* mVideoExtractor;
+
+    // Disallow copy and assignment
+    AMediaObjects(const AMediaObjects&);
+    void operator=(const AMediaObjects&);
+};
+
+AMediaObjects::AMediaObjects(void) : mCrypto(NULL), mDrm(NULL),
+        mAudioExtractor(NULL), mVideoExtractor(NULL) {
+}
+
+AMediaObjects::~AMediaObjects() {
+    if (mCrypto) {
+        AMediaCrypto_delete(mCrypto);
+    }
+    if (mAudioExtractor) {
+        AMediaExtractor_delete(mAudioExtractor);
+    }
+    if (mVideoExtractor) {
+        AMediaExtractor_delete(mVideoExtractor);
+    }
+    if (mDrm) {
+        AMediaDrm_release(mDrm);
+    }
+}
+
+}  // anonymous namespace
+#endif  // AMEDIAOBJECTS_H_
+
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
index 259c95e..5aa222a 100644
--- a/tests/tests/media/libmediandkjni/Android.mk
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -14,27 +14,66 @@
 #
 LOCAL_PATH := $(call my-dir)
 
+#------------------------------------------------------------------------------
+# Builds libctsmediacodec_jni.so
+#
 include $(CLEAR_VARS)
 
-LOCAL_MODULE    := libctsmediacodec_jni
+LOCAL_MODULE := libctsmediacodec_jni
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
-	native-media-jni.cpp \
-	codec-utils-jni.cpp  \
-	md5_utils.cpp
+  native-media-jni.cpp \
+  codec-utils-jni.cpp  \
+  md5_utils.cpp
 
 LOCAL_C_INCLUDES := \
-	$(JNI_H_INCLUDE) \
-	system/core/include
+  $(JNI_H_INCLUDE) \
+  system/core/include
 
 LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
 
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog libmediandk
+LOCAL_SHARED_LIBRARIES := \
+  libandroid libnativehelper_compat_libc++ \
+  liblog libmediandk libEGL
 
-LOCAL_SDK_VERSION := 23
+LOCAL_SDK_VERSION := 24
 
-LOCAL_CFLAGS := -Werror -Wall
+LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES -std=gnu++14
+
+include $(BUILD_SHARED_LIBRARY)
+
+#------------------------------------------------------------------------------
+# Builds libctsmediadrm_jni.so
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctsmediadrm_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  CtsMediaDrmJniOnLoad.cpp \
+  codec-utils-jni.cpp  \
+  md5_utils.cpp \
+  native-mediadrm-jni.cpp \
+
+LOCAL_C_INCLUDES := \
+  $(JNI_H_INCLUDE) \
+  system/core/include
+
+LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
+
+LOCAL_SHARED_LIBRARIES := \
+  libandroid libnativehelper_compat_libc++ \
+  liblog libmediandk libdl libEGL
+
+LOCAL_SDK_VERSION := 24
+
+LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES
+
+LOCAL_NDK_STL_VARIANT := c++_static
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
new file mode 100644
index 0000000..24714a3
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_media_cts_NativeClearKeySystemTest(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM *vm, void */*reserved*/) {
+    JNIEnv *env = NULL;
+
+    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+        return JNI_ERR;
+    }
+
+    if (register_android_media_cts_NativeClearKeySystemTest(env)) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/media/libmediandkjni/md5_utils.cpp b/tests/tests/media/libmediandkjni/md5_utils.cpp
index f4f893a..7850cac 100644
--- a/tests/tests/media/libmediandkjni/md5_utils.cpp
+++ b/tests/tests/media/libmediandkjni/md5_utils.cpp
@@ -157,7 +157,7 @@
  */
 void
 MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) {
-  register UWORD32 a, b, c, d;
+  /*register*/ UWORD32 a, b, c, d;
 
   a = buf[0];
   b = buf[1];
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
new file mode 100644
index 0000000..571cec4
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define TAG "NativeMediaDrm"
+
+#include <utils/Log.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <assert.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <android/native_window_jni.h>
+
+#include "AMediaObjects.h"
+
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaMuxer.h"
+
+typedef std::vector<uint8_t> Uuid;
+
+struct fields_t {
+    jfieldID surface;
+    jfieldID mimeType;
+    jfieldID audioUrl;
+    jfieldID videoUrl;
+};
+
+struct PlaybackParams {
+    jobject surface;
+    jstring mimeType;
+    jstring audioUrl;
+    jstring videoUrl;
+};
+
+static fields_t gFieldIds;
+static bool gGotVendorDefinedEvent = false;
+
+static const size_t kPlayTimeSeconds = 30;
+static const size_t kUuidSize = 16;
+
+static const uint8_t kClearKeyUuid[kUuidSize] = {
+    0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+    0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
+};
+
+// The test content is not packaged with clearkey UUID,
+// we have to use a canned clearkey pssh for the test.
+static const uint8_t kClearkeyPssh[] = {
+    // BMFF box header (4 bytes size + 'pssh')
+    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+    // full box header (version = 1 flags = 0)
+    0x01, 0x00, 0x00, 0x00,
+    // system id
+    0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+    0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+    // number of key ids
+    0x00, 0x00, 0x00, 0x01,
+    // key id
+    0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    // size of data, must be zero
+    0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t kKeyRequestData[] = {
+    0x7b, 0x22, 0x6b, 0x69, 0x64,
+    0x73, 0x22, 0x3a, 0x5b, 0x22,
+    0x4d, 0x44, 0x41, 0x77, 0x4d,
+    0x44, 0x41, 0x77, 0x4d, 0x44,
+    0x41, 0x77, 0x4d, 0x44, 0x41,
+    0x77, 0x4d, 0x44, 0x41, 0x77,
+    0x4d, 0x41, 0x22, 0x5d, 0x2c,
+    0x22, 0x74, 0x79, 0x70, 0x65,
+    0x22, 0x3a, 0x22, 0x74, 0x65,
+    0x6d, 0x70, 0x6f, 0x72, 0x61,
+    0x72, 0x79, 0x22, 0x7d
+};
+
+static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
+
+// base 64 encoded JSON response string, must not contain padding character '='
+static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
+        "\"kid\":\"MDAwMDAwMDAwMDAwMDAwMA\",\"k\":" \
+        "\"Pwoz80CYueIrwHjgobXoVA\"}]}";
+
+static bool isUuidSizeValid(Uuid uuid) {
+    return (uuid.size() == kUuidSize);
+}
+
+static std::vector<uint8_t> jbyteArrayToVector(
+    JNIEnv* env, jbyteArray const &byteArray) {
+    uint8_t* buffer = reinterpret_cast<uint8_t*>(
+        env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
+    std::vector<uint8_t> vector;
+    for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
+        vector.push_back(buffer[i]);
+    }
+    return vector;
+}
+
+static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
+    Uuid juuid;
+    juuid.resize(0);
+    if (uuid != NULL) {
+        juuid = jbyteArrayToVector(env, uuid);
+    }
+    return juuid;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
+
+    if (NULL == uuid) {
+        jniThrowException(env, "java/lang/NullPointerException", "null uuid");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (isUuidSizeValid(juuid)) {
+         return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
+    } else {
+          jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                  "invalid UUID size, expected %u bytes", kUuidSize);
+    }
+    return JNI_FALSE;
+}
+
+void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams &params) {
+    params.surface = env->GetObjectField(
+        playbackParams, gFieldIds.surface);
+
+    params.mimeType = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.mimeType));
+
+    params.audioUrl = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.audioUrl));
+
+    params.videoUrl = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.videoUrl));
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative(
+    JNIEnv* env, jclass clazz, jbyteArray uuid,
+    jstring name, jobject outValue) {
+
+    if (NULL == uuid || NULL == name || NULL == outValue) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "One or more null input parameters");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    AMediaObjects aMediaObjects;
+    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+    if (NULL == aMediaObjects.getDrm()) {
+        jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+        return JNI_FALSE;
+    }
+
+    const char *utf8_name = env->GetStringUTFChars(name, NULL);
+    const char *utf8_outValue = NULL;
+    media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+            utf8_name, &utf8_outValue);
+    env->ReleaseStringUTFChars(name, utf8_name);
+
+    if (NULL != utf8_outValue) {
+        clazz = env->GetObjectClass(outValue);
+        jmethodID mId = env->GetMethodID (clazz, "append",
+                "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+        jstring outString = env->NewStringUTF(
+                static_cast<const char *>(utf8_outValue));
+        env->CallObjectMethod(outValue, mId, outString);
+    } else {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "get property string returns %d", status);
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest__testPsshNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
+
+    if (NULL == uuid || NULL == videoUrl) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "null uuid or null videoUrl");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    AMediaObjects aMediaObjects;
+    aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+    const char* url = env->GetStringUTFChars(videoUrl, 0);
+    if (url) {
+        media_status_t status = AMediaExtractor_setDataSource(
+            aMediaObjects.getVideoExtractor(), url);
+        env->ReleaseStringUTFChars(videoUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set video data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
+    if (psshInfo == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
+        return JNI_FALSE;
+    }
+
+    jboolean testResult = JNI_FALSE;
+    for (size_t i = 0; i < psshInfo->numentries; i++) {
+        PsshEntry *entry = &psshInfo->entries[i];
+
+        if (0 == memcmp(entry->uuid, kClearKeyUuid, sizeof(entry->uuid))) {
+            aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+            if (aMediaObjects.getDrm()) {
+                testResult = JNI_TRUE;
+            } else {
+                ALOGE("Failed to create media drm=%zd", i);
+                testResult = JNI_FALSE;
+            }
+            break;
+        }
+    }
+    return testResult;
+}
+
+static bool isVideo(const char* mime) {
+    return !strncmp(mime, "video/", 6) ? true : false;
+}
+
+static bool isAudio(const char* mime) {
+    return !strncmp(mime, "audio/", 6) ? true : false;
+}
+
+static void addTrack(const AMediaFormat* format,
+        const char* mime, const AMediaCrypto* crypto,
+        const ANativeWindow* window, AMediaCodec** codec) {
+
+    *codec = AMediaCodec_createDecoderByType(mime);
+    if (codec == NULL) {
+        ALOGE("cannot create codec for %s", mime);
+        return;
+    }
+
+    AMediaCodec_configure(*codec, format,
+            const_cast<ANativeWindow*>(window),
+            const_cast<AMediaCrypto*>(crypto), 0);
+}
+
+static void addTracks(const AMediaExtractor* extractor,
+        const AMediaCrypto* crypto, const ANativeWindow* window,
+        AMediaCodec** codec) {
+    size_t numTracks = AMediaExtractor_getTrackCount(
+        const_cast<AMediaExtractor*>(extractor));
+
+    AMediaFormat* trackFormat = NULL;
+    for (size_t i = 0; i < numTracks; ++i) {
+        trackFormat = AMediaExtractor_getTrackFormat(
+            const_cast<AMediaExtractor*>(extractor), i);
+        if (trackFormat) {
+            ALOGV("track %zd format: %s", i,
+                    AMediaFormat_toString(trackFormat));
+
+            const char* mime = "";
+            if (!AMediaFormat_getString(
+                trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+                ALOGE("no mime type");
+
+                AMediaFormat_delete(trackFormat);
+                return;
+            } else if (isAudio(mime) || isVideo(mime)) {
+                AMediaExtractor_selectTrack(
+                    const_cast<AMediaExtractor*>(extractor), i);
+                ALOGV("track %zd codec format: %s", i,
+                        AMediaFormat_toString(trackFormat));
+
+                addTrack(trackFormat, mime, crypto, window, codec);
+                AMediaCodec_start(*codec);
+                AMediaCodec_flush(*codec);
+                AMediaExtractor_seekTo(
+                    const_cast<AMediaExtractor*>(extractor), 0,
+                            AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+            }
+            AMediaFormat_delete(trackFormat);
+        }
+    }
+}
+
+static int64_t getSystemNanoTime() {
+    timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
+        int64_t* presentationTimeUs, bool* eosReached) {
+    media_status_t status = AMEDIA_OK;
+
+    ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
+    if (bufferIndex >= 0) {
+        size_t bufsize;
+        uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
+
+        int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
+        if (sampleSize < 0) {
+            sampleSize = 0;
+            *eosReached = true;
+        }
+
+        *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
+
+        AMediaCodecCryptoInfo *cryptoInfo =
+            AMediaExtractor_getSampleCryptoInfo(extractor);
+
+        if (cryptoInfo) {
+            status = AMediaCodec_queueSecureInputBuffer(
+                codec, bufferIndex, 0, cryptoInfo,
+                *presentationTimeUs,
+                *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+            AMediaCodecCryptoInfo_delete(cryptoInfo);
+        } else {
+            status = AMediaCodec_queueInputBuffer(
+                codec, bufferIndex, 0, sampleSize,
+                *presentationTimeUs,
+                *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+        }
+        AMediaExtractor_advance(extractor);
+    }
+}
+
+static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
+    int64_t* startTimeNano) {
+
+    AMediaCodecBufferInfo info;
+    ssize_t bufferIndex  = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
+    if (bufferIndex >= 0) {
+        if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+            return true;  // eos reached
+        }
+
+        if (*startTimeNano < 0) {
+            *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
+        }
+        int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
+                getSystemNanoTime();
+        if (delay > 0) {
+            usleep(delay / 1000);
+        }
+
+        AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
+    } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+        ALOGV("output buffers changed");
+    } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+        AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
+        ALOGV("format changed to: %s", AMediaFormat_toString(format));
+        AMediaFormat_delete(format);
+    } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+         ALOGV("no output buffer right now");
+         usleep(20000);
+    } else {
+         ALOGV("unexpected info code: %zd", bufferIndex);
+    }
+    return false;
+}
+
+static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
+        PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
+
+    ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
+    AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
+    AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
+
+    AMediaCodec* audioCodec = NULL;
+    AMediaCodec* videoCodec = NULL;
+    AMediaCrypto* crypto = NULL;
+
+    crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
+    if (crypto == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "failed to create crypto object");
+        return JNI_FALSE;
+    }
+
+    addTracks(audioExtractor, NULL, NULL, &audioCodec);
+
+    addTracks(videoExtractor, crypto, window, &videoCodec);
+
+    bool sawAudioInputEos = false;
+    bool sawAudioOutputEos = false;
+    bool sawVideoInputEos = false;
+    bool sawVideoOutputEos = false;
+    int64_t videoPresentationTimeUs = 0;
+    int64_t videoStartTimeNano = -1;
+    struct timespec timeSpec;
+    clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+    time_t startTimeSec = timeSpec.tv_sec;
+
+    while (!sawAudioOutputEos && !sawVideoOutputEos) {
+        if (!sawVideoInputEos) {
+            fillDecoder(videoCodec, videoExtractor,
+                    &videoPresentationTimeUs, &sawVideoInputEos);
+        }
+
+        if (!sawAudioInputEos) {
+            // skip audio, still need to advance the audio extractor
+            AMediaExtractor_advance(audioExtractor);
+        }
+
+        if (!sawVideoOutputEos) {
+            sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
+                    &videoStartTimeNano);
+        }
+
+        clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+        if (timeSpec.tv_sec >= static_cast<time_t>(
+            (startTimeSec + kPlayTimeSeconds))) {
+            // stop reading samples and drain the output buffers
+            sawAudioInputEos = sawVideoInputEos = true;
+            sawAudioOutputEos = true; // ignore audio
+        }
+    }
+
+    if (audioCodec) {
+        AMediaCodec_stop(audioCodec);
+        AMediaCodec_delete(audioCodec);
+    }
+    if (videoCodec) {
+        AMediaCodec_stop(videoCodec);
+        AMediaCodec_delete(videoCodec);
+    }
+
+    AMediaCrypto_delete(crypto);
+    ANativeWindow_release(window);
+    return JNI_TRUE;
+}
+
+static void listener(
+    AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
+    AMediaDrmEventType eventType,
+    int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
+
+    switch (eventType) {
+        case EVENT_PROVISION_REQUIRED:
+            ALOGD("EVENT_PROVISION_REQUIRED received");
+            break;
+        case EVENT_KEY_REQUIRED:
+            ALOGD("EVENT_KEY_REQUIRED received");
+            break;
+        case EVENT_KEY_EXPIRED:
+            ALOGD("EVENT_KEY_EXPIRED received");
+            break;
+        case EVENT_VENDOR_DEFINED:
+            gGotVendorDefinedEvent = true;
+            ALOGD("EVENT_VENDOR_DEFINED received");
+            break;
+        default:
+            ALOGD("Unknown event received");
+            break;
+    }
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
+    if (NULL == uuid || NULL == playbackParams) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "null uuid or null playback parameters");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    PlaybackParams params;
+    initPlaybackParams(env, playbackParams, params);
+
+    AMediaObjects aMediaObjects;
+    media_status_t status = AMEDIA_OK;
+    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+    if (NULL == aMediaObjects.getDrm()) {
+        jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+        return JNI_FALSE;
+    }
+
+    status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "setOnEventListener failed");
+        return JNI_FALSE;
+    }
+
+    aMediaObjects.setAudioExtractor(AMediaExtractor_new());
+    const char* url = env->GetStringUTFChars(params.audioUrl, 0);
+    if (url) {
+        status = AMediaExtractor_setDataSource(
+            aMediaObjects.getAudioExtractor(), url);
+        env->ReleaseStringUTFChars(params.audioUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set audio data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+    url = env->GetStringUTFChars(params.videoUrl, 0);
+    if (url) {
+        status = AMediaExtractor_setDataSource(
+            aMediaObjects.getVideoExtractor(), url);
+        env->ReleaseStringUTFChars(params.videoUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set video data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    AMediaDrmSessionId sessionId;
+    status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "openSession failed");
+        return JNI_FALSE;
+    }
+
+    // Pointer to keyRequest memory, which remains until the next
+    // AMediaDrm_getKeyRequest call or until the drm object is released.
+    const uint8_t* keyRequest;
+    size_t keyRequestSize = 0;
+
+    // The server recognizes "video/mp4" but not "video/avc".
+    status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(), &sessionId,
+            kClearkeyPssh, sizeof(kClearkeyPssh),
+            "video/mp4" /*mimeType*/, KEY_TYPE_STREAMING,
+            NULL, 0, &keyRequest, &keyRequestSize);
+    if (status != AMEDIA_OK) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "getKeyRequest failed, error = %d", status);
+        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+        return JNI_FALSE;
+    }
+
+    if (kKeyRequestSize != keyRequestSize) {
+        ALOGE("Invalid keyRequestSize %zd", keyRequestSize);
+        return JNI_FALSE;
+    }
+
+    if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
+        ALOGE("Invalid keyRequest data is returned");
+        return JNI_FALSE;
+    }
+
+    AMediaDrmKeySetId keySetId;
+    gGotVendorDefinedEvent = false;
+    status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
+            reinterpret_cast<const uint8_t*>(kResponse),
+            sizeof(kResponse), &keySetId);
+    if (status != AMEDIA_OK) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "provideKeyResponse failed, error = %d", status);
+        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+        return JNI_FALSE;
+    }
+
+    // Check if the event listener has received the expected event sent by
+    // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
+    const char *utf8_outValue = NULL;
+    status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+            "listenerTestSupport", &utf8_outValue);
+    if (status == AMEDIA_OK && NULL != utf8_outValue) {
+        std::string eventType(utf8_outValue);
+        if (eventType.compare("true") == 0) {
+            int count = 0;
+            while (!gGotVendorDefinedEvent && count++ < 5) {
+               // Prevents race condition when the event arrives late
+               usleep(2000);
+            }
+            if (!gGotVendorDefinedEvent) {
+                ALOGE("Event listener did not receive the expected event.");
+                jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                        "Event listener did not receive the expected event.");
+                AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+                return JNI_FALSE;
+           }
+        }
+    }
+
+    playContent(env, aMediaObjects, params, sessionId, juuid);
+
+    status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "closeSession failed");
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+static JNINativeMethod gMethods[] = {
+    { "isCryptoSchemeSupportedNative", "([B)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative },
+
+    { "testClearKeyPlaybackNative",
+            "([BLandroid/media/cts/NativeClearKeySystemTest$PlaybackParams;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative },
+
+    { "testGetPropertyStringNative",
+            "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative },
+
+    { "testPsshNative", "([BLjava/lang/String;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest__testPsshNative },
+};
+
+int register_android_media_cts_NativeClearKeySystemTest(JNIEnv* env) {
+    jint result = JNI_ERR;
+    jclass testClass =
+        env->FindClass("android/media/cts/NativeClearKeySystemTest");
+    if (testClass) {
+        jclass playbackParamsClass = env->FindClass(
+            "android/media/cts/NativeClearKeySystemTest$PlaybackParams");
+        if (playbackParamsClass) {
+            jclass surfaceClass =
+                env->FindClass("android/view/Surface");
+            if (surfaceClass) {
+                gFieldIds.surface = env->GetFieldID(playbackParamsClass,
+                        "surface", "Landroid/view/Surface;");
+            } else {
+                gFieldIds.surface = NULL;
+            }
+            gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
+                    "mimeType", "Ljava/lang/String;");
+            gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
+                    "audioUrl", "Ljava/lang/String;");
+            gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
+                    "videoUrl", "Ljava/lang/String;");
+        } else {
+            ALOGE("PlaybackParams class not found");
+        }
+
+    } else {
+        ALOGE("NativeClearKeySystemTest class not found");
+    }
+
+    result = env->RegisterNatives(testClass, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+    return result;
+}
diff --git a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
new file mode 100644
index 0000000..9ead8fb
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 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.media.cts;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import android.net.Uri;
+import android.os.Build;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import android.cts.util.MediaUtils;
+import com.google.android.collect.Lists;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.UUID;
+
+/**
+ * Tests MediaDrm NDK APIs. ClearKey system uses a subset of NDK APIs,
+ * this test only tests the APIs that are supported by ClearKey system.
+ */
+public class NativeClearKeySystemTest extends MediaPlayerTestBase {
+    private static final String TAG = NativeClearKeySystemTest.class.getSimpleName();
+
+    private static final int CONNECTION_RETRIES = 10;
+    private static final int VIDEO_WIDTH_CENC = 1280;
+    private static final int VIDEO_HEIGHT_CENC = 720;
+    private static final String ISO_BMFF_VIDEO_MIME_TYPE = "video/avc";
+    private static final String ISO_BMFF_AUDIO_MIME_TYPE = "audio/avc";
+    private static final Uri CENC_AUDIO_URL = Uri.parse(
+        "https://storage.googleapis.com/wvmedia/clear/h264/llama/" +
+        "llama_aac_audio.mp4");
+
+    private static final Uri CENC_CLEARKEY_VIDEO_URL = Uri.parse(
+        "https://storage.googleapis.com/wvmedia/clearkey/" +
+        "llama_h264_main_720p_8000.mp4");
+
+    private static final int UUID_BYTE_SIZE = 16;
+    private static final UUID CLEARKEY_SCHEME_UUID =
+            new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+    private static final UUID BAD_SCHEME_UUID =
+            new UUID(0xffffffffffffffffL, 0xffffffffffffffffL);
+    private MediaCodecClearKeyPlayer mMediaCodecPlayer;
+
+    static {
+        try {
+            System.loadLibrary("ctsmediadrm_jni");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+            e.printStackTrace();
+        }
+        try {
+            System.loadLibrary("mediandk");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+            e.printStackTrace();
+        }
+    }
+
+    public static class PlaybackParams {
+        public Surface surface;
+        public String mimeType;
+        public String audioUrl;
+        public String videoUrl;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (false == deviceHasMediaDrm()) {
+            tearDown();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private boolean deviceHasMediaDrm() {
+        // ClearKey is introduced after KitKat.
+        if (Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT) {
+            Log.i(TAG, "This test is designed to work after Android KitKat.");
+            return false;
+        }
+        return true;
+    }
+
+    private static final byte[] uuidByteArray(UUID uuid) {
+        ByteBuffer buffer = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
+        buffer.putLong(uuid.getMostSignificantBits());
+        buffer.putLong(uuid.getLeastSignificantBits());
+        return buffer.array();
+    }
+
+    public void testIsCryptoSchemeSupported() throws Exception {
+        assertTrue(isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID)));
+    }
+
+    public void testIsCryptoSchemeNotSupported() throws Exception {
+        assertFalse(isCryptoSchemeSupportedNative(uuidByteArray(BAD_SCHEME_UUID)));
+    }
+
+    public void testPssh() throws Exception {
+        assertTrue(testPsshNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+                CENC_CLEARKEY_VIDEO_URL.toString()));
+    }
+
+    public void testGetPropertyString() throws Exception {
+        StringBuffer value = new StringBuffer();
+        testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value);
+        assertEquals("ClearKey CDM", value.toString());
+    }
+
+    public void testUnknownPropertyString() throws Exception {
+        try {
+            StringBuffer value = new StringBuffer();
+            testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+                    "unknown-property", value);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'");
+            assertThat(e.getMessage(), containsString("get property string returns"));
+        }
+    }
+
+    /**
+     * Tests native clear key system playback.
+     */
+    private void testClearKeyPlayback(
+            String mimeType, /*String initDataType,*/ Uri audioUrl, Uri videoUrl,
+            int videoWidth, int videoHeight) throws Exception {
+
+        if (!isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID))) {
+            throw new Error("Crypto scheme is not supported.");
+        }
+
+        IConnectionStatus connectionStatus = new ConnectionStatus(mContext);
+        if (!connectionStatus.isAvailable()) {
+            throw new Error("Network is not available, reason: " +
+                    connectionStatus.getNotConnectedReason());
+        }
+
+        // If device is not online, recheck the status a few times.
+        int retries = 0;
+        while (!connectionStatus.isConnected()) {
+            if (retries++ >= CONNECTION_RETRIES) {
+                throw new Error("Device is not online, reason: " +
+                        connectionStatus.getNotConnectedReason());
+            }
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+        connectionStatus.testConnection(videoUrl);
+
+        if (!MediaUtils.checkCodecsForPath(mContext, videoUrl.toString())) {
+            Log.i(TAG, "Device does not support " +
+                  videoWidth + "x" + videoHeight + " resolution for " + mimeType);
+            return;  // skip
+        }
+
+        PlaybackParams params = new PlaybackParams();
+        params.surface = mActivity.getSurfaceHolder().getSurface();
+        params.mimeType = mimeType;
+        params.audioUrl = audioUrl.toString();
+        params.videoUrl = videoUrl.toString();
+
+        if (!testClearKeyPlaybackNative(
+            uuidByteArray(CLEARKEY_SCHEME_UUID), params)) {
+            Log.e(TAG, "Fails play back using native media drm APIs.");
+        }
+        params.surface.release();
+    }
+
+    private ArrayList<Integer> intVersion(String version) {
+        String versions[] = version.split("\\.");
+
+        ArrayList<Integer> versionNumbers = Lists.newArrayList();
+        for (String subVersion : versions) {
+            versionNumbers.add(Integer.parseInt(subVersion));
+        }
+        return versionNumbers;
+    }
+
+    private static native boolean isCryptoSchemeSupportedNative(final byte[] uuid);
+
+    private static native boolean testClearKeyPlaybackNative(final byte[] uuid,
+            PlaybackParams params);
+
+    private static native boolean testGetPropertyStringNative(final byte[] uuid,
+            final String name, StringBuffer value);
+
+    private static native boolean testPsshNative(final byte[] uuid, final String videoUrl);
+
+    public void testClearKeyPlaybackCenc() throws Exception {
+        testClearKeyPlayback(
+                ISO_BMFF_VIDEO_MIME_TYPE,
+                CENC_AUDIO_URL,
+                CENC_CLEARKEY_VIDEO_URL,
+                VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+    }
+}
diff --git a/tests/tests/net/native/Android.mk b/tests/tests/net/native/Android.mk
new file mode 100644
index 0000000..8338432
--- /dev/null
+++ b/tests/tests/net/native/Android.mk
@@ -0,0 +1,2 @@
+include $(call all-subdir-makefiles)
+
diff --git a/tests/tests/net/native/qtaguid/Android.mk b/tests/tests/net/native/qtaguid/Android.mk
new file mode 100644
index 0000000..4f5bf9f
--- /dev/null
+++ b/tests/tests/net/native/qtaguid/Android.mk
@@ -0,0 +1,71 @@
+# Copyright (C) 2017 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.
+
+# Build the unit tests.
+
+LOCAL_PATH:= $(call my-dir)
+
+test_executable := CtsNativeNetTestCases
+list_executable := $(test_executable)_list
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+
+LOCAL_MODULE := $(test_executable)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SRC_FILES := \
+    src/NativeQtaguidTest.cpp
+
+LOCAL_C_INCLUDES := \
+    external/gtest/include \
+
+LOCAL_SHARED_LIBRARIES := \
+    libutils \
+    liblog \
+    libcutils \
+
+LOCAL_STATIC_LIBRARIES := \
+    libgtest
+
+LOCAL_CTS_TEST_PACKAGE := android.net.native
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_CFLAGS := -Werror -Wall
+
+include $(BUILD_CTS_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := $(list_executable)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+    src/NativeQtaguidTest.cpp
+
+LOCAL_CFLAGS := \
+    -DBUILD_ONLY \
+
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/tests/tests/net/native/qtaguid/AndroidTest.xml b/tests/tests/net/native/qtaguid/AndroidTest.xml
new file mode 100644
index 0000000..6f4e8f5
--- /dev/null
+++ b/tests/tests/net/native/qtaguid/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 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.
+-->
+<configuration description="Config for CTS Native Network xt_qtaguid test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsNativeNetTestCases->/data/local/tmp/CtsNativeNetTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsNativeNetTestCases" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/tests/net/native/qtaguid/src/NativeQtaguidTest.cpp b/tests/tests/net/native/qtaguid/src/NativeQtaguidTest.cpp
new file mode 100644
index 0000000..0301c81
--- /dev/null
+++ b/tests/tests/net/native/qtaguid/src/NativeQtaguidTest.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <gtest/gtest.h>
+
+#if !defined(BUILD_ONLY)
+#include <cutils/qtaguid.h>
+#endif
+
+#if !defined(BUILD_ONLY)
+int getCtrlSkInfo(int tag, uid_t uid, uint64_t* sk_addr, int* ref_cnt) {
+    FILE *fp;
+    fp = fopen("/proc/net/xt_qtaguid/ctrl", "r");
+    if (!fp)
+        return -ENOENT;
+    uint64_t full_tag = (uint64_t)tag << 32 | uid;
+    char pattern[40];
+    snprintf(pattern, sizeof(pattern), " tag=0x%" PRIx64 " (uid=%" PRIu32 ")", full_tag, uid);
+
+    size_t len;
+    char *line_buffer = NULL;
+    while(getline(&line_buffer, &len, fp) != -1) {
+        if (strstr(line_buffer, pattern) == NULL)
+            continue;
+        int res;
+        pid_t dummy_pid;
+        uint64_t k_tag;
+        uint32_t k_uid;
+        const int TOTAL_PARAM = 5;
+        res = sscanf(line_buffer, "sock=%" PRIx64 " tag=0x%" PRIx64 " (uid=%" PRIu32 ") "
+                     "pid=%u f_count=%u", sk_addr, &k_tag, &k_uid,
+                     &dummy_pid, ref_cnt);
+        if (!(res == TOTAL_PARAM && k_tag == full_tag && k_uid == uid))
+            return -EINVAL;
+        free(line_buffer);
+        return 0;
+    }
+    free(line_buffer);
+    return -ENOENT;
+}
+
+void checkNoSocketPointerLeaks(int family) {
+    int sockfd = socket(family, SOCK_STREAM, 0);
+    uid_t uid = getuid();
+    int tag = arc4random();
+    int ref_cnt;
+    uint64_t sk_addr;
+    uint64_t expect_addr = 0;
+
+    EXPECT_EQ(0, qtaguid_tagSocket(sockfd, tag, uid));
+    EXPECT_EQ(0, getCtrlSkInfo(tag, uid, &sk_addr, &ref_cnt));
+    EXPECT_EQ(expect_addr, sk_addr);
+    EXPECT_EQ(0, qtaguid_untagSocket(sockfd));
+    EXPECT_EQ(-ENOENT, getCtrlSkInfo(tag, uid, &sk_addr, &ref_cnt));
+}
+#else
+void checkNoSocketPointerLeaks(int family) {}
+#endif
+
+TEST (NativeQtaguidTest, no_socket_addr_leak) {
+  checkNoSocketPointerLeaks(AF_INET);
+  checkNoSocketPointerLeaks(AF_INET6);
+}
+
+int main(int argc, char **argv) {
+      testing::InitGoogleTest(&argc, argv);
+
+      return RUN_ALL_TESTS();
+}
diff --git a/tests/tests/security/res/raw/bug_21443020_webm.mp4 b/tests/tests/security/res/raw/bug_21443020_webm.mp4
new file mode 100644
index 0000000..edb761a
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_21443020_webm.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_22771132.mp4 b/tests/tests/security/res/raw/bug_22771132.mp4
new file mode 100644
index 0000000..f97e1f3
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_22771132.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
deleted file mode 100644
index 9041013..0000000
--- a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.security.cts.activity.ISecureRandomService;
-import android.security.cts.activity.SecureRandomService;
-import android.test.AndroidTestCase;
-
-import java.io.BufferedReader;
-import java.io.EOFException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class ClonedSecureRandomTest extends AndroidTestCase {
-    private static final int MAX_SHUTDOWN_TRIES = 50;
-
-    private static final int ANSWER_TIMEOUT_SECONDS = 180;
-
-    private static final String SEPARATE_PROCESS_NAME = ":secureRandom";
-
-    private static final int MAX_PID = 32768;
-
-    /**
-     * Attempt to burn through PIDs faster after this many iterations to reach a
-     * wrap-around point faster.
-     */
-    private static final int PRIMING_ITERATIONS = 128;
-
-    private static final int RANDOM_BYTES_PER_PID = 8;
-
-    private static final int MAX_PIDS_WASTED = 1024;
-
-    private static final int PID_WASTING_SKIP_LOWER = 64;
-
-    private static final int PID_WASTING_SKIP_UPPER = 2048;
-
-    private volatile CountDownLatch mLatch;
-
-    private Intent mSeparateIntent;
-
-    private ISecureRandomService mSecureRandomService;
-
-    private ServiceConnection mServiceConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            mSecureRandomService = ISecureRandomService.Stub.asInterface(service);
-            mLatch.countDown();
-        }
-
-        public void onServiceDisconnected(ComponentName className) {
-        }
-    };
-
-    private boolean mHasDisconnected;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mSeparateIntent = new Intent(getContext(), SecureRandomService.class);
-    }
-
-    /**
-     * This test spawns a Service in a new process to check the initial state of
-     * SecureRandom. It then attempts to make the PID number wrap around so it
-     * sees a new process with the same PID twice. The test completes when it
-     * sees two newly started processes with the same PID and compares their
-     * output.
-     */
-    public void testCheckForDuplicateOutput() throws Exception {
-        assertEquals("Only supports up to " + MAX_PID + " because of memory requirements",
-                Integer.toString(MAX_PID), getFirstLineFromFile("/proc/sys/kernel/pid_max"));
-
-        final String packageName = getContext().getPackageName();
-        String separateProcessName = packageName + SEPARATE_PROCESS_NAME;
-
-        /*
-         * Using a byte[][] and BitSet gives us a fixed upper bound for the
-         * memory cost of this test. One could possibly use a SparseArray if the
-         * upper bound becomes too large (for instance, if PID_MAX is large),
-         * only keep track of a smaller number of outputs, and just cause a
-         * wrap-around of PIDs to keep the test working.
-         */
-        byte[][] outputs = new byte[MAX_PID][RANDOM_BYTES_PER_PID];
-        BitSet seenPids = new BitSet(MAX_PID);
-
-        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-
-        int myPid = android.os.Process.myPid();
-
-        /*
-         * We're guaranteed to see at least one duplicate if we iterate MAX_PID
-         * number of times because of the pigeonhole principle. In an attempt to
-         * hit a collision faster, first get a closely-spaced sampling of PIDs
-         * then spin up a bunch of threads locally to get us closer to wrapping
-         * around to the first PID.
-         */
-        int firstPid = -1;
-        int previousPid = -1;
-        int lastPid = -1;
-        for (int i = 0; i < MAX_PID; i++) {
-            byte[] output = new byte[RANDOM_BYTES_PER_PID];
-            int pid;
-
-            mLatch = new CountDownLatch(1);
-            getContext().startService(mSeparateIntent);
-            getContext().bindService(mSeparateIntent, mServiceConnection, 0);
-            if (!mLatch.await(ANSWER_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
-                fail("Timeout waiting for answer from SecureRandomService; cannot complete test");
-            }
-
-            // Create another latch we'll use to ensure the service has stopped.
-            final CountDownLatch serviceStopLatch = new CountDownLatch(1);
-            mSecureRandomService.asBinder().linkToDeath(new IBinder.DeathRecipient() {
-                @Override
-                public void binderDied() {
-                    serviceStopLatch.countDown();
-                }
-            }, 0);
-
-            try {
-                pid = mSecureRandomService.getRandomBytesAndPid(output);
-            } catch (RemoteException e) {
-                // The process died before we could query. Try again.
-                continue;
-            }
-
-            getContext().unbindService(mServiceConnection);
-
-            /*
-             * Ensure the background process has stopped by waiting for the
-             * latch to fire.
-             */
-            int tries = 0;
-            do {
-                /*
-                 * If this has looped more than once, try to yield to
-                 * system_server.
-                 */
-                if (tries > 0) {
-                    Thread.yield();
-                }
-                getContext().stopService(mSeparateIntent);
-                am.killBackgroundProcesses(packageName);
-            } while (!serviceStopLatch.await(100, TimeUnit.MILLISECONDS) && tries++ < MAX_SHUTDOWN_TRIES);
-            assertTrue("Background process should have stopped already", tries < MAX_SHUTDOWN_TRIES);
-
-            /*
-             * Make sure the AndroidManifest.xml wasn't altered in a way that
-             * breaks the test.
-             */
-            assertFalse("SecureRandomService must run in a different process. Check "
-                    + "AndroidManifest.xml to ensure it has a unique android:process=\"...\"",
-                    myPid == pid);
-
-            // We didn't get a new process for some reason. Try again.
-            if (previousPid == pid) {
-                i--;
-                continue;
-            } else if (previousPid == -1 && firstPid == -1) {
-                /*
-                 * The first time around, we'll discard the output. This is
-                 * needed because we don't know if the SecureRandomService instance
-                 * has been running before or not. To be consistent, we only
-                 * want the first outputs from SecureRandom for this test.
-                 */
-                i--;
-                previousPid = pid;
-                continue;
-            } else {
-                previousPid = pid;
-            }
-
-            if (seenPids.get(pid)) {
-                assertFalse("SecureRandom should not output the same value twice (pid=" + pid
-                                + ", output=" + Arrays.toString(output) + ", outputs[pid]="
-                                + Arrays.toString(outputs[pid]) + ")",
-                        Arrays.equals(output, outputs[pid]));
-                return;
-            }
-
-            seenPids.set(pid);
-            System.arraycopy(output, 0, outputs[pid], 0, output.length);
-
-            if (firstPid == -1) {
-                firstPid = pid;
-            }
-
-            if (i <= PRIMING_ITERATIONS) {
-                lastPid = pid;
-            } else if (pid > lastPid && (lastPid > firstPid || pid < firstPid)) {
-                wastePids(firstPid, previousPid);
-            }
-        }
-
-        /*
-         * This should never be reached unless the test was altered to break it.
-         * Since we're looping until we see PID_MAX unique answers, we must have
-         * seen a duplicate by the pigeonhole principle.
-         */
-        fail("Must see a duplicate PID");
-    }
-
-    /**
-     * This is an attempt to get the PIDs to roll over faster. Threads use up
-     * PIDs on Android and spawning a new thread is much faster than having
-     * another service spawned as we are doing in this test.
-     */
-    private static void wastePids(int firstPid, int previousPid) {
-        int distance = (firstPid - previousPid + MAX_PID) % MAX_PID;
-
-        // Don't waste PIDs if we're close to wrap-around to improve odds of
-        // collision.
-        if ((distance < PID_WASTING_SKIP_LOWER) || (MAX_PID - distance < PID_WASTING_SKIP_UPPER)) {
-            return;
-        }
-
-        for (int i = 0; i < distance; i++) {
-            Thread t = new Thread();
-            t.start();
-        }
-    }
-
-    private static String getFirstLineFromFile(String filename) throws IOException {
-        BufferedReader in = null;
-        try {
-            in = new BufferedReader(new FileReader(filename));
-            final String line = in.readLine();
-            if (line == null) {
-                throw new EOFException("EOF encountered before reading first line of " + filename);
-            }
-            return line.trim();
-        } finally {
-            if (in != null) {
-                in.close();
-            }
-        }
-    }
-}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 3286280..0f6cbbd 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -45,6 +45,7 @@
 import android.webkit.cts.CtsTestServer;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.concurrent.locks.Condition;
@@ -70,6 +71,14 @@
      before any existing test methods
      ***********************************************************/
 
+    public void testStagefright_bug_22771132() throws Exception {
+        doStagefrightTest(R.raw.bug_22771132);
+    }
+
+    public void testStagefright_bug_21443020() throws Exception {
+        doStagefrightTest(R.raw.bug_21443020_webm);
+    }
+
     public void testStagefright_bug_34360591() throws Exception {
         doStagefrightTest(R.raw.bug_34360591);
     }
@@ -150,19 +159,23 @@
         doStagefrightTest(R.raw.cve_2015_6598);
     }
 
-    public void testStagefright_bug_32873375() throws Exception {
-        doStagefrightTest(R.raw.bug_32873375);
-    }
-
     public void testStagefright_bug_26366256() throws Exception {
         doStagefrightTest(R.raw.bug_26366256);
     }
 
+    public void testStagefright_cve_2016_2429_b_27211885() throws Exception {
+        doStagefrightTest(R.raw.cve_2016_2429_b_27211885);
+    }
+
     /***********************************************************
      to prevent merge conflicts, add M tests below this comment,
      before any existing test methods
      ***********************************************************/
 
+    public void testStagefright_bug_32873375() throws Exception {
+        doStagefrightTest(R.raw.bug_32873375);
+    }
+
     public void testStagefright_bug_33818508() throws Exception {
         doStagefrightTest(R.raw.bug_33818508);
     }
@@ -255,10 +268,6 @@
         doStagefrightTest(R.raw.bug_27855419);
     }
 
-    public void testStagefright_cve_2016_2429_b_27211885() throws Exception {
-        doStagefrightTest(R.raw.cve_2016_2429_b_27211885);
-    }
-
     /***********************************************************
      to prevent merge conflicts, add N tests below this comment,
      before any existing test methods
@@ -407,7 +416,6 @@
 
         final MediaPlayerCrashListener mpcl = new MediaPlayerCrashListener();
 
-
         LooperThread t = new LooperThread(new Runnable() {
             @Override
             public void run() {
@@ -514,7 +522,6 @@
                 // indicative of problems with our tame CTS test web server
             }
         }
-        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
         int numtracks = ex.getTrackCount();
         String rname = url != null ? url: resources.getResourceEntryName(rid);
         Log.i(TAG, "start mediacodec test for: " + rname + ", which has " + numtracks + " tracks");
@@ -529,13 +536,15 @@
                 continue;
             }
             String mime = format.getString(MediaFormat.KEY_MIME);
-            for (MediaCodecInfo info: codecList.getCodecInfos()) {
+            int numCodecs = MediaCodecList.getCodecCount();
+            for (int i = 0; i < numCodecs; i++) {
+                MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
                 if (info.isEncoder()) {
                     continue;
                 }
                 try {
                     MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mime);
-                    if (caps != null && caps.isFormatSupported(format)) {
+                    if (caps != null) {
                         matchingCodecs.add(info.getName());
                     }
                 } catch (IllegalArgumentException e) {
@@ -556,17 +565,22 @@
                 if (mime.startsWith("video/")) {
                     surface = getDummySurface();
                 }
-                codec.configure(format, surface, null, 0);
-                codec.start();
+                try {
+                    codec.configure(format, surface, null, 0);
+                    codec.start();
+                } catch (Exception e) {
+                    Log.i(TAG, "Failed to start/configure:", e);
+                }
                 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
                 try {
+                    ByteBuffer [] inputBuffers = codec.getInputBuffers();
                     while (true) {
                         int flags = ex.getSampleFlags();
                         long time = ex.getSampleTime();
                         ex.getCachedDuration();
                         int bufidx = codec.dequeueInputBuffer(5000);
                         if (bufidx >= 0) {
-                            int n = ex.readSampleData(codec.getInputBuffer(bufidx), 0);
+                            int n = ex.readSampleData(inputBuffers[bufidx], 0);
                             if (n < 0) {
                                 flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
                                 time = 0;
@@ -587,14 +601,8 @@
                             codec.releaseOutputBuffer(status, true);
                         }
                     }
-                } catch (MediaCodec.CodecException ce) {
-                    if (ce.getErrorCode() == MediaCodec.CodecException.ERROR_RECLAIMED) {
-                        // This indicates that the remote service is dead, suggesting a crash.
-                        throw new RuntimeException(ce);
-                    }
-                    // Other errors ignored.
-                } catch (IllegalStateException ise) {
-                    // Other errors ignored.
+                } catch (Exception e) {
+                    // local exceptions ignored, not security issues
                 } finally {
                     codec.release();
                 }
@@ -606,7 +614,7 @@
         assertFalse("Device *IS* vulnerable to " + cve,
                     mpcl.waitForError() == MediaPlayer.MEDIA_ERROR_SERVER_DIED);
         thr.stopLooper();
-
+        thr.join();
     }
 
     private void doStagefrightTestMediaMetadataRetriever(final int rid) throws Exception {
diff --git a/tests/tests/text/src/android/text/method/cts/TouchTest.java b/tests/tests/text/src/android/text/method/cts/TouchTest.java
index 233539c..d9b8aa9 100644
--- a/tests/tests/text/src/android/text/method/cts/TouchTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TouchTest.java
@@ -27,6 +27,7 @@
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.MotionEvent;
+import android.view.ViewGroup;
 import android.widget.TextView;
 
 public class TouchTest extends ActivityInstrumentationTestCase2<CtsActivity> {
@@ -58,7 +59,11 @@
 
         runTestOnUiThread(new Runnable() {
             public void run() {
-                mActivity.setContentView(tv);
+                final float pixelPerSp =
+                    mActivity.getResources().getDisplayMetrics().scaledDensity;
+                // Explicitly set the width so that |LONG_TEXT| causes horizontal scroll.
+                mActivity.setContentView(tv, new ViewGroup.LayoutParams(
+                    (int)(100 * pixelPerSp), ViewGroup.LayoutParams.MATCH_PARENT));
                 tv.setSingleLine(true);
                 tv.setLines(2);
             }
diff --git a/tools/cts-tradefed/Android.mk b/tools/cts-tradefed/Android.mk
index 0c94c39..8fb638c 100644
--- a/tools/cts-tradefed/Android.mk
+++ b/tools/cts-tradefed/Android.mk
@@ -25,7 +25,7 @@
 LOCAL_SUITE_TARGET_ARCH := $(TARGET_ARCH)
 LOCAL_SUITE_NAME := CTS
 LOCAL_SUITE_FULLNAME := "Compatibility Test Suite"
-LOCAL_SUITE_VERSION := 7.1_r7
+LOCAL_SUITE_VERSION := 7.1_r8
 
 LOCAL_MODULE := cts-tradefed