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