1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.carrierapi.cts; 18 19 import static android.carrierapi.cts.FcpTemplate.FILE_IDENTIFIER; 20 import static android.carrierapi.cts.IccUtils.bytesToHexString; 21 import static android.carrierapi.cts.IccUtils.hexStringToBytes; 22 import static android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL; 23 import static android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR; 24 25 import static org.junit.Assert.assertArrayEquals; 26 import static org.junit.Assert.assertNotEquals; 27 28 import android.content.BroadcastReceiver; 29 import android.content.ContentProviderClient; 30 import android.content.ContentValues; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.PackageInfo; 35 import android.content.pm.PackageManager; 36 import android.database.Cursor; 37 import android.net.Uri; 38 import android.os.Build; 39 import android.os.AsyncTask; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.ParcelUuid; 43 import android.os.PersistableBundle; 44 import android.provider.Telephony; 45 import android.provider.VoicemailContract; 46 import android.telephony.AvailableNetworkInfo; 47 import android.telephony.CarrierConfigManager; 48 import android.telephony.IccOpenLogicalChannelResponse; 49 import android.telephony.PhoneStateListener; 50 import android.telephony.SubscriptionInfo; 51 import android.telephony.SubscriptionManager; 52 import android.telephony.TelephonyManager; 53 import android.test.AndroidTestCase; 54 import android.util.Log; 55 56 import com.android.compatibility.common.util.ShellIdentityUtils; 57 58 import java.security.MessageDigest; 59 import java.security.NoSuchAlgorithmException; 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.Collections; 63 import java.util.HashSet; 64 import java.util.List; 65 import java.util.Set; 66 import java.util.concurrent.CountDownLatch; 67 import java.util.concurrent.TimeUnit; 68 import java.util.concurrent.atomic.AtomicReference; 69 import java.util.function.Consumer; 70 import java.util.stream.Collectors; 71 72 import javax.annotation.Nonnull; 73 74 // TODO(b/130187425): Split CarrierApiTest apart to have separate test classes for functionality 75 public class CarrierApiTest extends AndroidTestCase { 76 private static final String TAG = "CarrierApiTest"; 77 private TelephonyManager mTelephonyManager; 78 private CarrierConfigManager mCarrierConfigManager; 79 private PackageManager mPackageManager; 80 private SubscriptionManager mSubscriptionManager; 81 private ContentProviderClient mVoicemailProvider; 82 private ContentProviderClient mStatusProvider; 83 private Uri mVoicemailContentUri; 84 private Uri mStatusContentUri; 85 private boolean hasCellular; 86 private String selfPackageName; 87 private String selfCertHash; 88 private HandlerThread mListenerThread; 89 90 private static final String FiDevCert = "24EB92CBB156B280FA4E1429A6ECEEB6E5C1BFE4"; 91 // The minimum allocatable logical channel number, per TS 102 221 Section 11.1.17.1 92 private static final int MIN_LOGICAL_CHANNEL = 1; 93 // The maximum allocatable logical channel number in the standard range, per TS 102 221 Section 94 // 11.1.17.1 95 private static final int MAX_LOGICAL_CHANNEL = 3; 96 // Class bytes. The logical channel used should be included for bits b2b1. TS 102 221 Table 11.5 97 private static final String CLA_ENVELOPE = "80"; 98 private static final int CLA_GET_RESPONSE = 0x00; 99 private static final int CLA_MANAGE_CHANNEL = 0x00; 100 private static final int CLA_READ_BINARY = 0x00; 101 private static final int CLA_SELECT = 0x00; 102 private static final int CLA_STATUS = 0x80; 103 private static final String CLA_STATUS_STRING = "80"; 104 // APDU Instruction Bytes. TS 102 221 Section 10.1.2 105 private static final String COMMAND_ENVELOPE = "C2"; 106 private static final int COMMAND_GET_RESPONSE = 0xC0; 107 private static final int COMMAND_MANAGE_CHANNEL = 0x70; 108 private static final int COMMAND_READ_BINARY = 0xB0; 109 private static final int COMMAND_SELECT = 0xA4; 110 private static final int COMMAND_STATUS = 0xF2; 111 private static final String COMMAND_STATUS_STRING = "F2"; 112 // Status words. TS 102 221 Section 10.2.1 113 private static final byte[] STATUS_NORMAL = {(byte) 0x90, (byte) 0x00}; 114 private static final String STATUS_NORMAL_STRING = "9000"; 115 private static final String STATUS_BYTES_REMAINING = "61"; 116 private static final String STATUS_WARNING_A = "62"; 117 private static final String STATUS_WARNING_B = "63"; 118 private static final String STATUS_FILE_NOT_FOUND = "6a82"; 119 private static final String STATUS_INCORRECT_PARAMETERS = "6a86"; 120 private static final String STATUS_WRONG_PARAMETERS = "6b00"; 121 private static final Set<String> INVALID_PARAMETERS_STATUSES = 122 new HashSet<>(Arrays.asList(STATUS_INCORRECT_PARAMETERS, STATUS_WRONG_PARAMETERS)); 123 private static final String STATUS_WRONG_CLASS = "6e00"; 124 // File ID for the EF ICCID. TS 102 221 125 private static final String ICCID_FILE_ID = "2FE2"; 126 // File ID for the master file. TS 102 221 127 private static final String MF_FILE_ID = "3F00"; 128 private static final int MF_FILE_ID_HEX = 0x3F00; 129 // File ID for the MF Access Rule Reference. TS 102 221 130 private static final String MF_ARR_FILE_ID = "2F06"; 131 private static final String ALPHA_TAG_A = "tagA"; 132 private static final String ALPHA_TAG_B = "tagB"; 133 private static final String NUMBER_A = "1234567890"; 134 private static final String NUMBER_B = "0987654321"; 135 136 private static final int DSDS_PHONE_COUNT = 2; 137 138 @Override 139 protected void setUp() throws Exception { 140 super.setUp(); 141 mPackageManager = getContext().getPackageManager(); 142 mTelephonyManager = (TelephonyManager) 143 getContext().getSystemService(Context.TELEPHONY_SERVICE); 144 hasCellular = hasCellular(); 145 if (!hasCellular) { 146 Log.e(TAG, "No cellular support, all tests will be skipped."); 147 return; 148 } 149 150 mCarrierConfigManager = (CarrierConfigManager) 151 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 152 mSubscriptionManager = (SubscriptionManager) 153 getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 154 selfPackageName = getContext().getPackageName(); 155 selfCertHash = getCertHash(selfPackageName); 156 mVoicemailContentUri = VoicemailContract.Voicemails.buildSourceUri(selfPackageName); 157 mVoicemailProvider = getContext().getContentResolver() 158 .acquireContentProviderClient(mVoicemailContentUri); 159 mStatusContentUri = VoicemailContract.Status.buildSourceUri(selfPackageName); 160 mStatusProvider = getContext().getContentResolver() 161 .acquireContentProviderClient(mStatusContentUri); 162 mListenerThread = new HandlerThread("CarrierApiTest"); 163 mListenerThread.start(); 164 165 // We need to close all logical channels in the standard range, [1, 3], before each test. 166 // This makes sure each SIM-related test starts with a clean slate. 167 for (int i = MIN_LOGICAL_CHANNEL; i <= MAX_LOGICAL_CHANNEL; i++) { 168 mTelephonyManager.iccCloseLogicalChannel(i); 169 } 170 } 171 172 @Override 173 public void tearDown() throws Exception { 174 if (!hasCellular) return; 175 176 // We need to close all logical channels in the standard range, [1, 3], after each test. 177 // This makes sure each SIM-related test releases any opened channels. Channels should only 178 // be closed for devices that have cellular capabilities. 179 for (int i = MIN_LOGICAL_CHANNEL; i <= MAX_LOGICAL_CHANNEL; i++) { 180 mTelephonyManager.iccCloseLogicalChannel(i); 181 } 182 mListenerThread.quit(); 183 try { 184 mStatusProvider.delete(mStatusContentUri, null, null); 185 mVoicemailProvider.delete(mVoicemailContentUri, null, null); 186 } catch (Exception e) { 187 Log.w(TAG, "Failed to clean up voicemail tables in tearDown", e); 188 } 189 super.tearDown(); 190 } 191 192 /** 193 * Checks whether the cellular stack should be running on this device. 194 */ 195 private boolean hasCellular() { 196 return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) && 197 mTelephonyManager.getPhoneCount() > 0; 198 } 199 200 private boolean isSimCardPresent() { 201 return mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE && 202 mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_ABSENT; 203 } 204 205 private String getCertHash(String pkgName) { 206 try { 207 PackageInfo pInfo = mPackageManager.getPackageInfo(pkgName, 208 PackageManager.GET_SIGNATURES | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); 209 MessageDigest md = MessageDigest.getInstance("SHA-1"); 210 return bytesToHexString(md.digest(pInfo.signatures[0].toByteArray())); 211 } catch (PackageManager.NameNotFoundException ex) { 212 Log.e(TAG, pkgName + " not found", ex); 213 } catch (NoSuchAlgorithmException ex) { 214 Log.e(TAG, "Algorithm SHA1 is not found."); 215 } 216 return ""; 217 } 218 219 private void failMessage() { 220 if (FiDevCert.equalsIgnoreCase(selfCertHash)) { 221 fail("This test requires a Project Fi SIM card."); 222 } else { 223 fail("This test requires a SIM card with carrier privilege rule on it.\n" + 224 "Cert hash: " + selfCertHash + "\n" + 225 "Visit https://source.android.com/devices/tech/config/uicc.html"); 226 } 227 } 228 229 public void testSimCardPresent() { 230 if (!hasCellular) return; 231 assertTrue("This test requires SIM card.", isSimCardPresent()); 232 } 233 234 public void testHasCarrierPrivileges() { 235 if (!hasCellular) return; 236 if (!mTelephonyManager.hasCarrierPrivileges()) { 237 failMessage(); 238 } 239 } 240 241 private static void assertUpdateAvailableNetworkSuccess(int value) { 242 assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS, value); 243 } 244 245 private static void assertUpdateAvailableNetworkInvalidArguments(int value) { 246 assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS, value); 247 } 248 249 private static void assertSetOpportunisticSubSuccess(int value) { 250 assertEquals(TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS, value); 251 } 252 253 private int getFirstActivateCarrierPrivilegedSubscriptionId() { 254 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 255 List<SubscriptionInfo> subscriptionInfos = 256 mSubscriptionManager.getActiveSubscriptionInfoList(); 257 if (subscriptionInfos != null) { 258 for (SubscriptionInfo info : subscriptionInfos) { 259 TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId( 260 info.getSubscriptionId()); 261 if (telephonyManager.hasCarrierPrivileges()) { 262 subId = info.getSubscriptionId(); 263 return subId; 264 } 265 } 266 } 267 return subId; 268 } 269 270 public void testUpdateAvailableNetworksWithCarrierPrivilege() { 271 if (!hasCellular) return; 272 273 int subIdWithCarrierPrivilege = getFirstActivateCarrierPrivilegedSubscriptionId(); 274 int activeSubscriptionInfoCount = ShellIdentityUtils.invokeMethodWithShellPermissions( 275 mSubscriptionManager, (tm) -> tm.getActiveSubscriptionInfoCount()); 276 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 277 return; 278 } 279 if (mTelephonyManager.getPhoneCount() == 1) { 280 return; 281 } 282 if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) { 283 fail("This test requires two SIM cards."); 284 } 285 if (subIdWithCarrierPrivilege == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 286 failMessage(); 287 } 288 289 List<SubscriptionInfo> subscriptionInfoList = 290 mSubscriptionManager.getOpportunisticSubscriptions(); 291 List<String> mccMncs = new ArrayList<String>(); 292 List<Integer> bands = new ArrayList<Integer>(); 293 List<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<AvailableNetworkInfo>(); 294 Consumer<Integer> callbackSuccess = 295 CarrierApiTest::assertUpdateAvailableNetworkSuccess; 296 Consumer<Integer> callbackFailure = 297 CarrierApiTest::assertUpdateAvailableNetworkInvalidArguments; 298 Consumer<Integer> setOpCallbackSuccess = CarrierApiTest::assertSetOpportunisticSubSuccess; 299 if (subscriptionInfoList == null || subscriptionInfoList.size() == 0 300 || !mSubscriptionManager.isActiveSubscriptionId( 301 subscriptionInfoList.get(0).getSubscriptionId())) { 302 try { 303 AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo( 304 subIdWithCarrierPrivilege, AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, 305 bands); 306 availableNetworkInfos.add(availableNetworkInfo); 307 // Call updateAvailableNetworks without opportunistic subscription. 308 // callbackFailure is expected to be triggered and the return value will be checked 309 // against UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS 310 mTelephonyManager.updateAvailableNetworks(availableNetworkInfos, 311 AsyncTask.SERIAL_EXECUTOR, callbackFailure); 312 } finally { 313 // clear all the operations at the end of test. 314 availableNetworkInfos.clear(); 315 mTelephonyManager.updateAvailableNetworks(availableNetworkInfos, 316 AsyncTask.SERIAL_EXECUTOR, callbackFailure); 317 } 318 } else { 319 // This is case of DSDS phone, one active opportunistic subscription and one 320 // active primary subscription. 321 int resultSubId; 322 try { 323 AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo( 324 subscriptionInfoList.get(0).getSubscriptionId(), 325 AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands); 326 availableNetworkInfos.add(availableNetworkInfo); 327 mTelephonyManager.updateAvailableNetworks(availableNetworkInfos, 328 AsyncTask.SERIAL_EXECUTOR, callbackSuccess); 329 // wait for the data change to take effect 330 waitForMs(500); 331 // Call setPreferredData and reconfirm with getPreferred data 332 // that the same is updated. 333 int preferSubId = subscriptionInfoList.get(0).getSubscriptionId(); 334 mTelephonyManager.setPreferredOpportunisticDataSubscription( 335 preferSubId, false, AsyncTask.SERIAL_EXECUTOR, setOpCallbackSuccess); 336 // wait for the data change to take effect 337 waitForMs(500); 338 resultSubId = mTelephonyManager.getPreferredOpportunisticDataSubscription(); 339 assertEquals(preferSubId, resultSubId); 340 } finally { 341 // clear all the operations at the end of test. 342 availableNetworkInfos.clear(); 343 mTelephonyManager.updateAvailableNetworks(availableNetworkInfos, 344 AsyncTask.SERIAL_EXECUTOR, callbackSuccess); 345 waitForMs(500); 346 mTelephonyManager.setPreferredOpportunisticDataSubscription( 347 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, 348 AsyncTask.SERIAL_EXECUTOR, callbackSuccess); 349 waitForMs(500); 350 resultSubId = mTelephonyManager.getPreferredOpportunisticDataSubscription(); 351 assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, resultSubId); 352 } 353 } 354 } 355 356 public static void waitForMs(long ms) { 357 try { 358 Thread.sleep(ms); 359 } catch (InterruptedException e) { 360 Log.d(TAG, "InterruptedException while waiting: " + e); 361 } 362 } 363 364 public void testGetIccAuthentication() { 365 // EAP-SIM rand is 16 bytes. 366 String base64Challenge = "ECcTqwuo6OfY8ddFRboD9WM="; 367 String base64Challenge2 = "EMNxjsFrPCpm+KcgCmQGnwQ="; 368 if (!hasCellular) return; 369 try { 370 assertNull("getIccAuthentication should return null for empty data.", 371 mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM, 372 TelephonyManager.AUTHTYPE_EAP_AKA, "")); 373 String response = mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM, 374 TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge); 375 assertTrue("Response to EAP-SIM Challenge must not be Null.", response != null); 376 // response is base64 encoded. After decoding, the value should be: 377 // 1 length byte + SRES(4 bytes) + 1 length byte + Kc(8 bytes) 378 byte[] result = android.util.Base64.decode(response, android.util.Base64.DEFAULT); 379 assertTrue("Result length must be 14 bytes.", 14 == result.length); 380 String response2 = mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM, 381 TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge2); 382 assertTrue("Two responses must be different.", !response.equals(response2)); 383 } catch (SecurityException e) { 384 failMessage(); 385 } 386 } 387 388 public void testSendDialerSpecialCode() { 389 if (!hasCellular) return; 390 try { 391 IntentReceiver intentReceiver = new IntentReceiver(); 392 final IntentFilter intentFilter = new IntentFilter(); 393 intentFilter.addAction(Telephony.Sms.Intents.SECRET_CODE_ACTION); 394 intentFilter.addDataScheme("android_secret_code"); 395 getContext().registerReceiver(intentReceiver, intentFilter); 396 397 mTelephonyManager.sendDialerSpecialCode("4636"); 398 assertTrue("Did not receive expected Intent: " + 399 Telephony.Sms.Intents.SECRET_CODE_ACTION, 400 intentReceiver.waitForReceive()); 401 } catch (SecurityException e) { 402 failMessage(); 403 } catch (InterruptedException e) { 404 Log.d(TAG, "Broadcast receiver wait was interrupted."); 405 } 406 } 407 408 public void testSubscriptionInfoListing() { 409 if (!hasCellular) return; 410 try { 411 assertTrue("getActiveSubscriptionInfoCount() should be non-zero", 412 mSubscriptionManager.getActiveSubscriptionInfoCount() > 0); 413 List<SubscriptionInfo> subInfoList = 414 mSubscriptionManager.getActiveSubscriptionInfoList(); 415 assertNotNull("getActiveSubscriptionInfoList() returned null", subInfoList); 416 assertFalse("getActiveSubscriptionInfoList() returned an empty list", 417 subInfoList.isEmpty()); 418 for (SubscriptionInfo info : subInfoList) { 419 TelephonyManager tm = 420 mTelephonyManager.createForSubscriptionId(info.getSubscriptionId()); 421 assertTrue("getActiveSubscriptionInfoList() returned an inaccessible subscription", 422 tm.hasCarrierPrivileges()); 423 424 // Check other APIs to make sure they are accessible and return consistent info. 425 SubscriptionInfo infoForSlot = 426 mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex( 427 info.getSimSlotIndex()); 428 assertNotNull("getActiveSubscriptionInfoForSimSlotIndex() returned null", 429 infoForSlot); 430 assertEquals( 431 "getActiveSubscriptionInfoForSimSlotIndex() returned inconsistent info", 432 info.getSubscriptionId(), infoForSlot.getSubscriptionId()); 433 434 SubscriptionInfo infoForSubId = 435 mSubscriptionManager.getActiveSubscriptionInfo(info.getSubscriptionId()); 436 assertNotNull("getActiveSubscriptionInfo() returned null", infoForSubId); 437 assertEquals("getActiveSubscriptionInfo() returned inconsistent info", 438 info.getSubscriptionId(), infoForSubId.getSubscriptionId()); 439 } 440 } catch (SecurityException e) { 441 failMessage(); 442 } 443 } 444 445 public void testCarrierConfigIsAccessible() { 446 if (!hasCellular) return; 447 try { 448 PersistableBundle bundle = mCarrierConfigManager.getConfig(); 449 assertNotNull("CarrierConfigManager#getConfig() returned null", bundle); 450 assertFalse("CarrierConfigManager#getConfig() returned empty bundle", bundle.isEmpty()); 451 452 int subId = SubscriptionManager.getDefaultSubscriptionId(); 453 bundle = mCarrierConfigManager.getConfigForSubId(subId); 454 assertNotNull("CarrierConfigManager#getConfigForSubId() returned null", bundle); 455 assertFalse("CarrierConfigManager#getConfigForSubId() returned empty bundle", 456 bundle.isEmpty()); 457 } catch (SecurityException e) { 458 failMessage(); 459 } 460 } 461 462 public void testTelephonyApisAreAccessible() { 463 if (!hasCellular) return; 464 // The following methods may return any value depending on the state of the device. Simply 465 // call them to make sure they do not throw any exceptions. Methods that return a device 466 // identifier will be accessible to apps with carrier privileges in Q, but this may change 467 // in a future release. 468 try { 469 mTelephonyManager.getDeviceId(); 470 mTelephonyManager.getImei(); 471 mTelephonyManager.getMeid(); 472 mTelephonyManager.getDeviceSoftwareVersion(); 473 mTelephonyManager.getNai(); 474 mTelephonyManager.getDataNetworkType(); 475 mTelephonyManager.getVoiceNetworkType(); 476 mTelephonyManager.getSimSerialNumber(); 477 mTelephonyManager.getSubscriberId(); 478 mTelephonyManager.getGroupIdLevel1(); 479 mTelephonyManager.getLine1Number(); 480 mTelephonyManager.getVoiceMailNumber(); 481 mTelephonyManager.getVisualVoicemailPackageName(); 482 mTelephonyManager.getVoiceMailAlphaTag(); 483 mTelephonyManager.getForbiddenPlmns(); 484 mTelephonyManager.getServiceState(); 485 } catch (SecurityException e) { 486 failMessage(); 487 } 488 } 489 490 public void testVoicemailTableIsAccessible() throws Exception { 491 if (!hasCellular) return; 492 ContentValues value = new ContentValues(); 493 value.put(VoicemailContract.Voicemails.NUMBER, "0123456789"); 494 value.put(VoicemailContract.Voicemails.SOURCE_PACKAGE, selfPackageName); 495 try { 496 Uri uri = mVoicemailProvider.insert(mVoicemailContentUri, value); 497 assertNotNull(uri); 498 Cursor cursor = mVoicemailProvider.query(uri, 499 new String[] { 500 VoicemailContract.Voicemails.NUMBER, 501 VoicemailContract.Voicemails.SOURCE_PACKAGE 502 }, null, null, null); 503 assertNotNull(cursor); 504 assertTrue(cursor.moveToFirst()); 505 assertEquals("0123456789", cursor.getString(0)); 506 assertEquals(selfPackageName, cursor.getString(1)); 507 assertFalse(cursor.moveToNext()); 508 } catch (SecurityException e) { 509 failMessage(); 510 } 511 } 512 513 public void testVoicemailStatusTableIsAccessible() throws Exception { 514 if (!hasCellular) return; 515 ContentValues value = new ContentValues(); 516 value.put(VoicemailContract.Status.CONFIGURATION_STATE, 517 VoicemailContract.Status.CONFIGURATION_STATE_OK); 518 value.put(VoicemailContract.Status.SOURCE_PACKAGE, selfPackageName); 519 try { 520 Uri uri = mStatusProvider.insert(mStatusContentUri, value); 521 assertNotNull(uri); 522 Cursor cursor = mVoicemailProvider.query(uri, 523 new String[] { 524 VoicemailContract.Status.CONFIGURATION_STATE, 525 VoicemailContract.Status.SOURCE_PACKAGE 526 }, null, null, null); 527 assertNotNull(cursor); 528 assertTrue(cursor.moveToFirst()); 529 assertEquals(VoicemailContract.Status.CONFIGURATION_STATE_OK, cursor.getInt(0)); 530 assertEquals(selfPackageName, cursor.getString(1)); 531 assertFalse(cursor.moveToNext()); 532 } catch (SecurityException e) { 533 failMessage(); 534 } 535 } 536 537 public void testPhoneStateListener() throws Exception { 538 if (!hasCellular) return; 539 final AtomicReference<SecurityException> error = new AtomicReference<>(); 540 final CountDownLatch latch = new CountDownLatch(1); 541 new Handler(mListenerThread.getLooper()).post(() -> { 542 PhoneStateListener listener = new PhoneStateListener() {}; 543 try { 544 mTelephonyManager.listen( 545 listener, PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR); 546 mTelephonyManager.listen( 547 listener, PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR); 548 } catch (SecurityException e) { 549 error.set(e); 550 } finally { 551 mTelephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE); 552 latch.countDown(); 553 } 554 }); 555 assertTrue("Test timed out", latch.await(30L, TimeUnit.SECONDS)); 556 if (error.get() != null) { 557 failMessage(); 558 } 559 } 560 561 public void testSubscriptionInfoChangeListener() throws Exception { 562 if (!hasCellular) return; 563 final AtomicReference<SecurityException> error = new AtomicReference<>(); 564 final CountDownLatch latch = new CountDownLatch(1); 565 new Handler(mListenerThread.getLooper()).post(() -> { 566 SubscriptionManager.OnSubscriptionsChangedListener listener = 567 new SubscriptionManager.OnSubscriptionsChangedListener(); 568 try { 569 mSubscriptionManager.addOnSubscriptionsChangedListener(listener); 570 } catch (SecurityException e) { 571 error.set(e); 572 } finally { 573 mSubscriptionManager.removeOnSubscriptionsChangedListener(listener); 574 latch.countDown(); 575 } 576 }); 577 assertTrue("Test timed out", latch.await(30L, TimeUnit.SECONDS)); 578 if (error.get() != null) { 579 failMessage(); 580 } 581 582 } 583 584 /** 585 * Test that it's possible to open logical channels to the ICC. This mirrors the Manage Channel 586 * command described in TS 102 221 Section 11.1.17. 587 */ 588 public void testIccOpenLogicalChannel() { 589 if (!hasCellular) return; 590 591 // The AID here doesn't matter - we just need to open a valid connection. In this case, the 592 // specified AID ("") opens a channel and selects the MF. 593 IccOpenLogicalChannelResponse response = mTelephonyManager.iccOpenLogicalChannel(""); 594 verifyValidIccOpenLogicalChannelResponse(response); 595 mTelephonyManager.iccCloseLogicalChannel(response.getChannel()); 596 597 // {@link TelephonyManager#iccOpenLogicalChannel} sends a Manage Channel (open) APDU 598 // followed by a Select APDU with the given AID and p2 values. See Open Mobile API 599 // Specification v3.2 Section 6.2.7.h and TS 102 221 for details. 600 int p2 = 0x0C; // '0C' for no data returned (TS 102 221 Section 11.1.1.2) 601 response = mTelephonyManager.iccOpenLogicalChannel("", p2); 602 verifyValidIccOpenLogicalChannelResponse(response); 603 mTelephonyManager.iccCloseLogicalChannel(response.getChannel()); 604 605 // Valid p2 values are defined in TS 102 221 Table 11.2. Per Table 11.2, 0xF0 should be 606 // invalid. Any p2 values that produce non '9000'/'62xx'/'63xx' status words are treated as 607 // an error and the channel is not opened. Due to compatibility issues with older devices, 608 // this check is only enabled for new devices launching on Q+. 609 if (Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.Q) { 610 p2 = 0xF0; 611 response = mTelephonyManager.iccOpenLogicalChannel("", p2); 612 assertEquals(INVALID_CHANNEL, response.getChannel()); 613 assertNotEquals(STATUS_NO_ERROR, response.getStatus()); 614 } 615 } 616 617 /** 618 * Test that it's possible to close logical channels to the ICC. This follows the Manage Channel 619 * command described in TS 102 221 Section 11.1.17. 620 */ 621 public void testIccCloseLogicalChannel() { 622 if (!hasCellular) return; 623 624 // The directory here doesn't matter - we just need to open a valid connection that can 625 // later be closed. In this case, the specified AID ("") opens a channel and selects the MF. 626 IccOpenLogicalChannelResponse response = mTelephonyManager.iccOpenLogicalChannel(""); 627 // Check that the select command succeeded. This ensures that the logical channel is indeed 628 // open. 629 assertArrayEquals(STATUS_NORMAL, response.getSelectResponse()); 630 assertTrue(mTelephonyManager.iccCloseLogicalChannel(response.getChannel())); 631 632 // Close opened channel twice. 633 assertFalse(mTelephonyManager.iccCloseLogicalChannel(response.getChannel())); 634 635 // Close channel that is not open. 636 assertFalse(mTelephonyManager.iccCloseLogicalChannel(2)); 637 638 // Channel 0 is guaranteed to be always available and cannot be closed, per TS 102 221 639 // Section 11.1.17 640 assertFalse(mTelephonyManager.iccCloseLogicalChannel(0)); 641 } 642 643 /** 644 * This test ensures that valid APDU instructions can be sent and processed by the ICC. To do 645 * so, APDUs are sent to: 646 * - get the status of the MF 647 * - select the Access Rule Reference (ARR) for the MF 648 * - get the FCP template response for the select 649 */ 650 public void testIccTransmitApduLogicalChannel() { 651 if (!hasCellular) return; 652 653 // An open LC is required for transmitting APDU commands. This opens an LC to the MF. 654 IccOpenLogicalChannelResponse logicalChannel = mTelephonyManager.iccOpenLogicalChannel(""); 655 656 // Get the status of the current directory. This should match the MF. TS 102 221 Section 657 // 11.1.2 658 int channel = logicalChannel.getChannel(); 659 int cla = CLA_STATUS; 660 int p1 = 0; // no indication of application status 661 int p2 = 0; // same response parameters as the SELECT in the iccOpenLogicalChannel() above 662 int p3 = 0; // length of 'data' payload 663 String data = ""; 664 String response = mTelephonyManager 665 .iccTransmitApduLogicalChannel(channel, cla, COMMAND_STATUS, p1, p2, p3, data); 666 FcpTemplate fcpTemplate = FcpTemplate.parseFcpTemplate(response); 667 // Check that the FCP Template's file ID matches the MF 668 assertTrue(containsFileId(fcpTemplate, MF_FILE_ID)); 669 assertEquals(STATUS_NORMAL_STRING, fcpTemplate.getStatus()); 670 671 // Select the Access Rule Reference for the MF. Similar to the MF, this will exist across 672 // all SIM cards. TS 102 221 Section 11.1.1 673 cla = CLA_SELECT; 674 p1 = 0; // select EF by FID 675 p2 = 0x04; // requesting FCP template 676 p3 = 2; // data (FID to be selected) is 2 bytes 677 data = MF_ARR_FILE_ID; 678 response = mTelephonyManager 679 .iccTransmitApduLogicalChannel( 680 channel, cla, COMMAND_SELECT, p1, p2, p3, data); 681 682 // Devices launching with Q or later must immediately return the FCP template from the 683 // previous SELECT command. Some devices that launched before Q return TPDUs (instead of 684 // APDUs) - these devices must issue a subsequent GET RESPONSE command to get the FCP 685 // template. 686 if (Build.VERSION.FIRST_SDK_INT < Build.VERSION_CODES.Q) { 687 // Conditionally need to send GET RESPONSE apdu based on response from TelephonyManager 688 if (response.startsWith(STATUS_BYTES_REMAINING)) { 689 // Read the FCP template from the ICC. TS 102 221 Section 12.1.1 690 cla = CLA_GET_RESPONSE; 691 p1 = 0; 692 p2 = 0; 693 p3 = 0; 694 data = ""; 695 response = mTelephonyManager 696 .iccTransmitApduLogicalChannel( 697 channel, cla, COMMAND_GET_RESPONSE, p1, p2, p3, data); 698 } 699 } 700 701 fcpTemplate = FcpTemplate.parseFcpTemplate(response); 702 // Check that the FCP Template's file ID matches the selected ARR 703 assertTrue(containsFileId(fcpTemplate, MF_ARR_FILE_ID)); 704 assertEquals(STATUS_NORMAL_STRING, fcpTemplate.getStatus()); 705 706 mTelephonyManager.iccCloseLogicalChannel(channel); 707 } 708 709 /** 710 * Tests several invalid APDU instructions over a logical channel and makes sure appropriate 711 * errors are returned from the UICC. 712 */ 713 public void testIccTransmitApduLogicalChannelWithInvalidInputs() { 714 if (!hasCellular) return; 715 716 // An open LC is required for transmitting apdu commands. This opens an LC to the MF. 717 IccOpenLogicalChannelResponse logicalChannel = mTelephonyManager.iccOpenLogicalChannel(""); 718 int channel = logicalChannel.getChannel(); 719 720 // Make some invalid APDU commands and make sure they fail as expected. 721 // Use an invalid p1 value for Status apdu 722 int cla = CLA_STATUS | channel; 723 int p1 = 0xFF; // only '00', '01', and '02' are allowed 724 int p2 = 0; // same response parameters as the SELECT in the iccOpenLogicalChannel() above 725 int p3 = 0; // length of 'data' payload 726 String data = ""; 727 String response = mTelephonyManager 728 .iccTransmitApduLogicalChannel(channel, cla, COMMAND_STATUS, p1, p2, p3, data); 729 assertTrue(INVALID_PARAMETERS_STATUSES.contains(response)); 730 731 // Select a file that doesn't exist 732 cla = CLA_SELECT; 733 p1 = 0x00; // select by file ID 734 p2 = 0x0C; // no data returned 735 p3 = 0x02; // length of 'data' payload 736 data = "FFFF"; // invalid file ID 737 response = mTelephonyManager 738 .iccTransmitApduLogicalChannel(channel, cla, COMMAND_SELECT, p1, p2, p3, data); 739 assertEquals(STATUS_FILE_NOT_FOUND, response); 740 741 // Manage channel with incorrect p1 parameter 742 cla = CLA_MANAGE_CHANNEL | channel; 743 p1 = 0x83; // Only '80' or '00' allowed for Manage Channel p1 744 p2 = channel; // channel to be closed 745 p3 = 0; // length of 'data' payload 746 data = ""; 747 response = mTelephonyManager 748 .iccTransmitApduLogicalChannel(channel, cla, COMMAND_MANAGE_CHANNEL, p1, p2, p3, data); 749 assertTrue(isErrorResponse(response)); 750 751 // Use an incorrect class byte for Status apdu 752 cla = 0xFF; 753 p1 = 0; // no indication of application status 754 p2 = 0; // same response parameters as the SELECT in the iccOpenLogicalChannel() above 755 p3 = 0; // length of 'data' payload 756 data = ""; 757 response = mTelephonyManager 758 .iccTransmitApduLogicalChannel(channel, cla, COMMAND_STATUS, p1, p2, p3, data); 759 assertEquals(STATUS_WRONG_CLASS, response); 760 761 // Provide a data field that is longer than described for Select apdu 762 cla = CLA_SELECT | channel; 763 p1 = 0; // select by file ID 764 p2 = 0x0C; // no data returned 765 p3 = 0x04; // data passed is actually 2 bytes long 766 data = "3F00"; // valid ID 767 response = mTelephonyManager 768 .iccTransmitApduLogicalChannel(channel, cla, COMMAND_SELECT, p1, p2, p3, data); 769 assertTrue(isErrorResponse(response)); 770 771 // Use an invalid instruction 772 cla = 0; 773 p1 = 0; 774 p2 = 0; 775 p3 = 0; 776 data = ""; 777 int invalidInstruction = 0xFF; // see TS 102 221 Table 10.5 for valid instructions 778 response = mTelephonyManager 779 .iccTransmitApduLogicalChannel(channel, cla, invalidInstruction, p1, p2, p3, data); 780 assertTrue(isErrorResponse(response)); 781 782 mTelephonyManager.iccCloseLogicalChannel(channel); 783 } 784 785 /** 786 * This test ensures that files can be read off the UICC. This helps to test the SIM booting 787 * process, as it process involves several file-reads. The ICCID is one of the first files read. 788 */ 789 public void testApduFileRead() { 790 if (!hasCellular) return; 791 792 // Open a logical channel and select the MF. 793 IccOpenLogicalChannelResponse logicalChannel = mTelephonyManager.iccOpenLogicalChannel(""); 794 int channel = logicalChannel.getChannel(); 795 796 // Select the ICCID. TS 102 221 Section 13.2 797 int p1 = 0; // select by file ID 798 int p2 = 0x0C; // no data returned 799 int p3 = 2; // length of 'data' payload 800 String response = mTelephonyManager.iccTransmitApduLogicalChannel( 801 channel, CLA_SELECT, COMMAND_SELECT, p1, p2, p3, ICCID_FILE_ID); 802 assertEquals(STATUS_NORMAL_STRING, response); 803 804 // Read the contents of the ICCID. 805 p1 = 0; // 0-byte offset 806 p2 = 0; // 0-byte offset 807 p3 = 0; // length of 'data' payload 808 response = mTelephonyManager.iccTransmitApduLogicalChannel( 809 channel, CLA_READ_BINARY, COMMAND_READ_BINARY, p1, p2, p3, ""); 810 assertTrue(response.endsWith(STATUS_NORMAL_STRING)); 811 812 mTelephonyManager.iccCloseLogicalChannel(channel); 813 } 814 815 /** 816 * This test sends several valid APDU commands over the basic channel (channel 0). 817 */ 818 public void testIccTransmitApduBasicChannel() { 819 if (!hasCellular) return; 820 821 // select the MF 822 int cla = CLA_SELECT; 823 int p1 = 0; // select EF by FID 824 int p2 = 0x0C; // requesting FCP template 825 int p3 = 2; // length of 'data' payload 826 String data = MF_FILE_ID; 827 String response = mTelephonyManager 828 .iccTransmitApduBasicChannel(cla, COMMAND_SELECT, p1, p2, p3, data); 829 assertEquals(STATUS_NORMAL_STRING, response); 830 831 // get the Status of the current file/directory 832 cla = CLA_STATUS; 833 p1 = 0; // no indication of application status 834 p2 = 0; // same response parameters as the SELECT in the iccOpenLogicalChannel() above 835 p3 = 0; // length of 'data' payload 836 data = ""; 837 response = mTelephonyManager 838 .iccTransmitApduBasicChannel(cla, COMMAND_STATUS, p1, p2, p3, data); 839 FcpTemplate fcpTemplate = FcpTemplate.parseFcpTemplate(response); 840 assertTrue(containsFileId(fcpTemplate, MF_FILE_ID)); 841 842 // Manually open a logical channel 843 cla = CLA_MANAGE_CHANNEL; 844 p1 = 0; // open a logical channel 845 p2 = 0; // '00' for open command 846 p3 = 0; // length of data payload 847 data = ""; 848 response = mTelephonyManager 849 .iccTransmitApduBasicChannel(cla, COMMAND_MANAGE_CHANNEL, p1, p2, p3, data); 850 // response is in the format | 1 byte: channel number | 2 bytes: status word | 851 String responseStatus = response.substring(2); 852 assertEquals(STATUS_NORMAL_STRING, responseStatus); 853 854 // Close the open channel 855 byte[] responseBytes = hexStringToBytes(response); 856 int channel = responseBytes[0]; 857 cla = CLA_MANAGE_CHANNEL; 858 p1 = 0x80; // close a logical channel 859 p2 = channel; // the channel to be closed 860 p3 = 0; // length of data payload 861 data = ""; 862 response = mTelephonyManager 863 .iccTransmitApduBasicChannel(cla, COMMAND_MANAGE_CHANNEL, p1, p2, p3, data); 864 assertEquals(STATUS_NORMAL_STRING, response); 865 } 866 867 /** 868 * This test verifies that {@link TelephonyManager#setLine1NumberForDisplay(String, String)} 869 * correctly sets the Line 1 alpha tag and number when called. 870 */ 871 public void testLine1NumberForDisplay() { 872 if (!hasCellular) return; 873 874 // Cache original alpha tag and number values. 875 String originalAlphaTag = mTelephonyManager.getLine1AlphaTag(); 876 String originalNumber = mTelephonyManager.getLine1Number(); 877 878 try { 879 // clear any potentially overridden values and cache defaults 880 mTelephonyManager.setLine1NumberForDisplay(null, null); 881 String defaultAlphaTag = mTelephonyManager.getLine1AlphaTag(); 882 String defaultNumber = mTelephonyManager.getLine1Number(); 883 884 assertTrue(mTelephonyManager.setLine1NumberForDisplay(ALPHA_TAG_A, NUMBER_A)); 885 assertEquals(ALPHA_TAG_A, mTelephonyManager.getLine1AlphaTag()); 886 assertEquals(NUMBER_A, mTelephonyManager.getLine1Number()); 887 888 assertTrue(mTelephonyManager.setLine1NumberForDisplay(ALPHA_TAG_B, NUMBER_B)); 889 assertEquals(ALPHA_TAG_B, mTelephonyManager.getLine1AlphaTag()); 890 assertEquals(NUMBER_B, mTelephonyManager.getLine1Number()); 891 892 // null is used to clear the Line 1 alpha tag and number values. 893 assertTrue(mTelephonyManager.setLine1NumberForDisplay(null, null)); 894 assertEquals(defaultAlphaTag, mTelephonyManager.getLine1AlphaTag()); 895 assertEquals(defaultNumber, mTelephonyManager.getLine1Number()); 896 } finally { 897 // Reset original alpha tag and number values. 898 mTelephonyManager.setLine1NumberForDisplay(originalAlphaTag, originalNumber); 899 } 900 } 901 902 /** 903 * This test verifies that {@link TelephonyManager#setVoiceMailNumber(String, String)} correctly 904 * sets the VoiceMail alpha tag and number when called. 905 */ 906 public void testVoiceMailNumber() { 907 if (!hasCellular) return; 908 909 // Cache original alpha tag and number values. 910 String originalAlphaTag = mTelephonyManager.getVoiceMailAlphaTag(); 911 String originalNumber = mTelephonyManager.getVoiceMailNumber(); 912 913 try { 914 assertTrue(mTelephonyManager.setVoiceMailNumber(ALPHA_TAG_A, NUMBER_A)); 915 assertEquals(ALPHA_TAG_A, mTelephonyManager.getVoiceMailAlphaTag()); 916 assertEquals(NUMBER_A, mTelephonyManager.getVoiceMailNumber()); 917 918 assertTrue(mTelephonyManager.setVoiceMailNumber(ALPHA_TAG_B, NUMBER_B)); 919 assertEquals(ALPHA_TAG_B, mTelephonyManager.getVoiceMailAlphaTag()); 920 assertEquals(NUMBER_B, mTelephonyManager.getVoiceMailNumber()); 921 } finally { 922 // Reset original alpha tag and number values. 923 mTelephonyManager.setVoiceMailNumber(originalAlphaTag, originalNumber); 924 } 925 } 926 927 /** 928 * This test verifies that {@link SubscriptionManager#createSubscriptionGroup(List)} correctly 929 * create a group with the given subscription id. 930 * 931 * This also verifies that 932 * {@link SubscriptionManager#removeSubscriptionsFromGroup(List, ParcelUuid)} correctly remove 933 * the given subscription group. 934 */ 935 public void testCreateAndRemoveSubscriptionGroup() { 936 if (!hasCellular) return; 937 // Set subscription group with current sub Id. 938 int subId = SubscriptionManager.getDefaultSubscriptionId(); 939 List<Integer> subGroup = Arrays.asList(subId); 940 ParcelUuid uuid = mSubscriptionManager.createSubscriptionGroup(subGroup); 941 942 // Getting subscriptions in group. 943 List<SubscriptionInfo> infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid); 944 945 try { 946 assertEquals(1, infoList.size()); 947 assertEquals(uuid, infoList.get(0).getGroupUuid()); 948 assertEquals(subId, infoList.get(0).getSubscriptionId()); 949 } finally { 950 // Verify that the given subGroup has been removed. 951 mSubscriptionManager.removeSubscriptionsFromGroup(subGroup, uuid); 952 infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid); 953 assertTrue(infoList.isEmpty()); 954 } 955 } 956 957 public void testAddSubscriptionToExistingGroupForMultipleSims() { 958 if (!hasCellular || mTelephonyManager.getPhoneCount() < DSDS_PHONE_COUNT) return; 959 960 // Set subscription group with current sub Id. 961 int subId = SubscriptionManager.getDefaultDataSubscriptionId(); 962 ParcelUuid uuid = mSubscriptionManager.createSubscriptionGroup(Arrays.asList(subId)); 963 964 try { 965 // Get all active subscriptions. 966 List<SubscriptionInfo> activeSubInfos = 967 mSubscriptionManager.getActiveSubscriptionInfoList(); 968 969 // Verify that the device has at least two active subscriptions. 970 assertTrue(activeSubInfos.size() >= DSDS_PHONE_COUNT); 971 972 List<Integer> activeSubGroup = getSubscriptionIdList(activeSubInfos); 973 activeSubGroup.removeIf(id -> id == subId); 974 975 mSubscriptionManager.addSubscriptionsIntoGroup(activeSubGroup, uuid); 976 977 List<Integer> infoList = 978 getSubscriptionIdList(mSubscriptionManager.getSubscriptionsInGroup(uuid)); 979 activeSubGroup.add(subId); 980 assertEquals(activeSubGroup.size(), infoList.size()); 981 assertTrue(activeSubGroup.containsAll(infoList)); 982 } finally { 983 removeSubscriptionsFromGroup(uuid); 984 } 985 } 986 987 /** 988 * This test verifies that 989 * {@link SubscriptionManager#addSubscriptionsIntoGroup(List, ParcelUuid)}} correctly add some 990 * additional subscriptions to the existing group. 991 * 992 * This test required the device has more than one subscription. 993 */ 994 public void testAddSubscriptionToExistingGroupForEsim() { 995 if (!hasCellular) return; 996 997 // Set subscription group with current sub Id. 998 int subId = SubscriptionManager.getDefaultDataSubscriptionId(); 999 ParcelUuid uuid = mSubscriptionManager.createSubscriptionGroup(Arrays.asList(subId)); 1000 1001 try { 1002 // Get all accessible eSim subscription. 1003 List<SubscriptionInfo> accessibleSubInfos = 1004 mSubscriptionManager.getAccessibleSubscriptionInfoList(); 1005 if (accessibleSubInfos != null && accessibleSubInfos.size() > 1) { 1006 List<Integer> accessibleSubGroup = getSubscriptionIdList(accessibleSubInfos); 1007 accessibleSubGroup.removeIf(id -> id == subId); 1008 1009 mSubscriptionManager.addSubscriptionsIntoGroup(accessibleSubGroup, uuid); 1010 1011 List<Integer> infoList = 1012 getSubscriptionIdList(mSubscriptionManager.getSubscriptionsInGroup(uuid)); 1013 accessibleSubGroup.add(subId); 1014 assertEquals(accessibleSubGroup.size(), infoList.size()); 1015 assertTrue(accessibleSubGroup.containsAll(infoList)); 1016 } 1017 } finally { 1018 removeSubscriptionsFromGroup(uuid); 1019 } 1020 } 1021 1022 /** 1023 * This test verifies that {@link SubscriptionManager#setOpportunistic(boolean, int)} correctly 1024 * set the opportunistic property of the given subscription. 1025 */ 1026 public void testOpportunistic() { 1027 if (!hasCellular) return; 1028 1029 int subId = SubscriptionManager.getDefaultDataSubscriptionId(); 1030 SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(subId); 1031 boolean oldOpportunistic = info.isOpportunistic(); 1032 boolean newOpportunistic = !oldOpportunistic; 1033 1034 try { 1035 // Mark the given subscription as opportunistic subscription. 1036 boolean successed = mSubscriptionManager.setOpportunistic(newOpportunistic, subId); 1037 assertTrue(successed); 1038 1039 // Verify that the given subscription is opportunistic subscription. 1040 info = mSubscriptionManager.getActiveSubscriptionInfo(subId); 1041 assertEquals(newOpportunistic, info.isOpportunistic()); 1042 } finally { 1043 // Set back to original opportunistic property. 1044 mSubscriptionManager.setOpportunistic(oldOpportunistic, subId); 1045 info = mSubscriptionManager.getActiveSubscriptionInfo(subId); 1046 assertEquals(oldOpportunistic, info.isOpportunistic()); 1047 } 1048 } 1049 1050 /** 1051 * This test verifies that {@link TelephonyManager#iccExchangeSimIO(int, int, int, int, int, 1052 * String)} correctly transmits iccIO commands to the UICC card. First, the MF is selected via a 1053 * SELECT apdu via the basic channel, then a STATUS AT-command is sent. 1054 */ 1055 public void testIccExchangeSimIO() { 1056 if (!hasCellular) return; 1057 1058 // select the MF first. This makes sure the next STATUS AT-command returns a FCP template 1059 // for the right file. 1060 int cla = CLA_SELECT; 1061 int p1 = 0; // select EF by FID 1062 int p2 = 0x0C; // requesting FCP template 1063 int p3 = 2; // length of 'data' payload 1064 String data = MF_FILE_ID; 1065 String response = mTelephonyManager 1066 .iccTransmitApduBasicChannel(cla, COMMAND_SELECT, p1, p2, p3, data); 1067 assertEquals(STATUS_NORMAL_STRING, response); 1068 1069 // The iccExchangeSimIO command implements the +CRSM command defined in TS 27.007 section 1070 // 8.18. A STATUS command is sent and the returned value will be an FCP template. 1071 byte[] result = mTelephonyManager.iccExchangeSimIO( 1072 0, // fileId: not required for STATUS 1073 COMMAND_STATUS, // command: STATUS 1074 0, // p1: not required for STATUS 1075 0, // p2: not required for STATUS 1076 0, // p3: not required for STATUS 1077 ""); // filePath: not required for STATUS 1078 String resultString = bytesToHexString(result); 1079 FcpTemplate fcpTemplate = FcpTemplate.parseFcpTemplate(resultString); 1080 assertTrue(containsFileId(fcpTemplate, MF_FILE_ID)); 1081 assertEquals("iccExchangeSimIO returned non-normal Status byte: " + resultString, 1082 STATUS_NORMAL_STRING, fcpTemplate.getStatus()); 1083 } 1084 1085 /** 1086 * This test checks that a STATUS apdu can be sent as an encapsulated envelope to the UICC via 1087 * {@link TelephonyManager#sendEnvelopeWithStatus(String)}. 1088 */ 1089 public void testSendEnvelopeWithStatus() { 1090 if (!hasCellular) return; 1091 1092 // STATUS apdu as hex String 1093 String envelope = 1094 CLA_STATUS_STRING 1095 + COMMAND_STATUS_STRING 1096 + "00" // p1: no indication of application status 1097 + "00"; // p2: identical parameters to 1098 String lc = "0" + (envelope.length() / 2); // number of bytes in data field 1099 String response = mTelephonyManager.sendEnvelopeWithStatus( 1100 CLA_ENVELOPE 1101 + COMMAND_ENVELOPE 1102 + "00" // p1: value required for Envelope command 1103 + "00" // p2: value required for Envelope command 1104 + lc 1105 + envelope); 1106 assertEquals("sendEnvelopeWithStatus returned: " + response, 1107 STATUS_NORMAL_STRING, response); 1108 } 1109 1110 private void verifyValidIccOpenLogicalChannelResponse(IccOpenLogicalChannelResponse response) { 1111 // The assigned channel should be between the min and max allowed channel numbers 1112 int channel = response.getChannel(); 1113 assertTrue(MIN_LOGICAL_CHANNEL <= channel && channel <= MAX_LOGICAL_CHANNEL); 1114 assertEquals(STATUS_NO_ERROR, response.getStatus()); 1115 assertArrayEquals(STATUS_NORMAL, response.getSelectResponse()); 1116 } 1117 1118 private void removeSubscriptionsFromGroup(ParcelUuid uuid) { 1119 List<SubscriptionInfo> infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid); 1120 if (!infoList.isEmpty()) { 1121 mSubscriptionManager.removeSubscriptionsFromGroup( 1122 getSubscriptionIdList(infoList), 1123 uuid); 1124 } 1125 infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid); 1126 assertTrue(infoList.isEmpty()); 1127 } 1128 1129 private List<Integer> getSubscriptionIdList(List<SubscriptionInfo> subInfoList) { 1130 if (subInfoList == null || subInfoList.isEmpty()) return Collections.EMPTY_LIST; 1131 return subInfoList.stream() 1132 .map(info -> info.getSubscriptionId()) 1133 .collect(Collectors.toList()); 1134 } 1135 1136 /** 1137 * Checks whether the a {@code fcpTemplate} contains the given {@code fileId}. 1138 * 1139 * @param fcpTemplate The FCP Template to be checked. 1140 * @param fileId The file ID that is being searched for 1141 * 1142 * @return true iff fcpTemplate contains fileId. 1143 */ 1144 private boolean containsFileId(FcpTemplate fcpTemplate, String fileId) { 1145 return fcpTemplate.getTlvs().stream().anyMatch(tlv -> 1146 tlv.getTag() == FILE_IDENTIFIER && tlv.getValue().equals(fileId)); 1147 } 1148 1149 /** 1150 * Returns true iff {@code response} indicates an error with the previous APDU. 1151 * 1152 * @param response The APDU response to be checked. 1153 * 1154 * @return true iff the given response indicates an error occurred 1155 */ 1156 private boolean isErrorResponse(@Nonnull String response) { 1157 return !(STATUS_NORMAL_STRING.equals(response) || 1158 response.startsWith(STATUS_WARNING_A) || 1159 response.startsWith(STATUS_WARNING_B) || 1160 response.startsWith(STATUS_BYTES_REMAINING)); 1161 } 1162 1163 private static class IntentReceiver extends BroadcastReceiver { 1164 private final CountDownLatch mReceiveLatch = new CountDownLatch(1); 1165 1166 @Override 1167 public void onReceive(Context context, Intent intent) { 1168 mReceiveLatch.countDown(); 1169 } 1170 1171 public boolean waitForReceive() throws InterruptedException { 1172 return mReceiveLatch.await(30, TimeUnit.SECONDS); 1173 } 1174 } 1175 } 1176