Merge "Remove confused comment in AndroidManifest.xml for android.content test" into lmp-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 4bbd783..a90fbf5 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -24,6 +24,16 @@
     CtsSharedUidInstallDiffCert \
     CtsSimpleAppInstall \
     CtsSimpleAppInstallDiffCert \
+    CtsSplitApp \
+    CtsSplitApp_x86 \
+    CtsSplitApp_x86_64 \
+    CtsSplitApp_armeabi-v7a \
+    CtsSplitApp_armeabi \
+    CtsSplitApp_arm64-v8a \
+    CtsSplitApp_mips64 \
+    CtsSplitApp_mips \
+    CtsSplitAppDiffVersion \
+    CtsSplitAppDiffCert \
     CtsTargetInstrumentationApp \
     CtsUsePermissionDiffCert \
     CtsWriteExternalStorageApp \
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
new file mode 100644
index 0000000..21a76bb
--- /dev/null
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.appsecurity;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.cts.util.AbiUtils;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tests that verify installing of various split APKs from host side.
+ */
+public class SplitTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+    private static final String PKG = "com.android.cts.splitapp";
+
+    private static final String APK = "CtsSplitApp.apk";
+
+    private static final String APK_mdpi = "CtsSplitApp_mdpi-v4.apk";
+    private static final String APK_hdpi = "CtsSplitApp_hdpi-v4.apk";
+    private static final String APK_xhdpi = "CtsSplitApp_xhdpi-v4.apk";
+    private static final String APK_xxhdpi = "CtsSplitApp_xxhdpi-v4.apk";
+
+    private static final String APK_v7 = "CtsSplitApp_v7.apk";
+    private static final String APK_fr = "CtsSplitApp_fr.apk";
+    private static final String APK_de = "CtsSplitApp_de.apk";
+
+    private static final String APK_x86 = "CtsSplitApp_x86.apk";
+    private static final String APK_x86_64 = "CtsSplitApp_x86_64.apk";
+    private static final String APK_armeabi_v7a = "CtsSplitApp_armeabi-v7a.apk";
+    private static final String APK_armeabi = "CtsSplitApp_armeabi.apk";
+    private static final String APK_arm64_v8a = "CtsSplitApp_arm64-v8a.apk";
+    private static final String APK_mips64 = "CtsSplitApp_mips64.apk";
+    private static final String APK_mips = "CtsSplitApp_mips.apk";
+
+    private static final String APK_DIFF_VERSION_v7 = "CtsSplitAppDiffVersion_v7.apk";
+    private static final String APK_DIFF_CERT_v7 = "CtsSplitAppDiffCert_v7.apk";
+
+    private static final HashMap<String, String> ABI_TO_APK = new HashMap<>();
+
+    static {
+        ABI_TO_APK.put("x86", APK_x86);
+        ABI_TO_APK.put("x86_64", APK_x86_64);
+        ABI_TO_APK.put("armeabi-v7a", APK_armeabi_v7a);
+        ABI_TO_APK.put("armeabi", APK_armeabi);
+        ABI_TO_APK.put("arm64-v8a", APK_arm64_v8a);
+        ABI_TO_APK.put("mips64", APK_mips64);
+        ABI_TO_APK.put("mips", APK_mips);
+    }
+
+    private IAbi mAbi;
+    private CtsBuildHelper mCtsBuild;
+
+    @Override
+    public void setAbi(IAbi abi) {
+        mAbi = abi;
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        assertNotNull(mAbi);
+        assertNotNull(mCtsBuild);
+
+        getDevice().uninstallPackage(PKG);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        getDevice().uninstallPackage(PKG);
+    }
+
+    public void testSingleBase() throws Exception {
+        new InstallMultiple().addApk(APK).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
+    }
+
+    public void testDensitySingle() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_mdpi).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testDensitySingle");
+    }
+
+    public void testDensityAll() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_mdpi).addApk(APK_hdpi).addApk(APK_xhdpi)
+                .addApk(APK_xxhdpi).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testDensityAll");
+    }
+
+    /**
+     * Install first with low-resolution resources, then add a split that offers
+     * higher-resolution resources.
+     */
+    public void testDensityBest() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_mdpi).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testDensityBest1");
+
+        // Now splice in an additional split which offers better resources
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_xxhdpi).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testDensityBest2");
+    }
+
+    /**
+     * Verify that an API-based split can change enabled/disabled state of
+     * manifest elements.
+     */
+    public void testApi() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testApi");
+    }
+
+    public void testLocale() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_de).addApk(APK_fr).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testLocale");
+    }
+
+    /**
+     * Install test app with <em>single</em> split that exactly matches the
+     * currently active ABI. This also explicitly forces ABI when installing.
+     */
+    public void testNativeSingle() throws Exception {
+        final String abi = mAbi.getName();
+        final String apk = ABI_TO_APK.get(abi);
+        assertNotNull("Failed to find APK for ABI " + abi, apk);
+
+        new InstallMultiple().addApk(APK).addApk(apk).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testNative");
+    }
+
+    /**
+     * Install test app with <em>single</em> split that exactly matches the
+     * currently active ABI. This variant <em>does not</em> force the ABI when
+     * installing, instead exercising the system's ability to choose the ABI
+     * through inspection of the installed app.
+     */
+    public void testNativeSingleNatural() throws Exception {
+        final String abi = mAbi.getName();
+        final String apk = ABI_TO_APK.get(abi);
+        assertNotNull("Failed to find APK for ABI " + abi, apk);
+
+        new InstallMultiple().useNaturalAbi().addApk(APK).addApk(apk).run();
+        runDeviceTests(PKG, ".SplitAppTest", "testNative");
+    }
+
+    /**
+     * Install test app with <em>all</em> possible ABI splits. This also
+     * explicitly forces ABI when installing.
+     */
+    public void testNativeAll() throws Exception {
+        final InstallMultiple inst = new InstallMultiple().addApk(APK);
+        for (String apk : ABI_TO_APK.values()) {
+            inst.addApk(apk);
+        }
+        inst.run();
+        runDeviceTests(PKG, ".SplitAppTest", "testNative");
+    }
+
+    /**
+     * Install test app with <em>all</em> possible ABI splits. This variant
+     * <em>does not</em> force the ABI when installing, instead exercising the
+     * system's ability to choose the ABI through inspection of the installed
+     * app.
+     */
+    public void testNativeAllNatural() throws Exception {
+        final InstallMultiple inst = new InstallMultiple().useNaturalAbi().addApk(APK);
+        for (String apk : ABI_TO_APK.values()) {
+            inst.addApk(apk);
+        }
+        inst.run();
+        runDeviceTests(PKG, ".SplitAppTest", "testNative");
+    }
+
+    public void testDuplicateBase() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK).runExpectingFailure();
+    }
+
+    public void testDuplicateSplit() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_v7).addApk(APK_v7).runExpectingFailure();
+    }
+
+    public void testDiffCert() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
+    }
+
+    public void testDiffCertInherit() throws Exception {
+        new InstallMultiple().addApk(APK).run();
+        // TODO: remove this once we fix 17900178
+        runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
+    }
+
+    public void testDiffVersion() throws Exception {
+        new InstallMultiple().addApk(APK).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
+    }
+
+    public void testDiffVersionInherit() throws Exception {
+        new InstallMultiple().addApk(APK).run();
+        // TODO: remove this once we fix 17900178
+        runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
+        new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
+    }
+
+    public void testInheritNewSplit() throws Exception {
+        // TODO: flesh out this test
+    }
+
+    public void testInheritUpdatedBase() throws Exception {
+        // TODO: flesh out this test
+    }
+
+    public void testInheritUpdatedSplit() throws Exception {
+        // TODO: flesh out this test
+    }
+
+    public void testSplitOrdering() throws Exception {
+        // TODO: flesh out this test
+    }
+
+    class InstallMultiple {
+        private List<String> mArgs = new ArrayList<>();
+        private List<File> mApks = new ArrayList<>();
+        private boolean mUseNaturalAbi;
+
+        InstallMultiple addArg(String arg) {
+            mArgs.add(arg);
+            return this;
+        }
+
+        InstallMultiple addApk(String apk) throws FileNotFoundException {
+            mApks.add(mCtsBuild.getTestApp(apk));
+            return this;
+        }
+
+        InstallMultiple inheritFrom(String packageName) {
+            addArg("-r");
+            addArg("-p " + packageName);
+            return this;
+        }
+
+        InstallMultiple useNaturalAbi() {
+            mUseNaturalAbi = true;
+            return this;
+        }
+
+        void run() throws DeviceNotAvailableException {
+            run(true);
+        }
+
+        void runExpectingFailure() throws DeviceNotAvailableException {
+            run(false);
+        }
+
+        private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
+            final ITestDevice device = getDevice();
+
+            // Create an install session
+            final StringBuilder cmd = new StringBuilder();
+            cmd.append("pm install-create");
+            for (String arg : mArgs) {
+                cmd.append(' ').append(arg);
+            }
+            if (!mUseNaturalAbi) {
+                cmd.append(' ').append(AbiUtils.createAbiFlag(mAbi.getName()));
+            }
+
+            String result = device.executeShellCommand(cmd.toString());
+            assertTrue(result, result.startsWith("Success"));
+
+            final int start = result.lastIndexOf("[");
+            final int end = result.lastIndexOf("]");
+            int sessionId = -1;
+            try {
+                if (start != -1 && end != -1 && start < end) {
+                    sessionId = Integer.parseInt(result.substring(start + 1, end));
+                }
+            } catch (NumberFormatException e) {
+            }
+            if (sessionId == -1) {
+                throw new IllegalStateException("Failed to create install session: " + result);
+            }
+
+            // Push our files into session. Ideally we'd use stdin streaming,
+            // but ddmlib doesn't support it yet.
+            for (int i = 0; i < mApks.size(); i++) {
+                final File apk = mApks.get(i);
+                final String remotePath = "/data/local/tmp/" + i + "_" + apk.getName();
+                if (!device.pushFile(apk, remotePath)) {
+                    throw new IllegalStateException("Failed to push " + apk);
+                }
+
+                cmd.setLength(0);
+                cmd.append("pm install-write");
+                cmd.append(' ').append(sessionId);
+                cmd.append(' ').append(i + "_" + apk.getName());
+                cmd.append(' ').append(remotePath);
+
+                result = device.executeShellCommand(cmd.toString());
+                assertTrue(result, result.startsWith("Success"));
+            }
+
+            // Everything staged; let's pull trigger
+            cmd.setLength(0);
+            cmd.append("pm install-commit");
+            cmd.append(' ').append(sessionId);
+
+            result = device.executeShellCommand(cmd.toString());
+            if (expectingSuccess) {
+                assertTrue(result, result.startsWith("Success"));
+            } else {
+                assertFalse(result, result.startsWith("Success"));
+            }
+        }
+    }
+
+    public void runDeviceTests(String packageName) throws DeviceNotAvailableException {
+        runDeviceTests(packageName, null, null);
+    }
+
+    public void runDeviceTests(String packageName, String testClassName, String testMethodName)
+            throws DeviceNotAvailableException {
+        if (testClassName != null && testClassName.startsWith(".")) {
+            testClassName = packageName + testClassName;
+        }
+
+        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
+                "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
+        if (testClassName != null && testMethodName != null) {
+            testRunner.setMethodName(testClassName, testMethodName);
+        }
+
+        final CollectingTestListener listener = new CollectingTestListener();
+        getDevice().runInstrumentationTests(testRunner, listener);
+
+        final TestRunResult result = listener.getCurrentRunResults();
+        if (result.isRunFailure()) {
+            fail("Failed to successfully run device tests for " + result.getName() + ": "
+                    + result.getRunFailureMessage());
+        }
+
+        if (result.hasFailedTests()) {
+            // build a meaningful error message
+            StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+            for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+                result.getTestResults().entrySet()) {
+                if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+                    errorBuilder.append(resultEntry.getKey().toString());
+                    errorBuilder.append(":\n");
+                    errorBuilder.append(resultEntry.getValue().getStackTrace());
+                }
+            }
+            fail(errorBuilder.toString());
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
new file mode 100644
index 0000000..fc24aee
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
@@ -0,0 +1,89 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp
+LOCAL_PACKAGE_SPLITS := mdpi-v4 hdpi-v4 xhdpi-v4 xxhdpi-v4 v7 fr de
+
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+
+################################################
+# Define a variant with a different version code
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CtsSplitAppDiffVersion
+LOCAL_PACKAGE_SPLITS := v7
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version -c mdpi
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+
+################################################
+# Define a variant with a different signature
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CtsSplitAppDiffCert
+LOCAL_PACKAGE_SPLITS := v7
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call all-makefiles-under,$(LOCAL_PATH)/libs)
+endif
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
new file mode 100644
index 0000000..dba384c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp">
+
+    <uses-permission android:name="android.permission.CAMERA" />
+
+    <application android:label="SplitApp">
+        <activity android:name=".MyActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <meta-data android:name="android.service.wallpaper" android:resource="@xml/my_activity_meta" />
+        </activity>
+        <receiver android:name=".MyReceiver"
+                android:enabled="@bool/my_receiver_enabled">
+            <intent-filter>
+                <action android:name="android.intent.action.DATE_CHANGED" />
+            </intent-filter>
+        </receiver>
+
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.splitapp" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/NativeTemplate.mk b/hostsidetests/appsecurity/test-apps/SplitApp/NativeTemplate.mk
new file mode 100644
index 0000000..b61c5d6
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/NativeTemplate.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp_ARCHARCH
+
+LOCAL_JAVA_RESOURCE_DIRS := raw
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --replace-version
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/README b/hostsidetests/appsecurity/test-apps/SplitApp/README
new file mode 100644
index 0000000..480289e
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/README
@@ -0,0 +1,7 @@
+
+The entire libs/ directory is built and constructed automatically with
+the build_libs.sh script.  Don't attempt to modify manually.  To rebuild
+the native code, make the following change to the NDK to pass through
+the target architecture, and then run build_libs.sh:
+
+build/core/build-binary.mk:LOCAL_CFLAGS := -DANDROID -D__ANDROID_ARCH__=\"$(TARGET_ARCH_ABI)\" $(LOCAL_CFLAGS)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/assets/dir/dirfile1.txt b/hostsidetests/appsecurity/test-apps/SplitApp/assets/dir/dirfile1.txt
new file mode 100644
index 0000000..8fba750
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/assets/dir/dirfile1.txt
@@ -0,0 +1 @@
+DIRFILE1
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/assets/file1.txt b/hostsidetests/appsecurity/test-apps/SplitApp/assets/file1.txt
new file mode 100644
index 0000000..cb4ee5e
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/assets/file1.txt
@@ -0,0 +1 @@
+FILE1
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/build_libs.sh b/hostsidetests/appsecurity/test-apps/SplitApp/build_libs.sh
new file mode 100755
index 0000000..6090374
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/build_libs.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+NDK_BUILD="$HOME/android-ndk-r10b/ndk-build"
+
+# Go build everything
+rm -rf libs
+cd jni/
+$NDK_BUILD clean
+$NDK_BUILD
+cd ../
+
+for arch in `ls libs/`;
+do
+    (
+    mkdir -p tmp/$arch/raw/lib/$arch/
+    mv libs/$arch/* tmp/$arch/raw/lib/$arch/
+
+    echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>
+<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"
+        package=\"com.android.cts.splitapp\"
+        split=\"lib_${arch//[^a-zA-Z0-9_]/_}\">
+    <application android:hasCode=\"false\" />
+</manifest>" > tmp/$arch/AndroidManifest.xml
+
+    cp NativeTemplate.mk tmp/$arch/Android.mk
+    sed -i -r "s/ARCHARCH/$arch/" tmp/$arch/Android.mk
+
+    )
+done
+
+echo "include \$(call all-subdir-makefiles)" > tmp/Android.mk
+
+rm -rf libs
+rm -rf obj
+
+mv tmp libs
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/jni/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/jni/Android.mk
new file mode 100644
index 0000000..507b13a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/jni/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libsplitappjni
+LOCAL_SRC_FILES := com_android_cts_splitapp_Native.cpp
+
+LOCAL_LDLIBS += -llog
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/jni/Application.mk b/hostsidetests/appsecurity/test-apps/SplitApp/jni/Application.mk
new file mode 100644
index 0000000..a304c8f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/jni/Application.mk
@@ -0,0 +1,2 @@
+APP_ABI := all
+APP_PLATFORM := android-10
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/jni/com_android_cts_splitapp_Native.cpp b/hostsidetests/appsecurity/test-apps/SplitApp/jni/com_android_cts_splitapp_Native.cpp
new file mode 100644
index 0000000..01302f5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/jni/com_android_cts_splitapp_Native.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SplitApp"
+
+#include <android/log.h>
+#include <stdio.h>
+
+#include "jni.h"
+
+#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+
+
+static jint add(JNIEnv *env, jobject thiz, jint a, jint b) {
+    int result = a + b;
+    LOGI("%d + %d = %d", a, b, result);
+    return result;
+}
+
+static jstring arch(JNIEnv *env, jobject thiz) {
+    return env->NewStringUTF(__ANDROID_ARCH__);
+}
+
+static const char *classPathName = "com/android/cts/splitapp/Native";
+
+static JNINativeMethod methods[] = {
+    {"add", "(II)I", (void*)add },
+    {"arch", "()Ljava/lang/String;", (void*)arch },
+};
+
+static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods) {
+    jclass clazz;
+
+    clazz = env->FindClass(className);
+    if (clazz == NULL) {
+        LOGE("Native registration unable to find class '%s'", className);
+        return JNI_FALSE;
+    }
+    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
+        LOGE("RegisterNatives failed for '%s'", className);
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+static int registerNatives(JNIEnv* env) {
+    if (!registerNativeMethods(env, classPathName, methods, sizeof(methods) / sizeof(methods[0]))) {
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+typedef union {
+    JNIEnv* env;
+    void* venv;
+} UnionJNIEnvToVoid;
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+    UnionJNIEnvToVoid uenv;
+    uenv.venv = NULL;
+    jint result = -1;
+    JNIEnv* env = NULL;
+
+    LOGI("JNI_OnLoad");
+
+    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
+        LOGE("ERROR: GetEnv failed");
+        goto bail;
+    }
+    env = uenv.env;
+
+    if (registerNatives(env) != JNI_TRUE) {
+        LOGE("ERROR: registerNatives failed");
+        goto bail;
+    }
+
+    result = JNI_VERSION_1_4;
+
+bail:
+    return result;
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/Android.mk
new file mode 100644
index 0000000..543e4ac
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp_arm64-v8a
+
+LOCAL_JAVA_RESOURCE_DIRS := raw
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --replace-version
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/AndroidManifest.xml
new file mode 100644
index 0000000..206e207
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        split="lib_arm64_v8a">
+    <application android:hasCode="false" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/raw/lib/arm64-v8a/libsplitappjni.so b/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/raw/lib/arm64-v8a/libsplitappjni.so
new file mode 100755
index 0000000..bcc8f51
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/raw/lib/arm64-v8a/libsplitappjni.so
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/Android.mk
new file mode 100644
index 0000000..7cdef62
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp_armeabi-v7a
+
+LOCAL_JAVA_RESOURCE_DIRS := raw
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --replace-version
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/AndroidManifest.xml
new file mode 100644
index 0000000..1d19420
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        split="lib_armeabi_v7a">
+    <application android:hasCode="false" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/raw/lib/armeabi-v7a/libsplitappjni.so b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/raw/lib/armeabi-v7a/libsplitappjni.so
new file mode 100755
index 0000000..010c372
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/raw/lib/armeabi-v7a/libsplitappjni.so
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/Android.mk
new file mode 100644
index 0000000..26ec5bd
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp_armeabi
+
+LOCAL_JAVA_RESOURCE_DIRS := raw
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --replace-version
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/AndroidManifest.xml
new file mode 100644
index 0000000..95fdb23
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        split="lib_armeabi">
+    <application android:hasCode="false" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/raw/lib/armeabi/libsplitappjni.so b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/raw/lib/armeabi/libsplitappjni.so
new file mode 100755
index 0000000..8977e70
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/raw/lib/armeabi/libsplitappjni.so
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/Android.mk
new file mode 100644
index 0000000..fea0603
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp_mips
+
+LOCAL_JAVA_RESOURCE_DIRS := raw
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --replace-version
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/AndroidManifest.xml
new file mode 100644
index 0000000..53ea38f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        split="lib_mips">
+    <application android:hasCode="false" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/raw/lib/mips/libsplitappjni.so b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/raw/lib/mips/libsplitappjni.so
new file mode 100755
index 0000000..45b8382
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/raw/lib/mips/libsplitappjni.so
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/Android.mk
new file mode 100644
index 0000000..3cc5609
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp_mips64
+
+LOCAL_JAVA_RESOURCE_DIRS := raw
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --replace-version
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/AndroidManifest.xml
new file mode 100644
index 0000000..0b75613
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        split="lib_mips64">
+    <application android:hasCode="false" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/raw/lib/mips64/libsplitappjni.so b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/raw/lib/mips64/libsplitappjni.so
new file mode 100755
index 0000000..8c29904
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/raw/lib/mips64/libsplitappjni.so
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/Android.mk
new file mode 100644
index 0000000..d45ca8f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp_x86
+
+LOCAL_JAVA_RESOURCE_DIRS := raw
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --replace-version
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/AndroidManifest.xml
new file mode 100644
index 0000000..4219791
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        split="lib_x86">
+    <application android:hasCode="false" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/raw/lib/x86/libsplitappjni.so b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/raw/lib/x86/libsplitappjni.so
new file mode 100755
index 0000000..2993d92
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/raw/lib/x86/libsplitappjni.so
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/Android.mk
new file mode 100644
index 0000000..fa0e488
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsSplitApp_x86_64
+
+LOCAL_JAVA_RESOURCE_DIRS := raw
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --replace-version
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/AndroidManifest.xml
new file mode 100644
index 0000000..e697d5c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.splitapp"
+        split="lib_x86_64">
+    <application android:hasCode="false" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/raw/lib/x86_64/libsplitappjni.so b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/raw/lib/x86_64/libsplitappjni.so
new file mode 100755
index 0000000..23f4169
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/raw/lib/x86_64/libsplitappjni.so
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-hdpi/image.png b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-hdpi/image.png
new file mode 100644
index 0000000..b5f1a13
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-hdpi/image.png
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-mdpi/image.png b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-mdpi/image.png
new file mode 100644
index 0000000..2d67c8f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-mdpi/image.png
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-xhdpi/image.png b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-xhdpi/image.png
new file mode 100644
index 0000000..2540371
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-xhdpi/image.png
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-xxhdpi/image.png b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-xxhdpi/image.png
new file mode 100644
index 0000000..18a3443
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/drawable-xxhdpi/image.png
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values-de/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-de/values.xml
new file mode 100644
index 0000000..88b6f84
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-de/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="my_string1">blau</string>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values-fr/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-fr/values.xml
new file mode 100644
index 0000000..c372f6a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-fr/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="my_string2">pourpre</string>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values-sw600dp/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-sw600dp/values.xml
new file mode 100644
index 0000000..edf4525
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-sw600dp/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <dimen name="my_dimen">46dp</dimen>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values-v7/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-v7/values.xml
new file mode 100644
index 0000000..d0a0db9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values-v7/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <bool name="my_receiver_enabled">true</bool>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/values/values.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/values/values.xml
new file mode 100644
index 0000000..3118fde
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/values/values.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <bool name="my_receiver_enabled">false</bool>
+
+    <string name="my_string1">blue</string>
+    <string name="my_string2">purple</string>
+
+    <string-array name="my_string_array">
+        <item>@string/my_string1</item>
+        <item>@string/my_string2</item>
+    </string-array>
+
+    <color name="my_color">#00FF00</color>
+    <dimen name="my_dimen">23dp</dimen>
+    <integer name="my_integer">123</integer>
+</resources>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/xml-v7/my_activity_meta.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/xml-v7/my_activity_meta.xml
new file mode 100644
index 0000000..50cb6ec
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/xml-v7/my_activity_meta.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<tag value="v7" />
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/res/xml/my_activity_meta.xml b/hostsidetests/appsecurity/test-apps/SplitApp/res/xml/my_activity_meta.xml
new file mode 100644
index 0000000..58f2a6a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/res/xml/my_activity_meta.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<tag value="base" />
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/BaseActivity.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/BaseActivity.java
new file mode 100644
index 0000000..efd1843
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/BaseActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.splitapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class BaseActivity extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        int sum = Native.add(2, 3);
+        tv.setText("2 + 3 = " + Integer.toString(sum));
+        setContentView(tv);
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/Native.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/Native.java
new file mode 100644
index 0000000..080053a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/Native.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.splitapp;
+
+public class Native {
+    static {
+        System.loadLibrary("splitappjni");
+    }
+
+    public static native int add(int a, int b);
+    public static native String arch();
+}
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
new file mode 100644
index 0000000..9eb17cd
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.splitapp;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.Locale;
+
+public class SplitAppTest extends AndroidTestCase {
+    private static final String TAG = "SplitAppTest";
+
+    public void testSingleBase() throws Exception {
+        final Resources r = getContext().getResources();
+        final PackageManager pm = getContext().getPackageManager();
+
+        // Should have untouched resources from base
+        assertEquals(false, r.getBoolean(R.bool.my_receiver_enabled));
+
+        assertEquals("blue", r.getString(R.string.my_string1));
+        assertEquals("purple", r.getString(R.string.my_string2));
+
+        assertEquals(0xff00ff00, r.getColor(R.color.my_color));
+        assertEquals(123, r.getInteger(R.integer.my_integer));
+
+        assertEquals("base", getXmlTestValue(r.getXml(R.xml.my_activity_meta)));
+
+        // We know about drawable IDs, but they're stripped from base
+        try {
+            r.getDrawable(R.drawable.image);
+            fail("Unexpected drawable in base");
+        } catch (Resources.NotFoundException expected) {
+        }
+
+        // Should have base assets
+        assertAssetContents(r, "file1.txt", "FILE1");
+        assertAssetContents(r, "dir/dirfile1.txt", "DIRFILE1");
+
+        try {
+            assertAssetContents(r, "file2.txt", null);
+            fail("Unexpected asset file2");
+        } catch (IOException expected) {
+        }
+
+        // Should only have base manifest items
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.setPackage("com.android.cts.splitapp");
+
+        List<ResolveInfo> result = pm.queryIntentActivities(intent, 0);
+        assertEquals(1, result.size());
+        assertEquals("com.android.cts.splitapp.MyActivity", result.get(0).activityInfo.name);
+
+        // Receiver disabled by default in base
+        intent = new Intent(Intent.ACTION_DATE_CHANGED);
+        intent.setPackage("com.android.cts.splitapp");
+
+        result = pm.queryBroadcastReceivers(intent, 0);
+        assertEquals(0, result.size());
+
+        // We shouldn't have any native code in base
+        try {
+            Native.add(2, 4);
+            fail("Unexpected native code in base");
+        } catch (UnsatisfiedLinkError expected) {
+        }
+    }
+
+    public void testDensitySingle() throws Exception {
+        final Resources r = getContext().getResources();
+
+        // We should still have base resources
+        assertEquals("blue", r.getString(R.string.my_string1));
+        assertEquals("purple", r.getString(R.string.my_string2));
+
+        // Now we know about drawables, but only mdpi
+        final Drawable d = r.getDrawable(R.drawable.image);
+        assertEquals(0xff7e00ff, getDrawableColor(d));
+    }
+
+    public void testDensityAll() throws Exception {
+        final Resources r = getContext().getResources();
+
+        // We should still have base resources
+        assertEquals("blue", r.getString(R.string.my_string1));
+        assertEquals("purple", r.getString(R.string.my_string2));
+
+        // Pretend that we're at each density
+        updateDpi(r, DisplayMetrics.DENSITY_MEDIUM);
+        assertEquals(0xff7e00ff, getDrawableColor(r.getDrawable(R.drawable.image)));
+
+        updateDpi(r, DisplayMetrics.DENSITY_HIGH);
+        assertEquals(0xff00fcff, getDrawableColor(r.getDrawable(R.drawable.image)));
+
+        updateDpi(r, DisplayMetrics.DENSITY_XHIGH);
+        assertEquals(0xff80ff00, getDrawableColor(r.getDrawable(R.drawable.image)));
+
+        updateDpi(r, DisplayMetrics.DENSITY_XXHIGH);
+        assertEquals(0xffff0000, getDrawableColor(r.getDrawable(R.drawable.image)));
+    }
+
+    public void testDensityBest1() throws Exception {
+        final Resources r = getContext().getResources();
+
+        // Pretend that we're really high density, but we only have mdpi installed
+        updateDpi(r, DisplayMetrics.DENSITY_XXHIGH);
+        assertEquals(0xff7e00ff, getDrawableColor(r.getDrawable(R.drawable.image)));
+    }
+
+    public void testDensityBest2() throws Exception {
+        final Resources r = getContext().getResources();
+
+        // Pretend that we're really high density, and now we have better match
+        updateDpi(r, DisplayMetrics.DENSITY_XXHIGH);
+        assertEquals(0xffff0000, getDrawableColor(r.getDrawable(R.drawable.image)));
+    }
+
+    public void testApi() throws Exception {
+        final Resources r = getContext().getResources();
+        final PackageManager pm = getContext().getPackageManager();
+
+        // We should have updated boolean, different from base
+        assertEquals(true, r.getBoolean(R.bool.my_receiver_enabled));
+
+        // Receiver should be enabled now
+        Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
+        intent.setPackage("com.android.cts.splitapp");
+
+        List<ResolveInfo> result = pm.queryBroadcastReceivers(intent, 0);
+        assertEquals(1, result.size());
+        assertEquals("com.android.cts.splitapp.MyReceiver", result.get(0).activityInfo.name);
+    }
+
+    public void testLocale() throws Exception {
+        final Resources r = getContext().getResources();
+
+        updateLocale(r, Locale.ENGLISH);
+        assertEquals("blue", r.getString(R.string.my_string1));
+        assertEquals("purple", r.getString(R.string.my_string2));
+
+        updateLocale(r, Locale.GERMAN);
+        assertEquals("blau", r.getString(R.string.my_string1));
+        assertEquals("purple", r.getString(R.string.my_string2));
+
+        updateLocale(r, Locale.FRENCH);
+        assertEquals("blue", r.getString(R.string.my_string1));
+        assertEquals("pourpre", r.getString(R.string.my_string2));
+    }
+
+    public void testNative() throws Exception {
+        Log.d(TAG, "testNative() thinks it's using ABI " + Native.arch());
+
+        // Make sure we can do the maths
+        assertEquals(11642, Native.add(4933, 6709));
+    }
+
+    private static void updateDpi(Resources r, int densityDpi) {
+        final Configuration c = new Configuration(r.getConfiguration());
+        c.densityDpi = densityDpi;
+        r.updateConfiguration(c, r.getDisplayMetrics());
+    }
+
+    private static void updateLocale(Resources r, Locale locale) {
+        final Configuration c = new Configuration(r.getConfiguration());
+        c.locale = locale;
+        r.updateConfiguration(c, r.getDisplayMetrics());
+    }
+
+    private static int getDrawableColor(Drawable d) {
+        final Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
+                Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(bitmap);
+        d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
+        d.draw(canvas);
+        return bitmap.getPixel(0, 0);
+    }
+
+    private static String getXmlTestValue(XmlPullParser in) throws XmlPullParserException,
+            IOException {
+        int type;
+        while ((type = in.next()) != END_DOCUMENT) {
+            if (type == START_TAG) {
+                final String tag = in.getName();
+                if ("tag".equals(tag)) {
+                    return in.getAttributeValue(null, "value");
+                }
+            }
+        }
+        return null;
+    }
+
+    private static void assertAssetContents(Resources r, String path, String expected)
+            throws IOException {
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new InputStreamReader(r.getAssets().open(path)));
+            assertEquals(expected, in.readLine());
+        } finally {
+            if (in != null) in.close();
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
index c0ca8e2..42aa847 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
@@ -30,6 +30,9 @@
 
     private static final String TEST_PACKAGE = "com.google.android.example.somepackage";
 
+    private static final int ACTIVITY_RESUMED_TIMEOUT_MILLIS = 60000;  // 60 seconds
+    private static final int ACTIVITY_RUNNING_TIMEOUT_MILLIS = 20000;  // 20 seconds
+
     /**
      * The tests below need to keep detailed track of the state of the activity
      * that is started and stopped frequently.  To do this it sends a number of
@@ -139,52 +142,49 @@
     // This test has the UtilityActivity trigger starting another activity (settings)
     // this should be permitted as a part of lock task (since it isn't a new task).
     // As a result onPause should be called as it goes to a new activity.
-// TODO: Reinstate once we make this test not flaky (if fails on Nexus 7 v2 most of the time,
-//       especially if testCannotStartActivityOutsideTask() is commented out.
-//    public void testStartActivityWithinTask() {
-//        mDevicePolicyManager.setLockTaskPackages(getWho(), new String[] { PACKAGE_NAME });
-//        startLockTask();
-//        waitForResume();
-//
-//        Intent launchIntent = new Intent(Settings.ACTION_SETTINGS);
-//        Intent lockTaskUtility = getLockTaskUtility();
-//        lockTaskUtility.putExtra(LockTaskUtilityActivity.START_ACTIVITY, launchIntent);
-//        mContext.startActivity(lockTaskUtility);
-//
-//        synchronized (mActivityResumedLock) {
-//            if (mIsActivityResumed) {
-//                try {
-//                    mActivityResumedLock.wait(60000);
-//                } catch (InterruptedException e) {
-//                }
-//                assertFalse(mIsActivityResumed);
-//            }
-//        }
-//        stopAndFinish(null);
-//    }
+    public void testStartActivityWithinTask() {
+        mDevicePolicyManager.setLockTaskPackages(getWho(), new String[] { PACKAGE_NAME });
+        startLockTask();
+        waitForResume();
+
+        Intent launchIntent = new Intent(Settings.ACTION_SETTINGS);
+        Intent lockTaskUtility = getLockTaskUtility();
+        lockTaskUtility.putExtra(LockTaskUtilityActivity.START_ACTIVITY, launchIntent);
+        mContext.startActivity(lockTaskUtility);
+
+        synchronized (mActivityResumedLock) {
+            if (mIsActivityResumed) {
+                try {
+                    mActivityResumedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+                } catch (InterruptedException e) {
+                }
+                assertFalse(mIsActivityResumed);
+            }
+        }
+        stopAndFinish(null);
+    }
 
     // This launches an activity that is not part of the current task and therefore
     // should be blocked.  This is verified by making sure that the activity does
     // not get a call to onPause.
-// TODO: Reinstate once we make this test not flaky (if fails on Nexus 7 v2 most of the time) 
-//    public void testCannotStartActivityOutsideTask() {
-//        mDevicePolicyManager.setLockTaskPackages(getWho(), new String[] { PACKAGE_NAME });
-//        startLockTask();
-//        waitForResume();
-//
-//        Intent launchIntent = new Intent(Settings.ACTION_SETTINGS);
-//        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-//        mContext.startActivity(launchIntent);
-//
-//        synchronized (mActivityResumedLock) {
-//            try {
-//                mActivityResumedLock.wait(90000);
-//            } catch (InterruptedException e) {
-//            }
-//            assertTrue(mIsActivityResumed);
-//        }
-//        stopAndFinish(null);
-//    }
+    public void testCannotStartActivityOutsideTask() {
+        mDevicePolicyManager.setLockTaskPackages(getWho(), new String[] { PACKAGE_NAME });
+        startLockTask();
+        waitForResume();
+
+        Intent launchIntent = new Intent(Settings.ACTION_SETTINGS);
+        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(launchIntent);
+
+        synchronized (mActivityResumedLock) {
+            try {
+                mActivityResumedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+            } catch (InterruptedException e) {
+            }
+            assertTrue(mIsActivityResumed);
+        }
+        stopAndFinish(null);
+    }
 
     /**
      * Call stopLockTask and finish on the LockTaskUtilityActivity.
@@ -212,7 +212,7 @@
             finish();
             if (mIsActivityRunning) {
                 try {
-                    mActivityRunningLock.wait(20000);
+                    mActivityRunningLock.wait(ACTIVITY_RUNNING_TIMEOUT_MILLIS);
                 } catch (InterruptedException e) {
                 }
             }
@@ -227,7 +227,7 @@
         synchronized (mActivityResumedLock) {
             if (!mIsActivityResumed) {
                 try {
-                    mActivityResumedLock.wait(20000);
+                    mActivityResumedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
                 } catch (InterruptedException e) {
                 }
             }
@@ -272,7 +272,7 @@
             mContext.startActivity(intent);
             // Give 20 secs to finish.
             try {
-                wait(20000);
+                wait(ACTIVITY_RUNNING_TIMEOUT_MILLIS);
             } catch (InterruptedException e) {
             }
             assertTrue(mIntentHandled);
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 9ec1f61..444d730 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -111,6 +111,27 @@
   bug: 17508787
 },
 {
+  description: "New tests recently added for Android Enterprise. To be moved out of CTS-staging as soon as they show that they are stable",
+  names: [
+    "com.android.cts.devicepolicy.DeviceOwnerTest#testApplicationRestrictions",
+    "com.android.cts.devicepolicy.DeviceOwnerTest#testCaCertManagement",
+    "com.android.cts.devicepolicy.DeviceOwnerTest#testDeviceOwnerSetup",
+    "com.android.cts.devicepolicy.DeviceOwnerTest#testPersistentIntentResolving",
+    "com.android.cts.devicepolicy.DeviceOwnerTest#testScreenCaptureDisabled",
+    "com.android.cts.devicepolicy.ManagedProfileTest#testManagedProfileSetup",
+    "com.android.cts.devicepolicy.ManagedProfileTest#testWipeData",
+    "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileIntentFilters",
+    "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileContent"
+  ]
+},
+{
+  description: "Flaky test which ocassionally fails",
+  names: [
+    "com.android.cts.devicepolicy.DeviceOwnerTest#testLockTask"
+  ],
+  bug: 17890673
+},
+{
 
   description: "These tests fail on some devices.",
   names: [
@@ -299,5 +320,12 @@
     "android.hardware.cts.SensorTest#testBatchAndFlushWithHandler"
   ],
   bug: 17675466
+},
+{
+  description: "This test failed on hw decoder that doesn't output frame with the configured format.",
+  names: [
+    "android.meida.cts.ImageReaderDecoderTest#testHwAVCDecode360pForFlexibleYuv"
+  ],
+  bug: 17144778
 }
 ]
diff --git a/tests/tests/dpi/Android.mk b/tests/tests/dpi/Android.mk
index fde990b..4c05ecf 100644
--- a/tests/tests/dpi/Android.mk
+++ b/tests/tests/dpi/Android.mk
@@ -44,6 +44,4 @@
 
 LOCAL_MODULE := android.cts.dpi
 
-LOCAL_SDK_VERSION := current
-
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index eef99a2..0c36832 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -413,7 +413,13 @@
 
         int offset = 0;
         data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];
-        byte[] rowData = new byte[planes[0].getRowStride()];
+        int maxRowSize = planes[0].getRowStride();
+        for (int i = 0; i < planes.length; i++) {
+            if (maxRowSize < planes[i].getRowStride()) {
+                maxRowSize = planes[i].getRowStride();
+            }
+        }
+        byte[] rowData = new byte[maxRowSize];
         if(VERBOSE) Log.v(TAG, "get data from " + planes.length + " planes");
         for (int i = 0; i < planes.length; i++) {
             buffer = planes[i].getBuffer();
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index f115b63..d620995 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -27,6 +27,7 @@
 import android.media.ImageReader;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
@@ -166,6 +167,8 @@
                 vidFD.getLength());
 
         MediaFormat mediaFmt = extractor.getTrackFormat(0);
+        mediaFmt.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                            CodecCapabilities.COLOR_FormatYUV420Flexible);
         String mime = mediaFmt.getString(MediaFormat.KEY_MIME);
         try {
             // Create decoder
@@ -191,10 +194,10 @@
             throws Exception, InterruptedException {
         ByteBuffer[] decoderInputBuffers;
         ByteBuffer[] decoderOutputBuffers;
-        // Get decoder output ImageFormat, will be used to create ImageReader
-        int codecImageFormat = getImageFormatFromCodecType(mime);
-        assertEquals("Codec image format should match image reader format",
-                imageFormat, codecImageFormat);
+        if (!imageFormatSupported(decoder, imageFormat, mime)) {
+            // TODO: SKIPPING TEST
+            return;
+        }
         createImageReader(width, height, imageFormat, MAX_NUM_IMAGES, mImageListener);
 
         // Configure decoder.
@@ -293,74 +296,19 @@
         }
     }
 
-    private int getImageFormatFromCodecType(String mimeType) {
-        // TODO: Need pick a codec first, then get the codec info, will revisit for future.
-        MediaCodecInfo codecInfo = getCodecInfoByType(mimeType);
-        if (VERBOSE) Log.v(TAG, "found decoder: " + codecInfo.getName());
-
-        int colorFormat = selectDecoderOutputColorFormat(codecInfo, mimeType);
-        if (VERBOSE) Log.v(TAG, "found decoder output color format: " + colorFormat);
-        switch (colorFormat) {
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
-                // TODO: This is fishy, OMX YUV420P is not identical as YV12, U and V planes are
-                // swapped actually. It should give YV12 if producer is setup first, that is, after
-                // Configuring the Surface (provided by ImageReader object) into codec, but this
-                // is Chicken-egg issue, do the translation on behalf of driver here:)
-                return ImageFormat.YV12;
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
-                // same as above.
-                return ImageFormat.NV21;
-            default:
-                return colorFormat;
+    private boolean imageFormatSupported(MediaCodec decoder, int imageFormat, String mime) {
+        MediaCodecInfo codecInfo = decoder.getCodecInfo();
+        if (codecInfo == null) {
+            return false;
         }
-    }
-
-    private static MediaCodecInfo getCodecInfoByType(String mimeType) {
-        int numCodecs = MediaCodecList.getCodecCount();
-        for (int i = 0; i < numCodecs; i++) {
-            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
-
-            if (codecInfo.isEncoder()) {
-                continue;
-            }
-
-            String[] types = codecInfo.getSupportedTypes();
-            for (int j = 0; j < types.length; j++) {
-                if (types[j].equalsIgnoreCase(mimeType)) {
-                    return codecInfo;
-                }
-            }
-        }
-        return null;
-    }
-
-    private static int selectDecoderOutputColorFormat(MediaCodecInfo codecInfo, String mimeType) {
-        MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
-        for (int i = 0; i < capabilities.colorFormats.length; i++) {
-            int colorFormat = capabilities.colorFormats[i];
-            if (isRecognizedFormat(colorFormat)) {
-                return colorFormat;
-            }
-        }
-        fail("couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
-        return 0;   // not reached
-    }
-
-    // Need make this function simple, may be merge into above functions.
-    private static boolean isRecognizedFormat(int colorFormat) {
-        if (VERBOSE) Log.v(TAG, "colorformat: " + colorFormat);
-        switch (colorFormat) {
-            // these are the formats we know how to handle for this test
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
-            case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
-            case ImageFormat.YUV_420_888:
+        MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mime);
+        for (int colorFormat : capabilities.colorFormats) {
+            if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible
+                    && imageFormat == ImageFormat.YUV_420_888) {
                 return true;
-            default:
-                return false;
+            }
         }
+        return false;
     }
 
     private MediaCodec createDecoder(String mime, boolean useHw) throws Exception {
@@ -404,7 +352,7 @@
     private static void validateYuvData(byte[] yuvData, int width, int height, int format,
             long ts, String fileName) {
 
-        assertTrue("YUV format must be one of the YUV420_888, NV21, or YV12",
+        assertTrue("YUV format must be one of the YUV_420_888, NV21, or YV12",
                 format == ImageFormat.YUV_420_888 ||
                 format == ImageFormat.NV21 ||
                 format == ImageFormat.YV12);
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index e7ad28e..6069ee6 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -28,9 +28,8 @@
 public class BuildVersionTest extends TestCase {
 
     private static final String LOG_TAG = "BuildVersionTest";
-    private static final Set<String> EXPECTED_RELEASES =
-        new HashSet<String>(Arrays.asList("4.4W.1", "4.4W", "4.4", "4.4.1", "4.4.2", "4.4.3"));
-    private static final int EXPECTED_SDK = 20;
+    private static final Set<String> EXPECTED_RELEASES = new HashSet<String>(Arrays.asList("5.0"));
+    private static final int EXPECTED_SDK = 21;
     private static final String EXPECTED_BUILD_VARIANT = "user";
     private static final String EXPECTED_TAG = "release-keys";
 
@@ -38,8 +37,8 @@
     public void testReleaseVersion() {
         // Applications may rely on the exact release version
         assertAnyOf("BUILD.VERSION.RELEASE", Build.VERSION.RELEASE, EXPECTED_RELEASES);
-        assertEquals("" + EXPECTED_SDK, Build.VERSION.SDK);
-        assertEquals(EXPECTED_SDK, Build.VERSION.SDK_INT);
+        assertEquals("Build.VERSION.SDK", "" + EXPECTED_SDK, Build.VERSION.SDK);
+        assertEquals("Build.VERSION.SDK_INT", EXPECTED_SDK, Build.VERSION.SDK_INT);
     }
 
     public void testIncremental() {
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 7ade7c7..8575c32 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -88,13 +88,11 @@
                     }
                 };
                 mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_CELL_LOCATION);
-
+                CellLocation.requestLocationUpdate();
                 Looper.loop();
             }
         });
         t.start();
-
-        CellLocation.requestLocationUpdate();
         synchronized (mLock) {
             while (!mOnCellLocationChangedCalled) {
                 mLock.wait();
@@ -106,24 +104,21 @@
         t = new TestThread(new Runnable() {
             public void run() {
                 Looper.prepare();
-
                 // unregister the listener
                 mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
                 mOnCellLocationChangedCalled = false;
                 // unregister again, to make sure doing so does not call the listener
                 mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
-
+                CellLocation.requestLocationUpdate();
                 Looper.loop();
             }
         });
-        t.start();
 
-        CellLocation.requestLocationUpdate();
+        t.start();
         synchronized (mLock) {
             mLock.wait(TOLERANCE);
         }
-        //Fix me: unregister for listener is not support today. Will be added soon
-        //assertFalse(mOnCellLocationChangedCalled);
+        assertFalse(mOnCellLocationChangedCalled);
     }
 
     /**
diff --git a/tests/tests/widget/res/layout/autocompletetextview_layout.xml b/tests/tests/widget/res/layout/autocompletetextview_layout.xml
index 7fd183c..25a8541 100644
--- a/tests/tests/widget/res/layout/autocompletetextview_layout.xml
+++ b/tests/tests/widget/res/layout/autocompletetextview_layout.xml
@@ -28,5 +28,6 @@
         android:completionThreshold="1"
         android:completionHint="@string/tabs_1"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
+        android:layout_height="wrap_content"
+        android:inputType="none"/>
 </LinearLayout>
diff --git a/tests/tests/widget/res/layout/popupwindow.xml b/tests/tests/widget/res/layout/popupwindow.xml
index e6b0aed..2508115 100644
--- a/tests/tests/widget/res/layout/popupwindow.xml
+++ b/tests/tests/widget/res/layout/popupwindow.xml
@@ -16,17 +16,16 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
+                android:layout_height="match_parent"
                 android:orientation="vertical">
 
     <TextView android:id="@+id/anchor_upper"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/text_view_hint"
-        android:layout_weight="1"/>
+        android:text="@string/text_view_hint" />
 
     <LinearLayout android:layout_width="match_parent"
-                android:layout_height="wrap_content"
+                android:layout_height="0dp"
                 android:layout_weight="1">
 
         <TextView android:id="@+id/anchor_middle_left"
@@ -46,7 +45,6 @@
     <TextView android:id="@+id/anchor_lower"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:text="@string/text_view_hint"
-        android:layout_weight="1"/>
+        android:text="@string/text_view_hint" />
 
 </LinearLayout>
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
index 35b8a66..8eb1621 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
@@ -77,6 +77,17 @@
     }
 
     /**
+     * Set the CTS build container.
+     * <p/>
+     * Exposed so unit tests can mock the provided build.
+     *
+     * @param buildHelper
+     */
+    public void setBuildHelper(CtsBuildHelper buildHelper) {
+        mCtsBuild = buildHelper;
+    }
+
+    /**
      * Enable or disable raw dEQP test log collection.
      */
     public void setCollectLogs(boolean logData) {
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
index a5c3e4c..e3ff825 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
@@ -113,7 +113,7 @@
         WrappedGTestResultParser resultParser = new WrappedGTestResultParser(id, listener);
         resultParser.setFakePackagePrefix(mPackageName + ".");
         try {
-            String options = mAbi == null ? "" : String.format("--abi %s ", mAbi);
+            String options = mAbi == null ? "" : String.format("--abi %s ", mAbi.getName());
             String command = String.format("am instrument -w %s%s/.%s", options, mAppNameSpace, mRunner);
             mDevice.executeShellCommand(command, resultParser, mMaxTestTimeMs, 0);
         } catch (DeviceNotAvailableException e) {
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestRunnerTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestRunnerTest.java
index cd9c738..c41793f 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestRunnerTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestRunnerTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.tradefed.build.StubCtsBuildHelper;
 import com.android.cts.tradefed.UnitTests;
 import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.IShellOutputReceiver;
@@ -29,6 +30,7 @@
 import org.easymock.EasyMock;
 import org.easymock.IAnswer;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Map;
@@ -43,6 +45,8 @@
     private static final String LOG_FILE_NAME = "/sdcard/TestLog.qpa";
     private static final String INSTRUMENTATION_NAME =
             "com.drawelements.deqp/com.drawelements.deqp.testercore.DeqpInstrumentation";
+    private static final String DEQP_ONDEVICE_APK = "com.drawelements.deqp.apk";
+    private static final String DEQP_ONDEVICE_PKG = "com.drawelements.deqp";
 
     /**
      * {@inheritDoc}
@@ -118,6 +122,13 @@
         if (majorVersion > requiredMajorVersion
                 || (majorVersion == requiredMajorVersion && minorVersion >= requiredMinorVersion)) {
 
+            EasyMock.expect(mockDevice.uninstallPackage(EasyMock.eq(DEQP_ONDEVICE_PKG)))
+                    .andReturn("").once();
+            EasyMock.expect(mockDevice.installPackage(EasyMock.<File>anyObject(),
+                    EasyMock.eq(true),
+                    EasyMock.eq(AbiUtils.createAbiFlag(UnitTests.ABI.getName()))))
+                    .andReturn(null).once();
+
             EasyMock.expect(mockDevice.executeShellCommand(
                     EasyMock.eq("rm " + CASE_LIST_FILE_NAME))).andReturn("").once();
 
@@ -129,7 +140,7 @@
 
             String command = String.format(
                     "am instrument %s -w -e deqpLogFileName \"%s\" -e deqpCmdLine \""
-                        + "--deqp-caselist-file=%s --deqp-gl-config-name=rgba8888d24s8\""
+                        + "--deqp-caselist-file=%s --deqp-gl-config-name=rgba8888d24s8\" "
                         + "-e deqpLogData \"%s\" %s",
                     AbiUtils.createAbiFlag(UnitTests.ABI.getName()), LOG_FILE_NAME,
                     CASE_LIST_FILE_NAME, false, INSTRUMENTATION_NAME);
@@ -149,6 +160,9 @@
                     return null;
                 }
             });
+
+            EasyMock.expect(mockDevice.uninstallPackage(EasyMock.eq(DEQP_ONDEVICE_PKG)))
+                    .andReturn("").once();
         }
 
         mockListener.testRunStarted(ID, 1);
@@ -167,6 +181,7 @@
         EasyMock.replay(mockListener);
 
         deqpTest.setDevice(mockDevice);
+        deqpTest.setBuildHelper(new StubCtsBuildHelper());
         deqpTest.run(mockListener);
 
         EasyMock.verify(mockListener);
@@ -223,6 +238,13 @@
         EasyMock.expect(mockDevice.getProperty("ro.opengles.version"))
                 .andReturn(Integer.toString(version)).atLeastOnce();
 
+        EasyMock.expect(mockDevice.uninstallPackage(EasyMock.eq(DEQP_ONDEVICE_PKG))).andReturn("")
+                .once();
+
+        EasyMock.expect(mockDevice.installPackage(EasyMock.<File>anyObject(),
+                EasyMock.eq(true), EasyMock.eq(AbiUtils.createAbiFlag(UnitTests.ABI.getName()))))
+                .andReturn(null).once();
+
         EasyMock.expect(mockDevice.executeShellCommand(EasyMock.eq("rm " + CASE_LIST_FILE_NAME)))
                 .andReturn("").once();
 
@@ -234,7 +256,7 @@
 
         String command = String.format(
                 "am instrument %s -w -e deqpLogFileName \"%s\" -e deqpCmdLine \""
-                    + "--deqp-caselist-file=%s --deqp-gl-config-name=rgba8888d24s8\""
+                    + "--deqp-caselist-file=%s --deqp-gl-config-name=rgba8888d24s8\" "
                     + "-e deqpLogData \"%s\" %s",
                 AbiUtils.createAbiFlag(UnitTests.ABI.getName()), LOG_FILE_NAME,
                 CASE_LIST_FILE_NAME, false, INSTRUMENTATION_NAME);
@@ -263,7 +285,7 @@
 
         if (!pass) {
             mockListener.testFailed(testId,
-                    resultCode + ":Detail" + resultCode);
+                    resultCode + ": Detail" + resultCode);
 
             EasyMock.expectLastCall().once();
         }
@@ -274,10 +296,14 @@
         mockListener.testRunEnded(EasyMock.anyLong(), EasyMock.<Map<String, String>>notNull());
         EasyMock.expectLastCall().once();
 
+        EasyMock.expect(mockDevice.uninstallPackage(EasyMock.eq(DEQP_ONDEVICE_PKG))).andReturn("")
+                .once();
+
         EasyMock.replay(mockDevice);
         EasyMock.replay(mockListener);
 
         deqpTest.setDevice(mockDevice);
+        deqpTest.setBuildHelper(new StubCtsBuildHelper());
         deqpTest.run(mockListener);
 
         EasyMock.verify(mockListener);
@@ -287,7 +313,7 @@
     /**
      * Test running multiple test cases.
      */
-    public void testRun_multipleTets() throws Exception {
+    public void testRun_multipleTests() throws Exception {
         /* MultiLineReceiver expects "\r\n" line ending. */
         final String output = "INSTRUMENTATION_STATUS: dEQP-SessionInfo-Name=releaseName\r\n"
                 + "INSTRUMENTATION_STATUS: dEQP-EventType=SessionInfo\r\n"
@@ -398,6 +424,12 @@
         EasyMock.expect(mockDevice.getProperty("ro.opengles.version"))
                 .andReturn(Integer.toString(version)).atLeastOnce();
 
+        EasyMock.expect(mockDevice.uninstallPackage(EasyMock.eq(DEQP_ONDEVICE_PKG))).andReturn("")
+                .once();
+        EasyMock.expect(mockDevice.installPackage(EasyMock.<File>anyObject(),
+                EasyMock.eq(true), EasyMock.eq(AbiUtils.createAbiFlag(UnitTests.ABI.getName()))))
+                .andReturn(null).once();
+
         EasyMock.expect(mockDevice.executeShellCommand(EasyMock.eq("rm " + CASE_LIST_FILE_NAME)))
                 .andReturn("").once();
 
@@ -409,7 +441,7 @@
 
         String command = String.format(
                 "am instrument %s -w -e deqpLogFileName \"%s\" -e deqpCmdLine \""
-                    + "--deqp-caselist-file=%s --deqp-gl-config-name=rgba8888d24s8\""
+                    + "--deqp-caselist-file=%s --deqp-gl-config-name=rgba8888d24s8\" "
                     + "-e deqpLogData \"%s\" %s",
                 AbiUtils.createAbiFlag(UnitTests.ABI.getName()), LOG_FILE_NAME,
                 CASE_LIST_FILE_NAME, false, INSTRUMENTATION_NAME);
@@ -446,10 +478,14 @@
         mockListener.testRunEnded(EasyMock.anyLong(), EasyMock.<Map<String, String>>notNull());
         EasyMock.expectLastCall().once();
 
+        EasyMock.expect(mockDevice.uninstallPackage(EasyMock.eq(DEQP_ONDEVICE_PKG))).andReturn("")
+                .once();
+
         EasyMock.replay(mockDevice);
         EasyMock.replay(mockListener);
 
         deqpTest.setDevice(mockDevice);
+        deqpTest.setBuildHelper(new StubCtsBuildHelper());
         deqpTest.run(mockListener);
 
         EasyMock.verify(mockListener);