1 /* 2 * Copyright (C) 2020 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.phone; 18 19 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN; 20 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN; 21 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN; 22 23 import static com.android.internal.telephony.testing.CursorSubject.assertThat; 24 import static com.android.internal.telephony.testing.TelephonyAssertions.assertThrows; 25 26 import static com.google.common.truth.Truth.assertThat; 27 28 import static org.mockito.ArgumentMatchers.any; 29 import static org.mockito.ArgumentMatchers.anyInt; 30 import static org.mockito.ArgumentMatchers.eq; 31 import static org.mockito.Mockito.doNothing; 32 import static org.mockito.Mockito.doReturn; 33 import static org.mockito.Mockito.mock; 34 import static org.mockito.Mockito.spy; 35 import static org.mockito.Mockito.times; 36 import static org.mockito.Mockito.verify; 37 38 import android.content.ContentResolver; 39 import android.content.ContentValues; 40 import android.database.Cursor; 41 import android.net.Uri; 42 import android.provider.SimPhonebookContract; 43 import android.provider.SimPhonebookContract.ElementaryFiles; 44 import android.provider.SimPhonebookContract.SimRecords; 45 import android.telephony.SubscriptionInfo; 46 import android.telephony.SubscriptionManager; 47 import android.util.Pair; 48 49 import androidx.test.core.app.ApplicationProvider; 50 import androidx.test.ext.junit.runners.AndroidJUnit4; 51 import androidx.test.platform.app.InstrumentationRegistry; 52 import androidx.test.rule.provider.ProviderTestRule; 53 54 import com.android.internal.telephony.IIccPhoneBook; 55 import com.android.internal.telephony.uicc.AdnCapacity; 56 import com.android.internal.telephony.uicc.AdnRecord; 57 import com.android.internal.telephony.uicc.IccConstants; 58 59 import com.google.common.collect.ImmutableList; 60 import com.google.common.io.Closeables; 61 import com.google.common.truth.Correspondence; 62 63 import org.junit.Before; 64 import org.junit.Rule; 65 import org.junit.Test; 66 import org.junit.runner.RunWith; 67 import org.mockito.AdditionalAnswers; 68 import org.mockito.ArgumentCaptor; 69 import org.mockito.Mockito; 70 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.Collections; 74 import java.util.HashMap; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.Objects; 78 import java.util.function.Predicate; 79 import java.util.stream.Collectors; 80 81 @RunWith(AndroidJUnit4.class) 82 public final class SimPhonebookProviderTest { 83 84 private static final String EMOJI = new String(Character.toChars(0x1F642)); 85 private static final Correspondence<AdnRecord, AdnRecord> ADN_RECORD_IS_EQUAL = 86 Correspondence.from(AdnRecord::isEqual, "isEqual"); 87 88 @Rule 89 public final ProviderTestRule mProviderRule = new ProviderTestRule.Builder( 90 TestableSimPhonebookProvider.class, SimPhonebookContract.AUTHORITY).build(); 91 92 private ContentResolver mResolver; 93 private FakeIccPhoneBook mIccPhoneBook; 94 private SubscriptionManager mMockSubscriptionManager; 95 createSubscriptionsWithIds(int... subscriptionIds)96 private static List<SubscriptionInfo> createSubscriptionsWithIds(int... subscriptionIds) { 97 ImmutableList.Builder<SubscriptionInfo> builder = ImmutableList.builderWithExpectedSize( 98 subscriptionIds.length); 99 for (int i = 0; i < subscriptionIds.length; i++) { 100 builder.add(createSubscriptionInfo(i, subscriptionIds[i])); 101 } 102 return builder.build(); 103 } 104 createSubscriptionInfo(int slotIndex, int subscriptiondId)105 private static SubscriptionInfo createSubscriptionInfo(int slotIndex, int subscriptiondId) { 106 return new SubscriptionInfo( 107 subscriptiondId, "", slotIndex, null, null, 0, 0, null, 0, null, null, null, null, 108 false, null, null); 109 } 110 111 @Before setUp()112 public void setUp() { 113 mMockSubscriptionManager = spy( 114 Objects.requireNonNull(ApplicationProvider.getApplicationContext() 115 .getSystemService(SubscriptionManager.class))); 116 mIccPhoneBook = new FakeIccPhoneBook(); 117 mResolver = mProviderRule.getResolver(); 118 119 TestableSimPhonebookProvider.setup(mResolver, mMockSubscriptionManager, mIccPhoneBook); 120 } 121 122 @Test query_entityFiles_returnsCursorWithCorrectProjection()123 public void query_entityFiles_returnsCursorWithCorrectProjection() { 124 // Null projection 125 try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null, 126 null)) { 127 assertThat(Objects.requireNonNull(cursor).getColumnNames()).asList() 128 .containsExactlyElementsIn( 129 SimPhonebookProvider.ELEMENTARY_FILES_ALL_COLUMNS); 130 } 131 132 // Empty projection 133 try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, new String[0], null, 134 null)) { 135 assertThat(cursor).hasColumnNames(); 136 } 137 138 // Single column 139 try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, new String[]{ 140 ElementaryFiles.EF_TYPE 141 }, null, null)) { 142 assertThat(cursor).hasColumnNames(ElementaryFiles.EF_TYPE); 143 } 144 145 // Duplicate column 146 try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, new String[]{ 147 ElementaryFiles.SUBSCRIPTION_ID, ElementaryFiles.SUBSCRIPTION_ID 148 }, null, null)) { 149 assertThat(cursor).hasColumnNames(ElementaryFiles.SUBSCRIPTION_ID, 150 ElementaryFiles.SUBSCRIPTION_ID); 151 } 152 153 // Random order of all columns 154 String[] projection = Arrays.copyOf( 155 SimPhonebookProvider.ELEMENTARY_FILES_ALL_COLUMNS, 156 SimPhonebookProvider.ELEMENTARY_FILES_ALL_COLUMNS.length); 157 Collections.shuffle(Arrays.asList(projection)); 158 try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, projection, null, null)) { 159 assertThat(cursor).hasColumnNames(projection); 160 } 161 } 162 163 @Test query_entityFiles_unrecognizedColumn_throwsIllegalArgumentException()164 public void query_entityFiles_unrecognizedColumn_throwsIllegalArgumentException() { 165 assertThrows(IllegalArgumentException.class, () -> 166 mResolver.query(ElementaryFiles.CONTENT_URI, new String[]{"invalid_column"}, null, 167 null)); 168 } 169 170 @Test query_entityFiles_noSim_returnsEmptyCursor()171 public void query_entityFiles_noSim_returnsEmptyCursor() { 172 doReturn(ImmutableList.of()).when(mMockSubscriptionManager).getActiveSubscriptionInfoList(); 173 174 try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null, null)) { 175 assertThat(cursor).hasCount(0); 176 } 177 } 178 179 @Test query_entityFiles_multiSim_returnsCursorWithRowForEachSimEf()180 public void query_entityFiles_multiSim_returnsCursorWithRowForEachSimEf() { 181 setupSimsWithSubscriptionIds(2, 3, 7); 182 183 mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_ADN, 10, 25); 184 mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_FDN, 5, 20); 185 mIccPhoneBook.setupEfWithSizes(2, IccConstants.EF_SDN, 15, 20); 186 mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_ADN, 100, 30); 187 // These Will be omitted from results because zero size indicates the EF is not supported. 188 mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_FDN, 0, 0); 189 mIccPhoneBook.setupEfWithSizes(3, IccConstants.EF_SDN, 0, 0); 190 mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_ADN, 0, 0); 191 mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_FDN, 0, 0); 192 mIccPhoneBook.setupEfWithSizes(7, IccConstants.EF_SDN, 0, 0); 193 194 String[] projection = { 195 ElementaryFiles.SLOT_INDEX, ElementaryFiles.SUBSCRIPTION_ID, 196 ElementaryFiles.EF_TYPE, ElementaryFiles.MAX_RECORDS, 197 ElementaryFiles.NAME_MAX_LENGTH, ElementaryFiles.PHONE_NUMBER_MAX_LENGTH 198 }; 199 try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, projection, null, null)) { 200 assertThat(cursor).hasColumnNames(projection); 201 202 assertThat(cursor) 203 .atRow(0).hasRowValues(0, 2, ElementaryFiles.EF_ADN, 10, 11, 20) 204 .atRow(1).hasRowValues(0, 2, ElementaryFiles.EF_FDN, 5, 6, 20) 205 .atRow(2).hasRowValues(0, 2, ElementaryFiles.EF_SDN, 15, 6, 20) 206 .atRow(3).hasRowValues(1, 3, ElementaryFiles.EF_ADN, 100, 16, 20); 207 } 208 } 209 210 @Test query_entityFiles_simWithZeroSizes_returnsEmptyCursor()211 public void query_entityFiles_simWithZeroSizes_returnsEmptyCursor() { 212 setupSimsWithSubscriptionIds(1); 213 214 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 0, 0); 215 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 0, 0); 216 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 0, 0); 217 218 try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null, null)) { 219 assertThat(cursor).hasCount(0); 220 } 221 } 222 223 /** 224 * USIM cards support more than 255 records by having multiple files for one EF type but 225 * IIccPhoneBook.getAdnRecordsSizeForSubscriber returns the size for a single file and so is 226 * inaccurate for such SIMs. 227 * 228 * <p>See b/201385523#comment4 and b/201685690 229 */ 230 @Test query_entityFiles_adnRecordCountExceedsSize_returnsAdnRecordCountAsMaxRecords()231 public void query_entityFiles_adnRecordCountExceedsSize_returnsAdnRecordCountAsMaxRecords() { 232 setupSimsWithSubscriptionIds(1); 233 234 // There are 400 records returned by getAdnRecordsInEfForSubscriber but the count returned 235 // by getAdnRecordsSizeForSubscriber is only 200. 236 AdnRecord[] records = mIccPhoneBook.createEmptyRecords(IccConstants.EF_ADN, 400); 237 mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 200, 20); 238 mIccPhoneBook.setRecords(1, IccConstants.EF_ADN, records); 239 240 String[] projection = { 241 ElementaryFiles.SUBSCRIPTION_ID, ElementaryFiles.EF_TYPE, 242 ElementaryFiles.MAX_RECORDS 243 }; 244 try (Cursor cursor = mResolver.query( 245 ElementaryFiles.CONTENT_URI, projection, null, null)) { 246 assertThat(cursor).hasCount(1); 247 assertThat(cursor) 248 .atRow(0).hasRowValues(1, ElementaryFiles.EF_ADN, 400); 249 } 250 } 251 252 @Test query_entityFilesItem_nullProjection_returnsCursorWithCorrectProjection()253 public void query_entityFilesItem_nullProjection_returnsCursorWithCorrectProjection() { 254 setupSimsWithSubscriptionIds(1); 255 mIccPhoneBook.makeAllEfsSupported(1); 256 257 // Null projection 258 try (Cursor cursor = mResolver.query(ElementaryFiles.getItemUri(1, EF_ADN), null, null, 259 null)) { 260 assertThat(Objects.requireNonNull(cursor).getColumnNames()).asList() 261 .containsExactlyElementsIn( 262 SimPhonebookProvider.ELEMENTARY_FILES_ALL_COLUMNS); 263 } 264 } 265 266 @Test query_elementaryFilesItem_nonExistentSubscriptionId_returnsEmptyCursor()267 public void query_elementaryFilesItem_nonExistentSubscriptionId_returnsEmptyCursor() { 268 setupSimsWithSubscriptionIds(1); 269 mIccPhoneBook.makeAllEfsSupported(1); 270 271 // Subscription ID 2 does not exist 272 Uri nonExistentElementaryFileItemUri = ElementaryFiles.getItemUri(2, EF_ADN); 273 274 try (Cursor cursor = mResolver.query(nonExistentElementaryFileItemUri, null, null, null)) { 275 assertThat(Objects.requireNonNull(cursor)).hasCount(0); 276 } 277 } 278 279 @Test query_adnRecords_returnsCursorWithMatchingProjection()280 public void query_adnRecords_returnsCursorWithMatchingProjection() { 281 setupSimsWithSubscriptionIds(1); 282 mIccPhoneBook.makeAllEfsSupported(1); 283 Uri contentAdn = SimRecords.getContentUri(1, EF_ADN); 284 285 // Null projection 286 try (Cursor cursor = mResolver.query(contentAdn, null, null, null)) { 287 assertThat(Objects.requireNonNull(cursor).getColumnNames()).asList() 288 .containsExactlyElementsIn(SimPhonebookProvider.SIM_RECORDS_ALL_COLUMNS); 289 } 290 291 // Empty projection 292 try (Cursor cursor = mResolver.query(contentAdn, new String[0], null, null)) { 293 assertThat(cursor).hasColumnNames(); 294 } 295 296 // Single column 297 try (Cursor cursor = mResolver.query(contentAdn, new String[]{ 298 SimRecords.PHONE_NUMBER 299 }, null, null)) { 300 assertThat(cursor).hasColumnNames(SimRecords.PHONE_NUMBER); 301 } 302 303 // Duplicate column 304 try (Cursor cursor = mResolver.query(contentAdn, new String[]{ 305 SimRecords.PHONE_NUMBER, SimRecords.PHONE_NUMBER 306 }, null, null)) { 307 assertThat(cursor).hasColumnNames(SimRecords.PHONE_NUMBER, SimRecords.PHONE_NUMBER); 308 } 309 310 // Random order of all columns 311 String[] projection = Arrays.copyOf( 312 SimPhonebookProvider.SIM_RECORDS_ALL_COLUMNS, 313 SimPhonebookProvider.SIM_RECORDS_ALL_COLUMNS.length); 314 Collections.shuffle(Arrays.asList(projection)); 315 try (Cursor cursor = mResolver.query(contentAdn, projection, null, null)) { 316 assertThat(cursor).hasColumnNames(projection); 317 } 318 } 319 320 @Test query_adnRecords_invalidColumnProjection_throwsIllegalArgumentException()321 public void query_adnRecords_invalidColumnProjection_throwsIllegalArgumentException() { 322 setupSimsWithSubscriptionIds(1); 323 mIccPhoneBook.makeAllEfsSupported(1); 324 Uri contentAdn = SimRecords.getContentUri(1, EF_ADN); 325 326 assertThrows(IllegalArgumentException.class, () -> Closeables.close( 327 mResolver.query(contentAdn, new String[] { 328 "an_unsupported_column", 329 }, null, null), false) 330 ); 331 332 assertThrows(IllegalArgumentException.class, () -> Closeables.close( 333 mResolver.query(contentAdn, new String[] { 334 SimRecords.RECORD_NUMBER, 335 "an_unsupported_column" 336 }, null, null), false) 337 ); 338 339 assertThrows(IllegalArgumentException.class, () -> Closeables.close( 340 mResolver.query(contentAdn, new String[] { 341 "an_unsupported_column", 342 SimRecords.RECORD_NUMBER 343 }, null, null), false) 344 ); 345 } 346 347 @Test query_adnRecords_noRecords_returnsEmptyCursor()348 public void query_adnRecords_noRecords_returnsEmptyCursor() { 349 setupSimsWithSubscriptionIds(1); 350 mIccPhoneBook.makeAllEfsSupported(1); 351 352 try (Cursor cursor = mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null, 353 null)) { 354 assertThat(cursor).hasCount(0); 355 } 356 } 357 358 @Test query_simRecords_nullRecordList_returnsEmptyCursor()359 public void query_simRecords_nullRecordList_returnsEmptyCursor() throws Exception { 360 setupSimsWithSubscriptionIds(1); 361 mIccPhoneBook.makeAllEfsSupported(1); 362 // Use a mock so that a null list can be returned 363 IIccPhoneBook mockIccPhoneBook = mock( 364 IIccPhoneBook.class, AdditionalAnswers.delegatesTo(mIccPhoneBook)); 365 doReturn(null).when(mockIccPhoneBook).getAdnRecordsInEfForSubscriber(anyInt(), anyInt()); 366 TestableSimPhonebookProvider.setup(mResolver, mMockSubscriptionManager, mockIccPhoneBook); 367 368 try (Cursor adnCursor = mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null, 369 null); 370 Cursor fdnCursor = mResolver.query(SimRecords.getContentUri(1, EF_FDN), null, null, 371 null); 372 Cursor sdnCursor = mResolver.query(SimRecords.getContentUri(1, EF_SDN), null, null, 373 null) 374 ) { 375 assertThat(adnCursor).hasCount(0); 376 assertThat(fdnCursor).hasCount(0); 377 assertThat(sdnCursor).hasCount(0); 378 } 379 } 380 381 @Test query_simRecords_singleSim_returnsDataForCorrectEf()382 public void query_simRecords_singleSim_returnsDataForCorrectEf() { 383 setupSimsWithSubscriptionIds(1); 384 mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Adn1", "8005550101"); 385 mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Adn2", "8005550102"); 386 mIccPhoneBook.addRecord(1, IccConstants.EF_FDN, "Person Fdn", "8005550103"); 387 mIccPhoneBook.addRecord(1, IccConstants.EF_SDN, "Person Sdn", "8005550104"); 388 mIccPhoneBook.setDefaultSubscriptionId(1); 389 390 String[] projection = { 391 SimRecords.SUBSCRIPTION_ID, 392 SimRecords.ELEMENTARY_FILE_TYPE, 393 SimRecords.RECORD_NUMBER, 394 SimRecords.NAME, 395 SimRecords.PHONE_NUMBER 396 }; 397 try (Cursor adnCursor = mResolver.query(SimRecords.getContentUri(1, EF_ADN), 398 projection, null, null); 399 Cursor fdnCursor = mResolver.query(SimRecords.getContentUri(1, EF_FDN), 400 projection, null, null); 401 Cursor sdnCursor = mResolver.query(SimRecords.getContentUri(1, EF_SDN), 402 projection, null, null) 403 ) { 404 405 assertThat(adnCursor) 406 .atRow(0).hasRowValues(1, ElementaryFiles.EF_ADN, 1, "Person Adn1", 407 "8005550101") 408 .atRow(1).hasRowValues(1, ElementaryFiles.EF_ADN, 2, "Person Adn2", 409 "8005550102"); 410 assertThat(fdnCursor) 411 .atRow(0).hasRowValues(1, ElementaryFiles.EF_FDN, 1, "Person Fdn", 412 "8005550103"); 413 assertThat(sdnCursor) 414 .atRow(0).hasRowValues(1, ElementaryFiles.EF_SDN, 1, "Person Sdn", 415 "8005550104"); 416 } 417 } 418 419 @Test query_adnRecords_returnsAdnData()420 public void query_adnRecords_returnsAdnData() { 421 setupSimsWithSubscriptionIds(1, 2, 4); 422 mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Sim1", "8005550101"); 423 mIccPhoneBook.addRecord(1, IccConstants.EF_FDN, "Omitted Sim1", "8005550199"); 424 mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Sim2a", "8005550103"); 425 mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Sim2b", "8005550104"); 426 mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Sim2c", "8005550105"); 427 mIccPhoneBook.addRecord(2, IccConstants.EF_SDN, "Omitted Sim2", "8005550198"); 428 mIccPhoneBook.addRecord(4, IccConstants.EF_ADN, "Person Sim4", "8005550106"); 429 mIccPhoneBook.setDefaultSubscriptionId(1); 430 431 String[] projection = { 432 SimRecords.SUBSCRIPTION_ID, 433 SimRecords.ELEMENTARY_FILE_TYPE, 434 SimRecords.RECORD_NUMBER, 435 SimRecords.NAME, 436 SimRecords.PHONE_NUMBER 437 }; 438 try (Cursor cursorSim1 = mResolver.query(SimRecords.getContentUri(1, EF_ADN), 439 projection, null, null); 440 Cursor cursorSim2 = mResolver.query(SimRecords.getContentUri(2, EF_ADN), 441 projection, null, null); 442 Cursor cursorSim4 = mResolver.query(SimRecords.getContentUri(4, EF_ADN), 443 projection, null, null) 444 ) { 445 446 assertThat(cursorSim1).hasData(new Object[][]{ 447 {1, ElementaryFiles.EF_ADN, 1, "Person Sim1", "8005550101"}, 448 }); 449 assertThat(cursorSim2).hasData(new Object[][]{ 450 {2, ElementaryFiles.EF_ADN, 1, "Person Sim2a", "8005550103"}, 451 {2, ElementaryFiles.EF_ADN, 2, "Person Sim2b", "8005550104"}, 452 {2, ElementaryFiles.EF_ADN, 3, "Person Sim2c", "8005550105"}, 453 }); 454 assertThat(cursorSim4).hasData(new Object[][]{ 455 {4, ElementaryFiles.EF_ADN, 1, "Person Sim4", "8005550106"}, 456 }); 457 } 458 } 459 460 @Test query_fdnRecords_returnsFdnData()461 public void query_fdnRecords_returnsFdnData() { 462 setupSimsWithSubscriptionIds(1, 2, 4); 463 mIccPhoneBook.makeAllEfsSupported(1, 2, 4); 464 mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Sim1", "8005550101"); 465 mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Sim2a", "8005550103"); 466 mIccPhoneBook.addRecord(2, IccConstants.EF_FDN, "Person Sim2b", "8005550104"); 467 mIccPhoneBook.addRecord(2, IccConstants.EF_FDN, "Person Sim2c", "8005550105"); 468 mIccPhoneBook.addRecord(4, IccConstants.EF_SDN, "Person Sim4", "8005550106"); 469 mIccPhoneBook.setDefaultSubscriptionId(1); 470 471 String[] projection = { 472 SimRecords.SUBSCRIPTION_ID, 473 SimRecords.ELEMENTARY_FILE_TYPE, 474 SimRecords.RECORD_NUMBER, 475 SimRecords.NAME, 476 SimRecords.PHONE_NUMBER 477 }; 478 try (Cursor cursorSim1Fdn = mResolver.query(SimRecords.getContentUri(1, EF_FDN), 479 projection, null, null); 480 Cursor cursorSim2Fdn = mResolver.query(SimRecords.getContentUri(2, EF_FDN), 481 projection, null, null); 482 Cursor cursorSim4Fdn = mResolver.query(SimRecords.getContentUri(4, EF_FDN), 483 projection, null, null) 484 ) { 485 486 assertThat(cursorSim1Fdn).hasCount(0); 487 assertThat(cursorSim2Fdn).hasData(new Object[][]{ 488 {2, ElementaryFiles.EF_FDN, 1, "Person Sim2b", "8005550104"}, 489 {2, ElementaryFiles.EF_FDN, 2, "Person Sim2c", "8005550105"}, 490 }); 491 assertThat(cursorSim4Fdn).hasCount(0); 492 } 493 } 494 495 @Test query_sdnRecords_returnsSdnData()496 public void query_sdnRecords_returnsSdnData() { 497 setupSimsWithSubscriptionIds(1, 2, 4); 498 mIccPhoneBook.makeAllEfsSupported(1, 2, 4); 499 mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Adn1", "8005550101"); 500 mIccPhoneBook.addRecord(1, IccConstants.EF_FDN, "Person Fdn1", "8005550102"); 501 mIccPhoneBook.addRecord(1, IccConstants.EF_SDN, "Person Sdn1", "8005550103"); 502 mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Adn2a", "8005550104"); 503 mIccPhoneBook.addRecord(2, IccConstants.EF_FDN, "Person Fdn2b", "8005550105"); 504 mIccPhoneBook.addRecord(4, IccConstants.EF_SDN, "Person Sdn4a", "8005550106"); 505 mIccPhoneBook.addRecord(4, IccConstants.EF_SDN, "Person Sdn4b", "8005550107"); 506 mIccPhoneBook.setDefaultSubscriptionId(1); 507 508 String[] projection = { 509 SimRecords.SUBSCRIPTION_ID, 510 SimRecords.ELEMENTARY_FILE_TYPE, 511 SimRecords.RECORD_NUMBER, 512 SimRecords.NAME, 513 SimRecords.PHONE_NUMBER 514 }; 515 try (Cursor cursorSim1Sdn = mResolver.query(SimRecords.getContentUri(1, EF_SDN), 516 projection, null, null); 517 Cursor cursorSim2Sdn = mResolver.query(SimRecords.getContentUri(2, EF_SDN), 518 projection, null, null); 519 Cursor cursorSim4Sdn = mResolver.query(SimRecords.getContentUri(4, EF_SDN), 520 projection, null, null) 521 ) { 522 523 assertThat(cursorSim1Sdn) 524 .atRow(0).hasRowValues(1, ElementaryFiles.EF_SDN, 1, "Person Sdn1", 525 "8005550103"); 526 assertThat(cursorSim2Sdn).hasCount(0); 527 assertThat(cursorSim4Sdn) 528 .atRow(0).hasRowValues(4, ElementaryFiles.EF_SDN, 1, "Person Sdn4a", 529 "8005550106") 530 .atRow(1).hasRowValues(4, ElementaryFiles.EF_SDN, 2, "Person Sdn4b", 531 "8005550107"); 532 } 533 } 534 535 @Test query_adnRecords_nonExistentSim_throwsCorrectException()536 public void query_adnRecords_nonExistentSim_throwsCorrectException() { 537 setupSimsWithSubscriptionIds(1); 538 539 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 540 () -> mResolver.query(SimRecords.getContentUri(123, EF_ADN), null, null, null)); 541 assertThat(e).hasMessageThat().isEqualTo("No active SIM with subscription ID 123"); 542 } 543 544 @Test insert_nonExistentSim_throwsCorrectException()545 public void insert_nonExistentSim_throwsCorrectException() { 546 setupSimsWithSubscriptionIds(1); 547 ContentValues values = new ContentValues(); 548 values.put(SimRecords.NAME, "Name"); 549 values.put(SimRecords.PHONE_NUMBER, "123"); 550 551 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 552 () -> mResolver.insert(SimRecords.getContentUri(123, EF_ADN), values)); 553 assertThat(e).hasMessageThat().isEqualTo("No active SIM with subscription ID 123"); 554 } 555 556 @Test update_nonExistentSim_throwsCorrectException()557 public void update_nonExistentSim_throwsCorrectException() { 558 setupSimsWithSubscriptionIds(1); 559 ContentValues values = new ContentValues(); 560 values.put(SimRecords.NAME, "Name"); 561 values.put(SimRecords.PHONE_NUMBER, "123"); 562 563 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 564 () -> mResolver.update(SimRecords.getItemUri(123, EF_ADN, 1), values, null)); 565 assertThat(e).hasMessageThat().isEqualTo("No active SIM with subscription ID 123"); 566 } 567 568 @Test delete_nonExistentSim_throwsCorrectException()569 public void delete_nonExistentSim_throwsCorrectException() { 570 setupSimsWithSubscriptionIds(1); 571 572 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 573 () -> mResolver.delete(SimRecords.getItemUri(123, EF_ADN, 1), null)); 574 assertThat(e).hasMessageThat().isEqualTo("No active SIM with subscription ID 123"); 575 } 576 577 @Test query_adnRecords_zeroSizeEf_throwsCorrectException()578 public void query_adnRecords_zeroSizeEf_throwsCorrectException() { 579 setupSimsWithSubscriptionIds(1); 580 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 0, 0); 581 582 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 583 () -> mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null, null)); 584 assertThat(e).hasMessageThat().isEqualTo( 585 "adn is not supported for SIM with subscription ID 1"); 586 } 587 588 @Test query_itemUri_returnsCorrectRow()589 public void query_itemUri_returnsCorrectRow() { 590 setupSimsWithSubscriptionIds(1, 2); 591 mIccPhoneBook.addRecord(1, 592 new AdnRecord(IccConstants.EF_ADN, 1, "Name@Adn1[1]", "8005550101")); 593 mIccPhoneBook.addRecord(1, 594 new AdnRecord(IccConstants.EF_ADN, 2, "Name@Adn1[2]", "8005550102")); 595 mIccPhoneBook.addRecord(1, 596 new AdnRecord(IccConstants.EF_ADN, 3, "Name@Adn1[3]", "8005550103")); 597 mIccPhoneBook.addRecord(2, 598 new AdnRecord(IccConstants.EF_ADN, 3, "Name@Adn2[3]", "8005550104")); 599 mIccPhoneBook.addRecord(1, 600 new AdnRecord(IccConstants.EF_FDN, 1, "Name@Fdn1[1]", "8005550105")); 601 mIccPhoneBook.addRecord(2, 602 new AdnRecord(IccConstants.EF_SDN, 1, "Name@Sdn2[1]", "8005550106")); 603 604 String[] projection = { 605 SimRecords.SUBSCRIPTION_ID, SimRecords.ELEMENTARY_FILE_TYPE, 606 SimRecords.RECORD_NUMBER, SimRecords.NAME, SimRecords.PHONE_NUMBER 607 }; 608 try (Cursor item1 = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), 609 projection, null, null); 610 Cursor item2 = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 3), 611 projection, null, null); 612 Cursor item3 = mResolver.query(SimRecords.getItemUri(2, ElementaryFiles.EF_ADN, 3), 613 projection, null, null); 614 Cursor item4 = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1), 615 projection, null, null); 616 Cursor item5 = mResolver.query(SimRecords.getItemUri(2, ElementaryFiles.EF_SDN, 1), 617 projection, null, null) 618 ) { 619 assertThat(item1).hasSingleRow(1, ElementaryFiles.EF_ADN, 1, "Name@Adn1[1]", 620 "8005550101"); 621 assertThat(item2).hasSingleRow(1, ElementaryFiles.EF_ADN, 3, "Name@Adn1[3]", 622 "8005550103"); 623 assertThat(item3).hasSingleRow(2, ElementaryFiles.EF_ADN, 3, "Name@Adn2[3]", 624 "8005550104"); 625 assertThat(item4).hasSingleRow(1, ElementaryFiles.EF_FDN, 1, "Name@Fdn1[1]", 626 "8005550105"); 627 assertThat(item5).hasSingleRow(2, ElementaryFiles.EF_SDN, 1, "Name@Sdn2[1]", 628 "8005550106"); 629 } 630 } 631 632 @Test query_itemUriNullProjection_returnsCursorWithAllColumns()633 public void query_itemUriNullProjection_returnsCursorWithAllColumns() { 634 setupSimsWithSubscriptionIds(1); 635 mIccPhoneBook.makeAllEfsSupported(1); 636 637 try (Cursor cursor = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), 638 null, null, null) 639 ) { 640 assertThat(Objects.requireNonNull( 641 cursor).getColumnNames()).asList().containsExactlyElementsIn( 642 SimPhonebookProvider.SIM_RECORDS_ALL_COLUMNS); 643 } 644 } 645 646 @Test query_itemUriEmptyRecord_returnsEmptyCursor()647 public void query_itemUriEmptyRecord_returnsEmptyCursor() { 648 setupSimsWithSubscriptionIds(1); 649 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30); 650 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 1, 30); 651 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 1, 30); 652 653 try (Cursor adnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), 654 null, null, null); 655 Cursor fdnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1), 656 null, null, null); 657 Cursor sdnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 1), 658 null, null, null) 659 ) { 660 assertThat(adnItem).hasCount(0); 661 assertThat(fdnItem).hasCount(0); 662 assertThat(sdnItem).hasCount(0); 663 } 664 } 665 666 @Test query_itemUriIndexExceedsMax_returnsEmptyCursor()667 public void query_itemUriIndexExceedsMax_returnsEmptyCursor() { 668 setupSimsWithSubscriptionIds(1); 669 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30); 670 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_FDN, 1, 30); 671 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_SDN, 1, 30); 672 673 try (Cursor adnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), 674 null, null, null); 675 Cursor fdnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 2), 676 null, null, null); 677 Cursor sdnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 2), 678 null, null, null) 679 ) { 680 assertThat(adnItem).hasCount(0); 681 assertThat(fdnItem).hasCount(0); 682 assertThat(sdnItem).hasCount(0); 683 } 684 } 685 686 @Test query_invalidItemIndex_throwsIllegalArgumentException()687 public void query_invalidItemIndex_throwsIllegalArgumentException() { 688 setupSimsWithSubscriptionIds(1); 689 mIccPhoneBook.makeAllEfsSupported(1); 690 691 assertThrows(IllegalArgumentException.class, () -> 692 mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, -1), 693 null, null, null)); 694 assertThrows(IllegalArgumentException.class, () -> 695 mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, -1), 696 null, null, null)); 697 assertThrows(IllegalArgumentException.class, () -> 698 mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, -1), 699 null, null, null)); 700 assertThrows(IllegalArgumentException.class, () -> 701 mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 0), 702 null, null, null)); 703 assertThrows(IllegalArgumentException.class, () -> 704 mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 0), 705 null, null, null)); 706 assertThrows(IllegalArgumentException.class, () -> 707 mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 0), 708 null, null, null)); 709 } 710 711 @Test insert_adnRecord_addsAdnRecordAndReturnsUriForNewRecord()712 public void insert_adnRecord_addsAdnRecordAndReturnsUriForNewRecord() { 713 setupSimsWithSubscriptionIds(1); 714 mIccPhoneBook.makeAllEfsSupported(1); 715 716 ContentValues values = new ContentValues(); 717 values.put(SimRecords.NAME, "First Last"); 718 values.put(SimRecords.PHONE_NUMBER, "8005550101"); 719 720 Uri uri = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 721 722 List<AdnRecord> records = mIccPhoneBook.getAdnRecordsInEfForSubscriber( 723 1, IccConstants.EF_ADN).stream() 724 .filter(((Predicate<AdnRecord>) AdnRecord::isEmpty).negate()) 725 .collect(Collectors.toList()); 726 727 assertThat(records) 728 .comparingElementsUsing(ADN_RECORD_IS_EQUAL) 729 .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, "First Last", "8005550101")); 730 731 assertThat(uri).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1)); 732 } 733 734 @Test insert_adnRecordWithExistingRecords_returnsUriWithCorrectIndex()735 public void insert_adnRecordWithExistingRecords_returnsUriWithCorrectIndex() { 736 setupSimsWithSubscriptionIds(1); 737 mIccPhoneBook.setDefaultSubscriptionId(1); 738 mIccPhoneBook.addRecord(new AdnRecord(IccConstants.EF_ADN, 2, "Existing1", "8005550101")); 739 mIccPhoneBook.addRecord(new AdnRecord(IccConstants.EF_ADN, 3, "Existing2", "8005550102")); 740 mIccPhoneBook.addRecord(new AdnRecord(IccConstants.EF_ADN, 5, "Existing3", "8005550103")); 741 742 ContentValues values = new ContentValues(); 743 values.put(SimRecords.NAME, "New1"); 744 values.put(SimRecords.PHONE_NUMBER, "8005550104"); 745 746 Uri insert1 = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 747 values.put(SimRecords.NAME, "New2"); 748 values.put(SimRecords.PHONE_NUMBER, "8005550105"); 749 Uri insert2 = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 750 values.put(SimRecords.NAME, "New3"); 751 values.put(SimRecords.PHONE_NUMBER, "8005550106"); 752 Uri insert3 = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 753 754 assertThat( 755 mIccPhoneBook.getAdnRecordsInEfForSubscriber(1, IccConstants.EF_ADN).subList(0, 6)) 756 .comparingElementsUsing(ADN_RECORD_IS_EQUAL) 757 .containsExactly( 758 new AdnRecord(IccConstants.EF_ADN, 1, "New1", "8005550104"), 759 new AdnRecord(IccConstants.EF_ADN, 2, "Existing1", "8005550101"), 760 new AdnRecord(IccConstants.EF_ADN, 3, "Existing2", "8005550102"), 761 new AdnRecord(IccConstants.EF_ADN, 4, "New2", "8005550105"), 762 new AdnRecord(IccConstants.EF_ADN, 5, "Existing3", "8005550103"), 763 new AdnRecord(IccConstants.EF_ADN, 6, "New3", "8005550106")); 764 assertThat(insert1).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1)); 765 assertThat(insert2).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 4)); 766 assertThat(insert3).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 6)); 767 } 768 769 @Test insert_efFull_throwsCorrectException()770 public void insert_efFull_throwsCorrectException() { 771 setupSimsWithSubscriptionIds(1); 772 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30); 773 mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Existing", "8005550101"); 774 775 ContentValues values = new ContentValues(); 776 values.put(SimRecords.NAME, "New"); 777 values.put(SimRecords.PHONE_NUMBER, "8005550102"); 778 779 Uri uri = SimRecords.getContentUri(1, EF_ADN); 780 IllegalStateException e = assertThrows(IllegalStateException.class, 781 () -> mResolver.insert(uri, values)); 782 assertThat(e).hasMessageThat().isEqualTo( 783 uri + " is full. Please delete records to add new ones."); 784 } 785 786 @Test insert_nameWithNonGsmCharacters_addsAdnRecord()787 public void insert_nameWithNonGsmCharacters_addsAdnRecord() { 788 setupSimsWithSubscriptionIds(1); 789 mIccPhoneBook.makeAllEfsSupported(1); 790 791 ContentValues values = new ContentValues(); 792 String name = "abc日本" + EMOJI; 793 values.put(SimRecords.NAME, name); 794 values.put(SimRecords.PHONE_NUMBER, "8005550101"); 795 796 Uri uri = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 797 798 List<AdnRecord> records = mIccPhoneBook.getAdnRecordsInEfForSubscriber( 799 1, IccConstants.EF_ADN).stream() 800 .filter(((Predicate<AdnRecord>) AdnRecord::isEmpty).negate()) 801 .collect(Collectors.toList()); 802 803 assertThat(records) 804 .comparingElementsUsing(ADN_RECORD_IS_EQUAL) 805 .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, name, "8005550101")); 806 807 assertThat(uri).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1)); 808 } 809 810 @Test insert_nullValues_returnsNull()811 public void insert_nullValues_returnsNull() { 812 setupSimsWithSubscriptionIds(1); 813 mIccPhoneBook.makeAllEfsSupported(1); 814 815 Uri result = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), null); 816 817 assertThat(result).isNull(); 818 } 819 820 @Test update_nullValues_returnsZero()821 public void update_nullValues_returnsZero() { 822 setupSimsWithSubscriptionIds(1); 823 mIccPhoneBook.makeAllEfsSupported(1); 824 mIccPhoneBook.addAdnRecord(1, "Name", "5550101"); 825 826 int result = mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), null, 827 null); 828 829 assertThat(result).isEqualTo(0); 830 } 831 832 @Test insert_emptyValues_returnsNull()833 public void insert_emptyValues_returnsNull() { 834 setupSimsWithSubscriptionIds(1); 835 mIccPhoneBook.makeAllEfsSupported(1); 836 837 Uri result = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), new ContentValues()); 838 839 assertThat(result).isNull(); 840 } 841 842 @Test insert_nameOmitted_createsRecordWithJustPhoneNumber()843 public void insert_nameOmitted_createsRecordWithJustPhoneNumber() { 844 setupSimsWithSubscriptionIds(1); 845 mIccPhoneBook.makeAllEfsSupported(1); 846 847 ContentValues values = new ContentValues(); 848 // No name 849 values.put(SimRecords.PHONE_NUMBER, "18005550101"); 850 851 mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 852 853 // Null name 854 values.putNull(SimRecords.NAME); 855 values.put(SimRecords.PHONE_NUMBER, "18005550102"); 856 mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 857 858 // Empty name 859 values.put(SimRecords.NAME, ""); 860 values.put(SimRecords.PHONE_NUMBER, "18005550103"); 861 mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 862 863 assertThat(mIccPhoneBook.getAllValidRecords()) 864 .comparingElementsUsing(ADN_RECORD_IS_EQUAL) 865 .containsExactly( 866 new AdnRecord(IccConstants.EF_ADN, 1, "", "18005550101"), 867 new AdnRecord(IccConstants.EF_ADN, 2, "", "18005550102"), 868 new AdnRecord(IccConstants.EF_ADN, 3, "", "18005550103")); 869 } 870 871 @Test insert_phoneNumberOmitted_throwsCorrectException()872 public void insert_phoneNumberOmitted_throwsCorrectException() { 873 setupSimsWithSubscriptionIds(1); 874 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25); 875 876 ContentValues values = new ContentValues(); 877 values.put(SimRecords.NAME, "Name"); 878 879 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 880 () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values)); 881 assertThat(e).hasMessageThat().isEqualTo(SimRecords.PHONE_NUMBER + " is required."); 882 } 883 884 @Test insert_nameTooLong_throwsCorrectException()885 public void insert_nameTooLong_throwsCorrectException() { 886 setupSimsWithSubscriptionIds(1); 887 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25); 888 889 ContentValues values = new ContentValues(); 890 // Name is limited to 11 characters when the max record size is 25 891 values.put(SimRecords.NAME, "1234567890ab"); 892 values.put(SimRecords.PHONE_NUMBER, "8005550102"); 893 894 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 895 () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values)); 896 897 assertThat(e).hasMessageThat().isEqualTo(SimRecords.NAME + " is too long."); 898 899 // 2 bytes per character and 4 for the emoji. So this is 14 characters long. 900 values.put(SimRecords.NAME, "abc日本" + EMOJI); 901 e = assertThrows(IllegalArgumentException.class, 902 () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values)); 903 904 assertThat(e).hasMessageThat().isEqualTo(SimRecords.NAME + " is too long."); 905 } 906 907 @Test insert_phoneNumberTooLong_throwsCorrectException()908 public void insert_phoneNumberTooLong_throwsCorrectException() { 909 setupSimsWithSubscriptionIds(1); 910 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25); 911 912 ContentValues values = new ContentValues(); 913 values.put(SimRecords.NAME, "Name"); 914 // 21 digits is longer than max of 20 915 values.put(SimRecords.PHONE_NUMBER, "123456789012345678901"); 916 917 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 918 () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values)); 919 920 assertThat(e).hasMessageThat().isEqualTo(SimRecords.PHONE_NUMBER + " is too long."); 921 } 922 923 @Test insert_numberWithInvalidCharacters_throwsCorrectException()924 public void insert_numberWithInvalidCharacters_throwsCorrectException() { 925 setupSimsWithSubscriptionIds(1); 926 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 32); 927 928 ContentValues values = new ContentValues(); 929 values.put(SimRecords.NAME, "Name"); 930 values.put(SimRecords.PHONE_NUMBER, "(800)555-0190 x7777"); 931 932 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 933 () -> mResolver.insert(SimRecords.getContentUri(1, ElementaryFiles.EF_ADN), 934 values, 935 null)); 936 assertThat(e).hasMessageThat().isEqualTo( 937 SimRecords.PHONE_NUMBER + " contains unsupported characters."); 938 939 // The insert didn't actually change the data. 940 assertThat(mIccPhoneBook.getAllValidRecords()).isEmpty(); 941 } 942 943 @Test insert_unsupportedColumn_throwsCorrectException()944 public void insert_unsupportedColumn_throwsCorrectException() { 945 setupSimsWithSubscriptionIds(1); 946 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25); 947 948 ContentValues values = new ContentValues(); 949 values.put(SimRecords.NAME, "Name"); 950 values.put(SimRecords.PHONE_NUMBER, "18005550101"); 951 values.put(SimRecords.RECORD_NUMBER, 8); 952 values.put("extra_phone2", "18005550102"); 953 954 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 955 () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values)); 956 assertThat(e).hasMessageThat().isEqualTo("Unsupported columns: " 957 + SimRecords.RECORD_NUMBER + ",extra_phone2"); 958 } 959 960 @Test update_existingRecord_updatesRecord()961 public void update_existingRecord_updatesRecord() { 962 setupSimsWithSubscriptionIds(1, 2); 963 AdnRecord[] unchanged = new AdnRecord[]{ 964 new AdnRecord(IccConstants.EF_ADN, 3, "Other1", "8005550102"), 965 new AdnRecord(IccConstants.EF_ADN, 2, "Other2", "8005550103"), 966 new AdnRecord(IccConstants.EF_FDN, 2, "Other3", "8005550104") 967 }; 968 mIccPhoneBook.addRecord(1, unchanged[0]); 969 mIccPhoneBook.addRecord(2, unchanged[1]); 970 mIccPhoneBook.addRecord(2, unchanged[2]); 971 mIccPhoneBook.addRecord(1, 972 new AdnRecord(IccConstants.EF_ADN, 2, "Initial Name", "8005550101")); 973 974 ContentValues values = new ContentValues(); 975 values.put(SimRecords.NAME, "Updated Name"); 976 values.put(SimRecords.PHONE_NUMBER, "8005550105"); 977 978 int result = mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), values, 979 null); 980 981 assertThat(result).isEqualTo(1); 982 983 List<AdnRecord> finalRecords = mIccPhoneBook.getAllValidRecords(); 984 985 assertThat(finalRecords).comparingElementsUsing(ADN_RECORD_IS_EQUAL) 986 .containsAtLeastElementsIn(unchanged); 987 assertThat(finalRecords).comparingElementsUsing(ADN_RECORD_IS_EQUAL) 988 .doesNotContain( 989 new AdnRecord(IccConstants.EF_ADN, 2, "Initial Name", "80005550101")); 990 assertThat(finalRecords).comparingElementsUsing(ADN_RECORD_IS_EQUAL) 991 .contains(new AdnRecord(IccConstants.EF_ADN, 2, "Updated Name", "8005550105")); 992 } 993 994 @Test update_emptyRecord_updatesRecord()995 public void update_emptyRecord_updatesRecord() { 996 setupSimsWithSubscriptionIds(1); 997 mIccPhoneBook.makeAllEfsSupported(1); 998 999 ContentValues values = new ContentValues(); 1000 values.put(SimRecords.NAME, "name"); 1001 values.put(SimRecords.PHONE_NUMBER, "18005550101"); 1002 // No record actually exists with record number 10 but we allow clients to update it 1003 // as a way to set the information at a specific record number. 1004 int result = mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 10), 1005 values, null); 1006 1007 assertThat(result).isEqualTo(1); 1008 List<AdnRecord> finalRecords = mIccPhoneBook.getAllValidRecords(); 1009 assertThat(finalRecords).comparingElementsUsing(ADN_RECORD_IS_EQUAL) 1010 .containsExactly(new AdnRecord(IccConstants.EF_ADN, 10, "name", "18005550101")); 1011 } 1012 1013 @Test delete_existingRecord_deletesRecord()1014 public void delete_existingRecord_deletesRecord() { 1015 setupSimsWithSubscriptionIds(1, 2); 1016 AdnRecord[] unchanged = new AdnRecord[]{ 1017 new AdnRecord(IccConstants.EF_ADN, 3, "Other1", "8005550102"), 1018 new AdnRecord(IccConstants.EF_ADN, 2, "Other2", "8005550103"), 1019 new AdnRecord(IccConstants.EF_FDN, 2, "Other3", "8005550104") 1020 }; 1021 mIccPhoneBook.addRecord(1, 1022 new AdnRecord(IccConstants.EF_ADN, 2, "Initial Name", "8005550101")); 1023 mIccPhoneBook.addRecord(1, unchanged[0]); 1024 mIccPhoneBook.addRecord(2, unchanged[1]); 1025 mIccPhoneBook.addRecord(2, unchanged[2]); 1026 1027 int result = mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), null); 1028 1029 assertThat(result).isEqualTo(1); 1030 1031 assertThat(mIccPhoneBook.getAllValidRecords()).comparingElementsUsing(ADN_RECORD_IS_EQUAL) 1032 .containsExactlyElementsIn(unchanged); 1033 } 1034 1035 @Test update_indexExceedingMax_returnsZero()1036 public void update_indexExceedingMax_returnsZero() { 1037 setupSimsWithSubscriptionIds(1); 1038 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30); 1039 1040 ContentValues values = new ContentValues(); 1041 values.put(SimRecords.NAME, "name"); 1042 values.put(SimRecords.PHONE_NUMBER, "18005551010"); 1043 int result = mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), 1044 values, null); 1045 1046 assertThat(result).isEqualTo(0); 1047 } 1048 1049 @Test update_indexOverflow_throwsIllegalArgumentException()1050 public void update_indexOverflow_throwsIllegalArgumentException() { 1051 setupSimsWithSubscriptionIds(1); 1052 mIccPhoneBook.makeAllEfsSupported(1); 1053 1054 ContentValues values = new ContentValues(); 1055 values.put(SimRecords.NAME, "name"); 1056 values.put(SimRecords.PHONE_NUMBER, "18005551010"); 1057 assertThrows(IllegalArgumentException.class, () -> mResolver.update( 1058 SimRecords.getContentUri(1, EF_ADN).buildUpon().appendPath( 1059 String.valueOf((Long.MAX_VALUE))).build(), 1060 values, null)); 1061 } 1062 1063 @Test delete_emptyRecord_returnsZero()1064 public void delete_emptyRecord_returnsZero() { 1065 setupSimsWithSubscriptionIds(1); 1066 mIccPhoneBook.makeAllEfsSupported(1); 1067 1068 int result = mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), null); 1069 1070 assertThat(result).isEqualTo(0); 1071 } 1072 1073 @Test delete_indexExceedingMax_returnsZero()1074 public void delete_indexExceedingMax_returnsZero() { 1075 setupSimsWithSubscriptionIds(1); 1076 mIccPhoneBook.makeAllEfsSupported(1); 1077 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 30); 1078 1079 int result = mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), null); 1080 1081 assertThat(result).isEqualTo(0); 1082 } 1083 1084 @Test delete_indexOverflow_throwsIllegalArgumentException()1085 public void delete_indexOverflow_throwsIllegalArgumentException() { 1086 setupSimsWithSubscriptionIds(1); 1087 mIccPhoneBook.makeAllEfsSupported(1); 1088 1089 assertThrows(IllegalArgumentException.class, () -> mResolver.delete( 1090 SimRecords.getContentUri(1, EF_ADN).buildUpon().appendPath( 1091 String.valueOf((Long.MAX_VALUE))).build(), 1092 null)); 1093 } 1094 1095 @Test update_nameOrNumberTooLong_throwsCorrectException()1096 public void update_nameOrNumberTooLong_throwsCorrectException() { 1097 setupSimsWithSubscriptionIds(1); 1098 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 25); 1099 mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101"); 1100 1101 ContentValues values = new ContentValues(); 1102 // Name is limited to 11 characters 1103 values.put(SimRecords.NAME, "1234567890ab"); 1104 values.put(SimRecords.PHONE_NUMBER, "8005550102"); 1105 1106 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 1107 () -> mResolver.update(SimRecords.getItemUri( 1108 1, ElementaryFiles.EF_ADN, 1), values, null)); 1109 assertThat(e).hasMessageThat().isEqualTo(SimRecords.NAME + " is too long."); 1110 1111 values.put(SimRecords.NAME, "abc"); 1112 values.put(SimRecords.PHONE_NUMBER, "123456789012345678901"); 1113 1114 e = assertThrows(IllegalArgumentException.class, 1115 () -> mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), 1116 values, 1117 null)); 1118 assertThat(e).hasMessageThat().isEqualTo(SimRecords.PHONE_NUMBER + " is too long."); 1119 // The updates didn't actually change the data 1120 assertThat(mIccPhoneBook.getAllValidRecords()) 1121 .comparingElementsUsing(Correspondence.from(AdnRecord::isEqual, "isEqual")) 1122 .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, "Initial", "8005550101")); 1123 } 1124 1125 @Test update_numberWithInvalidCharacters_throwsCorrectException()1126 public void update_numberWithInvalidCharacters_throwsCorrectException() { 1127 setupSimsWithSubscriptionIds(1); 1128 mIccPhoneBook.setupEfWithSizes(1, IccConstants.EF_ADN, 1, 32); 1129 mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101"); 1130 1131 ContentValues values = new ContentValues(); 1132 values.put(SimRecords.NAME, "Name"); 1133 values.put(SimRecords.PHONE_NUMBER, "(800)555-0190 x7777"); 1134 1135 IllegalArgumentException e = assertThrows(IllegalArgumentException.class, 1136 () -> mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), 1137 values, 1138 null)); 1139 assertThat(e).hasMessageThat().isEqualTo( 1140 SimRecords.PHONE_NUMBER + " contains unsupported characters."); 1141 1142 // The update didn't actually change the data. 1143 assertThat(mIccPhoneBook.getAllValidRecords()) 1144 .comparingElementsUsing(Correspondence.from(AdnRecord::isEqual, "isEqual")) 1145 .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, "Initial", "8005550101")); 1146 } 1147 1148 @Test insert_nonAdnDirUris_throwsUnsupportedOperationException()1149 public void insert_nonAdnDirUris_throwsUnsupportedOperationException() { 1150 setupSimsWithSubscriptionIds(1); 1151 mIccPhoneBook.makeAllEfsSupported(1); 1152 1153 ContentValues values = new ContentValues(); 1154 values.put(SimRecords.NAME, "Name"); 1155 values.put(SimRecords.PHONE_NUMBER, "8005550101"); 1156 1157 assertThrows(UnsupportedOperationException.class, () -> 1158 mResolver.insert(ElementaryFiles.CONTENT_URI, values)); 1159 assertThrows(UnsupportedOperationException.class, 1160 () -> mResolver.insert(SimRecords.getContentUri(1, EF_FDN), values)); 1161 assertThrows(UnsupportedOperationException.class, 1162 () -> mResolver.insert(SimRecords.getContentUri(1, EF_SDN), values)); 1163 assertThrows(UnsupportedOperationException.class, () -> 1164 mResolver.insert(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1), values)); 1165 assertThrows(UnsupportedOperationException.class, () -> 1166 mResolver.insert(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 1), values)); 1167 } 1168 1169 @Test update_nonAdnDirUris_throwsUnsupportedOperationException()1170 public void update_nonAdnDirUris_throwsUnsupportedOperationException() { 1171 setupSimsWithSubscriptionIds(1); 1172 mIccPhoneBook.makeAllEfsSupported(1); 1173 1174 ContentValues values = new ContentValues(); 1175 values.put(SimRecords.NAME, "Name"); 1176 values.put(SimRecords.PHONE_NUMBER, "8005550101"); 1177 1178 assertThrows(UnsupportedOperationException.class, () -> 1179 mResolver.update(ElementaryFiles.CONTENT_URI, values, null)); 1180 assertThrows(UnsupportedOperationException.class, 1181 () -> mResolver.update(SimRecords.getContentUri(1, EF_FDN), values, null)); 1182 assertThrows(UnsupportedOperationException.class, 1183 () -> mResolver.update(SimRecords.getContentUri(1, EF_SDN), values, null)); 1184 assertThrows(UnsupportedOperationException.class, 1185 () -> mResolver.update(SimRecords.getContentUri(1, EF_SDN), values, null)); 1186 assertThrows(UnsupportedOperationException.class, () -> 1187 mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1), values, 1188 null)); 1189 assertThrows(UnsupportedOperationException.class, () -> 1190 mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 1), values, 1191 null)); 1192 } 1193 1194 @Test delete_nonAdnDirUris_throwsUnsupportedOperationException()1195 public void delete_nonAdnDirUris_throwsUnsupportedOperationException() { 1196 setupSimsWithSubscriptionIds(1); 1197 mIccPhoneBook.makeAllEfsSupported(1); 1198 1199 ContentValues values = new ContentValues(); 1200 values.put(SimRecords.NAME, "Name"); 1201 values.put(SimRecords.PHONE_NUMBER, "8005550101"); 1202 1203 assertThrows(UnsupportedOperationException.class, () -> 1204 mResolver.delete(ElementaryFiles.CONTENT_URI, null)); 1205 assertThrows(UnsupportedOperationException.class, 1206 () -> mResolver.delete(SimRecords.getContentUri(1, EF_FDN), null)); 1207 assertThrows(UnsupportedOperationException.class, 1208 () -> mResolver.delete(SimRecords.getContentUri(1, EF_SDN), null)); 1209 assertThrows(UnsupportedOperationException.class, 1210 () -> mResolver.delete(SimRecords.getContentUri(1, EF_SDN), null)); 1211 assertThrows(UnsupportedOperationException.class, () -> 1212 mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1), null)); 1213 assertThrows(UnsupportedOperationException.class, () -> 1214 mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 1), null)); 1215 } 1216 1217 @Test subscriptionsChange_callsNotifyChange()1218 public void subscriptionsChange_callsNotifyChange() { 1219 // Clear invocations that happened in setUp 1220 Mockito.reset(mMockSubscriptionManager); 1221 // Stubbing this prevents the spied instance from calling the listener when it is added 1222 // which may cause flakiness. 1223 doNothing().when(mMockSubscriptionManager) 1224 .addOnSubscriptionsChangedListener(any(), any()); 1225 setupSimsWithSubscriptionIds(1); 1226 mIccPhoneBook.makeAllEfsSupported(1); 1227 SimPhonebookProvider.ContentNotifier mockNotifier = mock( 1228 SimPhonebookProvider.ContentNotifier.class); 1229 ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> listenerCaptor = 1230 ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class); 1231 1232 TestableSimPhonebookProvider.setup( 1233 mResolver, mMockSubscriptionManager, mIccPhoneBook, mockNotifier); 1234 verify(mMockSubscriptionManager).addOnSubscriptionsChangedListener( 1235 any(), listenerCaptor.capture()); 1236 1237 // Fake the initial call that is made by SubscriptionManager when a listener is registered 1238 // with addOnSubscriptionsChangedListener 1239 listenerCaptor.getValue().onSubscriptionsChanged(); 1240 1241 // First subscription change 1242 setupSimsWithSubscriptionIds(1, 2); 1243 listenerCaptor.getValue().onSubscriptionsChanged(); 1244 1245 // Second subscription change 1246 setupSimsWithSubscriptionIds(1); 1247 listenerCaptor.getValue().onSubscriptionsChanged(); 1248 1249 // Listener is called but subscriptions didn't change so this won't notify 1250 listenerCaptor.getValue().onSubscriptionsChanged(); 1251 1252 verify(mockNotifier, times(2)).notifyChange(eq(SimPhonebookContract.AUTHORITY_URI)); 1253 } 1254 1255 @Test insert_callsNotifyChange()1256 public void insert_callsNotifyChange() { 1257 // Clear invocations that happened in setUp 1258 Mockito.reset(mMockSubscriptionManager); 1259 setupSimsWithSubscriptionIds(1); 1260 mIccPhoneBook.makeAllEfsSupported(1); 1261 SimPhonebookProvider.ContentNotifier mockNotifier = mock( 1262 SimPhonebookProvider.ContentNotifier.class); 1263 1264 TestableSimPhonebookProvider.setup( 1265 mResolver, mMockSubscriptionManager, mIccPhoneBook, mockNotifier); 1266 1267 ContentValues values = new ContentValues(); 1268 values.put(SimRecords.NAME, "name"); 1269 values.put(SimRecords.PHONE_NUMBER, "5550101"); 1270 mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values); 1271 1272 verify(mockNotifier).notifyChange(eq(SimPhonebookContract.AUTHORITY_URI)); 1273 } 1274 1275 @Test update_callsNotifyChange()1276 public void update_callsNotifyChange() { 1277 // Clear invocations that happened in setUp 1278 Mockito.reset(mMockSubscriptionManager); 1279 setupSimsWithSubscriptionIds(1); 1280 mIccPhoneBook.addAdnRecord(1, "Initial", "5550101"); 1281 SimPhonebookProvider.ContentNotifier mockNotifier = mock( 1282 SimPhonebookProvider.ContentNotifier.class); 1283 1284 TestableSimPhonebookProvider.setup( 1285 mResolver, mMockSubscriptionManager, mIccPhoneBook, mockNotifier); 1286 1287 ContentValues values = new ContentValues(); 1288 values.put(SimRecords.NAME, "Updated"); 1289 values.put(SimRecords.PHONE_NUMBER, "5550102"); 1290 mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), values, null); 1291 1292 verify(mockNotifier).notifyChange(eq(SimPhonebookContract.AUTHORITY_URI)); 1293 } 1294 1295 @Test delete_callsNotifyChange()1296 public void delete_callsNotifyChange() { 1297 // Clear invocations that happened in setUp 1298 Mockito.reset(mMockSubscriptionManager); 1299 setupSimsWithSubscriptionIds(1); 1300 mIccPhoneBook.addAdnRecord(1, "Initial", "5550101"); 1301 SimPhonebookProvider.ContentNotifier mockNotifier = mock( 1302 SimPhonebookProvider.ContentNotifier.class); 1303 1304 TestableSimPhonebookProvider.setup( 1305 mResolver, mMockSubscriptionManager, mIccPhoneBook, mockNotifier); 1306 1307 mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), null); 1308 1309 verify(mockNotifier).notifyChange(eq(SimPhonebookContract.AUTHORITY_URI)); 1310 } 1311 1312 @Test getEncodedNameLength_returnsValueIsCorrect()1313 public void getEncodedNameLength_returnsValueIsCorrect() { 1314 String name = ""; 1315 int length = SimRecords.getEncodedNameLength(mResolver, name); 1316 assertThat(length).isEqualTo(0); 1317 1318 name = "First Last"; 1319 length = SimRecords.getEncodedNameLength(mResolver, name); 1320 assertThat(length).isEqualTo(name.length()); 1321 1322 name = "日本"; 1323 length = SimRecords.getEncodedNameLength(mResolver, name); 1324 assertThat(length).isEqualTo(name.length() * 2 + 1); 1325 1326 name = EMOJI; 1327 length = SimRecords.getEncodedNameLength(mResolver, name); 1328 assertThat(length).isEqualTo(name.length() * 2 + 1); 1329 1330 name = "abc日本" + EMOJI; 1331 length = SimRecords.getEncodedNameLength(mResolver, name); 1332 assertThat(length).isEqualTo(name.length() * 2 + 1); 1333 } 1334 setupSimsWithSubscriptionIds(int... subscriptionIds)1335 private void setupSimsWithSubscriptionIds(int... subscriptionIds) { 1336 doReturn(subscriptionIds).when(mMockSubscriptionManager).getActiveSubscriptionIdList(); 1337 doReturn(subscriptionIds.length).when(mMockSubscriptionManager) 1338 .getActiveSubscriptionInfoCount(); 1339 List<SubscriptionInfo> subscriptions = createSubscriptionsWithIds(subscriptionIds); 1340 doReturn(subscriptions).when(mMockSubscriptionManager).getActiveSubscriptionInfoList(); 1341 for (SubscriptionInfo info : subscriptions) { 1342 doReturn(info).when(mMockSubscriptionManager) 1343 .getActiveSubscriptionInfo(info.getSubscriptionId()); 1344 } 1345 } 1346 1347 public static class FakeIccPhoneBook extends IIccPhoneBook.Default { 1348 1349 private static final int DEFAULT_RECORD_SIZE = 30; 1350 private static final int DEFAULT_RECORDS_COUNT = 100; 1351 1352 // The key for both maps is the (subscription ID, efid) 1353 private Map<Pair<Integer, Integer>, AdnRecord[]> mRecords = new HashMap<>(); 1354 // The value is the single record size 1355 private Map<Pair<Integer, Integer>, int[]> mRecordSizes = new HashMap<>(); 1356 1357 private int mDefaultSubscriptionId = 101; 1358 addRecord(Pair<Integer, Integer> key, AdnRecord record)1359 private void addRecord(Pair<Integer, Integer> key, AdnRecord record) { 1360 // Assume that if records are being added then the test wants it to be a valid 1361 // elementary file so set sizes as well. 1362 if (!mRecordSizes.containsKey(key)) { 1363 setupEfWithSizes(key.first, key.second, 1364 Math.max(record.getRecId(), DEFAULT_RECORDS_COUNT), DEFAULT_RECORD_SIZE); 1365 } 1366 mRecords.get(key)[record.getRecId() - 1] = record; 1367 } 1368 addRecord(AdnRecord record)1369 public void addRecord(AdnRecord record) { 1370 addRecord(Pair.create(mDefaultSubscriptionId, record.getEfid()), record); 1371 } 1372 addRecord(int subscriptionId, AdnRecord record)1373 public void addRecord(int subscriptionId, AdnRecord record) { 1374 addRecord(Pair.create(subscriptionId, record.getEfid()), record); 1375 } 1376 addRecord(int subscriptionId, int efId, String name, String phoneNumber)1377 public void addRecord(int subscriptionId, int efId, String name, String phoneNumber) { 1378 Pair<Integer, Integer> key = Pair.create(subscriptionId, efId); 1379 AdnRecord[] records = mRecords.computeIfAbsent(key, unused -> 1380 createEmptyRecords(efId, 100)); 1381 int recordIndex = -1; 1382 for (int i = 0; i < records.length; i++) { 1383 if (records[i].isEmpty()) { 1384 recordIndex = i; 1385 break; 1386 } 1387 } 1388 if (recordIndex == -1) { 1389 throw new IllegalStateException(""); 1390 } 1391 addRecord(key, new AdnRecord(efId, recordIndex + 1, name, phoneNumber)); 1392 } 1393 addAdnRecord(int subscriptionId, String name, String phoneNumber)1394 public void addAdnRecord(int subscriptionId, String name, String phoneNumber) { 1395 addRecord(subscriptionId, IccConstants.EF_ADN, name, phoneNumber); 1396 } 1397 addAdnRecord(String name, String phoneNumber)1398 public void addAdnRecord(String name, String phoneNumber) { 1399 addRecord(mDefaultSubscriptionId, IccConstants.EF_ADN, name, phoneNumber); 1400 } 1401 getAllValidRecords()1402 public List<AdnRecord> getAllValidRecords() { 1403 List<AdnRecord> result = new ArrayList<>(); 1404 for (AdnRecord[] records : mRecords.values()) { 1405 for (AdnRecord record : records) { 1406 if (!record.isEmpty()) { 1407 result.add(record); 1408 } 1409 } 1410 } 1411 return result; 1412 } 1413 makeAllEfsSupported()1414 public void makeAllEfsSupported() { 1415 makeAllEfsSupported(mDefaultSubscriptionId); 1416 } 1417 1418 /** 1419 * Sets up the fake to return valid records size for all elementary files for the provided 1420 * subscription IDs. 1421 */ makeAllEfsSupported(int... subscriptionIds)1422 public void makeAllEfsSupported(int... subscriptionIds) { 1423 for (int subId : subscriptionIds) { 1424 makeAllEfsSupported(subId); 1425 } 1426 } 1427 1428 /** 1429 * Sets up the fake to return valid records size for all elementary files for the provided 1430 * subscription IDs. 1431 */ makeAllEfsSupported(int subscriptionId)1432 public void makeAllEfsSupported(int subscriptionId) { 1433 setupEfWithSizes(subscriptionId, IccConstants.EF_ADN, DEFAULT_RECORDS_COUNT, 1434 DEFAULT_RECORD_SIZE); 1435 setupEfWithSizes(subscriptionId, IccConstants.EF_FDN, DEFAULT_RECORDS_COUNT, 1436 DEFAULT_RECORD_SIZE); 1437 setupEfWithSizes(subscriptionId, IccConstants.EF_SDN, DEFAULT_RECORDS_COUNT, 1438 DEFAULT_RECORD_SIZE); 1439 } 1440 setRecords(int subscriptionId, int efid, AdnRecord[] records)1441 public void setRecords(int subscriptionId, int efid, AdnRecord[] records) { 1442 mRecords.put(Pair.create(subscriptionId, efid), records); 1443 } 1444 setRecordsSize(int subscriptionId, int efid, int maxRecordCount, int maxRecordSize)1445 public void setRecordsSize(int subscriptionId, int efid, int maxRecordCount, 1446 int maxRecordSize) { 1447 setRecordsSize(Pair.create(subscriptionId, efid), maxRecordCount, maxRecordSize); 1448 } 1449 setRecordsSize(Pair<Integer, Integer> key, int maxRecordCount, int maxRecordSize)1450 private void setRecordsSize(Pair<Integer, Integer> key, int maxRecordCount, 1451 int maxRecordSize) { 1452 int[] sizes = { maxRecordSize, maxRecordSize * maxRecordCount, maxRecordCount }; 1453 mRecordSizes.put(key, sizes); 1454 } 1455 setupEfWithSizes(int subscriptionId, int efid, int maxRecordCount, int maxRecordSize)1456 public void setupEfWithSizes(int subscriptionId, int efid, int maxRecordCount, 1457 int maxRecordSize) { 1458 Pair<Integer, Integer> key = Pair.create(subscriptionId, efid); 1459 setRecordsSize(key, maxRecordCount, maxRecordSize); 1460 AdnRecord[] records = mRecords.computeIfAbsent(key, unused -> 1461 createEmptyRecords(efid, maxRecordCount)); 1462 if (records.length < maxRecordCount) { 1463 throw new IllegalStateException("Records already initialized with a smaller size"); 1464 } 1465 } 1466 createEmptyRecords(int efid, int count)1467 AdnRecord[] createEmptyRecords(int efid, int count) { 1468 AdnRecord[] records = new AdnRecord[count]; 1469 for (int i = 0; i < records.length; i++) { 1470 if (records[i] == null) { 1471 records[i] = new AdnRecord(efid, i + 1, "", ""); 1472 } 1473 } 1474 return records; 1475 } 1476 setDefaultSubscriptionId(int defaultSubscriptionId)1477 public void setDefaultSubscriptionId(int defaultSubscriptionId) { 1478 mDefaultSubscriptionId = defaultSubscriptionId; 1479 } 1480 reset()1481 public void reset() { 1482 mRecords.clear(); 1483 mRecordSizes.clear(); 1484 } 1485 1486 @Override getAdnRecordsInEf(int efid)1487 public List<AdnRecord> getAdnRecordsInEf(int efid) { 1488 return getAdnRecordsInEfForSubscriber(mDefaultSubscriptionId, efid); 1489 } 1490 1491 @Override getAdnRecordsInEfForSubscriber(int subId, int efid)1492 public List<AdnRecord> getAdnRecordsInEfForSubscriber(int subId, int efid) { 1493 return Arrays.asList( 1494 mRecords.getOrDefault(Pair.create(subId, efid), new AdnRecord[0])); 1495 } 1496 1497 @Override updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid, ContentValues values, String pin2)1498 public boolean updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid, 1499 ContentValues values, String pin2) { 1500 final String oldTag = values.getAsString(IccProvider.STR_TAG); 1501 final String oldPhoneNumber = values.getAsString(IccProvider.STR_NUMBER); 1502 final String newTag = values.getAsString(IccProvider.STR_NEW_TAG); 1503 final String newPhoneNumber = values.getAsString(IccProvider.STR_NEW_NUMBER); 1504 return updateAdnRecordsInEfBySearchForSubscriber(subId, efid, oldTag, oldPhoneNumber, 1505 newTag, newPhoneNumber, pin2); 1506 1507 } 1508 updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid, String oldTag, String oldPhoneNumber, String newTag, String newPhoneNumber, String pin2)1509 private boolean updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid, String oldTag, 1510 String oldPhoneNumber, String newTag, String newPhoneNumber, String pin2) { 1511 if (!oldTag.isEmpty() || !oldPhoneNumber.isEmpty()) { 1512 throw new IllegalArgumentException( 1513 "updateAdnRecordsInEfBySearchForSubscriber only supports insert"); 1514 } 1515 addRecord(subId, efid, newTag, newPhoneNumber); 1516 return true; 1517 } 1518 1519 @Override updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, ContentValues values, int index, String pin2)1520 public boolean updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, 1521 ContentValues values, int index, String pin2) { 1522 final String newTag = values.getAsString(IccProvider.STR_NEW_TAG); 1523 final String newPhoneNumber = values.getAsString(IccProvider.STR_NEW_NUMBER); 1524 return updateAdnRecordsInEfByIndexForSubscriber(subId, efid, newTag, newPhoneNumber, 1525 index, pin2); 1526 1527 } 1528 updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, String newTag, String newPhoneNumber, int index, String pin2)1529 private boolean updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, String newTag, 1530 String newPhoneNumber, int index, String pin2) { 1531 AdnRecord[] records = mRecords.computeIfAbsent(Pair.create(subId, efid), unused -> 1532 createEmptyRecords(efid, 100)); 1533 records[index - 1] = new AdnRecord(efid, index, newTag, newPhoneNumber); 1534 return true; 1535 } 1536 1537 @Override getAdnRecordsSize(int efid)1538 public int[] getAdnRecordsSize(int efid) { 1539 return getAdnRecordsSizeForSubscriber(mDefaultSubscriptionId, efid); 1540 } 1541 1542 @Override getAdnRecordsSizeForSubscriber(int subId, int efid)1543 public int[] getAdnRecordsSizeForSubscriber(int subId, int efid) { 1544 Pair<Integer, Integer> key = Pair.create(subId, efid); 1545 int[] recordsSize = mRecordSizes.get(key); 1546 if (recordsSize == null) { 1547 return new int[]{0, 0, 0}; 1548 } 1549 return recordsSize; 1550 } 1551 1552 @Override getAdnRecordsCapacityForSubscriber(int subId)1553 public AdnCapacity getAdnRecordsCapacityForSubscriber(int subId) { 1554 return new AdnCapacity(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 1555 } 1556 } 1557 1558 /** 1559 * Implementation of SimPhonebookProvider that allows test-doubles to be injected. 1560 * 1561 * <p>The ProviderTestRule doesn't seem to allow a better way to do this since it just 1562 * invokes the constructor. 1563 */ 1564 public static class TestableSimPhonebookProvider extends SimPhonebookProvider { 1565 setup( ContentResolver resolver, SubscriptionManager subscriptionManager, IIccPhoneBook iccPhoneBook)1566 public static void setup( 1567 ContentResolver resolver, 1568 SubscriptionManager subscriptionManager, 1569 IIccPhoneBook iccPhoneBook) { 1570 setup(resolver, subscriptionManager, iccPhoneBook, uri -> { 1571 }); 1572 } 1573 setup( ContentResolver resolver, SubscriptionManager subscriptionManager, IIccPhoneBook iccPhoneBook, ContentNotifier notifier)1574 public static void setup( 1575 ContentResolver resolver, 1576 SubscriptionManager subscriptionManager, 1577 IIccPhoneBook iccPhoneBook, 1578 ContentNotifier notifier) { 1579 TestableSimPhonebookProvider provider = 1580 (TestableSimPhonebookProvider) Objects.requireNonNull( 1581 resolver.acquireContentProviderClient( 1582 SimPhonebookContract.AUTHORITY)) 1583 .getLocalContentProvider(); 1584 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> 1585 provider.onCreate(subscriptionManager, () -> iccPhoneBook, notifier)); 1586 } 1587 1588 @Override onCreate()1589 public boolean onCreate() { 1590 // We stub super.onCreate because it initializes services which causes an 1591 // IllegalArgumentException because of the context used for the test. 1592 return true; 1593 } 1594 } 1595 } 1596