Test instant app's access to external storage

Add CtsScopedStorageHostTest to cts test-suite which runs tests in both
instant and non-instant mode. Annotate tests that will fail in instant
mode as @AppModeFull.

Bug: 149004073
Test: atest CtsScopedStorageHostTest
Test: atest CtsScopedStorageHostTest --instant
Change-Id: I9c9fa1cb094259bef414e39b36e1f52964b8cdc3
Merged-In: I9c9fa1cb094259bef414e39b36e1f52964b8cdc3
(cherry picked from commit cdd387a4c7f431c16c81a48ea7c923b23ad45144)
diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp
index 3e5096b..1a1a7f9 100644
--- a/hostsidetests/scopedstorage/Android.bp
+++ b/hostsidetests/scopedstorage/Android.bp
@@ -15,28 +15,28 @@
 android_test_helper_app {
     name: "CtsScopedStorageTestAppA",
     manifest: "ScopedStorageTestHelper/TestAppA.xml",
-    static_libs: ["androidx.test.rules", "cts-scopedstorage-lib"],
+    static_libs: ["cts-scopedstorage-lib"],
     sdk_version: "test_current",
     srcs: ["ScopedStorageTestHelper/src/**/*.java"],
 }
 android_test_helper_app {
     name: "CtsScopedStorageTestAppB",
     manifest: "ScopedStorageTestHelper/TestAppB.xml",
-    static_libs: ["androidx.test.rules", "cts-scopedstorage-lib"],
+    static_libs: ["cts-scopedstorage-lib"],
     sdk_version: "test_current",
     srcs: ["ScopedStorageTestHelper/src/**/*.java"],
 }
 android_test_helper_app {
     name: "CtsScopedStorageTestAppC",
     manifest: "ScopedStorageTestHelper/TestAppC.xml",
-    static_libs: ["androidx.test.rules", "cts-scopedstorage-lib"],
+    static_libs: ["cts-scopedstorage-lib"],
     sdk_version: "test_current",
     srcs: ["ScopedStorageTestHelper/src/**/*.java"],
 }
 android_test_helper_app {
     name: "CtsScopedStorageTestAppCLegacy",
     manifest: "ScopedStorageTestHelper/TestAppCLegacy.xml",
-    static_libs: ["androidx.test.rules", "cts-scopedstorage-lib"],
+    static_libs: ["cts-scopedstorage-lib"],
     sdk_version: "test_current",
     target_sdk_version: "28",
     srcs: ["ScopedStorageTestHelper/src/**/*.java"],
@@ -46,9 +46,9 @@
     name: "ScopedStorageTest",
     manifest: "AndroidManifest.xml",
     srcs: ["src/**/*.java"],
-    static_libs: ["androidx.test.rules", "truth-prebuilt", "cts-scopedstorage-lib"],
+    static_libs: ["truth-prebuilt", "cts-scopedstorage-lib"],
     compile_multilib: "both",
-    test_suites: ["general-tests", "mts"],
+    test_suites: ["general-tests", "mts", "cts"],
     sdk_version: "test_current",
     java_resources: [
         ":CtsScopedStorageTestAppA",
@@ -61,9 +61,9 @@
     name: "LegacyStorageTest",
     manifest: "legacy/AndroidManifest.xml",
     srcs: ["legacy/src/**/*.java"],
-    static_libs: ["androidx.test.rules", "truth-prebuilt",  "cts-scopedstorage-lib"],
+    static_libs: ["truth-prebuilt", "cts-scopedstorage-lib"],
     compile_multilib: "both",
-    test_suites: ["general-tests", "mts"],
+    test_suites: ["general-tests", "mts", "cts"],
     sdk_version: "test_current",
     target_sdk_version: "29",
     java_resources: [
@@ -74,17 +74,15 @@
 java_test_host {
     name: "CtsScopedStorageHostTest",
     srcs: ["host/src/**/*.java"],
-    libs: ["tradefed"],
-    static_libs: ["testng"],
-    test_suites: ["general-tests", "mts"],
+    libs: ["tradefed", "testng"],
+    test_suites: ["general-tests", "mts", "cts"],
     test_config: "AndroidTest.xml",
 }
 
 java_test_host {
     name: "CtsScopedStoragePublicVolumeHostTest",
     srcs: ["host/src/**/*.java"],
-    libs: ["tradefed"],
-    static_libs: ["testng"],
+    libs: ["tradefed", "testng"],
     test_suites: ["general-tests", "mts"],
     test_config: "PublicVolumeTest.xml",
 }
diff --git a/hostsidetests/scopedstorage/AndroidTest.xml b/hostsidetests/scopedstorage/AndroidTest.xml
index 64599d8..43208ac 100644
--- a/hostsidetests/scopedstorage/AndroidTest.xml
+++ b/hostsidetests/scopedstorage/AndroidTest.xml
@@ -15,6 +15,10 @@
 -->
 <configuration description="External storage host test for legacy and scoped storage">
     <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="ScopedStorageTest.apk" />
@@ -23,6 +27,7 @@
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="android.scopedstorage.cts.host.LegacyStorageHostTest" />
         <option name="class" value="android.scopedstorage.cts.host.ScopedStorageHostTest" />
+        <option name="class" value="android.scopedstorage.cts.host.ScopedStorageInstantAppHostTest" />
     </test>
 
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
index 7c2bf54..ef196c4 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
@@ -20,6 +20,8 @@
 
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.AppModeFull;
+
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -32,6 +34,7 @@
  * Runs the legacy file path access tests.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public class LegacyStorageHostTest extends BaseHostJUnit4Test {
     private boolean isExternalStorageSetup = false;
 
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
index c380d1a..3bbae2d 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertTrue;
 
+import android.platform.test.annotations.AppModeFull;
+
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -30,6 +32,7 @@
  * Runs the ScopedStorageTest tests.
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
 public class ScopedStorageHostTest extends BaseHostJUnit4Test {
     private boolean mIsExternalStorageSetup = false;
 
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageInstantAppHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageInstantAppHostTest.java
new file mode 100644
index 0000000..c97b41f
--- /dev/null
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageInstantAppHostTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.scopedstorage.cts.host;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeInstant;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Runs the ScopedStorageTest tests for an instant app.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ScopedStorageInstantAppHostTest extends BaseHostJUnit4Test {
+    /**
+     * Runs the given phase of Test by calling into the device.
+     * Throws an exception if the test phase fails.
+     */
+    protected void runDeviceTest(String phase) throws Exception {
+        assertTrue(runDeviceTests("android.scopedstorage.cts",
+                "android.scopedstorage.cts.ScopedStorageTest", phase));
+    }
+
+    @Test
+    @AppModeInstant
+    public void testInstantAppsCantAccessExternalStorage() throws Exception {
+        runDeviceTest("testInstantAppsCantAccessExternalStorage");
+    }
+}
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/Android.bp b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/Android.bp
index 3a5ba59..be2ae44 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/Android.bp
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/Android.bp
@@ -15,6 +15,6 @@
 java_library {
     name: "cts-scopedstorage-lib",
     srcs: ["src/**/*.java"],
-    static_libs: ["androidx.test.rules", "cts-install-lib"],
+    static_libs: ["androidx.test.rules", "cts-install-lib", "platform-test-annotations",],
     sdk_version: "test_current"
 }
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index 4022fb5..6c9a49f 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -118,6 +118,7 @@
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.platform.test.annotations.AppModeInstant;
 import android.provider.MediaStore;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -185,8 +186,10 @@
         // skips all test cases if FUSE is not active.
         assumeTrue(getBoolean("persist.sys.fuse", false));
 
-        pollForExternalStorageState();
-        getExternalFilesDir().mkdirs();
+        if (!getContext().getPackageManager().isInstantApp()) {
+            pollForExternalStorageState();
+            getExternalFilesDir().mkdirs();
+        }
     }
 
     /**
@@ -1577,8 +1580,7 @@
                     THIS_PACKAGE_NAME, TEST_APP_A.getPackageName()));
             final File otherAppExternalDataFile = new File(otherAppExternalDataDir,
                     NONMEDIA_FILE_NAME);
-            assertThat(createFileAs(TEST_APP_A, otherAppExternalDataFile.getAbsolutePath()))
-                    .isTrue();
+            assertCreateFilesAs(TEST_APP_A, otherAppExternalDataFile);
 
             // File Manager app gets global access with MANAGE_EXTERNAL_STORAGE permission, however,
             // file manager app doesn't have access to other app's external files directory
@@ -1598,6 +1600,41 @@
     }
 
     /**
+     * Tests that an instant app can't access external storage.
+     */
+    @Test
+    @AppModeInstant
+    public void testInstantAppsCantAccessExternalStorage() throws Exception {
+        assumeTrue("This test requires that the test runs as an Instant app",
+                getContext().getPackageManager().isInstantApp());
+        assertThat(getContext().getPackageManager().isInstantApp()).isTrue();
+
+        // Can't read ExternalStorageDir
+        assertThat(getExternalStorageDir().list()).isNull();
+
+        // Can't create a top-level direcotry
+        final File topLevelDir = new File(getExternalStorageDir(), TEST_DIRECTORY_NAME);
+        assertThat(topLevelDir.mkdir()).isFalse();
+
+        // Can't create file under root dir
+        final File newTxtFile = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
+        assertThrows(IOException.class,
+                () -> { newTxtFile.createNewFile(); });
+
+        // Can't create music file under /MUSIC
+        final File newMusicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
+        assertThrows(IOException.class,
+                () -> { newMusicFile.createNewFile(); });
+
+        // getExternalFilesDir() is not null
+        assertThat(getExternalFilesDir()).isNotNull();
+
+        // Can't read/write app specific dir
+        assertThat(getExternalFilesDir().list()).isNull();
+        assertThat(getExternalFilesDir().exists()).isFalse();
+    }
+
+    /**
      * Test that apps can create and delete hidden file.
      */
     @Test