1 /* 2 * Copyright (C) 2009 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.telephony.cts; 18 19 import static androidx.test.InstrumentationRegistry.getContext; 20 import static androidx.test.InstrumentationRegistry.getInstrumentation; 21 22 import static com.android.compatibility.common.util.BlockedNumberUtil.deleteBlockedNumber; 23 import static com.android.compatibility.common.util.BlockedNumberUtil.insertBlockedNumber; 24 25 import static org.hamcrest.Matchers.anyOf; 26 import static org.hamcrest.Matchers.emptyString; 27 import static org.hamcrest.Matchers.equalTo; 28 import static org.hamcrest.Matchers.greaterThan; 29 import static org.hamcrest.Matchers.startsWith; 30 import static org.junit.Assert.assertEquals; 31 import static org.junit.Assert.assertFalse; 32 import static org.junit.Assert.assertNotEquals; 33 import static org.junit.Assert.assertNotNull; 34 import static org.junit.Assert.assertNull; 35 import static org.junit.Assert.assertThat; 36 import static org.junit.Assert.assertTrue; 37 import static org.junit.Assert.fail; 38 39 import android.app.AppOpsManager; 40 import android.app.PendingIntent; 41 import android.app.UiAutomation; 42 import android.app.role.RoleManager; 43 import android.content.BroadcastReceiver; 44 import android.content.ComponentName; 45 import android.content.ContentResolver; 46 import android.content.ContentValues; 47 import android.content.Context; 48 import android.content.Intent; 49 import android.content.IntentFilter; 50 import android.content.pm.PackageManager; 51 import android.database.CursorWindow; 52 import android.net.Uri; 53 import android.os.AsyncTask; 54 import android.os.Bundle; 55 import android.os.ParcelFileDescriptor; 56 import android.os.RemoteCallback; 57 import android.os.SystemClock; 58 import android.provider.Telephony; 59 import android.telephony.SmsManager; 60 import android.telephony.SmsCbMessage; 61 import android.telephony.SmsMessage; 62 import android.telephony.TelephonyManager; 63 import android.telephony.cdma.CdmaSmsCbProgramData; 64 import android.text.TextUtils; 65 import android.util.Log; 66 67 import org.junit.After; 68 import org.junit.Before; 69 import org.junit.Test; 70 71 import java.io.BufferedReader; 72 import java.io.FileInputStream; 73 import java.io.IOException; 74 import java.io.InputStream; 75 import java.io.InputStreamReader; 76 import java.nio.charset.StandardCharsets; 77 import java.util.ArrayList; 78 import java.util.Date; 79 import java.util.List; 80 import java.util.concurrent.Callable; 81 import java.util.concurrent.CompletableFuture; 82 import java.util.concurrent.CountDownLatch; 83 import java.util.concurrent.TimeUnit; 84 85 /** 86 * Tests for {@link android.telephony.SmsManager}. 87 * 88 * Structured so tests can be reused to test {@link android.telephony.gsm.SmsManager} 89 */ 90 public class SmsManagerTest { 91 92 private static final String TAG = "SmsManagerTest"; 93 private static final String LONG_TEXT = 94 "This is a very long text. This text should be broken into three " + 95 "separate messages.This is a very long text. This text should be broken into " + 96 "three separate messages.This is a very long text. This text should be broken " + 97 "into three separate messages.This is a very long text. This text should be " + 98 "broken into three separate messages.";; 99 private static final String LONG_TEXT_WITH_32BIT_CHARS = 100 "Long dkkshsh jdjsusj kbsksbdf jfkhcu hhdiwoqiwyrygrvn?*?*!\";:'/,." 101 + "__?9#9292736&4;\"$+$+((]\\[\\℅©℅™^®°¥°¥=¢£}}£∆~¶~÷|√×." 102 + " ⛪⛲ "; 103 104 private static final String SMS_SEND_ACTION = "CTS_SMS_SEND_ACTION"; 105 private static final String SMS_DELIVERY_ACTION = "CTS_SMS_DELIVERY_ACTION"; 106 private static final String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED"; 107 public static final String SMS_DELIVER_DEFAULT_APP_ACTION = "CTS_SMS_DELIVERY_ACTION_DEFAULT_APP"; 108 public static final String LEGACY_SMS_APP = "android.telephony.cts.sms23"; 109 public static final String MODERN_SMS_APP = "android.telephony.cts.sms"; 110 private static final String SMS_RETRIEVER_APP = "android.telephony.cts.smsretriever"; 111 private static final String SMS_RETRIEVER_ACTION = "CTS_SMS_RETRIEVER_ACTION"; 112 private static final String FINANCIAL_SMS_APP = "android.telephony.cts.financialsms"; 113 114 private TelephonyManager mTelephonyManager; 115 private PackageManager mPackageManager; 116 private String mDestAddr; 117 private String mText; 118 private SmsBroadcastReceiver mSendReceiver; 119 private SmsBroadcastReceiver mDeliveryReceiver; 120 private SmsBroadcastReceiver mDataSmsReceiver; 121 private SmsBroadcastReceiver mSmsDeliverReceiver; 122 private SmsBroadcastReceiver mSmsReceivedReceiver; 123 private SmsBroadcastReceiver mSmsRetrieverReceiver; 124 private PendingIntent mSentIntent; 125 private PendingIntent mDeliveredIntent; 126 private Intent mSendIntent; 127 private Intent mDeliveryIntent; 128 private Context mContext; 129 private Uri mBlockedNumberUri; 130 private boolean mTestAppSetAsDefaultSmsApp; 131 private boolean mDeliveryReportSupported; 132 private static boolean mReceivedDataSms; 133 private static String mReceivedText; 134 private static boolean sHasShellPermissionIdentity = false; 135 136 private static final int TIME_OUT = 1000 * 60 * 4; 137 private static final int NO_CALLS_TIMEOUT_MILLIS = 1000; // 1 second 138 139 @Before setUp()140 public void setUp() throws Exception { 141 mContext = getContext(); 142 mTelephonyManager = 143 (TelephonyManager) getContext().getSystemService( 144 Context.TELEPHONY_SERVICE); 145 mPackageManager = mContext.getPackageManager(); 146 mDestAddr = mTelephonyManager.getLine1Number(); 147 mText = "This is a test message"; 148 149 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 150 mDeliveryReportSupported = false; 151 } else { 152 // exclude the networks that don't support SMS delivery report 153 String mccmnc = mTelephonyManager.getSimOperator(); 154 mDeliveryReportSupported = !(CarrierCapability.NO_DELIVERY_REPORTS.contains(mccmnc)); 155 } 156 } 157 158 @After tearDown()159 public void tearDown() throws Exception { 160 if (mBlockedNumberUri != null) { 161 unblockNumber(mBlockedNumberUri); 162 mBlockedNumberUri = null; 163 } 164 if (mTestAppSetAsDefaultSmsApp) { 165 setDefaultSmsApp(false); 166 } 167 } 168 169 @Test testDivideMessage()170 public void testDivideMessage() { 171 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 172 return; 173 } 174 ArrayList<String> dividedMessages = divideMessage(LONG_TEXT); 175 assertNotNull(dividedMessages); 176 if (TelephonyUtils.isSkt(mTelephonyManager)) { 177 assertTrue(isComplete(dividedMessages, 5, LONG_TEXT) 178 || isComplete(dividedMessages, 3, LONG_TEXT)); 179 } else if (TelephonyUtils.isKt(mTelephonyManager)) { 180 assertTrue(isComplete(dividedMessages, 4, LONG_TEXT) 181 || isComplete(dividedMessages, 3, LONG_TEXT)); 182 } else { 183 assertTrue(isComplete(dividedMessages, 3, LONG_TEXT)); 184 } 185 } 186 187 @Test testDivideUnicodeMessage()188 public void testDivideUnicodeMessage() { 189 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 190 return; 191 } 192 ArrayList<String> dividedMessages = divideMessage(LONG_TEXT_WITH_32BIT_CHARS); 193 assertNotNull(dividedMessages); 194 assertTrue(isComplete(dividedMessages, 3, LONG_TEXT_WITH_32BIT_CHARS)); 195 for (String messagePiece : dividedMessages) { 196 assertFalse(Character.isHighSurrogate( 197 messagePiece.charAt(messagePiece.length() - 1))); 198 } 199 } 200 isComplete(List<String> dividedMessages, int numParts, String longText)201 private boolean isComplete(List<String> dividedMessages, int numParts, String longText) { 202 if (dividedMessages.size() != numParts) { 203 return false; 204 } 205 206 String actualMessage = ""; 207 for (int i = 0; i < numParts; i++) { 208 actualMessage += dividedMessages.get(i); 209 } 210 return longText.equals(actualMessage); 211 } 212 213 @Test testSmsRetriever()214 public void testSmsRetriever() throws Exception { 215 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 216 return; 217 } 218 219 assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.", 220 TextUtils.isEmpty(mDestAddr)); 221 222 String mccmnc = mTelephonyManager.getSimOperator(); 223 setupBroadcastReceivers(); 224 init(); 225 226 CompletableFuture<Bundle> callbackResult = new CompletableFuture<>(); 227 228 mContext.startActivity(new Intent() 229 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 230 .setComponent(new ComponentName( 231 SMS_RETRIEVER_APP, SMS_RETRIEVER_APP + ".MainActivity")) 232 .putExtra("callback", new RemoteCallback(callbackResult::complete))); 233 234 235 Bundle bundle = callbackResult.get(200, TimeUnit.SECONDS); 236 String token = bundle.getString("token"); 237 assertThat(bundle.getString("class"), startsWith(SMS_RETRIEVER_APP)); 238 assertNotNull(token); 239 240 String composedText = "testprefix1" + mText + token; 241 sendTextMessage(mDestAddr, composedText, null, null); 242 243 assertTrue("[RERUN] SMS retriever message not received. Check signal.", 244 mSmsRetrieverReceiver.waitForCalls(1, TIME_OUT)); 245 } 246 247 @Test testSendAndReceiveMessages()248 public void testSendAndReceiveMessages() throws Exception { 249 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 250 return; 251 } 252 253 assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.", 254 TextUtils.isEmpty(mDestAddr)); 255 256 String mccmnc = mTelephonyManager.getSimOperator(); 257 setupBroadcastReceivers(); 258 259 // send single text sms 260 init(); 261 sendTextMessage(mDestAddr, mDestAddr, mSentIntent, mDeliveredIntent); 262 assertTrue("[RERUN] Could not send SMS. Check signal.", 263 mSendReceiver.waitForCalls(1, TIME_OUT)); 264 if (mDeliveryReportSupported) { 265 assertTrue("[RERUN] SMS message delivery notification not received. Check signal.", 266 mDeliveryReceiver.waitForCalls(1, TIME_OUT)); 267 } 268 // non-default app should receive only SMS_RECEIVED_ACTION 269 assertTrue(mSmsReceivedReceiver.waitForCalls(1, TIME_OUT)); 270 assertTrue(mSmsDeliverReceiver.waitForCalls(0, 0)); 271 272 // due to permission restrictions, currently there is no way to make this test app the 273 // default SMS app 274 275 if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { 276 // TODO: temp workaround, OCTET encoding for EMS not properly supported 277 return; 278 } 279 280 // send data sms 281 if (sendDataMessageIfSupported(mccmnc)) { 282 assertTrue("[RERUN] Could not send data SMS. Check signal.", 283 mSendReceiver.waitForCalls(1, TIME_OUT)); 284 if (mDeliveryReportSupported) { 285 assertTrue("[RERUN] Data SMS message delivery notification not received. " + 286 "Check signal.", mDeliveryReceiver.waitForCalls(1, TIME_OUT)); 287 } 288 mDataSmsReceiver.waitForCalls(1, TIME_OUT); 289 assertTrue("[RERUN] Data SMS message not received. Check signal.", mReceivedDataSms); 290 assertEquals(mReceivedText, mText); 291 } else { 292 // This GSM network doesn't support Data(binary) SMS message. 293 // Skip the test. 294 } 295 296 // send multi parts text sms 297 int numPartsSent = sendMultipartTextMessageIfSupported(mccmnc); 298 if (numPartsSent > 0) { 299 assertTrue("[RERUN] Could not send multi part SMS. Check signal.", 300 mSendReceiver.waitForCalls(numPartsSent, TIME_OUT)); 301 if (mDeliveryReportSupported) { 302 assertTrue("[RERUN] Multi part SMS message delivery notification not received. " + 303 "Check signal.", mDeliveryReceiver.waitForCalls(numPartsSent, TIME_OUT)); 304 } 305 // non-default app should receive only SMS_RECEIVED_ACTION 306 assertTrue(mSmsReceivedReceiver.waitForCalls(1, TIME_OUT)); 307 assertTrue(mSmsDeliverReceiver.waitForCalls(0, 0)); 308 } else { 309 // This GSM network doesn't support Multipart SMS message. 310 // Skip the test. 311 } 312 } 313 314 @Test testSmsBlocking()315 public void testSmsBlocking() throws Exception { 316 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 317 return; 318 } 319 320 assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.", 321 TextUtils.isEmpty(mDestAddr)); 322 323 // disable suppressing blocking. 324 TelephonyUtils.endBlockSuppression(getInstrumentation()); 325 326 String mccmnc = mTelephonyManager.getSimOperator(); 327 // Setting default SMS App is needed to be able to block numbers. 328 setDefaultSmsApp(true); 329 blockNumber(mDestAddr); 330 setupBroadcastReceivers(); 331 332 // single-part SMS blocking 333 init(); 334 sendTextMessage(mDestAddr, mDestAddr, mSentIntent, mDeliveredIntent); 335 assertTrue("[RERUN] Could not send SMS. Check signal.", 336 mSendReceiver.waitForCalls(1, TIME_OUT)); 337 assertTrue("Expected no messages to be received due to number blocking.", 338 mSmsReceivedReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS)); 339 assertTrue("Expected no messages to be delivered due to number blocking.", 340 mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS)); 341 342 // send data sms 343 if (!sendDataMessageIfSupported(mccmnc)) { 344 assertTrue("[RERUN] Could not send data SMS. Check signal.", 345 mSendReceiver.waitForCalls(1, TIME_OUT)); 346 if (mDeliveryReportSupported) { 347 assertTrue("[RERUN] Data SMS message delivery notification not received. " + 348 "Check signal.", mDeliveryReceiver.waitForCalls(1, TIME_OUT)); 349 } 350 assertTrue("Expected no messages to be delivered due to number blocking.", 351 mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS)); 352 } else { 353 // This GSM network doesn't support Data(binary) SMS message. 354 // Skip the test. 355 } 356 357 // multi-part SMS blocking 358 int numPartsSent = sendMultipartTextMessageIfSupported(mccmnc); 359 if (numPartsSent > 0) { 360 assertTrue("[RERUN] Could not send multi part SMS. Check signal.", 361 mSendReceiver.waitForCalls(numPartsSent, TIME_OUT)); 362 363 assertTrue("Expected no messages to be received due to number blocking.", 364 mSmsReceivedReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS)); 365 assertTrue("Expected no messages to be delivered due to number blocking.", 366 mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS)); 367 } else { 368 // This GSM network doesn't support Multipart SMS message. 369 // Skip the test. 370 } 371 } 372 373 @Test testGetSmsMessagesForFinancialAppPermissionRequestedNotGranted()374 public void testGetSmsMessagesForFinancialAppPermissionRequestedNotGranted() throws Exception { 375 CompletableFuture<Bundle> callbackResult = new CompletableFuture<>(); 376 377 mContext.startActivity(new Intent() 378 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 379 .setComponent(new ComponentName(FINANCIAL_SMS_APP, FINANCIAL_SMS_APP + ".MainActivity")) 380 .putExtra("callback", new RemoteCallback(callbackResult::complete))); 381 382 Bundle bundle = callbackResult.get(500, TimeUnit.SECONDS); 383 384 assertThat(bundle.getString("class"), startsWith(FINANCIAL_SMS_APP)); 385 assertThat(bundle.getInt("rowNum"), equalTo(-1)); 386 } 387 388 @Test testGetSmsMessagesForFinancialAppPermissionRequestedGranted()389 public void testGetSmsMessagesForFinancialAppPermissionRequestedGranted() throws Exception { 390 CompletableFuture<Bundle> callbackResult = new CompletableFuture<>(); 391 String ctsPackageName = getInstrumentation().getContext().getPackageName(); 392 393 executeWithShellPermissionIdentity(() -> { 394 setModeForOps(FINANCIAL_SMS_APP, 395 AppOpsManager.MODE_ALLOWED, 396 AppOpsManager.OPSTR_SMS_FINANCIAL_TRANSACTIONS); 397 }); 398 mContext.startActivity(new Intent() 399 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 400 .setComponent(new ComponentName(FINANCIAL_SMS_APP, FINANCIAL_SMS_APP + ".MainActivity")) 401 .putExtra("callback", new RemoteCallback(callbackResult::complete))); 402 403 404 Bundle bundle = callbackResult.get(500, TimeUnit.SECONDS); 405 406 assertThat(bundle.getString("class"), startsWith(FINANCIAL_SMS_APP)); 407 assertThat(bundle.getInt("rowNum"), equalTo(-1)); 408 } 409 410 @Test testSmsNotPersisted_failsWithoutCarrierPermissions()411 public void testSmsNotPersisted_failsWithoutCarrierPermissions() throws Exception { 412 if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { 413 return; 414 } 415 416 assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.", 417 TextUtils.isEmpty(mDestAddr)); 418 419 try { 420 getSmsManager().sendTextMessageWithoutPersisting(mDestAddr, null /*scAddress */, 421 mDestAddr, mSentIntent, mDeliveredIntent); 422 fail("We should get a SecurityException due to not having carrier privileges"); 423 } catch (SecurityException e) { 424 // Success 425 } 426 } 427 428 @Test testContentProviderAccessRestriction()429 public void testContentProviderAccessRestriction() throws Exception { 430 Uri dummySmsUri = null; 431 Context context = getInstrumentation().getContext(); 432 ContentResolver contentResolver = context.getContentResolver(); 433 int originalWriteSmsMode = -1; 434 String ctsPackageName = context.getPackageName(); 435 try { 436 // Insert some dummy sms 437 originalWriteSmsMode = context.getSystemService(AppOpsManager.class) 438 .unsafeCheckOpNoThrow(AppOpsManager.OPSTR_WRITE_SMS, 439 getPackageUid(ctsPackageName), ctsPackageName); 440 dummySmsUri = executeWithShellPermissionIdentity(() -> { 441 setModeForOps(ctsPackageName, 442 AppOpsManager.MODE_ALLOWED, AppOpsManager.OPSTR_WRITE_SMS); 443 ContentValues contentValues = new ContentValues(); 444 contentValues.put(Telephony.TextBasedSmsColumns.ADDRESS, "addr"); 445 contentValues.put(Telephony.TextBasedSmsColumns.READ, 1); 446 contentValues.put(Telephony.TextBasedSmsColumns.SUBJECT, "subj"); 447 contentValues.put(Telephony.TextBasedSmsColumns.BODY, "created_at_" + 448 new Date().toString().replace(" ", "_")); 449 return contentResolver.insert(Telephony.Sms.CONTENT_URI, contentValues); 450 }); 451 assertNotNull("Failed to insert dummy sms", dummySmsUri); 452 assertNotEquals("Failed to insert dummy sms", dummySmsUri.getLastPathSegment(), "0"); 453 testSmsAccessAboutDefaultApp(LEGACY_SMS_APP); 454 testSmsAccessAboutDefaultApp(MODERN_SMS_APP); 455 } finally { 456 if (dummySmsUri != null && !"/0".equals(dummySmsUri.getLastPathSegment())) { 457 final Uri finalDummySmsUri = dummySmsUri; 458 executeWithShellPermissionIdentity(() -> contentResolver.delete(finalDummySmsUri, 459 null, null)); 460 } 461 if (originalWriteSmsMode >= 0) { 462 int finalOriginalWriteSmsMode = originalWriteSmsMode; 463 executeWithShellPermissionIdentity(() -> 464 setModeForOps(ctsPackageName, 465 finalOriginalWriteSmsMode, AppOpsManager.OPSTR_WRITE_SMS)); 466 } 467 } 468 } 469 testSmsAccessAboutDefaultApp(String pkg)470 private void testSmsAccessAboutDefaultApp(String pkg) 471 throws Exception { 472 String originalSmsApp = getSmsApp(); 473 assertNotEquals(pkg, originalSmsApp); 474 assertCanAccessSms(pkg); 475 try { 476 setSmsApp(pkg); 477 assertCanAccessSms(pkg); 478 } finally { 479 resetReadWriteSmsAppOps(pkg); 480 setSmsApp(originalSmsApp); 481 } 482 } 483 resetReadWriteSmsAppOps(String pkg)484 private void resetReadWriteSmsAppOps(String pkg) throws Exception { 485 setModeForOps(pkg, AppOpsManager.MODE_DEFAULT, 486 AppOpsManager.OPSTR_READ_SMS, AppOpsManager.OPSTR_WRITE_SMS); 487 } 488 setModeForOps(String pkg, int mode, String... ops)489 private void setModeForOps(String pkg, int mode, String... ops) throws Exception { 490 // We cannot reset these app ops to DEFAULT via current API, so we reset them manually here 491 // temporarily as we will rewrite how the default SMS app is setup later. 492 executeWithShellPermissionIdentity(() -> { 493 int uid = getPackageUid(pkg); 494 AppOpsManager appOpsManager = 495 getInstrumentation().getContext().getSystemService(AppOpsManager.class); 496 for (String op : ops) { 497 appOpsManager.setUidMode(op, uid, mode); 498 } 499 }); 500 } 501 getPackageUid(String pkg)502 private int getPackageUid(String pkg) throws PackageManager.NameNotFoundException { 503 return getInstrumentation().getContext().getPackageManager().getPackageUid(pkg, 0); 504 } 505 getSmsApp()506 private String getSmsApp() throws Exception { 507 return executeWithShellPermissionIdentity(() -> getInstrumentation() 508 .getContext() 509 .getSystemService(RoleManager.class) 510 .getRoleHolders(RoleManager.ROLE_SMS) 511 .get(0)); 512 } 513 setSmsApp(String pkg)514 private void setSmsApp(String pkg) throws Exception { 515 executeWithShellPermissionIdentity(() -> { 516 Context context = getInstrumentation().getContext(); 517 CompletableFuture<Boolean> result = new CompletableFuture<>(); 518 context.getSystemService(RoleManager.class).addRoleHolderAsUser( 519 RoleManager.ROLE_SMS, pkg, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, 520 context.getUser(), AsyncTask.THREAD_POOL_EXECUTOR, result::complete); 521 assertTrue(result.get(5, TimeUnit.SECONDS)); 522 }); 523 } 524 executeWithShellPermissionIdentity(Callable<T> callable)525 private <T> T executeWithShellPermissionIdentity(Callable<T> callable) throws Exception { 526 if (sHasShellPermissionIdentity) { 527 return callable.call(); 528 } 529 UiAutomation uiAutomation = getInstrumentation().getUiAutomation( 530 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 531 uiAutomation.adoptShellPermissionIdentity(); 532 try { 533 sHasShellPermissionIdentity = true; 534 return callable.call(); 535 } finally { 536 uiAutomation.dropShellPermissionIdentity(); 537 sHasShellPermissionIdentity = false; 538 } 539 } 540 executeWithShellPermissionIdentity(RunnableWithException runnable)541 private void executeWithShellPermissionIdentity(RunnableWithException runnable) 542 throws Exception { 543 executeWithShellPermissionIdentity(() -> { 544 runnable.run(); 545 return null; 546 }); 547 } 548 549 private interface RunnableWithException { run()550 void run() throws Exception; 551 } 552 assertCanAccessSms(String pkg)553 private void assertCanAccessSms(String pkg) throws Exception { 554 CompletableFuture<Bundle> callbackResult = new CompletableFuture<>(); 555 mContext.startActivity(new Intent() 556 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 557 .setComponent(new ComponentName(pkg, pkg + ".MainActivity")) 558 .putExtra("callback", new RemoteCallback(callbackResult::complete))); 559 560 Bundle bundle = callbackResult.get(20, TimeUnit.SECONDS); 561 562 assertThat(bundle.getString("class"), startsWith(pkg)); 563 assertThat(bundle.getString("exceptionMessage"), anyOf(equalTo(null), emptyString())); 564 assertThat(bundle.getInt("queryCount"), greaterThan(0)); 565 } 566 init()567 private void init() { 568 mSendReceiver.reset(); 569 mDeliveryReceiver.reset(); 570 mDataSmsReceiver.reset(); 571 mSmsDeliverReceiver.reset(); 572 mSmsReceivedReceiver.reset(); 573 mSmsRetrieverReceiver.reset(); 574 mReceivedDataSms = false; 575 mSentIntent = PendingIntent.getBroadcast(mContext, 0, mSendIntent, 576 PendingIntent.FLAG_ONE_SHOT); 577 mDeliveredIntent = PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent, 578 PendingIntent.FLAG_ONE_SHOT); 579 } 580 setupBroadcastReceivers()581 private void setupBroadcastReceivers() { 582 mSendIntent = new Intent(SMS_SEND_ACTION); 583 mDeliveryIntent = new Intent(SMS_DELIVERY_ACTION); 584 585 IntentFilter sendIntentFilter = new IntentFilter(SMS_SEND_ACTION); 586 IntentFilter deliveryIntentFilter = new IntentFilter(SMS_DELIVERY_ACTION); 587 IntentFilter dataSmsReceivedIntentFilter = new IntentFilter(DATA_SMS_RECEIVED_ACTION); 588 IntentFilter smsDeliverIntentFilter = new IntentFilter(SMS_DELIVER_DEFAULT_APP_ACTION); 589 IntentFilter smsReceivedIntentFilter = 590 new IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION); 591 IntentFilter smsRetrieverIntentFilter = new IntentFilter(SMS_RETRIEVER_ACTION); 592 dataSmsReceivedIntentFilter.addDataScheme("sms"); 593 dataSmsReceivedIntentFilter.addDataAuthority("localhost", "19989"); 594 595 mSendReceiver = new SmsBroadcastReceiver(SMS_SEND_ACTION); 596 mDeliveryReceiver = new SmsBroadcastReceiver(SMS_DELIVERY_ACTION); 597 mDataSmsReceiver = new SmsBroadcastReceiver(DATA_SMS_RECEIVED_ACTION); 598 mSmsDeliverReceiver = new SmsBroadcastReceiver(SMS_DELIVER_DEFAULT_APP_ACTION); 599 mSmsReceivedReceiver = new SmsBroadcastReceiver(Telephony.Sms.Intents.SMS_RECEIVED_ACTION); 600 mSmsRetrieverReceiver = new SmsBroadcastReceiver(SMS_RETRIEVER_ACTION); 601 602 mContext.registerReceiver(mSendReceiver, sendIntentFilter); 603 mContext.registerReceiver(mDeliveryReceiver, deliveryIntentFilter); 604 mContext.registerReceiver(mDataSmsReceiver, dataSmsReceivedIntentFilter); 605 mContext.registerReceiver(mSmsDeliverReceiver, smsDeliverIntentFilter); 606 mContext.registerReceiver(mSmsReceivedReceiver, smsReceivedIntentFilter); 607 mContext.registerReceiver(mSmsRetrieverReceiver, smsRetrieverIntentFilter); 608 } 609 610 /** 611 * Returns the number of parts sent in the message. If Multi-part SMS is not supported, 612 * returns 0. 613 */ sendMultipartTextMessageIfSupported(String mccmnc)614 private int sendMultipartTextMessageIfSupported(String mccmnc) { 615 int numPartsSent = 0; 616 if (!CarrierCapability.UNSUPPORT_MULTIPART_SMS_MESSAGES.contains(mccmnc)) { 617 init(); 618 ArrayList<String> parts = divideMessage(LONG_TEXT); 619 numPartsSent = parts.size(); 620 ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(); 621 ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(); 622 for (int i = 0; i < numPartsSent; i++) { 623 sentIntents.add(PendingIntent.getBroadcast(mContext, 0, mSendIntent, 0)); 624 deliveryIntents.add(PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent, 0)); 625 } 626 sendMultiPartTextMessage(mDestAddr, parts, sentIntents, deliveryIntents); 627 } 628 return numPartsSent; 629 } 630 sendDataMessageIfSupported(String mccmnc)631 private boolean sendDataMessageIfSupported(String mccmnc) { 632 if (!CarrierCapability.UNSUPPORT_DATA_SMS_MESSAGES.contains(mccmnc)) { 633 byte[] data = mText.getBytes(); 634 short port = 19989; 635 636 init(); 637 sendDataMessage(mDestAddr, port, data, mSentIntent, mDeliveredIntent); 638 return true; 639 } 640 return false; 641 } 642 643 @Test testGetDefault()644 public void testGetDefault() { 645 assertNotNull(getSmsManager()); 646 } 647 648 @Test testGetSmscAddress()649 public void testGetSmscAddress() { 650 try { 651 getSmsManager().getSmscAddress(); 652 fail("SmsManager.getSmscAddress() should throw a SecurityException"); 653 } catch (SecurityException e) { 654 // expected 655 } 656 } 657 658 @Test testSetSmscAddress()659 public void testSetSmscAddress() { 660 try { 661 getSmsManager().setSmscAddress("fake smsc"); 662 fail("SmsManager.setSmscAddress() should throw a SecurityException"); 663 } catch (SecurityException e) { 664 // expected 665 } 666 } 667 668 @Test testDisableCellBroadcastRange()669 public void testDisableCellBroadcastRange() { 670 try { 671 int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP; 672 executeWithShellPermissionIdentity(() -> { 673 getSmsManager().disableCellBroadcastRange( 674 CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT, 675 CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT, 676 ranType); 677 }); 678 } catch (Exception e) { 679 // expected 680 } 681 } 682 683 @Test testEnableCellBroadcastRange()684 public void testEnableCellBroadcastRange() { 685 try { 686 int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP; 687 executeWithShellPermissionIdentity(() -> { 688 getSmsManager().enableCellBroadcastRange( 689 CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT, 690 CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT, 691 ranType); 692 }); 693 } catch (Exception e) { 694 // expected 695 } 696 } 697 divideMessage(String text)698 protected ArrayList<String> divideMessage(String text) { 699 return getSmsManager().divideMessage(text); 700 } 701 getSmsManager()702 private android.telephony.SmsManager getSmsManager() { 703 return android.telephony.SmsManager.getDefault(); 704 } 705 sendMultiPartTextMessage(String destAddr, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents)706 protected void sendMultiPartTextMessage(String destAddr, ArrayList<String> parts, 707 ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { 708 if (mContext.getOpPackageName() != null) { 709 getSmsManager().sendMultipartTextMessage(destAddr, null, parts, sentIntents, 710 deliveryIntents, mContext.getOpPackageName()); 711 } else { 712 getSmsManager().sendMultipartTextMessage(destAddr, null, parts, sentIntents, 713 deliveryIntents); 714 } 715 } 716 sendDataMessage(String destAddr,short port, byte[] data, PendingIntent sentIntent, PendingIntent deliveredIntent)717 protected void sendDataMessage(String destAddr,short port, byte[] data, PendingIntent sentIntent, PendingIntent deliveredIntent) { 718 getSmsManager().sendDataMessage(destAddr, null, port, data, sentIntent, deliveredIntent); 719 } 720 sendTextMessage(String destAddr, String text, PendingIntent sentIntent, PendingIntent deliveredIntent)721 protected void sendTextMessage(String destAddr, String text, PendingIntent sentIntent, PendingIntent deliveredIntent) { 722 getSmsManager().sendTextMessage(destAddr, null, text, sentIntent, deliveredIntent); 723 } 724 blockNumber(String number)725 private void blockNumber(String number) { 726 mBlockedNumberUri = insertBlockedNumber(mContext, number); 727 if (mBlockedNumberUri == null) { 728 fail("Failed to insert into blocked number provider."); 729 } 730 } 731 unblockNumber(Uri uri)732 private void unblockNumber(Uri uri) { 733 deleteBlockedNumber(mContext, uri); 734 } 735 setDefaultSmsApp(boolean setToSmsApp)736 private void setDefaultSmsApp(boolean setToSmsApp) 737 throws Exception { 738 String command = String.format( 739 "appops set --user 0 %s WRITE_SMS %s", 740 mContext.getPackageName(), 741 setToSmsApp ? "allow" : "default"); 742 assertTrue("Setting default SMS app failed : " + setToSmsApp, 743 executeShellCommand(command).isEmpty()); 744 mTestAppSetAsDefaultSmsApp = setToSmsApp; 745 } 746 executeShellCommand(String command)747 private String executeShellCommand(String command) 748 throws IOException { 749 ParcelFileDescriptor pfd = 750 getInstrumentation().getUiAutomation().executeShellCommand(command); 751 BufferedReader br = null; 752 try (InputStream in = new FileInputStream(pfd.getFileDescriptor());) { 753 br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); 754 String str; 755 StringBuilder out = new StringBuilder(); 756 while ((str = br.readLine()) != null) { 757 out.append(str); 758 } 759 return out.toString(); 760 } finally { 761 if (br != null) { 762 br.close(); 763 } 764 } 765 } 766 767 private static class SmsBroadcastReceiver extends BroadcastReceiver { 768 private int mCalls; 769 private int mExpectedCalls; 770 private String mAction; 771 private Object mLock; 772 SmsBroadcastReceiver(String action)773 SmsBroadcastReceiver(String action) { 774 mAction = action; 775 reset(); 776 mLock = new Object(); 777 } 778 reset()779 void reset() { 780 mExpectedCalls = Integer.MAX_VALUE; 781 mCalls = 0; 782 } 783 784 @Override onReceive(Context context, Intent intent)785 public void onReceive(Context context, Intent intent) { 786 if(mAction.equals(DATA_SMS_RECEIVED_ACTION)){ 787 StringBuilder sb = new StringBuilder(); 788 Bundle bundle = intent.getExtras(); 789 if (bundle != null) { 790 Object[] obj = (Object[]) bundle.get("pdus"); 791 String format = bundle.getString("format"); 792 SmsMessage[] message = new SmsMessage[obj.length]; 793 for (int i = 0; i < obj.length; i++) { 794 message[i] = SmsMessage.createFromPdu((byte[]) obj[i], format); 795 } 796 797 for (SmsMessage currentMessage : message) { 798 byte[] binaryContent = currentMessage.getUserData(); 799 String readableContent = new String(binaryContent); 800 sb.append(readableContent); 801 } 802 } 803 mReceivedDataSms = true; 804 mReceivedText=sb.toString(); 805 } 806 Log.i(TAG, "onReceive " + intent.getAction()); 807 if (intent.getAction().equals(mAction)) { 808 synchronized (mLock) { 809 mCalls += 1; 810 mLock.notify(); 811 } 812 } 813 } 814 verifyNoCalls(long timeout)815 private boolean verifyNoCalls(long timeout) throws InterruptedException { 816 synchronized(mLock) { 817 mLock.wait(timeout); 818 return mCalls == 0; 819 } 820 } 821 waitForCalls(int expectedCalls, long timeout)822 public boolean waitForCalls(int expectedCalls, long timeout) throws InterruptedException { 823 synchronized(mLock) { 824 mExpectedCalls = expectedCalls; 825 long startTime = SystemClock.elapsedRealtime(); 826 827 while (mCalls < mExpectedCalls) { 828 long waitTime = timeout - (SystemClock.elapsedRealtime() - startTime); 829 if (waitTime > 0) { 830 mLock.wait(waitTime); 831 } else { 832 return false; // timed out 833 } 834 } 835 return true; // success 836 } 837 } 838 } 839 } 840