1 /* 2 * Copyright (C) 2019 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.deviceconfig.cts; 18 19 import static android.provider.Settings.RESET_MODE_PACKAGE_DEFAULTS; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.fail; 24 25 import android.os.SystemClock; 26 import android.provider.DeviceConfig; 27 import android.provider.DeviceConfig.OnPropertiesChangedListener; 28 import android.provider.DeviceConfig.Properties; 29 30 import androidx.test.InstrumentationRegistry; 31 import androidx.test.runner.AndroidJUnit4; 32 33 import org.junit.After; 34 import org.junit.AfterClass; 35 import org.junit.BeforeClass; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.concurrent.Executor; 42 43 @RunWith(AndroidJUnit4.class) 44 public final class DeviceConfigApiTests { 45 private static final String NAMESPACE1 = "namespace1"; 46 private static final String NAMESPACE2 = "namespace2"; 47 private static final String EMPTY_NAMESPACE = "empty_namespace"; 48 private static final String KEY1 = "key1"; 49 private static final String KEY2 = "key2"; 50 private static final String VALUE1 = "value1"; 51 private static final String VALUE2 = "value2"; 52 private static final String DEFAULT_VALUE = "default_value"; 53 54 private static final boolean DEFAULT_BOOLEAN_TRUE = true; 55 private static final boolean DEFAULT_BOOLEAN_FALSE = false; 56 private static final boolean BOOLEAN_TRUE = true; 57 private static final boolean BOOLEAN_FALSE = false; 58 private static final String INVALID_BOOLEAN = "TR_UE"; 59 60 private static final int DEFAULT_INT = 999; 61 private static final int VALID_INT = 123; 62 private static final String INVALID_INT = "12E"; 63 64 private static final long DEFAULT_LONG = 123456; 65 private static final long VALID_LONG = 278724287; 66 private static final String INVALID_LONG = "23232R42"; 67 68 private static final float DEFAULT_FLOAT = 123.456f; 69 private static final float VALID_FLOAT = 456.789f; 70 private static final String INVALID_FLOAT = "34343et"; 71 72 private static final Executor EXECUTOR = InstrumentationRegistry.getContext().getMainExecutor(); 73 74 75 private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec 76 private final Object mLock = new Object(); 77 78 79 private static final String WRITE_DEVICE_CONFIG_PERMISSION = 80 "android.permission.WRITE_DEVICE_CONFIG"; 81 82 private static final String READ_DEVICE_CONFIG_PERMISSION = 83 "android.permission.READ_DEVICE_CONFIG"; 84 85 /** 86 * Get necessary permissions to access and modify properties through DeviceConfig API. 87 */ 88 @BeforeClass setUp()89 public static void setUp() throws Exception { 90 InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( 91 WRITE_DEVICE_CONFIG_PERMISSION, READ_DEVICE_CONFIG_PERMISSION); 92 } 93 94 /** 95 * Nullify properties in DeviceConfig API after completion of every test. 96 */ 97 @After cleanUp()98 public void cleanUp() { 99 nullifyProperty(NAMESPACE1, KEY1); 100 nullifyProperty(NAMESPACE2, KEY1); 101 nullifyProperty(NAMESPACE1, KEY2); 102 nullifyProperty(NAMESPACE2, KEY2); 103 } 104 105 /** 106 * Delete properties in DeviceConfig API after completion of all tests and drop shell 107 * permissions. 108 */ 109 @AfterClass cleanUpAfterAllTests()110 public static void cleanUpAfterAllTests() { 111 deletePropertyThrowShell(NAMESPACE1, KEY1); 112 deletePropertyThrowShell(NAMESPACE2, KEY1); 113 deletePropertyThrowShell(NAMESPACE1, KEY2); 114 deletePropertyThrowShell(NAMESPACE2, KEY2); 115 InstrumentationRegistry.getInstrumentation().getUiAutomation() 116 .dropShellPermissionIdentity(); 117 } 118 119 /** 120 * Checks that getting property which does not exist returns null. 121 */ 122 @Test getProperty_empty()123 public void getProperty_empty() { 124 String result = DeviceConfig.getProperty(EMPTY_NAMESPACE, KEY1); 125 assertNull("Request for non existant flag name in DeviceConfig API should return null " 126 + "while " + result + " was returned", result); 127 } 128 129 /** 130 * Checks that setting and getting property from the same namespace return correct value. 131 */ 132 @Test setAndGetProperty_sameNamespace()133 public void setAndGetProperty_sameNamespace() { 134 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 135 String result = DeviceConfig.getProperty(NAMESPACE1, KEY1); 136 assertEquals("Value read from DeviceConfig API does not match written value.", VALUE1, 137 result); 138 } 139 140 /** 141 * Checks that setting a property in one namespace does not set the same property in a different 142 * namespace. 143 */ 144 @Test setAndGetProperty_differentNamespace()145 public void setAndGetProperty_differentNamespace() { 146 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 147 String result = DeviceConfig.getProperty(NAMESPACE2, KEY1); 148 assertNull("Value for same keys written to different namespaces must not clash", result); 149 } 150 151 /** 152 * Checks that different namespaces can keep different values for the same key. 153 */ 154 @Test setAndGetProperty_multipleNamespaces()155 public void setAndGetProperty_multipleNamespaces() { 156 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 157 DeviceConfig.setProperty(NAMESPACE2, KEY1, VALUE2, /*makeDefault=*/false); 158 String result = DeviceConfig.getProperty(NAMESPACE1, KEY1); 159 assertEquals("Value read from DeviceConfig API does not match written value.", VALUE1, 160 result); 161 result = DeviceConfig.getProperty(NAMESPACE2, KEY1); 162 assertEquals("Value read from DeviceConfig API does not match written value.", VALUE2, 163 result); 164 } 165 166 /** 167 * Checks that saving value twice keeps the last value. 168 */ 169 @Test setAndGetProperty_overrideValue()170 public void setAndGetProperty_overrideValue() { 171 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 172 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false); 173 String result = DeviceConfig.getProperty(NAMESPACE1, KEY1); 174 assertEquals("New value written to the same namespace/key did not override previous" 175 + " value.", VALUE2, result); 176 } 177 178 /** 179 * Checks that getString() for null property returns default value. 180 */ 181 @Test getString_empty()182 public void getString_empty() { 183 final String result = DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE); 184 assertEquals("DeviceConfig.getString() must return default value if property is null", 185 DEFAULT_VALUE, result); 186 } 187 188 /** 189 * Checks that getString() for null property returns default value even if it is null. 190 */ 191 @Test getString_nullDefault()192 public void getString_nullDefault() { 193 final String result = DeviceConfig.getString(NAMESPACE1, KEY1, null); 194 assertEquals("DeviceConfig.getString() must return default value if property is null", 195 null, result); 196 } 197 198 /** 199 * Checks that getString() returns string saved in property. 200 */ 201 @Test getString_nonEmpty()202 public void getString_nonEmpty() { 203 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 204 205 final String result = DeviceConfig.getString(NAMESPACE1, KEY1, DEFAULT_VALUE); 206 assertEquals("DeviceConfig.getString() must return same value as getProperty() when " + 207 "property is not null", VALUE1, result); 208 } 209 210 /** 211 * Checks that getString() fails with NullPointerException when called with null namespace. 212 */ 213 @Test getString_nullNamespace()214 public void getString_nullNamespace() { 215 try { 216 DeviceConfig.getString(null, KEY1, DEFAULT_VALUE); 217 fail("DeviceConfig.getString() with null namespace must result in " 218 + "NullPointerException"); 219 } catch (NullPointerException e) { 220 // expected 221 } 222 } 223 224 /** 225 * Checks that getString() fails with NullPointerException when called with null key. 226 */ 227 @Test getString_nullName()228 public void getString_nullName() { 229 try { 230 DeviceConfig.getString(NAMESPACE1, null, DEFAULT_VALUE); 231 fail("DeviceConfig.getString() with null name must result in NullPointerException"); 232 } catch (NullPointerException e) { 233 // expected 234 } 235 } 236 237 /** 238 * Checks that getBoolean() for null property returns default value. 239 */ 240 @Test getBoolean_empty()241 public void getBoolean_empty() { 242 final boolean result = DeviceConfig.getBoolean(NAMESPACE1, KEY1, DEFAULT_BOOLEAN_TRUE); 243 assertEquals("DeviceConfig.getBoolean() must return default value if property is null", 244 DEFAULT_BOOLEAN_TRUE, result); 245 } 246 247 /** 248 * Checks that getBoolean() returns boolean representation of string saved in property. 249 */ 250 @Test getBoolean_valid()251 public void getBoolean_valid() { 252 DeviceConfig.setProperty(NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE), 253 /*makeDefault=*/false); 254 255 final boolean result = DeviceConfig.getBoolean(NAMESPACE1, KEY1, DEFAULT_BOOLEAN_FALSE); 256 assertEquals("DeviceConfig.getString() must return boolean equivalent value of" 257 + " getProperty() when property is not null", BOOLEAN_TRUE, result); 258 } 259 260 /** 261 * Checks that getBoolean() returns false for any invalid property value. 262 */ 263 @Test getBoolean_invalid()264 public void getBoolean_invalid() { 265 DeviceConfig.setProperty(NAMESPACE1, KEY1, INVALID_BOOLEAN, /*makeDefault=*/false); 266 267 final boolean result = DeviceConfig.getBoolean(NAMESPACE1, KEY1, DEFAULT_BOOLEAN_TRUE); 268 // Anything non-null other than case insensitive "true" parses to false. 269 assertEquals("DeviceConfig.getBoolean() must return boolean equivalent value of" 270 + " getProperty() when property is not null", BOOLEAN_FALSE, result); 271 } 272 273 /** 274 * Checks that getBoolean() fails with NullPointerException when called with null namespace. 275 */ 276 @Test getBoolean_nullNamespace()277 public void getBoolean_nullNamespace() { 278 try { 279 DeviceConfig.getBoolean(null, KEY1, DEFAULT_BOOLEAN_TRUE); 280 fail("DeviceConfig.getBoolean() with null namespace must result in " 281 + "NullPointerException"); 282 } catch (NullPointerException e) { 283 // expected 284 } 285 } 286 287 /** 288 * Checks that getBoolean() fails with NullPointerException when called with null name. 289 */ 290 @Test getBoolean_nullName()291 public void getBoolean_nullName() { 292 try { 293 DeviceConfig.getBoolean(NAMESPACE1, null, DEFAULT_BOOLEAN_TRUE); 294 fail("DeviceConfig.getBoolean() with null name must result in NullPointerException"); 295 } catch (NullPointerException e) { 296 // expected 297 } 298 } 299 300 /** 301 * Checks that getInt() for null property returns default value. 302 */ 303 @Test getInt_empty()304 public void getInt_empty() { 305 final int result = DeviceConfig.getInt(NAMESPACE1, KEY1, DEFAULT_INT); 306 assertEquals("DeviceConfig.getInt() must return default value if property is null", 307 DEFAULT_INT, result); 308 } 309 310 /** 311 * Checks that getInt() returns integer representation of string saved in property. 312 */ 313 @Test getInt_valid()314 public void getInt_valid() { 315 DeviceConfig.setProperty(NAMESPACE1, KEY1, String.valueOf(VALID_INT), 316 /*makeDefault=*/false); 317 318 final int result = DeviceConfig.getInt(NAMESPACE1, KEY1, DEFAULT_INT); 319 assertEquals("DeviceConfig.getInt() must return integer equivalent value of" 320 + " getProperty() when property is not null", VALID_INT, result); 321 } 322 323 /** 324 * Checks that getInt() returns default value if property is not well-formed integer value. 325 */ 326 @Test getInt_invalid()327 public void getInt_invalid() { 328 DeviceConfig.setProperty(NAMESPACE1, KEY1, INVALID_INT, /*makeDefault=*/false); 329 330 final int result = DeviceConfig.getInt(NAMESPACE1, KEY1, DEFAULT_INT); 331 // Failure to parse results in using the default value 332 assertEquals("DeviceConfig.getInt() must return integer equivalent value of" 333 + " getProperty() when property is not null", DEFAULT_INT, result); 334 } 335 336 /** 337 * Checks that getInt() fails with NullPointerException when called with null namespace. 338 */ 339 @Test getInt_nullNamespace()340 public void getInt_nullNamespace() { 341 try { 342 DeviceConfig.getInt(null, KEY1, VALID_INT); 343 fail("DeviceConfig.getInt() with null namespace must result in NullPointerException"); 344 } catch (NullPointerException e) { 345 // expected 346 } 347 } 348 349 /** 350 * Checks that getInt() fails with NullPointerException when called with null name. 351 */ 352 @Test getInt_nullName()353 public void getInt_nullName() { 354 try { 355 DeviceConfig.getInt(NAMESPACE1, null, VALID_INT); 356 fail("DeviceConfig.getInt() with null name must result in NullPointerException"); 357 } catch (NullPointerException e) { 358 // expected 359 } 360 } 361 362 /** 363 * Checks that getLong() for null property returns default value. 364 */ 365 @Test getLong_empty()366 public void getLong_empty() { 367 final long result = DeviceConfig.getLong(NAMESPACE1, KEY1, DEFAULT_LONG); 368 assertEquals("DeviceConfig.getLong() must return default value if property is null", 369 DEFAULT_LONG, result); 370 } 371 372 /** 373 * Checks that getLong() returns long representation of string saved in property. 374 */ 375 @Test getLong_valid()376 public void getLong_valid() { 377 DeviceConfig.setProperty(NAMESPACE1, KEY1, String.valueOf(VALID_LONG), 378 /*makeDefault=*/false); 379 380 final long result = DeviceConfig.getLong(NAMESPACE1, KEY1, DEFAULT_LONG); 381 assertEquals("DeviceConfig.getLong() must return long equivalent value of" 382 + " getProperty() when property is not null", VALID_LONG, result); 383 } 384 385 /** 386 * Checks that getLong() returns default value if property is not well-formed long value. 387 */ 388 @Test getLong_invalid()389 public void getLong_invalid() { 390 DeviceConfig.setProperty(NAMESPACE1, KEY1, INVALID_LONG, /*makeDefault=*/false); 391 392 final long result = DeviceConfig.getLong(NAMESPACE1, KEY1, DEFAULT_LONG); 393 // Failure to parse results in using the default value 394 assertEquals("DeviceConfig.getLong() must return long equivalent value of" 395 + " getProperty() when property is not null", DEFAULT_LONG, result); 396 } 397 398 /** 399 * Checks that getLong() fails with NullPointerException when called with null namespace. 400 */ 401 @Test getLong_nullNamespace()402 public void getLong_nullNamespace() { 403 try { 404 DeviceConfig.getLong(null, KEY1, DEFAULT_LONG); 405 fail("DeviceConfig.getLong() with null namespace must result in " 406 + "NullPointerException"); 407 } catch (NullPointerException e) { 408 // expected 409 } 410 } 411 412 /** 413 * Checks that getLong() fails with NullPointerException when called with null name. 414 */ 415 @Test getLong_nullName()416 public void getLong_nullName() { 417 try { 418 DeviceConfig.getLong(NAMESPACE1, null, 0); 419 fail("DeviceConfig.getLong() with null name must result in NullPointerException"); 420 } catch (NullPointerException e) { 421 // expected 422 } 423 } 424 425 /** 426 * Checks that getFloat() for null property returns default value. 427 */ 428 @Test getFloat_empty()429 public void getFloat_empty() { 430 final float result = DeviceConfig.getFloat(NAMESPACE1, KEY1, DEFAULT_FLOAT); 431 assertEquals("DeviceConfig.getFloat() must return default value if property is null", 432 DEFAULT_FLOAT, result, 0.0); 433 } 434 435 /** 436 * Checks that getFloat() returns float representation of string saved in property. 437 */ 438 @Test getFloat_valid()439 public void getFloat_valid() { 440 DeviceConfig.setProperty(NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT), 441 /*makeDefault=*/false); 442 443 final float result = DeviceConfig.getFloat(NAMESPACE1, KEY1, DEFAULT_FLOAT); 444 assertEquals("DeviceConfig.getFloat() must return float equivalent value of" 445 + " getProperty() when property is not null", VALID_FLOAT, result, 0.0); 446 } 447 448 /** 449 * Checks that getFloat() returns default value if property is not well-formed float value. 450 */ 451 @Test getFloat_invalid()452 public void getFloat_invalid() { 453 DeviceConfig.setProperty(NAMESPACE1, KEY1, INVALID_FLOAT, /*makeDefault=*/false); 454 455 final float result = DeviceConfig.getFloat(NAMESPACE1, KEY1, DEFAULT_FLOAT); 456 // Failure to parse results in using the default value 457 assertEquals("DeviceConfig.getFloat() must return float equivalent value of" 458 + " getProperty() when property is not null", DEFAULT_FLOAT, result, 0.0f); 459 } 460 461 /** 462 * Checks that getFloat() fails with NullPointerException when called with null namespace. 463 */ 464 @Test getFloat_nullNamespace()465 public void getFloat_nullNamespace() { 466 try { 467 DeviceConfig.getFloat(null, KEY1, DEFAULT_FLOAT); 468 fail("DeviceConfig.getFloat() with null namespace must result in " 469 + "NullPointerException"); 470 } catch (NullPointerException e) { 471 // expected 472 } 473 } 474 475 /** 476 * Checks that getFloat() fails with NullPointerException when called with null name. 477 */ 478 @Test getFloat_nullName()479 public void getFloat_nullName() { 480 try { 481 DeviceConfig.getFloat(NAMESPACE1, null, DEFAULT_FLOAT); 482 fail("DeviceConfig.getFloat() with null name must result in NullPointerException"); 483 } catch (NullPointerException e) { 484 // expected 485 } 486 } 487 488 /** 489 * Checks that setProperty() fails with NullPointerException when called with null namespace. 490 */ 491 @Test setProperty_nullNamespace()492 public void setProperty_nullNamespace() { 493 try { 494 DeviceConfig.setProperty(null, KEY1, DEFAULT_VALUE, /*makeDefault=*/false); 495 fail("DeviceConfig.setProperty() with null namespace must result in " 496 + "NullPointerException"); 497 } catch (NullPointerException e) { 498 // expected 499 } 500 } 501 502 /** 503 * Checks that setProperty() fails with NullPointerException when called with null name. 504 */ 505 @Test setProperty_nullName()506 public void setProperty_nullName() { 507 try { 508 DeviceConfig.setProperty(NAMESPACE1, null, DEFAULT_VALUE, /*makeDefault=*/false); 509 fail("DeviceConfig.setProperty() with null name must result in NullPointerException"); 510 } catch (NullPointerException e) { 511 // expected 512 } 513 } 514 515 /** 516 * Checks that Properties.getString() for null property returns default value. 517 */ 518 @Test getPropertiesString_empty()519 public void getPropertiesString_empty() { 520 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1); 521 final Properties properties = 522 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null); 523 final String result = properties.getString(KEY1, DEFAULT_VALUE); 524 assertEquals("DeviceConfig.Properties.getString() must return default value if property " 525 + "is null", DEFAULT_VALUE, result); 526 } 527 528 /** 529 * Checks that Properties.getString() for null property returns default value even if it is 530 * null. 531 */ 532 @Test getPropertiesString_nullDefault()533 public void getPropertiesString_nullDefault() { 534 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, DEFAULT_VALUE); 535 final Properties properties = 536 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null); 537 final String result = properties.getString(KEY1, null); 538 assertEquals("DeviceConfig.Properties.getString() must return default value if property is " 539 + "null", null, result); 540 } 541 542 /** 543 * Checks that Properties.getString() returns string saved in property. 544 */ 545 @Test getPropertiesString_nonEmpty()546 public void getPropertiesString_nonEmpty() { 547 final Properties properties = 548 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1); 549 550 final String result = properties.getString(KEY1, DEFAULT_VALUE); 551 assertEquals("DeviceConfig.Properties.getString() must return same value as getProperty() " 552 + "when property is not null", VALUE1, result); 553 } 554 555 /** 556 * Checks that Properties.getBoolean() for null property returns default value. 557 */ 558 @Test getPropertiesBoolean_empty()559 public void getPropertiesBoolean_empty() { 560 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE)); 561 final Properties properties = 562 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null); 563 final boolean result = properties.getBoolean(KEY1, DEFAULT_BOOLEAN_TRUE); 564 assertEquals("DeviceConfig.Properties.getBoolean() must return default value if property " 565 + "is null", DEFAULT_BOOLEAN_TRUE, result); 566 } 567 568 /** 569 * Checks that Properties.getBoolean() returns boolean representation of string saved in 570 * property. 571 */ 572 @Test getPropertiesBoolean_valid()573 public void getPropertiesBoolean_valid() { 574 final Properties properties = setPropertiesAndAssertSuccessfulChange( 575 NAMESPACE1, KEY1, String.valueOf(BOOLEAN_TRUE)); 576 final boolean result = properties.getBoolean(KEY1, DEFAULT_BOOLEAN_FALSE); 577 assertEquals("DeviceConfig.Properties.getString() must return boolean equivalent value of" 578 + " getProperty() when property is not null", BOOLEAN_TRUE, result); 579 } 580 581 /** 582 * Checks that Properties.getBoolean() returns false for any invalid (non parselable) property 583 * value. 584 */ 585 @Test getPropertiesBoolean_invalid()586 public void getPropertiesBoolean_invalid() { 587 final Properties properties = 588 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, INVALID_BOOLEAN); 589 590 final boolean result = properties.getBoolean(KEY1, DEFAULT_BOOLEAN_TRUE); 591 // Anything non-null other than case insensitive "true" parses to false. 592 assertEquals("DeviceConfig.Properties.getBoolean() must return boolean equivalent value of" 593 + " getProperty() when property is not null", BOOLEAN_FALSE, result); 594 } 595 596 /** 597 * Checks that Properties.getInt() for null property returns default value. 598 */ 599 @Test getPropertiesInt_empty()600 public void getPropertiesInt_empty() { 601 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_INT)); 602 final Properties properties = 603 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null); 604 605 final int result = properties.getInt(KEY1, DEFAULT_INT); 606 assertEquals("DeviceConfig.Properties.getInt() must return default value if property is " 607 + "null", DEFAULT_INT, result); 608 } 609 610 /** 611 * Checks that Properties.getInt() returns integer representation of string saved in property. 612 */ 613 @Test getPropertiesInt_valid()614 public void getPropertiesInt_valid() { 615 final Properties properties = 616 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_INT)); 617 618 final int result = properties.getInt(KEY1, DEFAULT_INT); 619 assertEquals("DeviceConfig.Properties.getInt() must return integer equivalent value of" 620 + " getProperty() when property is not null", VALID_INT, result); 621 } 622 623 /** 624 * Checks that Properties.getInt() returns default value if property is not well-formed integer 625 * value. 626 */ 627 @Test getPropertiesInt_invalid()628 public void getPropertiesInt_invalid() { 629 final Properties properties = 630 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, INVALID_INT); 631 632 final int result = properties.getInt(KEY1, DEFAULT_INT); 633 // Failure to parse results in using the default value 634 assertEquals("DeviceConfig.Properties.getInt() must return integer equivalent value of" 635 + " getProperty() when property is not null", DEFAULT_INT, result); 636 } 637 638 /** 639 * Checks that Properties.getLong() for null property returns default value. 640 */ 641 @Test getPropertiesLong_empty()642 public void getPropertiesLong_empty() { 643 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_LONG)); 644 final Properties properties = 645 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null); 646 647 final long result = properties.getLong(KEY1, DEFAULT_LONG); 648 assertEquals("DeviceConfig.Properties.getLong() must return default value if property is " 649 + "null", DEFAULT_LONG, result); 650 } 651 652 /** 653 * Checks that Properties.getLong() returns long representation of string saved in property. 654 */ 655 @Test getPropertiesLong_valid()656 public void getPropertiesLong_valid() { 657 final Properties properties = 658 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_LONG)); 659 660 final long result = properties.getLong(KEY1, DEFAULT_LONG); 661 assertEquals("DeviceConfig.Properties.getLong() must return long equivalent value of" 662 + " getProperty() when property is not null", VALID_LONG, result); 663 } 664 665 /** 666 * Checks that Properties.getLong() returns default value if property is not well-formed long 667 * value. 668 */ 669 @Test getPropertiesLong_invalid()670 public void getPropertiesLong_invalid() { 671 final Properties properties = 672 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, INVALID_LONG); 673 674 final long result = properties.getLong(KEY1, DEFAULT_LONG); 675 // Failure to parse results in using the default value 676 assertEquals("DeviceConfig.Properties.getLong() must return long equivalent value of" 677 + " getProperty() when property is not null", DEFAULT_LONG, result); 678 } 679 680 /** 681 * Checks that Properties.getFloat() for null property returns default value. 682 */ 683 @Test getPropertiesFloat_empty()684 public void getPropertiesFloat_empty() { 685 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT)); 686 final Properties properties = 687 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, null); 688 final float result = properties.getFloat(KEY1, DEFAULT_FLOAT); 689 assertEquals("DeviceConfig.Properties.getFloat() must return default value if property is " 690 + "null", DEFAULT_FLOAT, result, 0.0f); 691 } 692 693 /** 694 * Checks that Properties.getFloat() returns float representation of string saved in property. 695 */ 696 @Test getPropertiesFloat_valid()697 public void getPropertiesFloat_valid() { 698 final Properties properties = setPropertiesAndAssertSuccessfulChange( 699 NAMESPACE1, KEY1, String.valueOf(VALID_FLOAT)); 700 701 final float result = properties.getFloat(KEY1, DEFAULT_FLOAT); 702 assertEquals("DeviceConfig.Properties.getFloat() must return float equivalent value of" 703 + " getProperty() when property is not null", VALID_FLOAT, result, 0.0f); 704 } 705 706 /** 707 * Checks that Properties.getFloat() returns default value if property is not well-formed float 708 * value. 709 */ 710 @Test getPropertiesFloat_invalid()711 public void getPropertiesFloat_invalid() { 712 final Properties properties = setPropertiesAndAssertSuccessfulChange( 713 NAMESPACE1, KEY1, INVALID_FLOAT); 714 715 final float result = properties.getFloat(KEY1, DEFAULT_FLOAT); 716 // Failure to parse results in using the default value 717 assertEquals("DeviceConfig.Properties.getFloat() must return float equivalent value of" 718 + " getProperty() when property is not null", DEFAULT_FLOAT, result, 0.0f); 719 } 720 721 /** 722 * Test that properties listener is successfully registered and provides callbacks on value 723 * change. 724 */ 725 @Test testPropertiesListener()726 public void testPropertiesListener() { 727 setPropertiesAndAssertSuccessfulChange(NAMESPACE1, KEY1, VALUE1); 728 } 729 730 /** 731 * Test that two properties listeners subscribed to the same namespace are successfully 732 * registered and unregistered while receiving correct updates in all states. 733 */ 734 @Test testTwoPropertiesListenersSameNamespace()735 public void testTwoPropertiesListenersSameNamespace() { 736 final List<PropertyUpdate> receivedUpdates1 = new ArrayList<>(); 737 final List<PropertyUpdate> receivedUpdates2 = new ArrayList<>(); 738 739 OnPropertiesChangedListener listener1 = createOnPropertiesChangedListener(receivedUpdates1); 740 OnPropertiesChangedListener listener2 = createOnPropertiesChangedListener(receivedUpdates2); 741 742 try { 743 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE1, EXECUTOR, listener1); 744 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 745 746 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/1); 747 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/0); 748 749 assertEquals("OnPropertiesListener did not receive expected update", 750 receivedUpdates1.size(), /*expectedTotalUpdatesCount=*/1); 751 assertEquals("OnPropertiesListener received unexpected update", 752 receivedUpdates2.size(), /*expectedTotalUpdatesCount=*/0); 753 receivedUpdates1.get(0).assertEqual(NAMESPACE1, KEY1, VALUE1); 754 755 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE1, EXECUTOR, listener2); 756 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false); 757 758 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2); 759 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/1); 760 761 assertEquals("OnPropertiesListener did not receive expected update", 762 receivedUpdates1.size(), 2); 763 assertEquals("OnPropertiesListener did not receive expected update", 764 receivedUpdates2.size(), 1); 765 receivedUpdates1.get(1).assertEqual(NAMESPACE1, KEY1, VALUE2); 766 receivedUpdates2.get(0).assertEqual(NAMESPACE1, KEY1, VALUE2); 767 768 DeviceConfig.removeOnPropertiesChangedListener(listener1); 769 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 770 771 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/2); 772 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2); 773 774 assertEquals("OnPropertiesListener received unexpected update", 775 receivedUpdates1.size(), 2); 776 assertEquals("OnPropertiesListener did not receive expected update", 777 receivedUpdates2.size(), 2); 778 779 receivedUpdates2.get(1).assertEqual(NAMESPACE1, KEY1, VALUE1); 780 781 DeviceConfig.removeOnPropertiesChangedListener(listener2); 782 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false); 783 784 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/2); 785 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2); 786 787 assertEquals("OnPropertiesListener received unexpected update", 788 receivedUpdates1.size(), 2); 789 assertEquals("OnPropertiesListener received unexpected update", 790 receivedUpdates2.size(), 2); 791 } finally { 792 DeviceConfig.removeOnPropertiesChangedListener(listener1); 793 DeviceConfig.removeOnPropertiesChangedListener(listener2); 794 } 795 } 796 797 /** 798 * Test that two properties listeners subscribed to different namespaces are successfully 799 * registered and unregistered while receiving correct updates in all states. 800 */ 801 @Test testTwoPropertiesListenersDifferentNamespace()802 public void testTwoPropertiesListenersDifferentNamespace() { 803 final List<PropertyUpdate> receivedUpdates1 = new ArrayList<>(); 804 final List<PropertyUpdate> receivedUpdates2 = new ArrayList<>(); 805 806 OnPropertiesChangedListener listener1 = createOnPropertiesChangedListener(receivedUpdates1); 807 OnPropertiesChangedListener listener2 = createOnPropertiesChangedListener(receivedUpdates2); 808 809 try { 810 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE1, EXECUTOR, listener1); 811 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 812 813 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/1); 814 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/0); 815 816 assertEquals("OnPropertiesListener did not receive expected update", 817 receivedUpdates1.size(), 1); 818 assertEquals("OnPropertiesListener received unexpected update", 819 receivedUpdates2.size(), 0); 820 receivedUpdates1.get(0).assertEqual(NAMESPACE1, KEY1, VALUE1); 821 822 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE2, EXECUTOR, listener2); 823 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false); 824 825 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2); 826 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/0); 827 828 assertEquals("OnPropertiesListener did not receive expected update", 829 receivedUpdates1.size(), 2); 830 assertEquals("OnPropertiesListener received unexpected update", 831 receivedUpdates2.size(), 0); 832 receivedUpdates1.get(1).assertEqual(NAMESPACE1, KEY1, VALUE2); 833 834 DeviceConfig.setProperty(NAMESPACE2, KEY1, VALUE1, /*makeDefault=*/false); 835 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/1); 836 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2); 837 838 assertEquals("OnPropertiesListener received unexpected update", 839 receivedUpdates1.size(), 2); 840 assertEquals("OnPropertiesListener did not receive expected update", 841 receivedUpdates2.size(), 1); 842 843 receivedUpdates2.get(0).assertEqual(NAMESPACE2, KEY1, VALUE1); 844 845 DeviceConfig.removeOnPropertiesChangedListener(listener1); 846 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/false); 847 848 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/1); 849 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2); 850 851 assertEquals("OnPropertiesListener received unexpected update", 852 receivedUpdates1.size(), 2); 853 assertEquals("OnPropertiesListener received unexpected update", 854 receivedUpdates2.size(), 1); 855 856 DeviceConfig.setProperty(NAMESPACE2, KEY1, VALUE2, /*makeDefault=*/false); 857 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/2); 858 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2); 859 860 assertEquals("OnPropertiesListener received unexpected update", 861 receivedUpdates1.size(), 2); 862 assertEquals("OnPropertiesListener did not receive expected update", 863 receivedUpdates2.size(), 2); 864 865 receivedUpdates2.get(1).assertEqual(NAMESPACE2, KEY1, VALUE2); 866 DeviceConfig.removeOnPropertiesChangedListener(listener2); 867 868 waitForListenerUpdateOrTimeout(receivedUpdates2, /*expectedTotalUpdatesCount=*/2); 869 waitForListenerUpdateOrTimeout(receivedUpdates1, /*expectedTotalUpdatesCount=*/2); 870 871 assertEquals("OnPropertiesListener received unexpected update", 872 receivedUpdates1.size(), 2); 873 assertEquals("OnPropertiesListener received unexpected update", 874 receivedUpdates2.size(), 2); 875 876 } catch(Exception e) { 877 throw e; 878 } finally { 879 DeviceConfig.removeOnPropertiesChangedListener(listener1); 880 DeviceConfig.removeOnPropertiesChangedListener(listener2); 881 } 882 } 883 884 /** 885 * Test that reset to package default successfully resets values. 886 */ 887 @Test testResetToPackageDefaults()888 public void testResetToPackageDefaults() { 889 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE1, /*makeDefault=*/true); 890 DeviceConfig.setProperty(NAMESPACE1, KEY1, VALUE2, /*makeDefault=*/false); 891 892 assertEquals(DeviceConfig.getProperty(NAMESPACE1, KEY1), VALUE2); 893 894 DeviceConfig.resetToDefaults(RESET_MODE_PACKAGE_DEFAULTS, NAMESPACE1); 895 896 assertEquals(DeviceConfig.getProperty(NAMESPACE1, KEY1), VALUE1); 897 } 898 createOnPropertiesChangedListener( List<PropertyUpdate> receivedUpdates)899 private OnPropertiesChangedListener createOnPropertiesChangedListener( 900 List<PropertyUpdate> receivedUpdates) { 901 OnPropertiesChangedListener changeListener = new OnPropertiesChangedListener() { 902 @Override 903 public void onPropertiesChanged(Properties properties) { 904 synchronized (mLock) { 905 receivedUpdates.add(new PropertyUpdate(properties)); 906 mLock.notifyAll(); 907 } 908 } 909 }; 910 return changeListener; 911 } 912 waitForListenerUpdateOrTimeout( List<PropertyUpdate> receivedUpdates, int expectedTotalUpdatesCount)913 private void waitForListenerUpdateOrTimeout( 914 List<PropertyUpdate> receivedUpdates, int expectedTotalUpdatesCount) { 915 916 final long startTimeMillis = SystemClock.uptimeMillis(); 917 synchronized (mLock) { 918 while (true) { 919 if (receivedUpdates.size() >= expectedTotalUpdatesCount) { 920 return; 921 } 922 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 923 if (elapsedTimeMillis >= WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS) { 924 return; 925 } 926 final long remainingTimeMillis = WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS 927 - elapsedTimeMillis; 928 try { 929 mLock.wait(remainingTimeMillis); 930 } catch (InterruptedException ie) { 931 /* ignore */ 932 } 933 } 934 } 935 } 936 setPropertiesAndAssertSuccessfulChange(String setNamespace, String setName, String setValue)937 private Properties setPropertiesAndAssertSuccessfulChange(String setNamespace, String setName, 938 String setValue) { 939 final List<PropertyUpdate> receivedUpdates = new ArrayList<>(); 940 OnPropertiesChangedListener changeListener = createOnPropertiesChangedListener(receivedUpdates); 941 942 DeviceConfig.addOnPropertiesChangedListener(setNamespace, EXECUTOR, changeListener); 943 944 DeviceConfig.setProperty(setNamespace, setName, setValue, /*makeDefault=*/false); 945 waitForListenerUpdateOrTimeout(receivedUpdates, 1); 946 DeviceConfig.removeOnPropertiesChangedListener(changeListener); 947 948 assertEquals("Failed to receive update to OnPropertiesChangedListener", 949 receivedUpdates.size(), 1); 950 PropertyUpdate propertiesUpdate = receivedUpdates.get(0); 951 propertiesUpdate.assertEqual(setNamespace, setName, setValue); 952 953 return propertiesUpdate.properties; 954 } 955 nullifyProperty(String namespace, String key)956 private void nullifyProperty(String namespace, String key) { 957 if (DeviceConfig.getString(namespace, key, null) != null) { 958 setPropertiesAndAssertSuccessfulChange(namespace, key, null); 959 } 960 } 961 deletePropertyThrowShell(String namespace, String key)962 private static void deletePropertyThrowShell(String namespace, String key) { 963 InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand( 964 "device_config delete " + namespace + " " + key); 965 } 966 967 private static class PropertyUpdate { 968 String namespace; 969 String name; 970 String value; 971 Properties properties; 972 PropertyUpdate(String namespace, String name, String value)973 PropertyUpdate(String namespace, String name, String value) { 974 this.name = name; 975 this.namespace = namespace; 976 this.value = value; 977 this.properties = null; 978 } 979 PropertyUpdate(Properties properties)980 PropertyUpdate(Properties properties) { 981 if (properties.getKeyset().size() != 1) { 982 fail("Unexpected properties size."); 983 } 984 this.namespace = properties.getNamespace(); 985 this.name = properties.getKeyset().iterator().next(); 986 this.value = properties.getString(this.name, null); 987 this.properties = properties; 988 } 989 assertEqual(String namespace, String name, String value)990 void assertEqual(String namespace, String name, String value) { 991 assertEquals("Listener received update for unexpected namespace", 992 namespace, this.namespace); 993 assertEquals("Listener received update for unexpected property", 994 this.name, name); 995 assertEquals("Listener received update with unexpected value", 996 this.value, value); 997 } 998 999 } 1000 }