1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.media.audio.cts;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import android.app.Instrumentation;
25 import android.content.ContentProvider;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.pm.PackageManager;
29 import android.content.res.AssetFileDescriptor;
30 import android.database.Cursor;
31 import android.media.AudioManager;
32 import android.media.Ringtone;
33 import android.media.RingtoneManager;
34 import android.media.cts.Utils;
35 import android.net.Uri;
36 import android.os.ConditionVariable;
37 import android.platform.test.annotations.AppModeFull;
38 import android.provider.Settings;
39 
40 import androidx.test.core.app.ActivityScenario;
41 import androidx.test.ext.junit.runners.AndroidJUnit4;
42 import androidx.test.platform.app.InstrumentationRegistry;
43 
44 import org.junit.After;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.io.IOException;
50 
51 @AppModeFull(reason = "TODO: evaluate and port to instant")
52 @RunWith(AndroidJUnit4.class)
53 public class RingtoneManagerTest {
54 
55     private static final String PKG = "android.media.audio.cts";
56     private static final String TAG = "RingtoneManagerTest";
57 
58     private RingtonePickerActivity mActivity;
59     private ActivityScenario<RingtonePickerActivity> mActivityScenario;
60     private Instrumentation mInstrumentation;
61     private Context mContext;
62     private RingtoneManager mRingtoneManager;
63     private AudioManager mAudioManager;
64     private int mOriginalRingerMode;
65     private Uri mDefaultUri;
66 
67     @Before
setUp()68     public void setUp() throws Exception {
69         mActivityScenario = ActivityScenario.launch(RingtonePickerActivity.class);
70         ConditionVariable activityReferenceObtained = new ConditionVariable();
71         mActivityScenario.onActivity(activity -> {
72             mActivity = activity;
73             activityReferenceObtained.open();
74         });
75         activityReferenceObtained.block(10000);
76         assertNotNull("Failed to acquire activity reference.", mActivity);
77 
78         mInstrumentation = InstrumentationRegistry.getInstrumentation();
79         mContext = mInstrumentation.getTargetContext();
80         mInstrumentation.waitForIdleSync();
81 
82         Utils.enableAppOps(mContext.getPackageName(), "android:write_settings", mInstrumentation);
83         mRingtoneManager = new RingtoneManager(mActivity);
84         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
85         // backup ringer settings
86         mDefaultUri = ContentProvider.maybeAddUserId(
87                 RingtoneManager.getActualDefaultRingtoneUri(
88                         mContext, RingtoneManager.TYPE_RINGTONE), mContext.getUserId());
89 
90         mOriginalRingerMode = mAudioManager.getRingerMode();
91         if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
92             try {
93                 Utils.toggleNotificationPolicyAccess(
94                         mContext.getPackageName(), mInstrumentation, true);
95                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
96             } finally {
97                 Utils.toggleNotificationPolicyAccess(
98                         mContext.getPackageName(), mInstrumentation, false);
99             }
100         }
101     }
102 
103     @After
tearDown()104     public void tearDown() throws Exception {
105         try {
106             Utils.toggleNotificationPolicyAccess(
107                     mContext.getPackageName(), mInstrumentation, true);
108             // restore original ringer settings
109             if (mAudioManager != null) {
110                 mAudioManager.setRingerMode(mOriginalRingerMode);
111             }
112         } finally {
113             Utils.toggleNotificationPolicyAccess(
114                     mContext.getPackageName(), mInstrumentation, false);
115         }
116         RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE,
117                 mDefaultUri);
118         Utils.disableAppOps(mContext.getPackageName(), "android:write_settings", mInstrumentation);
119     }
120 
isSupportedDevice()121     private boolean isSupportedDevice() {
122         final PackageManager pm = mContext.getPackageManager();
123         return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)
124                 && !pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY);
125     }
126 
127     @Test
testConstructors()128     public void testConstructors() {
129         if (!isSupportedDevice()) return;
130 
131         new RingtoneManager(mActivity);
132         new RingtoneManager(mContext);
133     }
134 
135     @Test
testAccessMethods()136     public void testAccessMethods() {
137         if (!isSupportedDevice()) return;
138 
139         Cursor c = mRingtoneManager.getCursor();
140         assertTrue("Must have at least one ring tone available", c.getCount() > 0);
141 
142         assertNotNull(mRingtoneManager.getRingtone(0));
143         assertNotNull(RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_RINGTONE_URI));
144         int expectedPosition = 0;
145         Uri uri = mRingtoneManager.getRingtoneUri(expectedPosition);
146         assertEquals(expectedPosition, mRingtoneManager.getRingtonePosition(uri));
147         assertNotNull(RingtoneManager.getValidRingtoneUri(mContext));
148         uri = ContentProvider.maybeAddUserId(uri, mContext.getUserId());
149 
150         RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE, uri);
151 
152         Uri actualDefaultRingtoneUri = ContentProvider.maybeAddUserId(
153                 RingtoneManager.getActualDefaultRingtoneUri(
154                         mContext, RingtoneManager.TYPE_RINGTONE), mContext.getUserId());
155 
156         assertEquals(uri, actualDefaultRingtoneUri);
157 
158         try (AssetFileDescriptor afd = RingtoneManager.openDefaultRingtoneUri(
159                 mActivity, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE))) {
160             assertNotNull(afd);
161         } catch (IOException e) {
162             fail(e.getMessage());
163         }
164 
165         Uri bogus = Uri.parse("content://a_bogus_uri");
166         RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE, bogus);
167         // not testing the matching getter after setting a bogus URI as ringtone
168         //assertNotEquals(bogus, RingtoneManager.getActualDefaultRingtoneUri(mContext,
169         //        RingtoneManager.TYPE_RINGTONE));
170 
171         assertEquals(Settings.System.DEFAULT_RINGTONE_URI,
172                 RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE));
173         assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI,
174                 RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
175         assertEquals(RingtoneManager.TYPE_RINGTONE,
176                 RingtoneManager.getDefaultType(Settings.System.DEFAULT_RINGTONE_URI));
177         assertEquals(RingtoneManager.TYPE_NOTIFICATION,
178                 RingtoneManager.getDefaultType(Settings.System.DEFAULT_NOTIFICATION_URI));
179         assertTrue(RingtoneManager.isDefault(Settings.System.DEFAULT_RINGTONE_URI));
180     }
181 
182     @Test
testSetType()183     public void testSetType() {
184         if (!isSupportedDevice()) return;
185 
186         mRingtoneManager.setType(RingtoneManager.TYPE_ALARM);
187         assertEquals(AudioManager.STREAM_ALARM, mRingtoneManager.inferStreamType());
188         Cursor c = mRingtoneManager.getCursor();
189         assertTrue("Must have at least one alarm tone available", c.getCount() > 0);
190         Ringtone r = mRingtoneManager.getRingtone(0);
191         assertEquals(RingtoneManager.TYPE_ALARM, r.getStreamType());
192     }
193 
194     @Test
testStopPreviousRingtone()195     public void testStopPreviousRingtone() {
196         if (!isSupportedDevice()) return;
197 
198         Cursor c = mRingtoneManager.getCursor();
199         assertTrue("Must have at least one ring tone available", c.getCount() > 0);
200 
201         mRingtoneManager.setStopPreviousRingtone(true);
202         assertTrue(mRingtoneManager.getStopPreviousRingtone());
203         Uri uri = Uri.parse("android.resource://" + PKG + "/" + R.raw.john_cage);
204         Ringtone ringtone = RingtoneManager.getRingtone(mContext, uri);
205         ringtone.play();
206         assertTrue(ringtone.isPlaying());
207         ringtone.stop();
208         assertFalse(ringtone.isPlaying());
209         Ringtone newRingtone = mRingtoneManager.getRingtone(0);
210         assertFalse(ringtone.isPlaying());
211         newRingtone.play();
212         assertTrue(newRingtone.isPlaying());
213         mRingtoneManager.stopPreviousRingtone();
214         assertFalse(newRingtone.isPlaying());
215     }
216 
217     @Test
testQuery()218     public void testQuery() {
219         if (!isSupportedDevice()) return;
220 
221         final Cursor c = mRingtoneManager.getCursor();
222         assertTrue(c.moveToFirst());
223         assertTrue(c.getInt(RingtoneManager.ID_COLUMN_INDEX) >= 0);
224         assertTrue(c.getString(RingtoneManager.TITLE_COLUMN_INDEX) != null);
225         assertTrue(c.getString(RingtoneManager.URI_COLUMN_INDEX),
226                 c.getString(RingtoneManager.URI_COLUMN_INDEX).startsWith("content://"));
227     }
228 
229     @Test
testHasHapticChannels()230     public void testHasHapticChannels() {
231         if (!isSupportedDevice()) return;
232 
233         Cursor c = mRingtoneManager.getCursor();
234         assertTrue("Must have at lease one ringtone available", c.getCount() > 0);
235         mRingtoneManager.hasHapticChannels(0);
236 
237         final String uriPrefix = ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
238                 mContext.getPackageName() + "/raw/";
239         assertTrue(RingtoneManager.hasHapticChannels(Uri.parse(uriPrefix + "a_4_haptic")));
240         assertFalse(RingtoneManager.hasHapticChannels(Uri.parse(uriPrefix + "a_4")));
241 
242         assertTrue(RingtoneManager.hasHapticChannels(
243                 mContext, Uri.parse(uriPrefix + "a_4_haptic")));
244         assertFalse(RingtoneManager.hasHapticChannels(mContext, Uri.parse(uriPrefix + "a_4")));
245     }
246 }
247