1 /* 2 * Copyright (C) 2018 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 android.provider; 18 19 import static android.provider.DeviceConfig.Properties; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.junit.Assert.assertTrue; 24 import static org.testng.Assert.assertThrows; 25 26 import android.app.ActivityThread; 27 import android.content.ContentResolver; 28 import android.os.Bundle; 29 import android.platform.test.annotations.Presubmit; 30 31 import androidx.test.InstrumentationRegistry; 32 import androidx.test.ext.junit.runners.AndroidJUnit4; 33 import androidx.test.filters.FlakyTest; 34 import androidx.test.filters.SmallTest; 35 36 import org.junit.After; 37 import org.junit.Assert; 38 import org.junit.Ignore; 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 42 import java.util.HashMap; 43 import java.util.Map; 44 import java.util.Objects; 45 import java.util.concurrent.CompletableFuture; 46 import java.util.concurrent.ExecutionException; 47 import java.util.concurrent.TimeUnit; 48 import java.util.concurrent.TimeoutException; 49 import java.util.concurrent.atomic.AtomicReference; 50 51 /** Tests that ensure appropriate settings are backed up. */ 52 @Presubmit 53 @RunWith(AndroidJUnit4.class) 54 @SmallTest 55 public class DeviceConfigTest { 56 private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec 57 private static final String DEFAULT_VALUE = "test_default_value"; 58 private static final String NAMESPACE = "namespace1"; 59 private static final String NAMESPACE2 = "namespace2"; 60 private static final String KEY = "key1"; 61 private static final String KEY2 = "key2"; 62 private static final String KEY3 = "key3"; 63 private static final String KEY4 = "key4"; 64 private static final String VALUE = "value1"; 65 private static final String VALUE2 = "value2"; 66 private static final String VALUE3 = "value3"; 67 private static final String NULL_VALUE = "null"; 68 69 @After cleanUp()70 public void cleanUp() { 71 deleteViaContentProvider(NAMESPACE, KEY); 72 deleteViaContentProvider(NAMESPACE, KEY2); 73 deleteViaContentProvider(NAMESPACE, KEY3); 74 DeviceConfig.clearAllLocalOverrides(); 75 DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_NONE); 76 } 77 78 /** 79 * Test that creating a sticky local override for a flag prevents further writes to that flag. 80 */ 81 @Test testAddStickyLocalOverridePreventsWrites()82 public void testAddStickyLocalOverridePreventsWrites() { 83 DeviceConfig.setLocalOverride(NAMESPACE, KEY, VALUE); 84 85 String key1Value = DeviceConfig.getProperty(NAMESPACE, KEY); 86 assertThat(key1Value).isEqualTo(VALUE); 87 88 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE2, /* makeDefault= */ false); 89 key1Value = DeviceConfig.getProperty(NAMESPACE, KEY); 90 assertThat(key1Value).isEqualTo(VALUE); 91 } 92 93 /** 94 * Test that when we locally override a flag, we can still write other flags. 95 */ 96 @Test testAddStickyLocalOverrideDoesNotAffectOtherFlags()97 public void testAddStickyLocalOverrideDoesNotAffectOtherFlags() { 98 DeviceConfig.setLocalOverride(NAMESPACE, KEY, VALUE); 99 DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, /* makeDefault= */ false); 100 String key2Value = DeviceConfig.getProperty(NAMESPACE, KEY2); 101 assertThat(key2Value).isEqualTo(VALUE2); 102 } 103 104 /** 105 * Test that when we apply some overrides, they show up in the override list. 106 */ 107 @Test testGetStickyLocalOverrides()108 public void testGetStickyLocalOverrides() { 109 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE2, false); 110 DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE, false); 111 DeviceConfig.setLocalOverride(NAMESPACE, KEY, VALUE); 112 DeviceConfig.setLocalOverride(NAMESPACE, KEY2, VALUE2); 113 114 Map<String, Map<String, String>> expectedOverrides = new HashMap<>(); 115 Map<String, String> expectedInnerMap = new HashMap<>(); 116 expectedInnerMap.put(KEY, VALUE2); 117 expectedInnerMap.put(KEY2, VALUE); 118 expectedOverrides.put(NAMESPACE, expectedInnerMap); 119 120 assertThat(DeviceConfig.getUnderlyingValuesForOverriddenFlags()) 121 .isEqualTo(expectedOverrides); 122 } 123 124 /** 125 * Test that when we clear all overrides, the override list is empty. 126 */ 127 @Test testClearStickyLocalOverrides()128 public void testClearStickyLocalOverrides() { 129 DeviceConfig.setLocalOverride(NAMESPACE2, KEY, VALUE); 130 DeviceConfig.setLocalOverride(NAMESPACE2, KEY2, VALUE2); 131 132 DeviceConfig.clearAllLocalOverrides(); 133 134 Map<String, Map<String, String>> overrides = 135 DeviceConfig.getUnderlyingValuesForOverriddenFlags(); 136 assertThat(overrides).isEmpty(); 137 } 138 139 /** 140 * Test that when we clear a single override, it doesn't appear in the list. 141 */ 142 @Test testClearStickyLocalOverride()143 public void testClearStickyLocalOverride() { 144 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE2, false); 145 DeviceConfig.setProperty(NAMESPACE2, KEY2, VALUE, false); 146 DeviceConfig.setLocalOverride(NAMESPACE, KEY, VALUE); 147 DeviceConfig.setLocalOverride(NAMESPACE2, KEY2, VALUE2); 148 149 DeviceConfig.clearLocalOverride(NAMESPACE, KEY); 150 151 Map<String, Map<String, String>> expectedOverrides = new HashMap<>(); 152 Map<String, String> expectedInnerMap = new HashMap<>(); 153 expectedInnerMap.put(KEY2, VALUE); 154 expectedOverrides.put(NAMESPACE2, expectedInnerMap); 155 156 assertThat(DeviceConfig.getUnderlyingValuesForOverriddenFlags()) 157 .isEqualTo(expectedOverrides); 158 } 159 160 @Test getProperty_empty()161 public void getProperty_empty() { 162 String result = DeviceConfig.getProperty(NAMESPACE, KEY); 163 assertThat(result).isNull(); 164 } 165 166 @Test getProperty_nullNamespace()167 public void getProperty_nullNamespace() { 168 try { 169 DeviceConfig.getProperty(null, KEY); 170 Assert.fail("Null namespace should have resulted in an NPE."); 171 } catch (NullPointerException e) { 172 // expected 173 } 174 } 175 176 @Test getProperty_nullName()177 public void getProperty_nullName() { 178 try { 179 DeviceConfig.getProperty(NAMESPACE, null); 180 Assert.fail("Null name should have resulted in an NPE."); 181 } catch (NullPointerException e) { 182 // expected 183 } 184 } 185 186 @Test getString_empty()187 public void getString_empty() { 188 final String default_value = "default_value"; 189 final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value); 190 assertThat(result).isEqualTo(default_value); 191 } 192 193 @Test getString_nullDefault()194 public void getString_nullDefault() { 195 final String result = DeviceConfig.getString(NAMESPACE, KEY, null); 196 assertThat(result).isNull(); 197 } 198 199 @Test getString_nonEmpty()200 public void getString_nonEmpty() { 201 final String value = "new_value"; 202 final String default_value = "default"; 203 DeviceConfig.setProperty(NAMESPACE, KEY, value, false); 204 205 final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value); 206 assertThat(result).isEqualTo(value); 207 } 208 209 @Test getString_nullNamespace()210 public void getString_nullNamespace() { 211 try { 212 DeviceConfig.getString(null, KEY, "default_value"); 213 Assert.fail("Null namespace should have resulted in an NPE."); 214 } catch (NullPointerException e) { 215 // expected 216 } 217 } 218 219 @Test getString_nullName()220 public void getString_nullName() { 221 try { 222 DeviceConfig.getString(NAMESPACE, null, "default_value"); 223 Assert.fail("Null name should have resulted in an NPE."); 224 } catch (NullPointerException e) { 225 // expected 226 } 227 } 228 229 @Test getBoolean_empty()230 public void getBoolean_empty() { 231 final boolean default_value = true; 232 final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value); 233 assertThat(result).isEqualTo(default_value); 234 } 235 236 @Test getBoolean_valid()237 public void getBoolean_valid() { 238 final boolean value = true; 239 final boolean default_value = false; 240 DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false); 241 242 final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value); 243 assertThat(result).isEqualTo(value); 244 } 245 246 @Test getBoolean_invalid()247 public void getBoolean_invalid() { 248 final boolean default_value = true; 249 DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_boolean", false); 250 251 final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value); 252 // Anything non-null other than case insensitive "true" parses to false. 253 assertThat(result).isFalse(); 254 } 255 256 @Test getBoolean_nullNamespace()257 public void getBoolean_nullNamespace() { 258 try { 259 DeviceConfig.getBoolean(null, KEY, false); 260 Assert.fail("Null namespace should have resulted in an NPE."); 261 } catch (NullPointerException e) { 262 // expected 263 } 264 } 265 266 @Test getBoolean_nullName()267 public void getBoolean_nullName() { 268 try { 269 DeviceConfig.getBoolean(NAMESPACE, null, false); 270 Assert.fail("Null name should have resulted in an NPE."); 271 } catch (NullPointerException e) { 272 // expected 273 } 274 } 275 276 @Test getInt_empty()277 public void getInt_empty() { 278 final int default_value = 999; 279 final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value); 280 assertThat(result).isEqualTo(default_value); 281 } 282 283 @Test getInt_valid()284 public void getInt_valid() { 285 final int value = 123; 286 final int default_value = 999; 287 DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false); 288 289 final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value); 290 assertThat(result).isEqualTo(value); 291 } 292 293 @Test getInt_invalid()294 public void getInt_invalid() { 295 final int default_value = 999; 296 DeviceConfig.setProperty(NAMESPACE, KEY, "not_an_int", false); 297 298 final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value); 299 // Failure to parse results in using the default value 300 assertThat(result).isEqualTo(default_value); 301 } 302 303 @Test getInt_nullNamespace()304 public void getInt_nullNamespace() { 305 try { 306 DeviceConfig.getInt(null, KEY, 0); 307 Assert.fail("Null namespace should have resulted in an NPE."); 308 } catch (NullPointerException e) { 309 // expected 310 } 311 } 312 313 @Test getInt_nullName()314 public void getInt_nullName() { 315 try { 316 DeviceConfig.getInt(NAMESPACE, null, 0); 317 Assert.fail("Null name should have resulted in an NPE."); 318 } catch (NullPointerException e) { 319 // expected 320 } 321 } 322 323 @Test getLong_empty()324 public void getLong_empty() { 325 final long default_value = 123456; 326 final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value); 327 assertThat(result).isEqualTo(default_value); 328 } 329 330 @Test getLong_valid()331 public void getLong_valid() { 332 final long value = 456789; 333 final long default_value = 123456; 334 DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false); 335 336 final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value); 337 assertThat(result).isEqualTo(value); 338 } 339 340 @Test getLong_invalid()341 public void getLong_invalid() { 342 final long default_value = 123456; 343 DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_long", false); 344 345 final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value); 346 // Failure to parse results in using the default value 347 assertThat(result).isEqualTo(default_value); 348 } 349 350 @Test getLong_nullNamespace()351 public void getLong_nullNamespace() { 352 try { 353 DeviceConfig.getLong(null, KEY, 0); 354 Assert.fail("Null namespace should have resulted in an NPE."); 355 } catch (NullPointerException e) { 356 // expected 357 } 358 } 359 360 @Test getLong_nullName()361 public void getLong_nullName() { 362 try { 363 DeviceConfig.getLong(NAMESPACE, null, 0); 364 Assert.fail("Null name should have resulted in an NPE."); 365 } catch (NullPointerException e) { 366 // expected 367 } 368 } 369 370 @Test getFloat_empty()371 public void getFloat_empty() { 372 final float default_value = 123.456f; 373 final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value); 374 assertThat(result).isEqualTo(default_value); 375 } 376 377 @Test getFloat_valid()378 public void getFloat_valid() { 379 final float value = 456.789f; 380 final float default_value = 123.456f; 381 DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false); 382 383 final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value); 384 assertThat(result).isEqualTo(value); 385 } 386 387 @Test getFloat_invalid()388 public void getFloat_invalid() { 389 final float default_value = 123.456f; 390 DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_float", false); 391 392 final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value); 393 // Failure to parse results in using the default value 394 assertThat(result).isEqualTo(default_value); 395 } 396 397 @Test getFloat_nullNamespace()398 public void getFloat_nullNamespace() { 399 try { 400 DeviceConfig.getFloat(null, KEY, 0); 401 Assert.fail("Null namespace should have resulted in an NPE."); 402 } catch (NullPointerException e) { 403 // expected 404 } 405 } 406 407 @Test getFloat_nullName()408 public void getFloat_nullName() { 409 try { 410 DeviceConfig.getFloat(NAMESPACE, null, 0); 411 Assert.fail("Null name should have resulted in an NPE."); 412 } catch (NullPointerException e) { 413 // expected 414 } 415 } 416 417 @Test setProperty_nullNamespace()418 public void setProperty_nullNamespace() { 419 try { 420 DeviceConfig.setProperty(null, KEY, VALUE, false); 421 Assert.fail("Null namespace should have resulted in an NPE."); 422 } catch (NullPointerException e) { 423 // expected 424 } 425 } 426 427 @Test setProperty_nullName()428 public void setProperty_nullName() { 429 try { 430 DeviceConfig.setProperty(NAMESPACE, null, VALUE, false); 431 Assert.fail("Null name should have resulted in an NPE."); 432 } catch (NullPointerException e) { 433 // expected 434 } 435 } 436 437 @Test setAndGetProperty_sameNamespace()438 public void setAndGetProperty_sameNamespace() { 439 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 440 String result = DeviceConfig.getProperty(NAMESPACE, KEY); 441 assertThat(result).isEqualTo(VALUE); 442 } 443 444 @Test setAndGetProperty_differentNamespace()445 public void setAndGetProperty_differentNamespace() { 446 String newNamespace = "namespace2"; 447 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 448 String result = DeviceConfig.getProperty(newNamespace, KEY); 449 assertThat(result).isNull(); 450 } 451 452 @Test setAndGetProperty_multipleNamespaces()453 public void setAndGetProperty_multipleNamespaces() { 454 String newNamespace = "namespace2"; 455 String newValue = "value2"; 456 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 457 DeviceConfig.setProperty(newNamespace, KEY, newValue, false); 458 String result = DeviceConfig.getProperty(NAMESPACE, KEY); 459 assertThat(result).isEqualTo(VALUE); 460 result = DeviceConfig.getProperty(newNamespace, KEY); 461 assertThat(result).isEqualTo(newValue); 462 463 // clean up 464 deleteViaContentProvider(newNamespace, KEY); 465 } 466 467 @Test setAndGetProperty_overrideValue()468 public void setAndGetProperty_overrideValue() { 469 String newValue = "value2"; 470 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 471 DeviceConfig.setProperty(NAMESPACE, KEY, newValue, false); 472 String result = DeviceConfig.getProperty(NAMESPACE, KEY); 473 assertThat(result).isEqualTo(newValue); 474 } 475 476 @Test resetToDefault_makeDefault()477 public void resetToDefault_makeDefault() { 478 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, true); 479 assertThat(DeviceConfig.getProperty(NAMESPACE, KEY)).isEqualTo(VALUE); 480 481 DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, NAMESPACE); 482 assertThat(DeviceConfig.getProperty(NAMESPACE, KEY)).isEqualTo(VALUE); 483 } 484 485 @Test resetToDefault_doNotMakeDefault()486 public void resetToDefault_doNotMakeDefault() { 487 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 488 assertThat(DeviceConfig.getProperty(NAMESPACE, KEY)).isEqualTo(VALUE); 489 490 DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, NAMESPACE); 491 assertThat(DeviceConfig.getProperty(NAMESPACE, KEY)).isNull(); 492 } 493 494 @Test getProperties_fullNamespace()495 public void getProperties_fullNamespace() { 496 Properties properties = DeviceConfig.getProperties(NAMESPACE); 497 assertThat(properties.getKeyset()).isEmpty(); 498 499 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 500 DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false); 501 properties = DeviceConfig.getProperties(NAMESPACE); 502 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); 503 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 504 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 505 506 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE3, false); 507 properties = DeviceConfig.getProperties(NAMESPACE); 508 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); 509 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3); 510 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 511 512 DeviceConfig.setProperty(NAMESPACE, KEY3, VALUE, false); 513 properties = DeviceConfig.getProperties(NAMESPACE); 514 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3); 515 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3); 516 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 517 assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(VALUE); 518 } 519 520 @Test getProperties_getString()521 public void getProperties_getString() { 522 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 523 DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false); 524 525 Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2); 526 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); 527 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 528 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 529 } 530 531 @Test getProperties_getBoolean()532 public void getProperties_getBoolean() { 533 DeviceConfig.setProperty(NAMESPACE, KEY, "true", false); 534 DeviceConfig.setProperty(NAMESPACE, KEY2, "false", false); 535 DeviceConfig.setProperty(NAMESPACE, KEY3, "not a valid boolean", false); 536 537 Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2, KEY3); 538 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3); 539 assertThat(properties.getBoolean(KEY, true)).isTrue(); 540 assertThat(properties.getBoolean(KEY, false)).isTrue(); 541 assertThat(properties.getBoolean(KEY2, true)).isFalse(); 542 assertThat(properties.getBoolean(KEY2, false)).isFalse(); 543 // KEY3 was set to garbage, anything nonnull but "true" will parse as false 544 assertThat(properties.getBoolean(KEY3, true)).isFalse(); 545 assertThat(properties.getBoolean(KEY3, false)).isFalse(); 546 // If a key was not set, it will return the default value 547 assertThat(properties.getBoolean("missing_key", true)).isTrue(); 548 assertThat(properties.getBoolean("missing_key", false)).isFalse(); 549 } 550 551 @Test getProperties_getInt()552 public void getProperties_getInt() { 553 final int value = 101; 554 555 DeviceConfig.setProperty(NAMESPACE, KEY, Integer.toString(value), false); 556 DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid int", false); 557 558 Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2); 559 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); 560 assertThat(properties.getInt(KEY, -1)).isEqualTo(value); 561 // KEY2 was set to garbage, the default value is returned if an int cannot be parsed 562 assertThat(properties.getInt(KEY2, -1)).isEqualTo(-1); 563 } 564 565 @Test getProperties_getFloat()566 public void getProperties_getFloat() { 567 final float value = 101.010f; 568 569 DeviceConfig.setProperty(NAMESPACE, KEY, Float.toString(value), false); 570 DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid float", false); 571 572 Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2); 573 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); 574 assertThat(properties.getFloat(KEY, -1.0f)).isEqualTo(value); 575 // KEY2 was set to garbage, the default value is returned if a float cannot be parsed 576 assertThat(properties.getFloat(KEY2, -1.0f)).isEqualTo(-1.0f); 577 } 578 579 @Test getProperties_getLong()580 public void getProperties_getLong() { 581 final long value = 101; 582 583 DeviceConfig.setProperty(NAMESPACE, KEY, Long.toString(value), false); 584 DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid long", false); 585 586 Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2); 587 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); 588 assertThat(properties.getLong(KEY, -1)).isEqualTo(value); 589 // KEY2 was set to garbage, the default value is returned if a long cannot be parsed 590 assertThat(properties.getLong(KEY2, -1)).isEqualTo(-1); 591 } 592 593 @Test getProperties_defaults()594 public void getProperties_defaults() { 595 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 596 DeviceConfig.setProperty(NAMESPACE, KEY3, VALUE3, false); 597 598 Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2); 599 assertThat(properties.getKeyset()).containsExactly(KEY); 600 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 601 // not set in DeviceConfig, but requested in getProperties 602 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 603 // set in DeviceConfig, but not requested in getProperties 604 assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 605 } 606 607 @Test setProperties()608 public void setProperties() throws DeviceConfig.BadConfigException { 609 Properties properties = new Properties.Builder(NAMESPACE).setString(KEY, VALUE) 610 .setString(KEY2, VALUE2).build(); 611 612 DeviceConfig.setProperties(properties); 613 properties = DeviceConfig.getProperties(NAMESPACE); 614 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); 615 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 616 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 617 618 properties = new Properties.Builder(NAMESPACE).setString(KEY, VALUE2) 619 .setString(KEY3, VALUE3).build(); 620 621 DeviceConfig.setProperties(properties); 622 properties = DeviceConfig.getProperties(NAMESPACE); 623 assertThat(properties.getKeyset()).containsExactly(KEY, KEY3); 624 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE2); 625 assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(VALUE3); 626 627 assertThat(properties.getKeyset()).doesNotContain(KEY2); 628 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 629 } 630 631 @Test 632 @FlakyTest(bugId = 299483542) setProperties_multipleNamespaces()633 public void setProperties_multipleNamespaces() throws DeviceConfig.BadConfigException { 634 final String namespace2 = "namespace2"; 635 Properties properties1 = new Properties.Builder(NAMESPACE).setString(KEY, VALUE) 636 .setString(KEY2, VALUE2).build(); 637 Properties properties2 = new Properties.Builder(namespace2).setString(KEY2, VALUE) 638 .setString(KEY3, VALUE2).build(); 639 640 assertTrue(DeviceConfig.setProperties(properties1)); 641 assertTrue(DeviceConfig.setProperties(properties2)); 642 643 Properties properties = DeviceConfig.getProperties(NAMESPACE); 644 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2); 645 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 646 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 647 648 assertThat(properties.getKeyset()).doesNotContain(KEY3); 649 assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 650 651 properties = DeviceConfig.getProperties(namespace2); 652 assertThat(properties.getKeyset()).containsExactly(KEY2, KEY3); 653 assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE); 654 assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(VALUE2); 655 656 assertThat(properties.getKeyset()).doesNotContain(KEY); 657 assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 658 659 // clean up 660 deleteViaContentProvider(namespace2, KEY); 661 deleteViaContentProvider(namespace2, KEY2); 662 deleteViaContentProvider(namespace2, KEY3); 663 } 664 665 @Test propertiesBuilder()666 public void propertiesBuilder() { 667 boolean booleanValue = true; 668 int intValue = 123; 669 float floatValue = 4.56f; 670 long longValue = -789L; 671 String key4 = "key4"; 672 String key5 = "key5"; 673 674 Properties properties = new Properties.Builder(NAMESPACE).setString(KEY, VALUE) 675 .setBoolean(KEY2, booleanValue).setInt(KEY3, intValue).setLong(key4, longValue) 676 .setFloat(key5, floatValue).build(); 677 assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); 678 assertThat(properties.getString(KEY, "defaultValue")).isEqualTo(VALUE); 679 assertThat(properties.getBoolean(KEY2, false)).isEqualTo(booleanValue); 680 assertThat(properties.getInt(KEY3, 0)).isEqualTo(intValue); 681 assertThat(properties.getLong("key4", 0L)).isEqualTo(longValue); 682 assertThat(properties.getFloat("key5", 0f)).isEqualTo(floatValue); 683 } 684 685 @Test banNamespaceProperties()686 public void banNamespaceProperties() throws DeviceConfig.BadConfigException { 687 // Given namespace will be permanently banned, thus it needs to be different every time 688 final String namespaceToBan = NAMESPACE + System.currentTimeMillis(); 689 Properties properties = new Properties.Builder(namespaceToBan).setString(KEY, VALUE) 690 .setString(KEY4, NULL_VALUE).build(); 691 // Set namespace properties 692 DeviceConfig.setProperties(properties); 693 // Ban namespace with related properties 694 DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, namespaceToBan); 695 // Verify given namespace properties are banned 696 assertThrows(DeviceConfig.BadConfigException.class, 697 () -> DeviceConfig.setProperties(properties)); 698 // Modify properties and verify we can set them 699 Properties modifiedProperties = new Properties.Builder(namespaceToBan).setString(KEY, VALUE) 700 .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build(); 701 DeviceConfig.setProperties(modifiedProperties); 702 modifiedProperties = DeviceConfig.getProperties(namespaceToBan); 703 assertThat(modifiedProperties.getKeyset()).containsExactly(KEY, KEY2, KEY4); 704 assertThat(modifiedProperties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 705 assertThat(modifiedProperties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 706 // Since value is null DEFAULT_VALUE should be returned 707 assertThat(modifiedProperties.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 708 } 709 710 @Test banEntireDeviceConfig()711 public void banEntireDeviceConfig() throws DeviceConfig.BadConfigException { 712 // Given namespaces will be permanently banned, thus they need to be different every time 713 final String namespaceToBan1 = NAMESPACE + System.currentTimeMillis(); 714 final String namespaceToBan2 = NAMESPACE + System.currentTimeMillis() + 1; 715 716 // Set namespaces properties 717 Properties properties1 = new Properties.Builder(namespaceToBan1).setString(KEY, VALUE) 718 .setString(KEY4, NULL_VALUE).build(); 719 DeviceConfig.setProperties(properties1); 720 Properties properties2 = new Properties.Builder(namespaceToBan2).setString(KEY2, VALUE2) 721 .setString(KEY4, NULL_VALUE).build(); 722 DeviceConfig.setProperties(properties2); 723 724 // Ban entire DeviceConfig 725 DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, null); 726 727 // Verify given namespace properties are banned 728 assertThrows(DeviceConfig.BadConfigException.class, 729 () -> DeviceConfig.setProperties(properties1)); 730 assertThrows(DeviceConfig.BadConfigException.class, 731 () -> DeviceConfig.setProperties(properties2)); 732 733 // Modify properties and verify we can set them 734 Properties modifiedProperties1 = new Properties.Builder(namespaceToBan1).setString(KEY, 735 VALUE) 736 .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build(); 737 DeviceConfig.setProperties(modifiedProperties1); 738 modifiedProperties1 = DeviceConfig.getProperties(namespaceToBan1); 739 assertThat(modifiedProperties1.getKeyset()).containsExactly(KEY, KEY2, KEY4); 740 assertThat(modifiedProperties1.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 741 assertThat(modifiedProperties1.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 742 // Since value is null DEFAULT_VALUE should be returned 743 assertThat(modifiedProperties1.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 744 745 Properties modifiedProperties2 = new Properties.Builder(namespaceToBan2).setString(KEY, 746 VALUE) 747 .setString(KEY3, NULL_VALUE).setString(KEY4, VALUE2).build(); 748 DeviceConfig.setProperties(modifiedProperties2); 749 modifiedProperties2 = DeviceConfig.getProperties(namespaceToBan2); 750 assertThat(modifiedProperties2.getKeyset()).containsExactly(KEY, KEY3, KEY4); 751 assertThat(modifiedProperties2.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 752 assertThat(modifiedProperties2.getString(KEY4, DEFAULT_VALUE)).isEqualTo(VALUE2); 753 // Since value is null DEFAULT_VALUE should be returned 754 assertThat(modifiedProperties2.getString(KEY3, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 755 } 756 757 @Test allConfigsUnbannedIfAnyUnbannedConfigUpdated()758 public void allConfigsUnbannedIfAnyUnbannedConfigUpdated() 759 throws DeviceConfig.BadConfigException { 760 // Given namespaces will be permanently banned, thus they need to be different every time 761 final String namespaceToBan1 = NAMESPACE + System.currentTimeMillis(); 762 final String namespaceToBan2 = NAMESPACE + System.currentTimeMillis() + 1; 763 764 // Set namespaces properties 765 Properties properties1 = new Properties.Builder(namespaceToBan1).setString(KEY, VALUE) 766 .setString(KEY4, NULL_VALUE).build(); 767 DeviceConfig.setProperties(properties1); 768 Properties properties2 = new Properties.Builder(namespaceToBan2).setString(KEY2, VALUE2) 769 .setString(KEY4, NULL_VALUE).build(); 770 DeviceConfig.setProperties(properties2); 771 772 // Ban namespace with related properties 773 DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, namespaceToBan1); 774 DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, namespaceToBan2); 775 776 // Verify given namespace properties are banned 777 assertThrows(DeviceConfig.BadConfigException.class, 778 () -> DeviceConfig.setProperties(properties1)); 779 assertThrows(DeviceConfig.BadConfigException.class, 780 () -> DeviceConfig.setProperties(properties2)); 781 782 // Modify properties and verify we can set them 783 Properties modifiedProperties1 = new Properties.Builder(namespaceToBan1).setString(KEY, 784 VALUE) 785 .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build(); 786 DeviceConfig.setProperties(modifiedProperties1); 787 modifiedProperties1 = DeviceConfig.getProperties(namespaceToBan1); 788 assertThat(modifiedProperties1.getKeyset()).containsExactly(KEY, KEY2, KEY4); 789 assertThat(modifiedProperties1.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE); 790 assertThat(modifiedProperties1.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 791 // Since value is null DEFAULT_VALUE should be returned 792 assertThat(modifiedProperties1.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 793 794 // verify that other banned namespaces are unbanned now. 795 DeviceConfig.setProperties(properties2); 796 Properties result = DeviceConfig.getProperties(namespaceToBan2); 797 assertThat(result.getKeyset()).containsExactly(KEY2, KEY4); 798 assertThat(result.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2); 799 // Since value is null DEFAULT_VALUE should be returned 800 assertThat(result.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE); 801 } 802 803 @Test onPropertiesChangedListener_setPropertyCallback()804 public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException { 805 final AtomicReference<Properties> changedProperties = new AtomicReference<>(); 806 final CompletableFuture<Boolean> completed = new CompletableFuture<>(); 807 808 DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> { 809 changedProperties.set(properties); 810 completed.complete(true); 811 }; 812 813 try { 814 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, 815 Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(), 816 changeListener); 817 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 818 819 assertThat(completed.get( 820 WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); 821 822 Properties properties = changedProperties.get(); 823 assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); 824 assertThat(properties.getKeyset()).contains(KEY); 825 assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE); 826 } catch (ExecutionException | TimeoutException | InterruptedException e) { 827 Assert.fail(e.getMessage()); 828 } finally { 829 DeviceConfig.removeOnPropertiesChangedListener(changeListener); 830 } 831 } 832 833 @Test 834 @FlakyTest(bugId = 299483542) onPropertiesChangedListener_setPropertiesCallback()835 public void onPropertiesChangedListener_setPropertiesCallback() { 836 DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false); 837 DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false); 838 839 Map<String, String> keyValues = new HashMap<>(2); 840 keyValues.put(KEY, VALUE2); 841 keyValues.put(KEY3, VALUE3); 842 Properties setProperties = new Properties(NAMESPACE, keyValues); 843 844 final AtomicReference<Properties> changedProperties = new AtomicReference<>(); 845 final CompletableFuture<Boolean> completed = new CompletableFuture<>(); 846 847 DeviceConfig.OnPropertiesChangedListener changeListener = (properties) -> { 848 changedProperties.set(properties); 849 completed.complete(true); 850 }; 851 852 try { 853 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, 854 Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(), 855 changeListener); 856 DeviceConfig.setProperties(setProperties); 857 858 assertThat(completed.get( 859 WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); 860 861 if (changedProperties.get().getKeyset().size() != 3) { 862 // Sometimes there are a few onChanged callback calls. Let's wait a bit more. 863 final int oneMoreOnChangedDelayMs = 100; 864 Thread.currentThread().sleep(oneMoreOnChangedDelayMs); 865 } 866 867 Properties properties = changedProperties.get(); 868 assertThat(properties.getNamespace()).isEqualTo(NAMESPACE); 869 assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3); 870 // KEY updated from VALUE to VALUE2 871 assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE2); 872 // KEY2 deleted (returns default_value) 873 assertThat(properties.getString(KEY2, "default_value")).isEqualTo("default_value"); 874 // KEY3 added with VALUE3 875 assertThat(properties.getString(KEY3, "default_value")).isEqualTo(VALUE3); 876 } catch (ExecutionException | TimeoutException | InterruptedException e) { 877 Assert.fail(e.getMessage()); 878 } catch (DeviceConfig.BadConfigException e) { 879 Assert.fail(e.getMessage()); 880 } finally { 881 DeviceConfig.removeOnPropertiesChangedListener(changeListener); 882 } 883 } 884 885 @Test 886 @Ignore("b/300174188") syncDisabling()887 public void syncDisabling() throws Exception { 888 Properties properties1 = new Properties.Builder(NAMESPACE) 889 .setString(KEY, VALUE) 890 .build(); 891 Properties properties2 = new Properties.Builder(NAMESPACE) 892 .setString(KEY, VALUE2) 893 .build(); 894 895 try { 896 // Ensure the device starts in a known state. 897 DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_NONE); 898 899 // Assert starting state. 900 assertThat(DeviceConfig.getSyncDisabledMode()) 901 .isEqualTo(DeviceConfig.SYNC_DISABLED_MODE_NONE); 902 assertThat(DeviceConfig.setProperties(properties1)).isTrue(); 903 assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) 904 .isEqualTo(VALUE); 905 906 // Test disabled (persistent). Persistence is not actually tested, that would require 907 // a host test. 908 DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_PERSISTENT); 909 assertThat(DeviceConfig.getSyncDisabledMode()) 910 .isEqualTo(DeviceConfig.SYNC_DISABLED_MODE_PERSISTENT); 911 assertThat(DeviceConfig.setProperties(properties2)).isFalse(); 912 assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) 913 .isEqualTo(VALUE); 914 915 // Return to not disabled. 916 DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_NONE); 917 assertThat(DeviceConfig.getSyncDisabledMode()) 918 .isEqualTo(DeviceConfig.SYNC_DISABLED_MODE_NONE); 919 assertThat(DeviceConfig.setProperties(properties2)).isTrue(); 920 assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) 921 .isEqualTo(VALUE2); 922 923 // Test disabled (persistent). Absence of persistence is not actually tested, that would 924 // require a host test. 925 DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_UNTIL_REBOOT); 926 assertThat(DeviceConfig.getSyncDisabledMode()) 927 .isEqualTo(DeviceConfig.SYNC_DISABLED_MODE_UNTIL_REBOOT); 928 assertThat(DeviceConfig.setProperties(properties1)).isFalse(); 929 assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) 930 .isEqualTo(VALUE2); 931 932 // Return to not disabled. 933 DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_NONE); 934 assertThat(DeviceConfig.getSyncDisabledMode()) 935 .isEqualTo(DeviceConfig.SYNC_DISABLED_MODE_NONE); 936 assertThat(DeviceConfig.setProperties(properties1)).isTrue(); 937 assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE)) 938 .isEqualTo(VALUE); 939 } finally { 940 // Try to return to the default sync disabled state in case of failure. 941 DeviceConfig.setSyncDisabledMode(DeviceConfig.SYNC_DISABLED_MODE_NONE); 942 943 // NAMESPACE will be cleared by cleanUp() 944 } 945 } 946 deleteViaContentProvider(String namespace, String key)947 private static boolean deleteViaContentProvider(String namespace, String key) { 948 ContentResolver resolver = InstrumentationRegistry.getContext().getContentResolver(); 949 String compositeName = namespace + "/" + key; 950 Bundle result = resolver.call( 951 Settings.Config.CONTENT_URI, 952 Settings.CALL_METHOD_DELETE_CONFIG, 953 compositeName, 954 null); 955 assertThat(result).isNotNull(); 956 return compositeName.equals(result.getString(Settings.NameValueTable.VALUE)); 957 } 958 959 @Test deleteProperty_nullNamespace()960 public void deleteProperty_nullNamespace() { 961 try { 962 DeviceConfig.deleteProperty(null, KEY); 963 Assert.fail("Null namespace should have resulted in an NPE."); 964 } catch (NullPointerException e) { 965 // expected 966 } 967 } 968 969 @Test deleteProperty_nullName()970 public void deleteProperty_nullName() { 971 try { 972 DeviceConfig.deleteProperty(NAMESPACE, null); 973 Assert.fail("Null name should have resulted in an NPE."); 974 } catch (NullPointerException e) { 975 // expected 976 } 977 } 978 979 @Test deletePropertyString()980 public void deletePropertyString() { 981 final String value = "new_value"; 982 final String default_value = "default"; 983 DeviceConfig.setProperty(NAMESPACE, KEY, value, false); 984 DeviceConfig.deleteProperty(NAMESPACE, KEY); 985 final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value); 986 assertThat(result).isEqualTo(default_value); 987 } 988 989 @Test deletePropertyBoolean()990 public void deletePropertyBoolean() { 991 final boolean value = true; 992 final boolean default_value = false; 993 DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false); 994 DeviceConfig.deleteProperty(NAMESPACE, KEY); 995 final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value); 996 assertThat(result).isEqualTo(default_value); 997 } 998 999 @Test deletePropertyInt()1000 public void deletePropertyInt() { 1001 final int value = 123; 1002 final int default_value = 999; 1003 DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false); 1004 DeviceConfig.deleteProperty(NAMESPACE, KEY); 1005 final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value); 1006 assertThat(result).isEqualTo(default_value); 1007 } 1008 1009 @Test deletePropertyLong()1010 public void deletePropertyLong() { 1011 final long value = 456789; 1012 final long default_value = 123456; 1013 DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false); 1014 DeviceConfig.deleteProperty(NAMESPACE, KEY); 1015 final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value); 1016 assertThat(result).isEqualTo(default_value); 1017 } 1018 1019 @Test deletePropertyFloat()1020 public void deletePropertyFloat() { 1021 final float value = 456.789f; 1022 final float default_value = 123.456f; 1023 DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false); 1024 DeviceConfig.deleteProperty(NAMESPACE, KEY); 1025 final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value); 1026 assertThat(result).isEqualTo(default_value); 1027 } 1028 1029 @Test deleteProperty_empty()1030 public void deleteProperty_empty() { 1031 assertThat(DeviceConfig.deleteProperty(NAMESPACE, KEY)).isTrue(); 1032 final String result = DeviceConfig.getString(NAMESPACE, KEY, null); 1033 assertThat(result).isNull(); 1034 } 1035 } 1036