am 9410c3b9: am 166f0aac: Merge "Test new READ/WRITE_EXTERNAL_STORAGE behavior." into klp-dev
* commit '9410c3b924943e9e6da633939a45c462c2de4954':
Test new READ/WRITE_EXTERNAL_STORAGE behavior.
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/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));
+ }
}