Tests setting default ringers to external storage.

In an app with WRITE_EXTERNAL_STORAGE, add an mp3 from external storage
to the media provider then set the default ringtone URI's to that entry.
In an app without READ_EXTERNAL_STORAGE, play the changed default URI's
as owner and additional users.

Test: cts-tradefed run cts --module CtsAppSecurityHostTestCases -t android.appsecurity.cts.ExternalStorageHostTest#testExternalStorageReadDefaultUris
Bug: 36571322
Bug: 30572189
Change-Id: I6818c751b21a26f3d25fbafb37c9e37688eb33b8
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index b2f4685..6ee51ae 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -280,6 +280,49 @@
         }
     }
 
+    /** Verify that app without READ_EXTERNAL can play default URIs in external storage. */
+    public void testExternalStorageReadDefaultUris() throws Exception {
+        final int[] users = createUsersForTest();
+        try {
+            wipePrimaryExternalStorage();
+
+            getDevice().uninstallPackage(NONE_PKG);
+            getDevice().uninstallPackage(WRITE_PKG);
+            final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+
+            assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
+            enableWriteSettings(WRITE_PKG);
+            runDeviceTests(
+                    WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testChangeDefaultUris", users[0]);
+
+            assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
+
+            for (int user : users) {
+                runDeviceTests(
+                        NONE_PKG, NONE_PKG + ".ReadDefaultUris", "testPlayDefaultUris", user);
+            }
+        } finally {
+            // Make sure the provider and uris are reset on failure.
+            runDeviceTests(
+                    WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testResetDefaultUris", users[0]);
+            getDevice().uninstallPackage(NONE_PKG);
+            getDevice().uninstallPackage(WRITE_PKG);
+            removeUsersForTest(users);
+        }
+    }
+
+    private void enableWriteSettings(String packageName) throws DeviceNotAvailableException {
+        StringBuilder cmd = new StringBuilder();
+        cmd.append("appops set ");
+        cmd.append(packageName);
+        cmd.append(" android:write_settings allow");
+        getDevice().executeShellCommand(cmd.toString());
+        try {
+            Thread.sleep(2200);
+        } catch (InterruptedException e) {
+        }
+    }
+
     private void wipePrimaryExternalStorage() throws DeviceNotAvailableException {
         getDevice().executeShellCommand("rm -rf /sdcard/Android");
         getDevice().executeShellCommand("rm -rf /sdcard/DCIM");
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ReadDefaultUris.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ReadDefaultUris.java
new file mode 100644
index 0000000..37476f7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ReadDefaultUris.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package com.android.cts.externalstorageapp;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+
+/** Verify system default URI's can be read without READ_EXTERNAL_STORAGE permission. */
+public class ReadDefaultUris extends AndroidTestCase {
+
+    private AudioManager mAudioManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+    }
+
+    public void testPlayDefaultUris() throws Exception {
+        final long timeToPlayMs = 1000;
+        playUri(
+                Settings.System.DEFAULT_NOTIFICATION_URI,
+                timeToPlayMs,
+                AudioAttributes.USAGE_NOTIFICATION,
+                AudioAttributes.CONTENT_TYPE_SONIFICATION);
+        playUri(
+                Settings.System.DEFAULT_RINGTONE_URI,
+                timeToPlayMs,
+                AudioAttributes.USAGE_NOTIFICATION_RINGTONE,
+                AudioAttributes.CONTENT_TYPE_SONIFICATION);
+        playUri(
+                Settings.System.DEFAULT_ALARM_ALERT_URI,
+                timeToPlayMs,
+                AudioAttributes.USAGE_ALARM,
+                AudioAttributes.CONTENT_TYPE_SONIFICATION);
+    }
+
+    private void playUri(final Uri uri, long timeToPlayMs, int usage, int contentType)
+            throws Exception {
+        MediaPlayer mp = new MediaPlayer();
+        assertNotNull(mp);
+        mp.setDataSource(mContext, uri);
+        mp.setAudioAttributes(
+                new AudioAttributes.Builder().setUsage(usage).setContentType(contentType).build());
+        mp.prepare();
+        mp.start();
+        Thread.sleep(timeToPlayMs);
+        mp.stop();
+        mp.release();
+        Thread.sleep(timeToPlayMs);
+        assertFalse(mAudioManager.isMusicActive());
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
index 0729b66..61d2493 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
@@ -18,11 +18,14 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test compatibility-device-util
+#ctsdeviceutil
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
 
+LOCAL_JAVA_RESOURCE_DIRS := $(LOCAL_PATH)/res
+
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts
 
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/res/raw/ringer.mp3 b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/res/raw/ringer.mp3
new file mode 100644
index 0000000..aa052e7
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/res/raw/ringer.mp3
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/ChangeDefaultUris.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/ChangeDefaultUris.java
new file mode 100644
index 0000000..7fa39b4
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/ChangeDefaultUris.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package com.android.cts.writeexternalstorageapp;
+
+import android.content.ContentValues;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.test.AndroidTestCase;
+import com.android.compatibility.common.util.FileCopyHelper;
+import java.io.File;
+
+/** Sets up providers and notifications using external storage. */
+public class ChangeDefaultUris extends AndroidTestCase {
+
+    /** Unique title for provider insert and delete. */
+    private static final String RINGER_TITLE = "CTS ringer title";
+
+    public void testChangeDefaultUris() throws Exception {
+        File mediaFile =
+                new File(
+                        Environment.getExternalStorageDirectory(),
+                        "ringer" + System.currentTimeMillis() + ".mp3");
+        FileCopyHelper copier = new FileCopyHelper(mContext);
+        copier.copyToExternalStorage(R.raw.ringer, mediaFile);
+
+        ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.DATA, mediaFile.getPath());
+        values.put(MediaStore.MediaColumns.TITLE, RINGER_TITLE);
+        values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3");
+        values.put(MediaStore.Audio.AudioColumns.ARTIST, "CTS ringer artist");
+        values.put(MediaStore.Audio.AudioColumns.IS_RINGTONE, true);
+        values.put(MediaStore.Audio.AudioColumns.IS_NOTIFICATION, true);
+        values.put(MediaStore.Audio.AudioColumns.IS_ALARM, true);
+        values.put(MediaStore.Audio.AudioColumns.IS_MUSIC, false);
+
+        Uri uri =
+                mContext.getContentResolver()
+                        .insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
+
+        RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE, uri);
+        RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_ALARM, uri);
+        RingtoneManager.setActualDefaultRingtoneUri(
+                mContext, RingtoneManager.TYPE_NOTIFICATION, uri);
+    }
+
+    /** Resets and cleans up to a valid state. This method must not fail. */
+    public void testResetDefaultUris() {
+        mContext.getContentResolver()
+                .delete(
+                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                        MediaStore.MediaColumns.TITLE + " = ?",
+                        new String[] {RINGER_TITLE});
+
+        Uri uri = RingtoneManager.getValidRingtoneUri(mContext);
+        RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE, uri);
+        RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_ALARM, uri);
+        RingtoneManager.setActualDefaultRingtoneUri(
+                mContext, RingtoneManager.TYPE_NOTIFICATION, uri);
+    }
+}