1 /*
2  * Copyright (C) 2017 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 
17 package com.android.providers.settings;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static junit.framework.Assert.assertEquals;
22 
23 import static org.mockito.ArgumentMatchers.eq;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.verifyNoMoreInteractions;
29 import static org.mockito.Mockito.when;
30 
31 import android.content.ContentProvider;
32 import android.content.ContentValues;
33 import android.content.Context;
34 import android.content.pm.ApplicationInfo;
35 import android.content.res.AssetFileDescriptor;
36 import android.content.res.Resources;
37 import android.database.Cursor;
38 import android.database.MatrixCursor;
39 import android.media.AudioManager;
40 import android.net.Uri;
41 import android.os.Bundle;
42 import android.os.LocaleList;
43 import android.provider.BaseColumns;
44 import android.provider.MediaStore;
45 import android.provider.Settings;
46 import android.telephony.TelephonyManager;
47 import android.test.mock.MockContentProvider;
48 import android.test.mock.MockContentResolver;
49 import android.util.ArrayMap;
50 
51 import androidx.test.runner.AndroidJUnit4;
52 
53 import com.android.internal.R;
54 
55 import org.junit.After;
56 import org.junit.Before;
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 import org.mockito.Mock;
60 import org.mockito.MockitoAnnotations;
61 
62 /**
63  * Tests for the SettingsHelperTest
64  */
65 @RunWith(AndroidJUnit4.class)
66 public class SettingsHelperTest {
67     private static final String SETTING_KEY = "setting_key";
68     private static final String SETTING_VALUE = "setting_value";
69     private static final String SETTING_REAL_VALUE = "setting_real_value";
70 
71     private static final String DEFAULT_RINGTONE_VALUE =
72             "content://media/internal/audio/media/10?title=DefaultRingtone&canonical=1";
73     private static final String DEFAULT_NOTIFICATION_VALUE =
74             "content://media/internal/audio/media/20?title=DefaultNotification&canonical=1";
75     private static final String DEFAULT_ALARM_VALUE =
76             "content://media/internal/audio/media/30?title=DefaultAlarm&canonical=1";
77 
78     private SettingsHelper mSettingsHelper;
79 
80     @Mock private Context mContext;
81     @Mock private Resources mResources;
82     @Mock private AudioManager mAudioManager;
83     @Mock private TelephonyManager mTelephonyManager;
84 
85     @Mock private MockContentResolver mContentResolver;
86     private MockSettingsProvider mSettingsProvider;
87 
88     @Before
setUp()89     public void setUp() {
90         MockitoAnnotations.initMocks(this);
91         when(mContext.getSystemService(eq(Context.AUDIO_SERVICE))).thenReturn(mAudioManager);
92         when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))).thenReturn(
93                 mTelephonyManager);
94         when(mContext.getResources()).thenReturn(mResources);
95         when(mContext.getApplicationContext()).thenReturn(mContext);
96         when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
97 
98         mSettingsHelper = spy(new SettingsHelper(mContext));
99         mContentResolver = spy(new MockContentResolver());
100         when(mContext.getContentResolver()).thenReturn(mContentResolver);
101         mSettingsProvider = new MockSettingsProvider(mContext);
102         mContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
103     }
104 
105     @After
tearDown()106     public void tearDown() {
107         Settings.Global.putString(mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS,
108                 null);
109         Settings.Global.putString(mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
110                 null);
111     }
112 
113     @Test
testOnBackupValue_settingReplaced_returnsRealValue()114     public void testOnBackupValue_settingReplaced_returnsRealValue() {
115         when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(true);
116         doReturn(SETTING_REAL_VALUE).when(mSettingsHelper).getRealValueForSystemSetting(
117                 eq(SETTING_KEY));
118 
119         assertEquals(SETTING_REAL_VALUE, mSettingsHelper.onBackupValue(SETTING_KEY, SETTING_VALUE));
120     }
121 
122     @Test
testGetRealValue_settingNotReplaced_returnsSameValue()123     public void testGetRealValue_settingNotReplaced_returnsSameValue() {
124         when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(false);
125 
126         assertEquals(SETTING_VALUE, mSettingsHelper.onBackupValue(SETTING_KEY, SETTING_VALUE));
127     }
128 
129     @Test
testRestoreValue_settingReplaced_doesNotRestore()130     public void testRestoreValue_settingReplaced_doesNotRestore() {
131         when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(true);
132         mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
133                 SETTING_KEY, SETTING_VALUE, /* restoredFromSdkInt */ 0);
134 
135         // The only time of interaction happened during setUp()
136         verify(mContentResolver, times(1))
137                 .addProvider(Settings.AUTHORITY, mSettingsProvider);
138 
139         verifyNoMoreInteractions(mContentResolver);
140     }
141 
142     @Test
testRestoreValue_lppForAssistantEnabled_updatesValue()143     public void testRestoreValue_lppForAssistantEnabled_updatesValue() {
144         when(mResources.getBoolean(
145                 R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
146                 true);
147 
148         mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
149                 Settings.Global.POWER_BUTTON_LONG_PRESS, "5", 0);
150 
151         assertThat(Settings.Global.getInt(
152                 mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(5);
153         assertThat(Settings.Global.getInt(
154                 mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(2);
155     }
156 
157     @Test
testRestoreValue_lppForAssistantNotEnabled_updatesValueToDefaultConfig()158     public void testRestoreValue_lppForAssistantNotEnabled_updatesValueToDefaultConfig() {
159         when(mResources.getBoolean(
160                 R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
161                 true);
162 
163         when(mResources.getInteger(
164                 R.integer.config_longPressOnPowerBehavior)).thenReturn(
165                 1);
166         when(mResources.getInteger(
167                 R.integer.config_keyChordPowerVolumeUp)).thenReturn(
168                 1);
169 
170         mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
171                 Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0);
172 
173         assertThat(Settings.Global.getInt(
174                 mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(1);
175         assertThat(Settings.Global.getInt(
176                 mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(1);
177     }
178 
179     @Test
testRestoreValue_lppForAssistantNotEnabledDefaultConfig_updatesValue()180     public void testRestoreValue_lppForAssistantNotEnabledDefaultConfig_updatesValue() {
181         when(mResources.getBoolean(
182                 R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
183                 true);
184 
185         when(mResources.getInteger(
186                 R.integer.config_longPressOnPowerBehavior)).thenReturn(
187                 5);
188         when(mResources.getInteger(
189                 R.integer.config_keyChordPowerVolumeUp)).thenReturn(
190                 1);
191 
192         mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
193                 Settings.Global.POWER_BUTTON_LONG_PRESS, "2", 0);
194 
195         assertThat(Settings.Global.getInt(
196                 mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(1);
197         assertThat(Settings.Global.getInt(
198                 mContentResolver, Settings.Global.KEY_CHORD_POWER_VOLUME_UP, -1)).isEqualTo(1);
199     }
200 
201     @Test
testRestoreValue_lppForAssistantNotAvailable_doesNotRestore()202     public void testRestoreValue_lppForAssistantNotAvailable_doesNotRestore() {
203         when(mResources.getBoolean(R.bool.config_longPressOnPowerForAssistantSettingAvailable))
204                 .thenReturn(false);
205 
206         mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
207                 Settings.Global.POWER_BUTTON_LONG_PRESS, "500", 0);
208 
209         assertThat((Settings.Global.getInt(
210                 mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))).isEqualTo(-1);
211     }
212 
213 
214     @Test
testRestoreValue_lppForAssistantInvalid_doesNotRestore()215     public void testRestoreValue_lppForAssistantInvalid_doesNotRestore() {
216         when(mResources.getBoolean(
217                 R.bool.config_longPressOnPowerForAssistantSettingAvailable)).thenReturn(
218                 false);
219 
220         mSettingsHelper.restoreValue(mContext, mContentResolver, new ContentValues(), Uri.EMPTY,
221                 Settings.Global.POWER_BUTTON_LONG_PRESS, "trees", 0);
222 
223         assertThat((Settings.Global.getInt(
224                 mContentResolver, Settings.Global.POWER_BUTTON_LONG_PRESS, -1))).isEqualTo(-1);
225     }
226 
227     @Test
testResolveLocales()228     public void testResolveLocales() throws Exception {
229         // Empty string from backup server
230         assertEquals(LocaleList.forLanguageTags("en-US"),
231                 SettingsHelper.resolveLocales(
232                         LocaleList.forLanguageTags(""),  // restore
233                         LocaleList.forLanguageTags("en-US"),  // current
234                         new String[] { "en-US" }));  // supported
235 
236         // Same as current settings
237         assertEquals(LocaleList.forLanguageTags("en-US"),
238                 SettingsHelper.resolveLocales(
239                         LocaleList.forLanguageTags("en-US"),  // restore
240                         LocaleList.forLanguageTags("en-US"),  // current
241                         new String[] { "en-US" }));  // supported
242 
243         assertEquals(LocaleList.forLanguageTags("en-US,ja-JP"),
244                 SettingsHelper.resolveLocales(
245                         LocaleList.forLanguageTags("en-US,ja-JP"),  // restore
246                         LocaleList.forLanguageTags("en-US,ja-JP"),  // current
247                         new String[] { "en-US", "ja-JP" }));  // supported
248 
249         // Current locale must be kept at the first place.
250         assertEquals(LocaleList.forLanguageTags("ja-JP,en-US"),
251                 SettingsHelper.resolveLocales(
252                         LocaleList.forLanguageTags("en-US"),  // restore
253                         LocaleList.forLanguageTags("ja-JP"),  // current
254                         new String[] { "en-US", "ja-JP" }));  // supported
255 
256         assertEquals(LocaleList.forLanguageTags("ja-JP,ko-KR,en-US"),
257                 SettingsHelper.resolveLocales(
258                         LocaleList.forLanguageTags("en-US"),  // restore
259                         LocaleList.forLanguageTags("ja-JP,ko-KR"),  // current
260                         new String[] { "en-US", "ja-JP", "ko-KR" }));  // supported
261 
262         assertEquals(LocaleList.forLanguageTags("ja-JP,en-US,ko-KR"),
263                 SettingsHelper.resolveLocales(
264                         LocaleList.forLanguageTags("en-US,ko-KR"),  // restore
265                         LocaleList.forLanguageTags("ja-JP"),  // current
266                         new String[] { "en-US", "ja-JP", "ko-KR" }));  // supported
267 
268         // Duplicated entries must be removed.
269         assertEquals(LocaleList.forLanguageTags("ja-JP,ko-KR,en-US"),
270                 SettingsHelper.resolveLocales(
271                         LocaleList.forLanguageTags("en-US,ko-KR"),  // restore
272                         LocaleList.forLanguageTags("ja-JP,ko-KR"),  // current
273                         new String[] { "en-US", "ja-JP", "ko-KR" }));  // supported
274 
275         // Drop unsupported locales.
276         assertEquals(LocaleList.forLanguageTags("en-US"),
277                 SettingsHelper.resolveLocales(
278                         LocaleList.forLanguageTags("en-US,zh-CN"),  // restore
279                         LocaleList.forLanguageTags("en-US"),  // current
280                         new String[] { "en-US" }));  // supported
281 
282         // Comparison happens on fully-expanded locale.
283         assertEquals(LocaleList.forLanguageTags("en-US,sr-Latn-SR,sr-Cryl-SR"),
284                 SettingsHelper.resolveLocales(
285                         LocaleList.forLanguageTags("sr-Cryl-SR"),  // restore
286                         LocaleList.forLanguageTags("en-US,sr-Latn-SR"),  // current
287                         new String[] { "en-US", "sr-Latn-SR", "sr-Cryl-SR" }));  // supported
288 
289         assertEquals(LocaleList.forLanguageTags("en-US"),
290                 SettingsHelper.resolveLocales(
291                         LocaleList.forLanguageTags("kk-Cryl-KZ"),  // restore
292                         LocaleList.forLanguageTags("en-US"),  // current
293                         new String[] { "en-US", "kk-Latn-KZ" }));  // supported
294 
295         assertEquals(LocaleList.forLanguageTags("en-US,kk-Cryl-KZ"),
296                 SettingsHelper.resolveLocales(
297                         LocaleList.forLanguageTags("kk-Cryl-KZ"),  // restore
298                         LocaleList.forLanguageTags("en-US"),  // current
299                         new String[] { "en-US", "kk-Cryl-KZ" }));  // supported
300 
301         assertEquals(LocaleList.forLanguageTags("en-US,zh-CN"),
302                 SettingsHelper.resolveLocales(
303                         LocaleList.forLanguageTags("zh-Hans-CN"),  // restore
304                         LocaleList.forLanguageTags("en-US"),  // current
305                         new String[] { "en-US", "zh-CN" }));  // supported
306 
307         assertEquals(LocaleList.forLanguageTags("en-US,zh-Hans-CN"),
308                 SettingsHelper.resolveLocales(
309                         LocaleList.forLanguageTags("zh-CN"),  // restore
310                         LocaleList.forLanguageTags("en-US"),  // current
311                         new String[] { "en-US", "zh-Hans-CN" }));  // supported
312 
313         // Old language code should be updated.
314         assertEquals(LocaleList.forLanguageTags("en-US,he-IL,id-ID,yi"),
315                 SettingsHelper.resolveLocales(
316                         LocaleList.forLanguageTags("iw-IL,in-ID,ji"),  // restore
317                         LocaleList.forLanguageTags("en-US"),  // current
318                         new String[] { "he-IL", "id-ID", "yi" }));  // supported
319 
320         // No matter the current locale has "nu" extension or not, if the restored locale has fw
321         // (first day of week) or mu(temperature unit) extension, we should restore fw or mu
322         // extensions as well and append these to restore and current locales.
323         assertEquals(LocaleList.forLanguageTags(
324                 "en-US-u-fw-mon-mu-celsius,zh-Hant-TW-u-fw-mon-mu-celsius"),
325                 SettingsHelper.resolveLocales(
326                         LocaleList.forLanguageTags("zh-Hant-TW-u-fw-mon-mu-celsius"),  // restore
327                         LocaleList.forLanguageTags("en-US"),  // current
328                         new String[] { "en-US", "zh-Hant-TW" }));  // supported
329 
330         // No matter the current locale has "nu" extension or not, if the restored locale has fw
331         // (first day of week) or mu(temperature unit) extension, we should restore fw or mu
332         // extensions as well and append these to restore and current locales.
333         assertEquals(LocaleList.forLanguageTags(
334                 "fa-Arab-AF-u-nu-latn-fw-mon-mu-celsius,zh-Hant-TW-u-fw-mon-mu-celsius"),
335                 SettingsHelper.resolveLocales(
336                         LocaleList.forLanguageTags("zh-Hant-TW-u-fw-mon-mu-celsius"),  // restore
337                         LocaleList.forLanguageTags("fa-Arab-AF-u-nu-latn"),  // current
338                         new String[] { "fa-Arab-AF-u-nu-latn", "zh-Hant-TW" }));  // supported
339 
340         // If the restored locale only has nu extension, we should not restore the nu extensions to
341         // current locales.
342         assertEquals(LocaleList.forLanguageTags("zh-Hant-TW,fa-Arab-AF-u-nu-latn"),
343                 SettingsHelper.resolveLocales(
344                         LocaleList.forLanguageTags("fa-Arab-AF-u-nu-latn"),  // restore
345                         LocaleList.forLanguageTags("zh-Hant-TW"),  // current
346                         new String[] { "fa-Arab-AF-u-nu-latn", "zh-Hant-TW" }));  // supported
347 
348 
349     }
350 
351     @Test
testRestoreValue_customRingtone_regularUncanonicalize_Success()352     public void testRestoreValue_customRingtone_regularUncanonicalize_Success() {
353         final String sourceRingtoneValue =
354                 "content://media/internal/audio/media/1?title=Song&canonical=1";
355         final String newRingtoneValueUncanonicalized =
356                 "content://media/internal/audio/media/100";
357         final String newRingtoneValueCanonicalized =
358                 "content://media/internal/audio/media/100?title=Song&canonical=1";
359 
360         ContentProvider mockMediaContentProvider =
361                 new MockContentProvider(mContext) {
362                     @Override
363                     public Uri uncanonicalize(Uri url) {
364                         assertThat(url).isEqualTo(Uri.parse(sourceRingtoneValue));
365                         return Uri.parse(newRingtoneValueUncanonicalized);
366                     }
367 
368                     @Override
369                     public Uri canonicalize(Uri url) {
370                         assertThat(url).isEqualTo(Uri.parse(newRingtoneValueUncanonicalized));
371                         return Uri.parse(newRingtoneValueCanonicalized);
372                     }
373 
374                     @Override
375                     public String getType(Uri url) {
376                         return "audio/ogg";
377                     }
378                 };
379 
380         mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
381 
382         resetRingtoneSettingsToDefault();
383         assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
384                 .isEqualTo(DEFAULT_RINGTONE_VALUE);
385 
386         mSettingsHelper.restoreValue(
387                 mContext,
388                 mContentResolver,
389                 new ContentValues(),
390                 Uri.EMPTY,
391                 Settings.System.RINGTONE,
392                 sourceRingtoneValue,
393                 0);
394 
395         assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
396                 .isEqualTo(newRingtoneValueCanonicalized);
397     }
398 
399     @Test
testRestoreValue_customRingtone_useCustomLookup_success()400     public void testRestoreValue_customRingtone_useCustomLookup_success() {
401         final String sourceRingtoneValue =
402                 "content://0@media/external/audio/media/1?title=Song&canonical=1";
403         final String newRingtoneValueUncanonicalized =
404                 "content://0@media/external/audio/media/100";
405         final String newRingtoneValueCanonicalized =
406                 "content://0@media/external/audio/media/100?title=Song&canonical=1";
407 
408         MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
409         cursor.addRow(new Object[] {100L});
410 
411         ContentProvider mockMediaContentProvider =
412                 new MockContentProvider(mContext) {
413                     @Override
414                     public Uri uncanonicalize(Uri url) {
415                         // mock the lookup failure in regular MediaProvider.uncanonicalize.
416                         return null;
417                     }
418 
419                     @Override
420                     public Uri canonicalize(Uri url) {
421                         assertThat(url).isEqualTo(Uri.parse(newRingtoneValueUncanonicalized));
422                         return Uri.parse(newRingtoneValueCanonicalized);
423                     }
424 
425                     @Override
426                     public String getType(Uri url) {
427                         return "audio/ogg";
428                     }
429 
430                     @Override
431                     public Cursor query(
432                             Uri uri,
433                             String[] projection,
434                             String selection,
435                             String[] selectionArgs,
436                             String sortOrder) {
437                         assertThat(uri)
438                                 .isEqualTo(Uri.parse("content://0@media/external/audio/media"));
439                         assertThat(projection).isEqualTo(new String[] {"_id"});
440                         assertThat(selection).isEqualTo("is_ringtone=1 AND title=?");
441                         assertThat(selectionArgs).isEqualTo(new String[] {"Song"});
442                         return cursor;
443                     }
444                 };
445 
446         mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
447         mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
448 
449         resetRingtoneSettingsToDefault();
450 
451         mSettingsHelper.restoreValue(
452                 mContext,
453                 mContentResolver,
454                 new ContentValues(),
455                 Uri.EMPTY,
456                 Settings.System.RINGTONE,
457                 sourceRingtoneValue,
458                 0);
459 
460         assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
461                 .isEqualTo(newRingtoneValueCanonicalized);
462     }
463 
464     @Test
testRestoreValue_customRingtone_notificationSound_useCustomLookup_success()465     public void testRestoreValue_customRingtone_notificationSound_useCustomLookup_success() {
466         final String sourceRingtoneValue =
467                 "content://0@media/external/audio/media/2?title=notificationPing&canonical=1";
468         final String newRingtoneValueUncanonicalized =
469                 "content://0@media/external/audio/media/200";
470         final String newRingtoneValueCanonicalized =
471                 "content://0@media/external/audio/media/200?title=notificationPing&canonicalize=1";
472 
473         MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
474         cursor.addRow(new Object[] {200L});
475 
476         ContentProvider mockMediaContentProvider =
477                 new MockContentProvider(mContext) {
478                     @Override
479                     public Uri uncanonicalize(Uri url) {
480                         // mock the lookup failure in regular MediaProvider.uncanonicalize.
481                         return null;
482                     }
483 
484                     @Override
485                     public Uri canonicalize(Uri url) {
486                         assertThat(url).isEqualTo(Uri.parse(newRingtoneValueUncanonicalized));
487                         return Uri.parse(newRingtoneValueCanonicalized);
488                     }
489 
490                     @Override
491                     public String getType(Uri url) {
492                         return "audio/ogg";
493                     }
494 
495                     @Override
496                     public Cursor query(
497                             Uri uri,
498                             String[] projection,
499                             String selection,
500                             String[] selectionArgs,
501                             String sortOrder) {
502                         assertThat(uri)
503                                 .isEqualTo(Uri.parse("content://0@media/external/audio/media"));
504                         assertThat(projection).isEqualTo(new String[] {"_id"});
505                         assertThat(selection).isEqualTo("is_notification=1 AND title=?");
506                         assertThat(selectionArgs).isEqualTo(new String[] {"notificationPing"});
507                         return cursor;
508                     }
509                 };
510 
511         mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
512         mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
513 
514         resetRingtoneSettingsToDefault();
515 
516         mSettingsHelper.restoreValue(
517                 mContext,
518                 mContentResolver,
519                 new ContentValues(),
520                 Uri.EMPTY,
521                 Settings.System.NOTIFICATION_SOUND,
522                 sourceRingtoneValue,
523                 0);
524 
525         assertThat(
526                         Settings.System.getString(
527                                 mContentResolver, Settings.System.NOTIFICATION_SOUND))
528                 .isEqualTo(newRingtoneValueCanonicalized);
529     }
530 
531     @Test
testRestoreValue_customRingtone_alarmSound_useCustomLookup_success()532     public void testRestoreValue_customRingtone_alarmSound_useCustomLookup_success() {
533         final String sourceRingtoneValue =
534                 "content://0@media/external/audio/media/3?title=alarmSound&canonical=1";
535         final String newRingtoneValueUncanonicalized =
536                 "content://0@media/external/audio/media/300";
537         final String newRingtoneValueCanonicalized =
538                 "content://0@media/external/audio/media/300?title=alarmSound&canonical=1";
539 
540         MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
541         cursor.addRow(new Object[] {300L});
542 
543         ContentProvider mockMediaContentProvider =
544                 new MockContentProvider(mContext) {
545                     @Override
546                     public Uri uncanonicalize(Uri url) {
547                         // mock the lookup failure in regular MediaProvider.uncanonicalize.
548                         return null;
549                     }
550 
551                     @Override
552                     public Uri canonicalize(Uri url) {
553                         assertThat(url).isEqualTo(Uri.parse(newRingtoneValueUncanonicalized));
554                         return Uri.parse(newRingtoneValueCanonicalized);
555                     }
556 
557                     @Override
558                     public String getType(Uri url) {
559                         return "audio/ogg";
560                     }
561 
562                     @Override
563                     public Cursor query(
564                             Uri uri,
565                             String[] projection,
566                             String selection,
567                             String[] selectionArgs,
568                             String sortOrder) {
569                         assertThat(uri)
570                                 .isEqualTo(Uri.parse("content://0@media/external/audio/media"));
571                         assertThat(projection).isEqualTo(new String[] {"_id"});
572                         assertThat(selection).isEqualTo("is_alarm=1 AND title=?");
573                         assertThat(selectionArgs).isEqualTo(new String[] {"alarmSound"});
574                         return cursor;
575                     }
576 
577                     @Override
578                     public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType,
579                             Bundle opts) {
580                         return null;
581                     }
582                 };
583 
584         mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
585         mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
586 
587         resetRingtoneSettingsToDefault();
588 
589         mSettingsHelper.restoreValue(
590                 mContext,
591                 mContentResolver,
592                 new ContentValues(),
593                 Uri.EMPTY,
594                 Settings.System.ALARM_ALERT,
595                 sourceRingtoneValue,
596                 0);
597 
598         assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT))
599                 .isEqualTo(newRingtoneValueCanonicalized);
600     }
601 
602     @Test
testRestoreValue_customRingtone_useCustomLookup_multipleResults_notRestore()603     public void testRestoreValue_customRingtone_useCustomLookup_multipleResults_notRestore() {
604         final String sourceRingtoneValue =
605                 "content://0@media/external/audio/media/1?title=Song&canonical=1";
606 
607         // This is to mock the case that there are multiple results by querying title +
608         // ringtone_type.
609         MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID});
610         cursor.addRow(new Object[] {100L});
611         cursor.addRow(new Object[] {110L});
612 
613         ContentProvider mockMediaContentProvider =
614                 new MockContentProvider(mContext) {
615                     @Override
616                     public Uri uncanonicalize(Uri url) {
617                         // mock the lookup failure in regular MediaProvider.uncanonicalize.
618                         return null;
619                     }
620 
621                     @Override
622                     public String getType(Uri url) {
623                         return "audio/ogg";
624                     }
625                 };
626 
627         mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
628         mContentResolver.addProvider("0@" + MediaStore.AUTHORITY, mockMediaContentProvider);
629 
630         resetRingtoneSettingsToDefault();
631 
632         mSettingsHelper.restoreValue(
633                 mContext,
634                 mContentResolver,
635                 new ContentValues(),
636                 Uri.EMPTY,
637                 Settings.System.RINGTONE,
638                 sourceRingtoneValue,
639                 0);
640 
641         assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
642                 .isEqualTo(DEFAULT_RINGTONE_VALUE);
643     }
644 
645     @Test
testRestoreValue_customRingtone_restoreSilentValue()646     public void testRestoreValue_customRingtone_restoreSilentValue() {
647         ContentProvider mockMediaContentProvider =
648                 new MockContentProvider(mContext) {
649                     @Override
650                     public Uri uncanonicalize(Uri url) {
651                         // mock the lookup failure in regular MediaProvider.uncanonicalize.
652                         return null;
653                     }
654 
655                     @Override
656                     public String getType(Uri url) {
657                         return "audio/ogg";
658                     }
659                 };
660 
661         mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider);
662 
663         resetRingtoneSettingsToDefault();
664 
665         mSettingsHelper.restoreValue(
666                 mContext,
667                 mContentResolver,
668                 new ContentValues(),
669                 Uri.EMPTY,
670                 Settings.System.RINGTONE,
671                 "_silent",
672                 0);
673 
674         assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
675                 .isEqualTo(null);
676     }
677 
678     private static class MockSettingsProvider extends MockContentProvider {
679         private final ArrayMap<String, String> mKeyValueStore = new ArrayMap<>();
MockSettingsProvider(Context context)680         MockSettingsProvider(Context context) {
681             super(context);
682         }
683 
684         @Override
call(String method, String request, Bundle args)685         public Bundle call(String method, String request, Bundle args) {
686             if (method.startsWith("PUT_")) {
687                 mKeyValueStore.put(request, args.getString("value"));
688                 return null;
689             } else if (method.startsWith("GET_")) {
690                 return Bundle.forPair("value", mKeyValueStore.getOrDefault(request, ""));
691             }
692             return null;
693         }
694 
695         @Override
insert(Uri uri, ContentValues values)696         public Uri insert(Uri uri, ContentValues values) {
697             String name = values.getAsString("name");
698             String value = values.getAsString("value");
699             mKeyValueStore.put(name, value);
700             return null;
701         }
702     }
703 
704     @Test
restoreValue_autoRotation_deviceStateAutoRotationDisabled_restoresValue()705     public void restoreValue_autoRotation_deviceStateAutoRotationDisabled_restoresValue() {
706         when(mResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
707                 .thenReturn(new String[]{});
708         int previousValue = 0;
709         int newValue = 1;
710         setAutoRotationSettingValue(previousValue);
711 
712         restoreAutoRotationSetting(newValue);
713 
714         assertThat(getAutoRotationSettingValue()).isEqualTo(newValue);
715     }
716 
717     @Test
restoreValue_autoRotation_deviceStateAutoRotationEnabled_doesNotRestoreValue()718     public void restoreValue_autoRotation_deviceStateAutoRotationEnabled_doesNotRestoreValue() {
719         when(mResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
720                 .thenReturn(new String[]{"0:1", "1:1"});
721         int previousValue = 0;
722         int newValue = 1;
723         setAutoRotationSettingValue(previousValue);
724 
725         restoreAutoRotationSetting(newValue);
726 
727         assertThat(getAutoRotationSettingValue()).isEqualTo(previousValue);
728     }
729 
getAutoRotationSettingValue()730     private int getAutoRotationSettingValue() {
731         return Settings.System.getInt(mContentResolver,
732                 Settings.System.ACCELEROMETER_ROTATION,
733                 /* default= */ -1);
734     }
735 
setAutoRotationSettingValue(int value)736     private void setAutoRotationSettingValue(int value) {
737         Settings.System.putInt(mContentResolver,
738                 Settings.System.ACCELEROMETER_ROTATION,
739                 value
740         );
741     }
742 
restoreAutoRotationSetting(int newValue)743     private void restoreAutoRotationSetting(int newValue) {
744         mSettingsHelper.restoreValue(
745                 mContext,
746                 mContentResolver,
747                 new ContentValues(),
748                 /* destination= */ Settings.System.CONTENT_URI,
749                 /* name= */ Settings.System.ACCELEROMETER_ROTATION,
750                 /* value= */ String.valueOf(newValue),
751                 /* restoredFromSdkInt= */ 0);
752     }
753 
resetRingtoneSettingsToDefault()754     private void resetRingtoneSettingsToDefault() {
755         Settings.System.putString(
756                 mContentResolver, Settings.System.RINGTONE, DEFAULT_RINGTONE_VALUE);
757         Settings.System.putString(
758                 mContentResolver, Settings.System.NOTIFICATION_SOUND, DEFAULT_NOTIFICATION_VALUE);
759         Settings.System.putString(
760                 mContentResolver, Settings.System.ALARM_ALERT, DEFAULT_ALARM_VALUE);
761 
762         assertThat(Settings.System.getString(mContentResolver, Settings.System.RINGTONE))
763                 .isEqualTo(DEFAULT_RINGTONE_VALUE);
764         assertThat(Settings.System.getString(mContentResolver, Settings.System.NOTIFICATION_SOUND))
765                 .isEqualTo(DEFAULT_NOTIFICATION_VALUE);
766         assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT))
767                 .isEqualTo(DEFAULT_ALARM_VALUE);
768     }
769 }
770