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.telephony.cts; 18 19 import android.app.Instrumentation; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.PackageManager; 26 import android.database.ContentObserver; 27 import android.database.Cursor; 28 import android.database.sqlite.SQLiteException; 29 import android.net.Uri; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.os.ParcelFileDescriptor; 33 import android.provider.Telephony.Sms; 34 import android.provider.Telephony.Sms.Intents; 35 import android.support.annotation.Nullable; 36 import android.telecom.PhoneAccount; 37 import android.telecom.PhoneAccountHandle; 38 import android.telecom.TelecomManager; 39 import android.telephony.SmsManager; 40 import android.telephony.SmsMessage; 41 import android.telephony.TelephonyManager; 42 import android.telephony.VisualVoicemailSms; 43 import android.telephony.VisualVoicemailSmsFilterSettings; 44 import android.test.InstrumentationTestCase; 45 import android.text.TextUtils; 46 import android.util.Log; 47 48 import java.io.BufferedReader; 49 import java.io.FileInputStream; 50 import java.io.InputStream; 51 import java.io.InputStreamReader; 52 import java.nio.ByteBuffer; 53 import java.nio.charset.CharacterCodingException; 54 import java.nio.charset.CharsetDecoder; 55 import java.nio.charset.StandardCharsets; 56 import java.util.Arrays; 57 import java.util.concurrent.CompletableFuture; 58 import java.util.concurrent.ExecutionException; 59 import java.util.concurrent.TimeUnit; 60 import java.util.concurrent.TimeoutException; 61 62 public class VisualVoicemailServiceTest extends InstrumentationTestCase { 63 64 private static final String TAG = "VvmServiceTest"; 65 66 private static final String COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer "; 67 68 private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer"; 69 70 private static final String PACKAGE = "android.telephony.cts"; 71 72 private static final long EVENT_RECEIVED_TIMEOUT_MILLIS = 60_000; 73 private static final long EVENT_NOT_RECEIVED_TIMEOUT_MILLIS = 1_000; 74 75 private Context mContext; 76 private TelephonyManager mTelephonyManager; 77 78 private String mPreviousDefaultDialer; 79 80 private PhoneAccountHandle mPhoneAccountHandle; 81 private String mPhoneNumber; 82 83 private SmsBroadcastReceiver mSmsReceiver; 84 85 @Override 86 protected void setUp() throws Exception { 87 super.setUp(); 88 mContext = getInstrumentation().getContext(); 89 if (hasTelephony(mContext)) { 90 mPreviousDefaultDialer = getDefaultDialer(getInstrumentation()); 91 setDefaultDialer(getInstrumentation(), PACKAGE); 92 93 TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); 94 mPhoneAccountHandle = telecomManager 95 .getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL); 96 mPhoneNumber = telecomManager.getLine1Number(mPhoneAccountHandle); 97 98 mTelephonyManager = mContext.getSystemService(TelephonyManager.class) 99 .createForPhoneAccountHandle(mPhoneAccountHandle); 100 } 101 102 PackageManager packageManager = mContext.getPackageManager(); 103 packageManager.setComponentEnabledSetting( 104 new ComponentName(mContext, MockVisualVoicemailService.class), 105 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); 106 packageManager.setComponentEnabledSetting( 107 new ComponentName(mContext, PermissionlessVisualVoicemailService.class), 108 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); 109 } 110 111 @Override 112 protected void tearDown() throws Exception { 113 if (hasTelephony(mContext)) { 114 if (!TextUtils.isEmpty(mPreviousDefaultDialer)) { 115 setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer); 116 } 117 118 if (mSmsReceiver != null) { 119 mContext.unregisterReceiver(mSmsReceiver); 120 } 121 } 122 super.tearDown(); 123 } 124 125 public void testPermissionlessService_ignored() { 126 if (!hasTelephony(mContext)) { 127 Log.d(TAG, "skipping test that requires telephony feature"); 128 return; 129 } 130 131 PackageManager packageManager = mContext.getPackageManager(); 132 packageManager.setComponentEnabledSetting( 133 new ComponentName(mContext, MockVisualVoicemailService.class), 134 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); 135 packageManager.setComponentEnabledSetting( 136 new ComponentName(mContext, PermissionlessVisualVoicemailService.class), 137 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); 138 String clientPrefix = "//CTSVVM"; 139 String text = "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1"; 140 141 mTelephonyManager.setVisualVoicemailSmsFilterSettings( 142 new VisualVoicemailSmsFilterSettings.Builder() 143 .setClientPrefix(clientPrefix) 144 .build()); 145 146 try { 147 mTelephonyManager 148 .sendVisualVoicemailSms(mPhoneNumber, 0, text, null); 149 fail("SecurityException expected"); 150 } catch (SecurityException e) { 151 // Expected 152 } 153 154 CompletableFuture<VisualVoicemailSms> future = new CompletableFuture<>(); 155 PermissionlessVisualVoicemailService.setSmsFuture(future); 156 157 setupSmsReceiver(text); 158 159 SmsManager.getDefault().sendTextMessage(mPhoneNumber, null, text, null, null); 160 161 mSmsReceiver.assertReceived(EVENT_RECEIVED_TIMEOUT_MILLIS); 162 try { 163 future.get(EVENT_NOT_RECEIVED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 164 throw new RuntimeException("Unexpected visual voicemail SMS received"); 165 } catch (TimeoutException e) { 166 // expected 167 } catch (ExecutionException | InterruptedException e) { 168 throw new RuntimeException(e); 169 } 170 171 } 172 173 public void testFilter() { 174 if (!hasTelephony(mContext)) { 175 Log.d(TAG, "skipping test that requires telephony feature"); 176 return; 177 } 178 VisualVoicemailSms result = getSmsFromText("//CTSVVM", 179 "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1"); 180 181 assertEquals("STATUS", result.getPrefix()); 182 assertEquals("R", result.getFields().getString("st")); 183 assertEquals("0", result.getFields().getString("rc")); 184 assertEquals("1", result.getFields().getString("srv")); 185 assertEquals("1", result.getFields().getString("dn")); 186 assertEquals("1", result.getFields().getString("ipt")); 187 assertEquals("0", result.getFields().getString("spt")); 188 assertEquals("eg (at) example.com", result.getFields().getString("u")); 189 assertEquals("1", result.getFields().getString("pw")); 190 } 191 192 public void testFilter_data() { 193 if (!hasTelephony(mContext)) { 194 Log.d(TAG, "skipping test that requires telephony feature"); 195 return; 196 } 197 if (!hasDataSms()) { 198 Log.d(TAG, "skipping test that requires data SMS feature"); 199 return; 200 } 201 202 VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder() 203 .setClientPrefix("//CTSVVM") 204 .build(); 205 VisualVoicemailSms result = getSmsFromData(settings, (short) 1000, 206 "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1", true); 207 208 assertEquals("STATUS", result.getPrefix()); 209 assertEquals("R", result.getFields().getString("st")); 210 assertEquals("0", result.getFields().getString("rc")); 211 assertEquals("1", result.getFields().getString("srv")); 212 assertEquals("1", result.getFields().getString("dn")); 213 assertEquals("1", result.getFields().getString("ipt")); 214 assertEquals("0", result.getFields().getString("spt")); 215 assertEquals("eg (at) example.com", result.getFields().getString("u")); 216 assertEquals("1", result.getFields().getString("pw")); 217 } 218 219 220 public void testFilter_TrailingSemiColon() { 221 if (!hasTelephony(mContext)) { 222 Log.d(TAG, "skipping test that requires telephony feature"); 223 return; 224 } 225 VisualVoicemailSms result = getSmsFromText("//CTSVVM", 226 "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1;"); 227 228 assertEquals("STATUS", result.getPrefix()); 229 assertEquals("R", result.getFields().getString("st")); 230 assertEquals("0", result.getFields().getString("rc")); 231 assertEquals("1", result.getFields().getString("srv")); 232 assertEquals("1", result.getFields().getString("dn")); 233 assertEquals("1", result.getFields().getString("ipt")); 234 assertEquals("0", result.getFields().getString("spt")); 235 assertEquals("eg (at) example.com", result.getFields().getString("u")); 236 assertEquals("1", result.getFields().getString("pw")); 237 } 238 239 public void testFilter_EmptyPrefix() { 240 if (!hasTelephony(mContext)) { 241 Log.d(TAG, "skipping test that requires telephony feature"); 242 return; 243 } 244 VisualVoicemailSms result = getSmsFromText("//CTSVVM", 245 "//CTSVVM::st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1"); 246 247 assertEquals("", result.getPrefix()); 248 assertEquals("R", result.getFields().getString("st")); 249 assertEquals("0", result.getFields().getString("rc")); 250 assertEquals("1", result.getFields().getString("srv")); 251 assertEquals("1", result.getFields().getString("dn")); 252 assertEquals("1", result.getFields().getString("ipt")); 253 assertEquals("0", result.getFields().getString("spt")); 254 assertEquals("eg (at) example.com", result.getFields().getString("u")); 255 assertEquals("1", result.getFields().getString("pw")); 256 } 257 258 public void testFilter_EmptyField() { 259 if (!hasTelephony(mContext)) { 260 Log.d(TAG, "skipping test that requires telephony feature"); 261 return; 262 } 263 VisualVoicemailSms result = getSmsFromText("//CTSVVM", 264 "//CTSVVM:STATUS:"); 265 assertTrue(result.getFields().isEmpty()); 266 } 267 268 public void testFilterFail_NotVvm() { 269 if (!hasTelephony(mContext)) { 270 Log.d(TAG, "skipping test that requires telephony feature"); 271 return; 272 } 273 assertVisualVoicemailSmsNotReceived("//CTSVVM", 274 "helloworld"); 275 } 276 277 public void testFilterFail_PrefixMismatch() { 278 if (!hasTelephony(mContext)) { 279 Log.d(TAG, "skipping test that requires telephony feature"); 280 return; 281 } 282 assertVisualVoicemailSmsNotReceived("//CTSVVM", 283 "//FOOVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1"); 284 } 285 286 public void testFilterFail_MissingFirstColon() { 287 if (!hasTelephony(mContext)) { 288 Log.d(TAG, "skipping test that requires telephony feature"); 289 return; 290 } 291 assertVisualVoicemailSmsNotReceived("//CTSVVM", 292 "//CTSVVMSTATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1"); 293 } 294 295 public void testFilterFail_MissingSecondColon() { 296 if (!hasTelephony(mContext)) { 297 Log.d(TAG, "skipping test that requires telephony feature"); 298 return; 299 } 300 assertVisualVoicemailSmsNotReceived("//CTSVVM", 301 "//CTSVVM:STATUSst=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1"); 302 } 303 304 public void testFilterFail_MessageEndAfterClientPrefix() { 305 if (!hasTelephony(mContext)) { 306 Log.d(TAG, "skipping test that requires telephony feature"); 307 return; 308 } 309 assertVisualVoicemailSmsNotReceived("//CTSVVM", 310 "//CTSVVM:"); 311 } 312 313 public void testFilterFail_MessageEndAfterPrefix() { 314 if (!hasTelephony(mContext)) { 315 Log.d(TAG, "skipping test that requires telephony feature"); 316 return; 317 } 318 assertVisualVoicemailSmsNotReceived("//CTSVVM", 319 "//CTSVVM:STATUS"); 320 } 321 322 public void testFilterFail_InvalidKeyValuePair() { 323 if (!hasTelephony(mContext)) { 324 Log.d(TAG, "skipping test that requires telephony feature"); 325 return; 326 } 327 assertVisualVoicemailSmsNotReceived("//CTSVVM", 328 "//CTSVVM:STATUS:key"); 329 } 330 331 public void testFilterFail_InvalidMissingKey() { 332 if (!hasTelephony(mContext)) { 333 Log.d(TAG, "skipping test that requires telephony feature"); 334 return; 335 } 336 assertVisualVoicemailSmsNotReceived("//CTSVVM", 337 "//CTSVVM:STATUS:=value"); 338 } 339 340 public void testFilter_MissingValue() { 341 if (!hasTelephony(mContext)) { 342 Log.d(TAG, "skipping test that requires telephony feature"); 343 return; 344 } 345 VisualVoicemailSms result = getSmsFromText("//CTSVVM", 346 "//CTSVVM:STATUS:key="); 347 assertEquals("STATUS", result.getPrefix()); 348 assertEquals("", result.getFields().getString("key")); 349 } 350 351 public void testFilter_originatingNumber_match_filtered() { 352 if (!hasTelephony(mContext)) { 353 Log.d(TAG, "skipping test that requires telephony feature"); 354 return; 355 } 356 VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder() 357 .setClientPrefix("//CTSVVM") 358 .setOriginatingNumbers(Arrays.asList(mPhoneNumber)) 359 .build(); 360 361 getSmsFromText(settings, "//CTSVVM:SYNC:key=value", true); 362 } 363 364 public void testFilter_originatingNumber_mismatch_notFiltered() { 365 if (!hasTelephony(mContext)) { 366 Log.d(TAG, "skipping test that requires telephony feature"); 367 return; 368 } 369 VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder() 370 .setClientPrefix("//CTSVVM") 371 .setOriginatingNumbers(Arrays.asList("1")) 372 .build(); 373 374 getSmsFromText(settings, "//CTSVVM:SYNC:key=value", false); 375 } 376 377 public void testFilter_port_match() { 378 if (!hasTelephony(mContext)) { 379 Log.d(TAG, "skipping test that requires telephony feature"); 380 return; 381 } 382 if (!hasDataSms()) { 383 Log.d(TAG, "skipping test that requires data SMS feature"); 384 return; 385 } 386 387 VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder() 388 .setClientPrefix("//CTSVVM") 389 .setDestinationPort(1000) 390 .build(); 391 getSmsFromData(settings, (short) 1000, 392 "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1", true); 393 } 394 395 public void testFilter_port_mismatch() { 396 if (!hasTelephony(mContext)) { 397 Log.d(TAG, "skipping test that requires telephony feature"); 398 return; 399 } 400 if (!hasDataSms()) { 401 Log.d(TAG, "skipping test that requires data SMS feature"); 402 return; 403 } 404 405 VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder() 406 .setClientPrefix("//CTSVVM") 407 .setDestinationPort(1001) 408 .build(); 409 getSmsFromData(settings, (short) 1000, 410 "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1", false); 411 } 412 413 public void testFilter_port_anydata() { 414 if (!hasTelephony(mContext)) { 415 Log.d(TAG, "skipping test that requires telephony feature"); 416 return; 417 } 418 if (!hasDataSms()) { 419 Log.d(TAG, "skipping test that requires data SMS feature"); 420 return; 421 } 422 423 VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder() 424 .setClientPrefix("//CTSVVM") 425 .setDestinationPort(VisualVoicemailSmsFilterSettings.DESTINATION_PORT_DATA_SMS) 426 .build(); 427 getSmsFromData(settings, (short) 1000, 428 "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1", true); 429 } 430 431 /** 432 * Text SMS should not be filtered with DESTINATION_PORT_DATA_SMS 433 */ 434 public void testFilter_port_anydata_notData() { 435 if (!hasTelephony(mContext)) { 436 Log.d(TAG, "skipping test that requires telephony feature"); 437 return; 438 } 439 if (!hasDataSms()) { 440 Log.d(TAG, "skipping test that requires data SMS feature"); 441 return; 442 } 443 444 VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder() 445 .setClientPrefix("//CTSVVM") 446 .setDestinationPort(VisualVoicemailSmsFilterSettings.DESTINATION_PORT_DATA_SMS) 447 .build(); 448 getSmsFromText(settings, 449 "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg (at) example.com;pw=1", false); 450 } 451 452 public void testGetVisualVoicemailPackageName_isSelf() { 453 if (!hasTelephony(mContext)) { 454 Log.d(TAG, "skipping test that requires telephony feature"); 455 return; 456 } 457 assertEquals(PACKAGE, mTelephonyManager.getVisualVoicemailPackageName()); 458 } 459 460 private VisualVoicemailSms getSmsFromText(String clientPrefix, String text) { 461 return getSmsFromText(clientPrefix, text, true); 462 } 463 464 @Nullable 465 private VisualVoicemailSms getSmsFromText(String clientPrefix, String text, 466 boolean expectVvmSms) { 467 return getSmsFromText( 468 new VisualVoicemailSmsFilterSettings.Builder() 469 .setClientPrefix(clientPrefix) 470 .build(), 471 text, 472 expectVvmSms); 473 } 474 475 private void assertVisualVoicemailSmsNotReceived(String clientPrefix, String text) { 476 getSmsFromText(clientPrefix, text, false); 477 } 478 479 /** 480 * Setup the SMS filter with only the {@code clientPrefix}, and sends {@code text} to the 481 * device. The SMS sent should not be written to the SMS provider. <p> If {@code expectVvmSms} 482 * is {@code true}, the SMS should be be caught by the SMS filter. The user should not receive 483 * the text, and the parsed result will be returned.* <p> If {@code expectVvmSms} is {@code 484 * false}, the SMS should pass through the SMS filter. The user should receive the text, and 485 * {@code null} be returned. 486 */ 487 @Nullable 488 private VisualVoicemailSms getSmsFromText(VisualVoicemailSmsFilterSettings settings, 489 String text, 490 boolean expectVvmSms) { 491 492 mTelephonyManager.setVisualVoicemailSmsFilterSettings(settings); 493 494 CompletableFuture<VisualVoicemailSms> future = new CompletableFuture<>(); 495 MockVisualVoicemailService.setSmsFuture(future); 496 497 setupSmsReceiver(text); 498 try (SentSmsObserver observer = new SentSmsObserver(mContext, text)) { 499 mTelephonyManager 500 .sendVisualVoicemailSms(mPhoneNumber,0, text, null); 501 502 if (expectVvmSms) { 503 VisualVoicemailSms sms; 504 try { 505 sms = future.get(EVENT_RECEIVED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 506 } catch (InterruptedException | ExecutionException | TimeoutException e) { 507 throw new RuntimeException(e); 508 } 509 mSmsReceiver.assertNotReceived(EVENT_NOT_RECEIVED_TIMEOUT_MILLIS); 510 observer.assertNotChanged(); 511 return sms; 512 } else { 513 mSmsReceiver.assertReceived(EVENT_RECEIVED_TIMEOUT_MILLIS); 514 try { 515 future.get(EVENT_NOT_RECEIVED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 516 throw new RuntimeException("Unexpected visual voicemail SMS received"); 517 } catch (TimeoutException e) { 518 // expected 519 return null; 520 } catch (ExecutionException | InterruptedException e) { 521 throw new RuntimeException(e); 522 } 523 } 524 } 525 } 526 527 @Nullable 528 private VisualVoicemailSms getSmsFromData(VisualVoicemailSmsFilterSettings settings, short port, 529 String text, boolean expectVvmSms) { 530 531 mTelephonyManager.setVisualVoicemailSmsFilterSettings(settings); 532 533 CompletableFuture<VisualVoicemailSms> future = new CompletableFuture<>(); 534 MockVisualVoicemailService.setSmsFuture(future); 535 536 setupSmsReceiver(text); 537 mTelephonyManager.sendVisualVoicemailSms(mPhoneNumber, port, text, null); 538 539 if (expectVvmSms) { 540 VisualVoicemailSms sms; 541 try { 542 sms = future.get(EVENT_RECEIVED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 543 } catch (InterruptedException | ExecutionException | TimeoutException e) { 544 throw new RuntimeException(e); 545 } 546 mSmsReceiver.assertNotReceived(EVENT_NOT_RECEIVED_TIMEOUT_MILLIS); 547 return sms; 548 } else { 549 mSmsReceiver.assertReceived(EVENT_RECEIVED_TIMEOUT_MILLIS); 550 try { 551 future.get(EVENT_NOT_RECEIVED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 552 throw new RuntimeException("Unexpected visual voicemail SMS received"); 553 } catch (TimeoutException e) { 554 // expected 555 return null; 556 } catch (ExecutionException | InterruptedException e) { 557 throw new RuntimeException(e); 558 } 559 } 560 } 561 562 private void setupSmsReceiver(String text) { 563 mSmsReceiver = new SmsBroadcastReceiver(text); 564 mContext.registerReceiver(mSmsReceiver, new IntentFilter(Intents.SMS_RECEIVED_ACTION)); 565 IntentFilter dataFilter = new IntentFilter(Intents.DATA_SMS_RECEIVED_ACTION); 566 dataFilter.addDataScheme("sms"); 567 mContext.registerReceiver(mSmsReceiver, dataFilter); 568 } 569 570 private static class SmsBroadcastReceiver extends BroadcastReceiver { 571 572 private final String mText; 573 574 private CompletableFuture<Boolean> mFuture = new CompletableFuture<>(); 575 576 public SmsBroadcastReceiver(String text) { 577 mText = text; 578 } 579 580 @Override 581 public void onReceive(Context context, Intent intent) { 582 SmsMessage[] messages = Sms.Intents.getMessagesFromIntent(intent); 583 StringBuilder messageBody = new StringBuilder(); 584 CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder(); 585 for (SmsMessage message : messages) { 586 if (message.getMessageBody() != null) { 587 messageBody.append(message.getMessageBody()); 588 } else if (message.getUserData() != null) { 589 ByteBuffer byteBuffer = ByteBuffer.wrap(message.getUserData()); 590 try { 591 messageBody.append(decoder.decode(byteBuffer).toString()); 592 } catch (CharacterCodingException e) { 593 return; 594 } 595 } 596 } 597 if (!TextUtils.equals(mText, messageBody.toString())) { 598 return; 599 } 600 mFuture.complete(true); 601 } 602 603 public void assertReceived(long timeoutMillis) { 604 try { 605 mFuture.get(timeoutMillis, TimeUnit.MILLISECONDS); 606 } catch (InterruptedException | ExecutionException | TimeoutException e) { 607 throw new RuntimeException(e); 608 } 609 } 610 611 public void assertNotReceived(long timeoutMillis) { 612 try { 613 mFuture.get(timeoutMillis, TimeUnit.MILLISECONDS); 614 throw new RuntimeException("Unexpected SMS received"); 615 } catch (TimeoutException e) { 616 // expected 617 } catch (InterruptedException | ExecutionException e) { 618 throw new RuntimeException(e); 619 } 620 } 621 } 622 623 private static class SentSmsObserver extends ContentObserver implements AutoCloseable { 624 625 private final Context mContext; 626 private final String mText; 627 628 public CompletableFuture<Boolean> mFuture = new CompletableFuture<>(); 629 630 public SentSmsObserver(Context context, String text) { 631 super(new Handler(Looper.getMainLooper())); 632 mContext = context; 633 mText = text; 634 mContext.getContentResolver().registerContentObserver(Sms.CONTENT_URI, true, this); 635 } 636 637 public void assertNotChanged() { 638 try { 639 mFuture.get(EVENT_NOT_RECEIVED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 640 fail("Visual voicemail SMS should not be added into the sent SMS"); 641 } catch (TimeoutException e) { 642 // expected 643 } catch (ExecutionException | InterruptedException e) { 644 throw new RuntimeException(e); 645 } 646 647 } 648 649 @Override 650 public void onChange(boolean selfChange, Uri uri) { 651 try (Cursor cursor = mContext.getContentResolver() 652 .query(uri, new String[] {Sms.TYPE, Sms.BODY}, null, null, null)) { 653 if (cursor == null){ 654 return; 655 } 656 if (!cursor.moveToFirst()){ 657 return; 658 } 659 if (cursor.getInt(0) == Sms.MESSAGE_TYPE_SENT && TextUtils 660 .equals(cursor.getString(1), mText)) { 661 mFuture.complete(true); 662 } 663 } catch (SQLiteException e) { 664 665 } 666 } 667 668 @Override 669 public void close() { 670 mContext.getContentResolver().unregisterContentObserver(this); 671 } 672 } 673 674 private static boolean hasTelephony(Context context) { 675 final PackageManager packageManager = context.getPackageManager(); 676 return packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) && 677 packageManager.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE); 678 } 679 680 private boolean hasDataSms() { 681 String mccmnc = mTelephonyManager.getSimOperator(); 682 return !CarrierCapability.UNSUPPORT_DATA_SMS_MESSAGES.contains(mccmnc); 683 } 684 685 private static String setDefaultDialer(Instrumentation instrumentation, String packageName) 686 throws Exception { 687 return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName); 688 } 689 690 private static String getDefaultDialer(Instrumentation instrumentation) throws Exception { 691 return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER); 692 } 693 694 /** 695 * Executes the given shell command and returns the output in a string. Note that even if we 696 * don't care about the output, we have to read the stream completely to make the command 697 * execute. 698 */ 699 private static String executeShellCommand(Instrumentation instrumentation, 700 String command) throws Exception { 701 final ParcelFileDescriptor parcelFileDescriptor = 702 instrumentation.getUiAutomation().executeShellCommand(command); 703 BufferedReader bufferedReader = null; 704 try (InputStream in = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) { 705 bufferedReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); 706 String string = null; 707 StringBuilder out = new StringBuilder(); 708 while ((string = bufferedReader.readLine()) != null) { 709 out.append(string); 710 } 711 return out.toString(); 712 } finally { 713 if (bufferedReader != null) { 714 closeQuietly(bufferedReader); 715 } 716 closeQuietly(parcelFileDescriptor); 717 } 718 } 719 720 private static void closeQuietly(AutoCloseable closeable) { 721 if (closeable != null) { 722 try { 723 closeable.close(); 724 } catch (RuntimeException rethrown) { 725 throw rethrown; 726 } catch (Exception ignored) { 727 } 728 } 729 } 730 } 731