Merge "Go faster by wasting CPU" into klp-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 755cb45..5d23eb5 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -19,6 +19,7 @@
 	CtsInstrumentationAppDiffCert \
 	CtsPermissionDeclareApp \
 	CtsPermissionDeclareAppCompat \
+	CtsReadExternalStorageApp \
 	CtsSharedUidInstall \
 	CtsSharedUidInstallDiffCert \
 	CtsSimpleAppInstall \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index d3068bd..8695981 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -20,8 +20,8 @@
       android:versionCode="1"
       android:versionName="1337">
 
-    <!-- Using 10 for more complete NFC support... -->
-    <uses-sdk android:minSdkVersion="11"></uses-sdk>
+    <!-- Using 10+ for more complete NFC support... -->
+    <uses-sdk android:minSdkVersion="12"></uses-sdk>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -41,6 +41,8 @@
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
+
     <uses-feature android:name="android.hardware.usb.accessory" />
 
     <!-- Needed by the Audio Quality Verifier to store the sound samples that will be mailed. -->
@@ -52,7 +54,6 @@
             android:debuggable="true"
             android:largeHeap="true">
 
-        <uses-library android:name="com.android.future.usb.accessory" />
         <meta-data android:name="com.google.android.backup.api_key"
                 android:value="AEdPqrEAAAAIbK6ldcOzoeRtQ1u1dFVJ1A7KetRhit-a1Xa82Q" />
 
@@ -354,7 +355,7 @@
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_camera" />
             <meta-data android:name="test_required_features" android:value="android.hardware.sensor.gyroscope" />
-            <meta-data android:name="test_required_features" android:value="android.hardware.camera.any"/>
+            <meta-data android:name="test_required_features" android:value="android.hardware.camera"/>
         </activity>
         <activity
             android:name=".camera.fov.DetermineFovActivity"
@@ -462,6 +463,18 @@
             <meta-data android:name="test_category" android:value="@string/test_category_other" />
         </activity>
 
+        <activity android:name=".deskclock.DeskClockTestsActivity"
+                  android:label="@string/deskclock_tests">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_deskclock" />
+        </activity>
+
+        <activity android:name=".deskclock.ShowAlarmsTestActivity"
+                  android:label="@string/dc_show_alarms_test"/>
+
         <receiver android:name=".widget.WidgetCtsProvider">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/apps/CtsVerifier/res/layout/dc_show_alarms.xml b/apps/CtsVerifier/res/layout/dc_show_alarms.xml
new file mode 100644
index 0000000..06a2bb1
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/dc_show_alarms.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+    >
+
+    <LinearLayout android:orientation="vertical"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:layout_alignParentTop="true"
+                  android:padding="5dp"
+        >
+
+        <TextView android:layout_width="wrap_content"
+                  android:textSize="18sp"
+                  android:layout_height="wrap_content"
+                  android:text="@string/dc_show_alarms_test_info"
+            />
+
+        <Button android:id="@+id/dc_show_alarms"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/dc_show_alarms_button"
+            />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        >
+        <include layout="@layout/pass_fail_buttons" />
+    </LinearLayout>
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index ed2ecd4..ef14be3 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -32,6 +32,7 @@
     <string name="test_category_security">Security</string>
     <string name="test_category_streaming">Streaming</string>
     <string name="test_category_features">Features</string>
+    <string name="test_category_deskclock">Desk Clock</string>
     <string name="test_category_other">Other</string>
     <string name="clear">Clear</string>
     <string name="test_results_cleared">Test results cleared.</string>
@@ -614,4 +615,22 @@
     <string name="widget_name">Widget Framework Test</string>
     <string name="widget_pass">Pass</string>
     <string name="widget_fail">Fail</string>
