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