Update permission checks for dual permission mode.

Callers must hold both the old and new permissions, so that we can
handle obscure cases like when an app targets Q but was installed on
a device that was originally running on P before being upgraded to Q.

Bug: 126785920, 126788266
Test: atest android.appsecurity.cts.ExternalStorageHostTest
Exempt-From-Owner-Approval: trivial tests
Change-Id: I7e474738053c0d7fb9e1a9a77911927558f9f051
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index fe0557a..2456c24 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -62,9 +62,13 @@
     private static final String MULTIUSER_APK = "CtsMultiUserStorageApp.apk";
     private static final String MULTIUSER_PKG = "com.android.cts.multiuserstorageapp";
     private static final String MULTIUSER_CLASS = MULTIUSER_PKG + ".MultiUserStorageTest";
+
     private static final String MEDIA_APK = "CtsMediaStorageApp.apk";
     private static final String MEDIA_PKG = "com.android.cts.mediastorageapp";
     private static final String MEDIA_CLASS = MEDIA_PKG + ".MediaStorageTest";
+    private static final String MEDIA28_APK = "CtsMediaStorageApp28.apk";
+    private static final String MEDIA28_PKG = "com.android.cts.mediastorageapp28";
+    private static final String MEDIA28_CLASS = MEDIA28_PKG + ".MediaStorageTest";
 
     private static final String PKG_A = "com.android.cts.storageapp_a";
     private static final String PKG_B = "com.android.cts.storageapp_b";
@@ -72,6 +76,8 @@
     private static final String APK_B = "CtsStorageAppB.apk";
     private static final String CLASS = "com.android.cts.storageapp.StorageTest";
 
+    private static final String PERM_READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
+    private static final String PERM_WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
     private static final String PERM_READ_MEDIA_AUDIO = "android.permission.READ_MEDIA_AUDIO";
     private static final String PERM_READ_MEDIA_VIDEO = "android.permission.READ_MEDIA_VIDEO";
     private static final String PERM_READ_MEDIA_IMAGES = "android.permission.READ_MEDIA_IMAGES";
@@ -545,6 +551,23 @@
     }
 
     @Test
+    public void testMediaNone28() throws Exception {
+        // STOPSHIP: remove this once isolated storage is always enabled
+        Assume.assumeTrue(hasIsolatedStorage());
+
+        installPackage(MEDIA28_APK);
+        for (int user : mUsers) {
+            updatePermissions(MEDIA28_PKG, user, new String[] {
+                    PERM_READ_EXTERNAL_STORAGE,
+                    PERM_WRITE_EXTERNAL_STORAGE,
+            }, false);
+            updateRole(MEDIA28_PKG, user, ROLE_GALLERY, false);
+
+            runDeviceTests(MEDIA28_PKG, MEDIA_CLASS, "testMediaNone", user);
+        }
+    }
+
+    @Test
     public void testMediaRead() throws Exception {
         // STOPSHIP: remove this once isolated storage is always enabled
         Assume.assumeTrue(hasIsolatedStorage());
@@ -563,6 +586,23 @@
     }
 
     @Test
+    public void testMediaRead28() throws Exception {
+        // STOPSHIP: remove this once isolated storage is always enabled
+        Assume.assumeTrue(hasIsolatedStorage());
+
+        installPackage(MEDIA28_APK);
+        for (int user : mUsers) {
+            updatePermissions(MEDIA28_PKG, user, new String[] {
+                    PERM_READ_EXTERNAL_STORAGE,
+                    PERM_WRITE_EXTERNAL_STORAGE,
+            }, true);
+            updateRole(MEDIA28_PKG, user, ROLE_GALLERY, false);
+
+            runDeviceTests(MEDIA28_PKG, MEDIA_CLASS, "testMediaRead", user);
+        }
+    }
+
+    @Test
     public void testMediaWrite() throws Exception {
         // STOPSHIP: remove this once isolated storage is always enabled
         Assume.assumeTrue(hasIsolatedStorage());
@@ -581,6 +621,23 @@
     }
 
     @Test
+    public void testMediaWrite28() throws Exception {
+        // STOPSHIP: remove this once isolated storage is always enabled
+        Assume.assumeTrue(hasIsolatedStorage());
+
+        installPackage(MEDIA28_APK);
+        for (int user : mUsers) {
+            updatePermissions(MEDIA28_PKG, user, new String[] {
+                    PERM_READ_EXTERNAL_STORAGE,
+                    PERM_WRITE_EXTERNAL_STORAGE,
+            }, true);
+            updateRole(MEDIA28_PKG, user, ROLE_GALLERY, true);
+
+            runDeviceTests(MEDIA28_PKG, MEDIA_CLASS, "testMediaWrite", user);
+        }
+    }
+
+    @Test
     public void testMediaEscalation() throws Exception {
         // STOPSHIP: remove this once isolated storage is always enabled
         Assume.assumeTrue(hasIsolatedStorage());
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.mk
index 569520f..92da12d 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/Android.mk
@@ -26,6 +26,7 @@
 LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
 
 LOCAL_PACKAGE_NAME := CtsMediaStorageApp
 
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest.xml
index f1c0239..41d07eb 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/AndroidManifest.xml
@@ -34,8 +34,6 @@
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.mediastorageapp" />
 
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
     <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
     <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
     <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/res/raw/testmp3.mp3 b/hostsidetests/appsecurity/test-apps/MediaStorageApp/assets/testmp3.mp3
similarity index 100%
rename from hostsidetests/appsecurity/test-apps/MediaStorageApp/res/raw/testmp3.mp3
rename to hostsidetests/appsecurity/test-apps/MediaStorageApp/assets/testmp3.mp3
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
index a546893..fdb46c2 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -44,9 +44,6 @@
 import com.android.cts.mediastorageapp.MediaStoreUtils.PendingParams;
 import com.android.cts.mediastorageapp.MediaStoreUtils.PendingSession;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -57,6 +54,9 @@
 import java.io.OutputStream;
 import java.util.HashSet;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
 @RunWith(AndroidJUnit4.class)
 public class MediaStorageTest {
     private Context mContext;
@@ -254,7 +254,7 @@
                 collectionUri, displayName, "audio/mpeg");
         final Uri pendingUri = MediaStoreUtils.createPending(context, params);
         try (PendingSession session = MediaStoreUtils.openPending(context, pendingUri)) {
-            try (InputStream in = context.getResources().openRawResource(R.raw.testmp3);
+            try (InputStream in = context.getResources().getAssets().open("testmp3.mp3");
                     OutputStream out = session.openOutputStream()) {
                 FileUtils.copy(in, out);
             }
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp28/Android.mk b/hostsidetests/appsecurity/test-apps/MediaStorageApp28/Android.mk
new file mode 100644
index 0000000..97f8a2c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp28/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2018 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 := test_current
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util-axt \
+    androidx.test.rules \
+    ub-uiautomator
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../MediaStorageApp/src/)
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/../MediaStorageApp/assets
+
+LOCAL_PACKAGE_NAME := CtsMediaStorageApp28
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp28/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MediaStorageApp28/AndroidManifest.xml
new file mode 100644
index 0000000..77acd3a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp28/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+       package="com.android.cts.mediastorageapp28">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name=".StubActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.APP_GALLERY" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".GetResultActivity" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.mediastorageapp28" />
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <uses-sdk
+        android:minSdkVersion="28"
+        android:targetSdkVersion="28" />
+
+</manifest>