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