+
+    <!-- Strings for DeskClock -->
+    <string name="deskclock_tests">Desk Clock Tests</string>
+    <string name="deskclock_tests_info">
+        The Desk Clock tests verify that the Desk Clock app implements the Clock API\'s properly.
+    </string>
+    <string name="deskclock_group_alarms">Alarms</string>
+    <string name="deskclock_group_timers">Timers</string>
+
+    <string name="dc_show_alarms_test">Show Alarms Test</string>
+    <string name="dc_show_alarms_test_info">
+        This test verifies that the SHOW_ALARMS API works.\n
+        1. Press the "Show Alarms" button.\n
+        2. Verify that the Desk Clock app is launched and displays the Alarms section\n
+    </string>
+    <string name="dc_show_alarms_button">Show Alarms</string>
+
+
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/DeskClockTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/DeskClockTestsActivity.java
new file mode 100644
index 0000000..91b9b98
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/DeskClockTestsActivity.java
@@ -0,0 +1,52 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+
+package com.android.cts.verifier.deskclock;
+
+import android.content.Intent;
+import android.database.DataSetObserver;
+import android.os.Bundle;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+/**
+ * Activity that lists all the DeskClock tests.
+ */
+public class DeskClockTestsActivity extends PassFailButtons.TestListActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setInfoResources(R.string.deskclock_tests, R.string.deskclock_tests_info, 0);
+        setPassFailButtonClickListeners();
+
+        getPassButton().setEnabled(false);
+
+        final ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+        adapter.add(TestListItem.newCategory(this, R.string.deskclock_group_alarms));
+        adapter.add(TestListItem.newTest(this,
+                R.string.dc_show_alarms_test,
+                ShowAlarmsTestActivity.class.getName(),
+                new Intent(this, ShowAlarmsTestActivity.class), null));
+
+        adapter.registerDataSetObserver(new DataSetObserver() {
+            @Override
+            public void onChanged() {
+                updatePassButton();
+            }
+        });
+
+        setTestListAdapter(adapter);
+    }
+
+    /**
+     * Enable Pass Button when the all tests passed.
+     */
+    private void updatePassButton() {
+        getPassButton().setEnabled(mAdapter.allTestsPassed());
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/ShowAlarmsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/ShowAlarmsTestActivity.java
new file mode 100644
index 0000000..0ce8f51
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/ShowAlarmsTestActivity.java
@@ -0,0 +1,37 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+
+package com.android.cts.verifier.deskclock;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.AlarmClock;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+public class ShowAlarmsTestActivity extends PassFailButtons.Activity implements OnClickListener {
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.dc_show_alarms);
+        setPassFailButtonClickListeners();
+
+        final View button = findViewById(R.id.dc_show_alarms);
+        button.setOnClickListener(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        setResult(RESULT_CANCELED);
+    }
+
+    @Override
+    public void onClick(View v) {
+        startActivity(new Intent(AlarmClock.ACTION_SHOW_ALARMS));
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index 46963a0..1a68b48 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -154,7 +154,7 @@
 
     public static final Feature[] ALL_HONEYCOMB_MR1_FEATURES = {
             new Feature("android.hardware.usb.host", false),
-            new Feature("android.hardware.usb.accessory", true),
+            new Feature("android.hardware.usb.accessory", false),
     };
 
     public static final Feature[] ALL_HONEYCOMB_MR2_FEATURES = {
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
index 3779db9..88b05fb 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
@@ -17,7 +17,6 @@
 package com.android.cts.appsecurity;
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.IDevice;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.testrunner.InstrumentationResultParser;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
@@ -66,15 +65,16 @@
     private static final String APP_ACCESS_DATA_PKG = "com.android.cts.appaccessdata";
 
     // External storage constants
+    private static final String COMMON_EXTERNAL_STORAGE_APP_CLASS = "com.android.cts.externalstorageapp.CommonExternalStorageTest";
     private static final String EXTERNAL_STORAGE_APP_APK = "CtsExternalStorageApp.apk";
     private static final String EXTERNAL_STORAGE_APP_PKG = "com.android.cts.externalstorageapp";
-    private static final String EXTERNAL_STORAGE_APP_CLASS = EXTERNAL_STORAGE_APP_PKG
-            + ".ExternalStorageTest";
+    private static final String EXTERNAL_STORAGE_APP_CLASS = EXTERNAL_STORAGE_APP_PKG + ".ExternalStorageTest";
+    private static final String READ_EXTERNAL_STORAGE_APP_APK = "CtsReadExternalStorageApp.apk";
+    private static final String READ_EXTERNAL_STORAGE_APP_PKG = "com.android.cts.readexternalstorageapp";
+    private static final String READ_EXTERNAL_STORAGE_APP_CLASS = READ_EXTERNAL_STORAGE_APP_PKG + ".ReadExternalStorageTest";
     private static final String WRITE_EXTERNAL_STORAGE_APP_APK = "CtsWriteExternalStorageApp.apk";
-    private static final String
-            WRITE_EXTERNAL_STORAGE_APP_PKG = "com.android.cts.writeexternalstorageapp";
-    private static final String WRITE_EXTERNAL_STORAGE_APP_CLASS = WRITE_EXTERNAL_STORAGE_APP_PKG
-            + ".WriteExternalStorageTest";
+    private static final String WRITE_EXTERNAL_STORAGE_APP_PKG = "com.android.cts.writeexternalstorageapp";
+    private static final String WRITE_EXTERNAL_STORAGE_APP_CLASS = WRITE_EXTERNAL_STORAGE_APP_PKG + ".WriteExternalStorageTest";
 
     // testInstrumentationDiffCert constants
     private static final String TARGET_INSTRUMENT_APK = "CtsTargetInstrumentationApp.apk";
@@ -207,20 +207,89 @@
     }
 
     /**
-     * Verify that legacy filesystem paths continue working, and that they all
-     * point to same location.
+     * Verify that app with no external storage permissions works correctly.
      */
-    public void testExternalStorageLegacyPaths() throws Exception {
+    public void testExternalStorageNone() throws Exception {
         try {
+            wipePrimaryExternalStorage(getDevice());
+
+            getDevice().uninstallPackage(EXTERNAL_STORAGE_APP_PKG);
+            assertNull(getDevice()
+                    .installPackage(getTestAppFile(EXTERNAL_STORAGE_APP_APK), false));
+            assertTrue("Failed external storage with no permissions",
+                    runDeviceTests(EXTERNAL_STORAGE_APP_PKG));
+        } finally {
+            getDevice().uninstallPackage(EXTERNAL_STORAGE_APP_PKG);
+        }
+    }
+
+    /**
+     * Verify that app with
+     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} works
+     * correctly.
+     */
+    public void testExternalStorageRead() throws Exception {
+        try {
+            wipePrimaryExternalStorage(getDevice());
+
+            getDevice().uninstallPackage(READ_EXTERNAL_STORAGE_APP_PKG);
+            assertNull(getDevice()
+                    .installPackage(getTestAppFile(READ_EXTERNAL_STORAGE_APP_APK), false));
+            assertTrue("Failed external storage with read permissions",
+                    runDeviceTests(READ_EXTERNAL_STORAGE_APP_PKG));
+        } finally {
+            getDevice().uninstallPackage(READ_EXTERNAL_STORAGE_APP_PKG);
+        }
+    }
+
+    /**
+     * Verify that app with
+     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} works
+     * correctly.
+     */
+    public void testExternalStorageWrite() throws Exception {
+        try {
+            wipePrimaryExternalStorage(getDevice());
+
             getDevice().uninstallPackage(WRITE_EXTERNAL_STORAGE_APP_PKG);
             assertNull(getDevice()
                     .installPackage(getTestAppFile(WRITE_EXTERNAL_STORAGE_APP_APK), false));
+            assertTrue("Failed external storage with write permissions",
+                    runDeviceTests(WRITE_EXTERNAL_STORAGE_APP_PKG));
+        } finally {
+            getDevice().uninstallPackage(WRITE_EXTERNAL_STORAGE_APP_PKG);
+        }
+    }
 
-            assertTrue("Failed to verify legacy filesystem paths", runDeviceTests(
-                    WRITE_EXTERNAL_STORAGE_APP_PKG, WRITE_EXTERNAL_STORAGE_APP_CLASS,
-                    "testLegacyPaths"));
+    /**
+     * Verify that app with WRITE_EXTERNAL can leave gifts in external storage
+     * directories belonging to other apps, and those apps can read.
+     */
+    public void testExternalStorageGifts() throws Exception {
+        try {
+            wipePrimaryExternalStorage(getDevice());
+
+            getDevice().uninstallPackage(EXTERNAL_STORAGE_APP_PKG);
+            getDevice().uninstallPackage(READ_EXTERNAL_STORAGE_APP_PKG);
+            getDevice().uninstallPackage(WRITE_EXTERNAL_STORAGE_APP_PKG);
+            assertNull(getDevice()
+                    .installPackage(getTestAppFile(EXTERNAL_STORAGE_APP_APK), false));
+            assertNull(getDevice()
+                    .installPackage(getTestAppFile(READ_EXTERNAL_STORAGE_APP_APK), false));
+            assertNull(getDevice()
+                    .installPackage(getTestAppFile(WRITE_EXTERNAL_STORAGE_APP_APK), false));
+
+            assertTrue("Failed to write gifts", runDeviceTests(WRITE_EXTERNAL_STORAGE_APP_PKG,
+                    WRITE_EXTERNAL_STORAGE_APP_CLASS, "doWriteGifts"));
+
+            assertTrue("Read failed to verify gifts", runDeviceTests(READ_EXTERNAL_STORAGE_APP_PKG,
+                    READ_EXTERNAL_STORAGE_APP_CLASS, "doVerifyGifts"));
+            assertTrue("None failed to verify gifts", runDeviceTests(EXTERNAL_STORAGE_APP_PKG,
+                    EXTERNAL_STORAGE_APP_CLASS, "doVerifyGifts"));
 
         } finally {
+            getDevice().uninstallPackage(EXTERNAL_STORAGE_APP_PKG);
+            getDevice().uninstallPackage(READ_EXTERNAL_STORAGE_APP_PKG);
             getDevice().uninstallPackage(WRITE_EXTERNAL_STORAGE_APP_PKG);
         }
     }
@@ -500,4 +569,9 @@
         getDevice().executeShellCommand(cmd, parser);
         return listener.getCurrentRunResults();
     }
+
+    private static void wipePrimaryExternalStorage(ITestDevice device)
+            throws DeviceNotAvailableException {
+        device.executeShellCommand("rm -rf /sdcard/*");
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
index 91d6ccf..bc99560 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
@@ -17,7 +17,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := 10
+LOCAL_SDK_VERSION := current
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PACKAGE_NAME := CtsExternalStorageApp
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
new file mode 100644
index 0000000..340ebed
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.externalstorageapp;
+
+import android.content.Context;
+import android.os.Environment;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests common functionality that should be supported regardless of external
+ * storage status.
+ */
+public class CommonExternalStorageTest extends AndroidTestCase {
+    private static final String TAG = "CommonExternalStorageTest";
+
+    public static final String PACKAGE_NONE = "com.android.cts.externalstorageapp";
+    public static final String PACKAGE_READ = "com.android.cts.readexternalstorageapp";
+    public static final String PACKAGE_WRITE = "com.android.cts.writeexternalstorageapp";
+
+    /**
+     * Primary storage must always be mounted.
+     */
+    public void testExternalStorageMounted() {
+        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+    }
+
+    /**
+     * Verify that single path is always first item in multiple.
+     */
+    public void testMultipleCacheDirs() throws Exception {
+        final File single = getContext().getExternalCacheDir();
+        assertNotNull("Primary storage must always be available", single);
+        final File firstMultiple = getContext().getExternalCacheDirs()[0];
+        assertEquals(single, firstMultiple);
+    }
+
+    /**
+     * Verify that single path is always first item in multiple.
+     */
+    public void testMultipleFilesDirs() throws Exception {
+        final File single = getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+        assertNotNull("Primary storage must always be available", single);
+        final File firstMultiple = getContext()
+                .getExternalFilesDirs(Environment.DIRECTORY_PICTURES)[0];
+        assertEquals(single, firstMultiple);
+    }
+
+    /**
+     * Verify that single path is always first item in multiple.
+     */
+    public void testMultipleObbDirs() throws Exception {
+        final File single = getContext().getObbDir();
+        assertNotNull("Primary storage must always be available", single);
+        final File firstMultiple = getContext().getObbDirs()[0];
+        assertEquals(single, firstMultiple);
+    }
+
+    /**
+     * Verify we can write to our own package dirs.
+     */
+    public void testPackageDirs() throws Exception {
+        final List<File> paths = getAllPackageSpecificPaths(getContext());
+        for (File path : paths) {
+            if (path == null) continue;
+
+            assertEquals(Environment.MEDIA_MOUNTED, Environment.getStorageState(path));
+            assertDirReadWriteAccess(path);
+
+            final File directChild = new File(path, "directChild");
+            final File subdir = new File(path, "subdir");
+            final File subdirChild = new File(path, "subdirChild");
+
+            writeInt(directChild, 32);
+            subdir.mkdirs();
+            assertDirReadWriteAccess(subdir);
+            writeInt(subdirChild, 64);
+
+            assertEquals(32, readInt(directChild));
+            assertEquals(64, readInt(subdirChild));
+        }
+
+        for (File path : paths) {
+            deleteContents(path);
+        }
+    }
+
+    /**
+     * Return a set of several package-specific external storage paths.
+     */
+    public static List<File> getAllPackageSpecificPaths(Context context) {
+        final List<File> paths = new ArrayList<File>();
+        Collections.addAll(paths, context.getExternalCacheDirs());
+        Collections.addAll(paths, context.getExternalFilesDirs(null));
+        Collections.addAll(paths, context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES));
+        Collections.addAll(paths, context.getObbDirs());
+        return paths;
+    }
+
+    public static List<File> getPrimaryPackageSpecificPaths(Context context) {
+        final List<File> paths = new ArrayList<File>();
+        Collections.addAll(paths, context.getExternalCacheDir());
+        Collections.addAll(paths, context.getExternalFilesDir(null));
+        Collections.addAll(paths, context.getExternalFilesDir(Environment.DIRECTORY_PICTURES));
+        Collections.addAll(paths, context.getObbDir());
+        return paths;
+    }
+
+    public static List<File> getSecondaryPackageSpecificPaths(Context context) {
+        final List<File> paths = new ArrayList<File>();
+        Collections.addAll(paths, dropFirst(context.getExternalCacheDirs()));
+        Collections.addAll(paths, dropFirst(context.getExternalFilesDirs(null)));
+        Collections.addAll(
+                paths, dropFirst(context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES)));
+        Collections.addAll(paths, dropFirst(context.getObbDirs()));
+        return paths;
+    }
+
+    private static File[] dropFirst(File[] before) {
+        final File[] after = new File[before.length - 1];
+        System.arraycopy(before, 1, after, 0, after.length);
+        return after;
+    }
+
+    public static File buildGiftForPackage(Context context, String packageName) {
+        final File myCache = context.getExternalCacheDir();
+        return new File(myCache.getAbsolutePath().replace(context.getPackageName(), packageName),
+                packageName + ".gift");
+    }
+
+    public static void assertDirReadOnlyAccess(File path) {
+        Log.d(TAG, "Asserting read-only access to " + path);
+
+        assertTrue("exists", path.exists());
+        assertTrue("read", path.canRead());
+        assertTrue("execute", path.canExecute());
+        assertNotNull("list", path.list());
+
+        try {
+            final File probe = new File(path, ".probe");
+            assertTrue(!probe.exists());
+            probe.createNewFile();
+            probe.delete();
+            fail("able to create probe!");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    public static void assertDirReadWriteAccess(File path) {
+        Log.d(TAG, "Asserting read/write access to " + path);
+
+        assertTrue("exists", path.exists());
+        assertTrue("read", path.canRead());
+        assertTrue("execute", path.canExecute());
+        assertNotNull("list", path.list());
+
+        try {
+            final File probe = new File(path, ".probe");
+            assertTrue(!probe.exists());
+            probe.createNewFile();
+            probe.delete();
+        } catch (IOException e) {
+            fail("failed to create probe!");
+        }
+    }
+
+    public static void assertDirNoAccess(File path) {
+        Log.d(TAG, "Asserting no access to " + path);
+
+        assertFalse("read", path.canRead());
+        assertNull("list", path.list());
+
+        try {
+            final File probe = new File(path, ".probe");
+            assertTrue(!probe.exists());
+            probe.createNewFile();
+            probe.delete();
+            fail("able to create probe!");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    public static void assertFileReadOnlyAccess(File path) {
+        try {
+            new FileInputStream(path).close();
+        } catch (IOException e) {
+            fail("failed to read!");
+        }
+
+        try {
+            new FileOutputStream(path, true).close();
+            fail("able to write!");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    public static void assertFileReadWriteAccess(File path) {
+        try {
+            new FileInputStream(path).close();
+        } catch (IOException e) {
+            fail("failed to read!");
+        }
+
+        try {
+            new FileOutputStream(path, true).close();
+        } catch (IOException e) {
+            fail("failed to write!");
+        }
+    }
+
+    public static void assertFileNoAccess(File path) {
+        try {
+            new FileInputStream(path).close();
+            fail("able to read!");
+        } catch (IOException e) {
+            // expected
+        }
+
+        try {
+            new FileOutputStream(path, true).close();
+            fail("able to write!");
+        } catch (IOException e) {
+            // expected
+        }
+    }
+
+    public static void deleteContents(File dir) throws IOException {
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    deleteContents(file);
+                }
+                assertTrue(file.delete());
+            }
+            assertEquals(0, dir.listFiles().length);
+        }
+    }
+
+    public static void writeInt(File file, int value) throws IOException {
+        final DataOutputStream os = new DataOutputStream(new FileOutputStream(file));
+        try {
+            os.writeInt(value);
+        } finally {
+            os.close();
+        }
+    }
+
+    public static int readInt(File file) throws IOException {
+        final DataInputStream is = new DataInputStream(new FileInputStream(file));
+        try {
+            return is.readInt();
+        } finally {
+            is.close();
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
index a3fcf4a..9d24f98 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
@@ -16,43 +16,69 @@
 
 package com.android.cts.externalstorageapp;
 
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_NONE;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_READ;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_WRITE;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileNoAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadWriteAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildGiftForPackage;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPaths;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
+
 import android.os.Environment;
 import android.test.AndroidTestCase;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import java.util.List;
 
 /**
- * Test if {@link Environment#getExternalStorageDirectory()} is readable.
+ * Test external storage from an application that has no external storage
+ * permissions.
  */
 public class ExternalStorageTest extends AndroidTestCase {
 
-    private static final String TEST_FILE = "meow";
-
-    private void assertExternalStorageMounted() {
-        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+    public void testPrimaryNoAccess() throws Exception {
+        assertDirNoAccess(Environment.getExternalStorageDirectory());
     }
 
-    private void readExternalStorage() throws IOException {
-        final File file = new File(Environment.getExternalStorageDirectory(), TEST_FILE);
-        final InputStream is = new FileInputStream(file);
-        try {
-            is.read();
-        } finally {
-            is.close();
+    /**
+     * Verify that above our package directories we always have no access.
+     */
+    public void testAllWalkingUpTreeNoAccess() throws Exception {
+        final List<File> paths = getAllPackageSpecificPaths(getContext());
+        final String packageName = getContext().getPackageName();
+
+        for (File path : paths) {
+            assertTrue(path.getAbsolutePath().contains(packageName));
+
+            // Walk up until we drop our package
+            while (path.getAbsolutePath().contains(packageName)) {
+                assertDirReadWriteAccess(path);
+                path = path.getParentFile();
+            }
+
+            // Keep walking up until we leave device
+            while (Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(path))) {
+                assertDirNoAccess(path);
+                path = path.getParentFile();
+            }
         }
     }
 
-    public void testFailReadExternalStorage() throws Exception {
-        assertExternalStorageMounted();
-        try {
-            readExternalStorage();
-            fail("able read external file");
-        } catch (IOException e) {
-            // expected
-            e.printStackTrace();
-        }
+    /**
+     * Verify we can read only our gifts.
+     */
+    public void doVerifyGifts() throws Exception {
+        final File none = buildGiftForPackage(getContext(), PACKAGE_NONE);
+        assertFileReadWriteAccess(none);
+        assertEquals(100, readInt(none));
+
+        final File read = buildGiftForPackage(getContext(), PACKAGE_READ);
+        assertFileNoAccess(read);
+
+        final File write = buildGiftForPackage(getContext(), PACKAGE_WRITE);
+        assertFileNoAccess(write);
     }
 }
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
new file mode 100644
index 0000000..44e4bef
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+
+LOCAL_PACKAGE_NAME := CtsReadExternalStorageApp
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml
new file mode 100644
index 0000000..f6582b9
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+       package="com.android.cts.readexternalstorageapp">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.cts.readexternalstorageapp" />
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/src/com/android/cts/readexternalstorageapp/ReadExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/src/com/android/cts/readexternalstorageapp/ReadExternalStorageTest.java
new file mode 100644
index 0000000..d315651
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/src/com/android/cts/readexternalstorageapp/ReadExternalStorageTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.readexternalstorageapp;
+
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_NONE;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_READ;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_WRITE;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadOnlyAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadOnlyAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadWriteAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildGiftForPackage;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPaths;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
+
+import android.os.Environment;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Test external storage from an application that has
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
+ */
+public class ReadExternalStorageTest extends AndroidTestCase {
+
+    public void testPrimaryReadOnly() throws Exception {
+        assertDirReadOnlyAccess(Environment.getExternalStorageDirectory());
+    }
+
+    /**
+     * Verify that above our package directories we always have read only
+     * access.
+     */
+    public void testAllWalkingUpTreeReadOnly() throws Exception {
+        final List<File> paths = getAllPackageSpecificPaths(getContext());
+        final String packageName = getContext().getPackageName();
+
+        for (File path : paths) {
+            assertTrue(path.getAbsolutePath().contains(packageName));
+
+            // Walk up until we drop our package
+            while (path.getAbsolutePath().contains(packageName)) {
+                assertDirReadWriteAccess(path);
+                path = path.getParentFile();
+            }
+
+            // Keep walking up until we leave device
+            while (Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(path))) {
+                assertDirReadOnlyAccess(path);
+                path = path.getParentFile();
+            }
+        }
+    }
+
+    /**
+     * Verify we can read all gifts.
+     */
+    public void doVerifyGifts() throws Exception {
+        final File none = buildGiftForPackage(getContext(), PACKAGE_NONE);
+        assertFileReadOnlyAccess(none);
+        assertEquals(100, readInt(none));
+
+        final File read = buildGiftForPackage(getContext(), PACKAGE_READ);
+        assertFileReadWriteAccess(read);
+        assertEquals(101, readInt(read));
+
+        final File write = buildGiftForPackage(getContext(), PACKAGE_WRITE);
+        assertFileReadOnlyAccess(write);
+        assertEquals(102, readInt(write));
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
index 9e056a9..4352bfb 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
@@ -17,9 +17,11 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := 10
+LOCAL_SDK_VERSION := current
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+
 LOCAL_PACKAGE_NAME := CtsWriteExternalStorageApp
 
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
index 3f103b6..ff2f1b7 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
@@ -16,19 +16,30 @@
 
 package com.android.cts.writeexternalstorageapp;
 
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_NONE;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_READ;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_WRITE;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadOnlyAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadWriteAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildGiftForPackage;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.deleteContents;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPaths;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getPrimaryPackageSpecificPaths;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getSecondaryPackageSpecificPaths;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.writeInt;
+
 import android.os.Environment;
 import android.test.AndroidTestCase;
 
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
+import java.util.List;
 import java.util.Random;
 
 /**
- * Test if {@link Environment#getExternalStorageDirectory()} is writable.
+ * Test external storage from an application that has
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}.
  */
 public class WriteExternalStorageTest extends AndroidTestCase {
 
@@ -57,6 +68,10 @@
         }
     }
 
+    private void assertExternalStorageMounted() {
+        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+    }
+
     public void testReadExternalStorage() throws Exception {
         assertExternalStorageMounted();
         Environment.getExternalStorageDirectory().list();
@@ -93,25 +108,124 @@
         }
     }
 
-    private static void assertExternalStorageMounted() {
-        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+    public void testPrimaryReadWrite() throws Exception {
+        assertDirReadWriteAccess(Environment.getExternalStorageDirectory());
     }
 
-    private static void writeInt(File file, int value) throws IOException {
-        final DataOutputStream os = new DataOutputStream(new FileOutputStream(file));
-        try {
-            os.writeInt(value);
-        } finally {
-            os.close();
+    /**
+     * Verify that above our package directories (on primary storage) we always
+     * have write access.
+     */
+    public void testPrimaryWalkingUpTreeReadWrite() throws Exception {
+        final List<File> paths = getPrimaryPackageSpecificPaths(getContext());
+        final String packageName = getContext().getPackageName();
+
+        for (File path : paths) {
+            assertTrue(path.getAbsolutePath().contains(packageName));
+
+            // Walk until we leave device, writing the whole way
+            while (Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(path))) {
+                assertDirReadWriteAccess(path);
+                path = path.getParentFile();
+            }
         }
     }
 
-    private static int readInt(File file) throws IOException {
-        final DataInputStream is = new DataInputStream(new FileInputStream(file));
-        try {
-            return is.readInt();
-        } finally {
-            is.close();
+    /**
+     * Verify that we have write access in other packages on primary external
+     * storage.
+     */
+    public void testPrimaryOtherPackageWriteAccess() throws Exception {
+        deleteContents(Environment.getExternalStorageDirectory());
+
+        final File ourCache = getContext().getExternalCacheDir();
+        final File otherCache = new File(ourCache.getAbsolutePath()
+                .replace(getContext().getPackageName(), PACKAGE_NONE));
+
+        assertTrue(otherCache.mkdirs());
+        assertDirReadWriteAccess(otherCache);
+    }
+
+    /**
+     * Verify that we have write access in our package-specific directories on
+     * secondary storage devices, but it becomes read-only access above them.
+     */
+    public void testSecondaryWalkingUpTreeReadOnly() throws Exception {
+        final List<File> paths = getSecondaryPackageSpecificPaths(getContext());
+        final String packageName = getContext().getPackageName();
+
+        for (File path : paths) {
+            assertTrue(path.getAbsolutePath().contains(packageName));
+
+            // Walk up until we drop our package
+            while (path.getAbsolutePath().contains(packageName)) {
+                assertDirReadWriteAccess(path);
+                path = path.getParentFile();
+            }
+
+            // Keep walking up until we leave device
+            while (Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(path))) {
+                assertDirReadOnlyAccess(path);
+                path = path.getParentFile();
+            }
         }
     }
+
+    /**
+     * Verify that .nomedia is created correctly.
+     */
+    public void testVerifyNoMediaCreated() throws Exception {
+        deleteContents(Environment.getExternalStorageDirectory());
+
+        final List<File> paths = getAllPackageSpecificPaths(getContext());
+
+        // Require that .nomedia was created somewhere above each dir
+        for (File path : paths) {
+            final File start = path;
+
+            boolean found = false;
+            while (Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(path))) {
+                final File test = new File(path, ".nomedia");
+                if (test.exists()) {
+                    found = true;
+                    break;
+                }
+                path = path.getParentFile();
+            }
+
+            if (!found) {
+                fail("Missing .nomedia file above package-specific directory " + start
+                        + "; gave up at " + path);
+            }
+        }
+    }
+
+    /**
+     * Leave gifts for other packages in their primary external cache dirs.
+     */
+    public void doWriteGifts() throws Exception {
+        final File none = buildGiftForPackage(getContext(), PACKAGE_NONE);
+        none.getParentFile().mkdirs();
+        none.createNewFile();
+        assertFileReadWriteAccess(none);
+
+        writeInt(none, 100);
+        assertEquals(100, readInt(none));
+
+        final File read = buildGiftForPackage(getContext(), PACKAGE_READ);
+        read.getParentFile().mkdirs();
+        read.createNewFile();
+        assertFileReadWriteAccess(read);
+
+        writeInt(read, 101);
+        assertEquals(101, readInt(read));
+
+        final File write = buildGiftForPackage(getContext(), PACKAGE_WRITE);
+        write.getParentFile().mkdirs();
+        write.createNewFile();
+        assertFileReadWriteAccess(write);
+
+        writeInt(write, 102);
+        assertEquals(102, readInt(write));
+    }
 }
diff --git a/tests/tests/app/src/android/app/cts/FragmentTest.java b/tests/tests/app/src/android/app/cts/FragmentTest.java
index c5c0d6c..006f0a9 100644
--- a/tests/tests/app/src/android/app/cts/FragmentTest.java
+++ b/tests/tests/app/src/android/app/cts/FragmentTest.java
@@ -47,7 +47,8 @@
             Fragment.instantiate(getContext(), TestNotFragment.class.getName());
             fail();
         } catch (Exception e) {
-            assertTrue(e instanceof IllegalStateException);
+            // Should get an exception and it shouldn't be an IllegalStateException
+            assertFalse(e instanceof IllegalStateException);
         }
     }
 }
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index a06b603..87d9039 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -16,11 +16,13 @@
 
 package android.content.cts;
 
+import android.app.DownloadManager;
 import android.app.SearchManager;
 import android.content.ContentUris;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.media.RingtoneManager;
 import android.net.Uri;
 import android.provider.AlarmClock;
 import android.provider.MediaStore;
@@ -197,4 +199,33 @@
         intent.putExtra(AlarmClock.EXTRA_MINUTES, 0);
         assertCanBeHandled(intent);
     }
+
+    public void testOpenDocumentAny() {
+        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+        intent.addCategory(Intent.CATEGORY_OPENABLE);
+        intent.setType("*/*");
+        assertCanBeHandled(intent);
+    }
+
+    public void testOpenDocumentImage() {
+        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+        intent.addCategory(Intent.CATEGORY_OPENABLE);
+        intent.setType("image/*");
+        assertCanBeHandled(intent);
+    }
+
+    public void testCreateDocument() {
+        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+        intent.addCategory(Intent.CATEGORY_OPENABLE);
+        intent.setType("text/plain");
+        assertCanBeHandled(intent);
+    }
+
+    public void testRingtonePicker() {
+        assertCanBeHandled(new Intent(RingtoneManager.ACTION_RINGTONE_PICKER));
+    }
+
+    public void testViewDownloads() {
+        assertCanBeHandled(new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS));
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
index 2968a47..7091cac 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -75,19 +75,33 @@
         String[] ids = mCameraManager.getCameraIdList();
         if (VERBOSE) Log.v(TAG, "CameraManager ids: " + Arrays.toString(ids));
 
-        // Test: that if the device has a camera, there must be at least one reported id.
-        assertTrue("At least one camera must be detected",
-               ! mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
-            || ids.length >= 1);
+        /**
+         * Test: that if there is at least one reported id, then the system must have
+         * the FEATURE_CAMERA_ANY feature.
+         */
+        assertTrue("System camera feature and camera id list don't match",
+                ids.length == 0 ||
+                mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
 
         /**
-         * Test: that if the device has both front and rear facing cameras, then there
-         * must be at lest two reported ids.
+         * Test: that if the device has front or rear facing cameras, then there
+         * must be matched system features.
          */
-        assertTrue("At least two cameras must be detected",
-               ! mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
-            || ! mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)
-            || ids.length >= 2);
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull("Can't get camera characteristics for camera " + ids[i], props);
+            Integer lensFacing = props.get(CameraCharacteristics.LENS_FACING);
+            assertNotNull("Can't get lens facing info", lensFacing);
+            if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
+                assertTrue("System doesn't have front camera feature",
+                        mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
+            } else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
+                assertTrue("System doesn't have back camera feature",
+                        mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA));
+            } else {
+                fail("Unknown camera lens facing " + lensFacing.toString());
+            }
+        }
 
         /**
          * Test: that if there is one camera device, then the system must have some
diff --git a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java b/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
index c2f0c32..326c959 100644
--- a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
@@ -21,6 +21,7 @@
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaFormat;
+import android.os.Bundle;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -71,6 +72,18 @@
         decode(BASIC_IVF);
     }
 
+    /**
+     * Check if MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME is honored.
+     *
+     * At frame 15, request a sync frame. If one does not occur by EOF the
+     * encoder fails. The test does not verify the output stream.
+     */
+    public void testSyncFrame() throws Exception {
+        encodeSyncFrame(R.raw.video_176x144_yv12,
+                        176, // width
+                        144, // height
+                        30); // framerate
+    }
 
     /**
      * A basic check if an encoded stream is decodable.
@@ -221,7 +234,8 @@
             ivf = new IvfWriter(outputFilename, frameWidth, frameHeight);
             // encode loop
             long presentationTimeUs = 0;
-            int frameIndex = 0;
+            int inputFrameIndex = 0;
+            int outputFrameIndex = 0;
             boolean sawInputEOS = false;
             boolean sawOutputEOS = false;
 
@@ -241,8 +255,8 @@
                         mInputBuffers[inputBufIndex].put(frame);
                         mInputBuffers[inputBufIndex].rewind();
 
-                        presentationTimeUs = (frameIndex * 1000000) / frameRate;
-                        Log.d(TAG, "Encoding frame at index " + frameIndex);
+                        presentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
+                        Log.d(TAG, "Encoding frame at index " + inputFrameIndex);
                         encoder.queueInputBuffer(
                                 inputBufIndex,
                                 0,  // offset
@@ -250,7 +264,7 @@
                                 presentationTimeUs,
                                 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
 
-                        frameIndex++;
+                        inputFrameIndex++;
                     }
                 }
 
@@ -261,6 +275,12 @@
                     mOutputBuffers[outputBufIndex].rewind();
                     mOutputBuffers[outputBufIndex].get(buffer, 0, mBufferInfo.size);
 
+                    if ((outputFrameIndex == 0)
+                        && ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) == 0)) {
+                      throw new RuntimeException("First frame is not a sync frame.");
+
+                    }
+
                     if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                         sawOutputEOS = true;
                     } else {
@@ -268,6 +288,8 @@
                     }
                     encoder.releaseOutputBuffer(outputBufIndex,
                                                 false);  // render
+
+                    outputFrameIndex++;
                 } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                     mOutputBuffers = encoder.getOutputBuffers();
                 }
@@ -285,4 +307,128 @@
             }
         }
     }
+
+
+    /**
+     * Request Sync Frames
+     *
+     * MediaCodec will raise an IllegalStateException
+     * whenever vp8 encoder fails to encode a frame.
+     *
+     * This presumes a file with 28 frames. Under normal circumstances there
+     * would only be one sync frame: the first one. This test will request an
+     * additional sync frame at 15 and ensure that it occurs by EOF.
+     *
+     * Color format of input file should be YUV420, and frameWidth,
+     * frameHeight should be supplied correctly as raw input file doesn't
+     * include any header data.
+     *
+     * @param rawInputFd      File descriptor for the raw input file (YUV420)
+     * @param frameWidth      Frame width of input file
+     * @param frameHeight     Frame height of input file
+     * @param frameRate       Frame rate of input file in frames per second
+     */
+    private void encodeSyncFrame(int rawInputFd, int frameWidth,
+                                 int frameHeight, int frameRate) throws Exception {
+        int frameSize = frameWidth * frameHeight * 3 / 2;
+
+
+        // Create a media format signifying desired output
+        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME, frameWidth, frameHeight);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, 100000);
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                          CodecCapabilities.COLOR_FormatYUV420Planar);
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
+
+        Log.d(TAG, "Creating encoder");
+        MediaCodec encoder;
+        encoder = MediaCodec.createByCodecName(VPX_ENCODER_NAME);
+        encoder.configure(format,
+                          null,  // surface
+                          null,  // crypto
+                          MediaCodec.CONFIGURE_FLAG_ENCODE);
+        encoder.start();
+
+        mInputBuffers = encoder.getInputBuffers();
+        mOutputBuffers = encoder.getOutputBuffers();
+
+        InputStream rawStream = null;
+
+        try {
+            rawStream = mResources.openRawResource(rawInputFd);
+            // encode loop
+            long presentationTimeUs = 0;
+            int inputFrameIndex = 0;
+            boolean sawInputEOS = false;
+            boolean sawOutputEOS = false;
+            boolean syncFrameRequested = false;
+            boolean matchedSyncFrame = false;
+
+            while (!sawOutputEOS) {
+                if (!sawInputEOS) {
+                    int inputBufIndex = encoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
+                    if (inputBufIndex >= 0) {
+                        byte[] frame = new byte[frameSize];
+                        int bytesRead = rawStream.read(frame);
+
+                        if (bytesRead == -1) {
+                            sawInputEOS = true;
+                            bytesRead = 0;
+                        }
+
+                        mInputBuffers[inputBufIndex].clear();
+                        mInputBuffers[inputBufIndex].put(frame);
+                        mInputBuffers[inputBufIndex].rewind();
+
+                        if (inputFrameIndex == 15) {
+                            Log.d(TAG, "Requesting sync frame at index " + inputFrameIndex);
+                            Bundle syncFrame = new Bundle();
+                            syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
+                            encoder.setParameters(syncFrame);
+                            syncFrameRequested = true;
+                        }
+
+                        presentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
+                        encoder.queueInputBuffer(
+                                inputBufIndex,
+                                0,  // offset
+                                bytesRead,  // size
+                                presentationTimeUs,
+                                sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                        inputFrameIndex++;
+                    }
+                }
+
+                int result = encoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
+                if (result >= 0) {
+                    if (syncFrameRequested && ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0)) {
+                        Log.d(TAG, "Found sync frame");
+                        matchedSyncFrame = true;
+                    }
+
+                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        sawOutputEOS = true;
+                    }
+
+                    encoder.releaseOutputBuffer(result,
+                                                false);  // render
+
+                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    mOutputBuffers = encoder.getOutputBuffers();
+                }
+            }
+
+            if (!matchedSyncFrame) {
+                throw new RuntimeException("Requested sync frame did not occur");
+            }
+
+            encoder.stop();
+            encoder.release();
+        } finally {
+            if (rawStream != null) {
+                rawStream.close();
+            }
+        }
+    }
 }
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
new file mode 100644
index 0000000..4cad6e4
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+
+/**
+ * Tests to verify that common actions on {@link MediaStore} content are
+ * available.
+ */
+public class MediaStoreIntentsTest extends AndroidTestCase {
+    public void assertCanBeHandled(Intent intent) {
+        List<ResolveInfo> resolveInfoList = getContext()
+                .getPackageManager().queryIntentActivities(intent, 0);
+        assertNotNull("Missing ResolveInfo", resolveInfoList);
+        assertTrue("No ResolveInfo found for " + intent.toInsecureString(),
+                resolveInfoList.size() > 0);
+    }
+
+    public void testPickImage() {
+        Intent intent = new Intent(Intent.ACTION_PICK);
+        intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+        assertCanBeHandled(intent);
+    }
+
+    public void testPickVideo() {
+        Intent intent = new Intent(Intent.ACTION_PICK);
+        intent.setData(MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
+        assertCanBeHandled(intent);
+    }
+
+    public void testPickAudio() {
+        Intent intent = new Intent(Intent.ACTION_PICK);
+        intent.setData(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
+        assertCanBeHandled(intent);
+    }
+
+    public void testViewImage() {
+        final String[] schemes = new String[] {
+                "file", "http", "https", "content" };
+        final String[] mimes = new String[] {
+                "image/bmp", "image/jpeg", "image/png", "image/gif", "image/webp" };
+
+        for (String scheme : schemes) {
+            for (String mime : mimes) {
+                Intent intent = new Intent(Intent.ACTION_VIEW);
+                final Uri uri = new Uri.Builder().scheme(scheme)
+                        .authority("example.com").path("image").build();
+                intent.setDataAndType(uri, mime);
+                assertCanBeHandled(intent);
+            }
+        }
+    }
+
+    public void testViewVideo() {
+        final String[] schemes = new String[] {
+                "file", "http", "https", "content" };
+        final String[] mimes = new String[] {
+                "video/mpeg4", "video/mp4", "video/3gp", "video/3gpp", "video/3gpp2",
+                "video/webm" };
+
+        for (String scheme : schemes) {
+            for (String mime : mimes) {
+                Intent intent = new Intent(Intent.ACTION_VIEW);
+                final Uri uri = new Uri.Builder().scheme(scheme)
+                        .authority("example.com").path("video").build();
+                intent.setDataAndType(uri, mime);
+                assertCanBeHandled(intent);
+            }
+        }
+    }
+
+    public void testViewAudio() {
+        final String[] schemes = new String[] {
+                "file", "http", "content" };
+        final String[] mimes = new String[] {
+                "audio/mpeg", "audio/mp4", "audio/ogg", "audio/webm", "application/ogg",
+                "application/x-ogg" };
+
+        for (String scheme : schemes) {
+            for (String mime : mimes) {
+                Intent intent = new Intent(Intent.ACTION_VIEW);
+                final Uri uri = new Uri.Builder().scheme(scheme)
+                        .authority("example.com").path("audio").build();
+                intent.setDataAndType(uri, mime);
+                assertCanBeHandled(intent);
+            }
+        }
+    }
+
+    public void testSearchActions() {
+        assertCanBeHandled(new Intent(MediaStore.INTENT_ACTION_MEDIA_SEARCH));
+        assertCanBeHandled(new Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH));
+        assertCanBeHandled(new Intent(MediaStore.INTENT_ACTION_TEXT_OPEN_FROM_SEARCH));
+        assertCanBeHandled(new Intent(MediaStore.INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH));
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index e68286f..3f28a34 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -78,6 +78,13 @@
 
         mHelper = new FileCopyHelper(mContext);
         mRowsAdded = new ArrayList<Uri>();
+
+        String campath = Environment.getExternalStorageDirectory() + File.separator +
+                Environment.DIRECTORY_DCIM + File.separator + "Camera";
+        File camfile = new File(campath);
+        if (!camfile.exists()) {
+            assertTrue("failed to create " + campath, camfile.mkdir());
+        }
     }
 
     public void testInsertImageWithImagePath() throws Exception {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
index e8a13a9..03adad7 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
@@ -68,6 +68,13 @@
 
         mHelper = new FileCopyHelper(mContext);
         mRowsAdded = new ArrayList<Uri>();
+
+        String campath = Environment.getExternalStorageDirectory() + File.separator +
+                Environment.DIRECTORY_DCIM + File.separator + "Camera";
+        File camfile = new File(campath);
+        if (!camfile.exists()) {
+            assertTrue("failed to create " + campath, camfile.mkdir());
+        }
     }
 
     public void testQueryInternalThumbnails() throws Exception {
diff --git a/tests/tests/text/src/android/text/cts/EmojiTest.java b/tests/tests/text/src/android/text/cts/EmojiTest.java
index a8d8d2d..f5f191f 100644
--- a/tests/tests/text/src/android/text/cts/EmojiTest.java
+++ b/tests/tests/text/src/android/text/cts/EmojiTest.java
@@ -222,6 +222,14 @@
 
             webViewOnUiThread.loadDataAndWaitForCompletion("<html><body>" + String.valueOf(c) + "</body></html>",
                     "text/html; charset=utf-8", "utf-8");
+            // The Chromium-powered WebView renders asynchronously and there's nothing reliable
+            // we can easily wait for to be sure that capturePicture will return a fresh frame.
+            // So, just sleep for a sufficient time.
+            try {
+                Thread.sleep(250);
+            } catch (InterruptedException e) {
+                return null;
+            }
 
             Picture picture = webViewOnUiThread.capturePicture();
             if (picture == null || picture.getHeight() <= 0 || picture.getWidth() <= 0) {