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 17package com.android.internal.telephony.gsm; 18 19import android.content.BroadcastReceiver; 20import android.content.ContentValues; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.database.Cursor; 25import android.database.MatrixCursor; 26import android.net.Uri; 27import android.os.AsyncResult; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.HandlerThread; 31import android.os.UserManager; 32import android.os.RemoteException; 33import android.os.UserHandle; 34import android.provider.Telephony; 35import android.test.mock.MockContentProvider; 36import android.test.mock.MockContentResolver; 37import android.test.suitebuilder.annotation.MediumTest; 38 39import com.android.internal.telephony.InboundSmsHandler; 40import com.android.internal.telephony.InboundSmsTracker; 41import com.android.internal.telephony.SmsBroadcastUndelivered; 42import com.android.internal.telephony.SmsHeader; 43import com.android.internal.telephony.SmsStorageMonitor; 44import com.android.internal.telephony.TelephonyTest; 45import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; 46import com.android.internal.util.HexDump; 47import com.android.internal.util.IState; 48import com.android.internal.util.StateMachine; 49 50import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; 51import static org.junit.Assert.*; 52import static org.mockito.Mockito.*; 53 54import org.junit.After; 55import org.junit.Before; 56import org.junit.Test; 57import org.mockito.ArgumentCaptor; 58import org.mockito.Mock; 59 60import java.lang.reflect.Method; 61import java.util.ArrayList; 62import java.util.List; 63 64public class GsmInboundSmsHandlerTest extends TelephonyTest { 65 @Mock 66 private SmsStorageMonitor mSmsStorageMonitor; 67 @Mock 68 private android.telephony.SmsMessage mSmsMessage; 69 @Mock 70 private SmsMessage mGsmSmsMessage; 71 @Mock 72 private SmsHeader mSmsHeader; 73 @Mock 74 private InboundSmsTracker mInboundSmsTrackerPart1; 75 @Mock 76 private InboundSmsTracker mInboundSmsTrackerPart2; 77 @Mock 78 private CdmaInboundSmsHandler mCdmaInboundSmsHandler; 79 80 private GsmInboundSmsHandler mGsmInboundSmsHandler; 81 82 private FakeSmsContentProvider mContentProvider; 83 private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); 84 private static final Uri sRawUriPermanentDelete = 85 Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw/permanentDelete"); 86 87 private ContentValues mInboundSmsTrackerCV = new ContentValues(); 88 // For multi-part SMS 89 private ContentValues mInboundSmsTrackerCVPart1; 90 private ContentValues mInboundSmsTrackerCVPart2; 91 private String mMessageBody = "This is the message body of a single-part message"; 92 private String mMessageBodyPart1 = "This is the first part of a multi-part message"; 93 private String mMessageBodyPart2 = "This is the second part of a multi-part message"; 94 95 byte[] mSmsPdu = new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF}; 96 97 public static class FakeSmsContentProvider extends MockContentProvider { 98 private String[] mRawColumns = {"_id", 99 "date", 100 "reference_number", 101 "count", 102 "sequence", 103 "destination_port", 104 "address", 105 "sub_id", 106 "pdu", 107 "deleted", 108 "message_body"}; 109 private List<ArrayList<Object>> mListOfRows = new ArrayList<ArrayList<Object>>(); 110 private int mNumRows = 0; 111 112 private int getColumnIndex(String columnName) { 113 int i = 0; 114 for (String s : mRawColumns) { 115 if (s.equals(columnName)) { 116 break; 117 } 118 i++; 119 } 120 return i; 121 } 122 123 @Override 124 public int delete(Uri uri, String selection, String[] selectionArgs) { 125 int count = 0; 126 if (mNumRows > 0) { 127 // parse selection and selectionArgs 128 SelectionParams selectionParams = new SelectionParams(); 129 selectionParams.parseSelectionParams(selection, selectionArgs); 130 131 List<Integer> deleteRows = new ArrayList<Integer>(); 132 int i = -1; 133 for (ArrayList<Object> row : mListOfRows) { 134 i++; 135 // filter based on selection parameters if needed 136 if (selection != null) { 137 if (!selectionParams.isMatch(row)) { 138 continue; 139 } 140 } 141 if (uri.compareTo(sRawUri) == 0) { 142 row.set(getColumnIndex("deleted"), "1"); 143 } else { 144 // save index for removal 145 deleteRows.add(i); 146 } 147 count++; 148 } 149 150 if (uri.compareTo(sRawUriPermanentDelete) == 0) { 151 for (i = deleteRows.size() - 1; i >= 0; i--) { 152 mListOfRows.remove(i); 153 } 154 } 155 } 156 return count; 157 } 158 159 @Override 160 public Uri insert(Uri uri, ContentValues values) { 161 Uri newUri = null; 162 if (uri.compareTo(sRawUri) == 0) { 163 if (values != null) { 164 mListOfRows.add(convertRawCVtoArrayList(values)); 165 mNumRows++; 166 newUri = Uri.withAppendedPath(uri, "" + mNumRows); 167 } 168 } 169 logd("insert called, new numRows: " + mNumRows); 170 return newUri; 171 } 172 173 private ArrayList<Object> convertRawCVtoArrayList(ContentValues values) { 174 ArrayList<Object> newRow = new ArrayList<>(); 175 for (String key : mRawColumns) { 176 if (values.containsKey(key)) { 177 newRow.add(values.getAsString(key)); 178 } else if (key.equals("_id")) { 179 newRow.add(mNumRows + 1); 180 } else if (key.equals("deleted")) { 181 newRow.add("0"); 182 } else { 183 newRow.add(null); 184 } 185 } 186 return newRow; 187 } 188 189 private class SelectionParams { 190 String[] paramName = null; 191 String[] paramValue = null; 192 193 private void parseSelectionParams(String selection, String[] selectionArgs) { 194 if (selection != null) { 195 selection = selection.toLowerCase(); 196 String[] selectionParams = selection.toLowerCase().split("and"); 197 int i = 0; 198 int j = 0; 199 paramName = new String[selectionParams.length]; 200 paramValue = new String[selectionParams.length]; 201 for (String param : selectionParams) { 202 String[] paramWithArg = param.split("="); 203 paramName[i] = paramWithArg[0].trim(); 204 if (param.contains("?")) { 205 paramValue[i] = selectionArgs[j]; 206 j++; 207 } else { 208 paramValue[i] = paramWithArg[1].trim(); 209 } 210 //logd(paramName[i] + " = " + paramValue[i]); 211 i++; 212 } 213 } 214 } 215 216 private boolean isMatch(ArrayList<Object> row) { 217 for (int i = 0; i < paramName.length; i++) { 218 int columnIndex = 0; 219 for (String columnName : mRawColumns) { 220 if (columnName.equals(paramName[i])) { 221 if ((paramValue[i] == null && row.get(columnIndex) != null) || 222 (paramValue[i] != null && 223 !paramValue[i].equals(row.get(columnIndex)))) { 224 logd("Not a match due to " + columnName + ": " + paramValue[i] + 225 ", " + row.get(columnIndex)); 226 return false; 227 } else { 228 // move on to next param 229 break; 230 } 231 } 232 columnIndex++; 233 } 234 } 235 return true; 236 } 237 } 238 239 @Override 240 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 241 String sortOrder) { 242 logd("query called for: " + selection); 243 MatrixCursor cursor = new MatrixCursor(projection); 244 if (mNumRows > 0) { 245 // parse selection and selectionArgs 246 SelectionParams selectionParams = new SelectionParams(); 247 selectionParams.parseSelectionParams(selection, selectionArgs); 248 249 for (ArrayList<Object> row : mListOfRows) { 250 ArrayList<Object> retRow = new ArrayList<>(); 251 // filter based on selection parameters if needed 252 if (selection != null) { 253 if (!selectionParams.isMatch(row)) { 254 continue; 255 } 256 } 257 258 for (String columnName : projection) { 259 int columnIndex = getColumnIndex(columnName); 260 retRow.add(row.get(columnIndex)); 261 } 262 cursor.addRow(retRow); 263 } 264 } 265 if (cursor != null) { 266 logd("returning rows: " + cursor.getCount()); 267 } 268 return cursor; 269 } 270 271 @Override 272 public Bundle call(String method, String request, Bundle args) { 273 return null; 274 } 275 } 276 277 private class GsmInboundSmsHandlerTestHandler extends HandlerThread { 278 279 private GsmInboundSmsHandlerTestHandler(String name) { 280 super(name); 281 } 282 283 @Override 284 public void onLooperPrepared() { 285 mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(mContext, 286 mSmsStorageMonitor, mPhone); 287 setReady(true); 288 } 289 } 290 291 private IState getCurrentState() { 292 try { 293 Method method = StateMachine.class.getDeclaredMethod("getCurrentState"); 294 method.setAccessible(true); 295 return (IState) method.invoke(mGsmInboundSmsHandler); 296 } catch (Exception e) { 297 fail(e.toString()); 298 return null; 299 } 300 } 301 302 @Before 303 public void setUp() throws Exception { 304 super.setUp("GsmInboundSmsHandlerTest"); 305 306 doReturn(true).when(mTelephonyManager).getSmsReceiveCapableForPhone(anyInt(), anyBoolean()); 307 doReturn(true).when(mSmsStorageMonitor).isStorageAvailable(); 308 309 UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE); 310 doReturn(true).when(userManager).isUserUnlocked(); 311 312 try { 313 doReturn(new int[]{UserHandle.USER_SYSTEM}).when(mIActivityManager).getRunningUserIds(); 314 } catch (RemoteException re) { 315 fail("Unexpected RemoteException: " + re.getStackTrace()); 316 } 317 318 mSmsMessage.mWrappedSmsMessage = mGsmSmsMessage; 319 mInboundSmsTrackerCV.put("destination_port", 1 << 16); 320 mInboundSmsTrackerCV.put("pdu", HexDump.toHexString(mSmsPdu)); 321 mInboundSmsTrackerCV.put("address", "1234567890"); 322 mInboundSmsTrackerCV.put("reference_number", 1); 323 mInboundSmsTrackerCV.put("sequence", 1); 324 mInboundSmsTrackerCV.put("count", 1); 325 mInboundSmsTrackerCV.put("date", System.currentTimeMillis()); 326 mInboundSmsTrackerCV.put("message_body", mMessageBody); 327 328 doReturn(1).when(mInboundSmsTracker).getMessageCount(); 329 doReturn(1).when(mInboundSmsTracker).getReferenceNumber(); 330 doReturn("1234567890").when(mInboundSmsTracker).getAddress(); 331 doReturn(1).when(mInboundSmsTracker).getSequenceNumber(); 332 doReturn(1).when(mInboundSmsTracker).getIndexOffset(); 333 doReturn(-1).when(mInboundSmsTracker).getDestPort(); 334 doReturn(mMessageBody).when(mInboundSmsTracker).getMessageBody(); 335 doReturn(mSmsPdu).when(mInboundSmsTracker).getPdu(); 336 doReturn(mInboundSmsTrackerCV.get("date")).when(mInboundSmsTracker).getTimestamp(); 337 doReturn(mInboundSmsTrackerCV).when(mInboundSmsTracker).getContentValues(); 338 339 mContentProvider = new FakeSmsContentProvider(); 340 ((MockContentResolver)mContext.getContentResolver()).addProvider( 341 Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider); 342 343 new GsmInboundSmsHandlerTestHandler(TAG).start(); 344 waitUntilReady(); 345 } 346 347 @After 348 public void tearDown() throws Exception { 349 // wait for wakelock to be released; timeout at 10s 350 int i = 0; 351 while (mGsmInboundSmsHandler.getWakeLock().isHeld() && i < 100) { 352 waitForMs(100); 353 i++; 354 } 355 assertFalse(mGsmInboundSmsHandler.getWakeLock().isHeld()); 356 mGsmInboundSmsHandler = null; 357 super.tearDown(); 358 } 359 360 private void transitionFromStartupToIdle() { 361 // verify initially in StartupState 362 assertEquals("StartupState", getCurrentState().getName()); 363 364 // trigger transition to IdleState 365 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS); 366 waitForMs(50); 367 368 assertEquals("IdleState", getCurrentState().getName()); 369 } 370 371 private void verifySmsIntentBroadcasts(int numPastBroadcasts) { 372 ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); 373 verify(mContext, times(1 + numPastBroadcasts)).sendBroadcast( 374 intentArgumentCaptor.capture()); 375 assertEquals(Telephony.Sms.Intents.SMS_DELIVER_ACTION, 376 intentArgumentCaptor.getAllValues().get(numPastBroadcasts).getAction()); 377 assertEquals("WaitingState", getCurrentState().getName()); 378 379 mContextFixture.sendBroadcastToOrderedBroadcastReceivers(); 380 381 intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); 382 verify(mContext, times(2 + numPastBroadcasts)).sendBroadcast( 383 intentArgumentCaptor.capture()); 384 assertEquals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION, 385 intentArgumentCaptor.getAllValues().get(numPastBroadcasts + 1).getAction()); 386 assertEquals("WaitingState", getCurrentState().getName()); 387 388 mContextFixture.sendBroadcastToOrderedBroadcastReceivers(); 389 waitForMs(50); 390 391 assertEquals("IdleState", getCurrentState().getName()); 392 } 393 394 @Test 395 @MediumTest 396 public void testNewSms() { 397 transitionFromStartupToIdle(); 398 399 // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION 400 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, 401 new AsyncResult(null, mSmsMessage, null)); 402 waitForMs(100); 403 404 verifySmsIntentBroadcasts(0); 405 406 // send same SMS again, verify no broadcasts are sent 407 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, 408 new AsyncResult(null, mSmsMessage, null)); 409 waitForMs(100); 410 411 verify(mContext, times(2)).sendBroadcast(any(Intent.class)); 412 assertEquals("IdleState", getCurrentState().getName()); 413 } 414 415 @Test 416 @MediumTest 417 public void testNewSmsFromBlockedNumber_noBroadcastsSent() { 418 String blockedNumber = "123456789"; 419 doReturn(blockedNumber).when(mInboundSmsTracker).getAddress(); 420 mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber); 421 422 transitionFromStartupToIdle(); 423 424 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, 425 new AsyncResult(null, mSmsMessage, null)); 426 waitForMs(100); 427 428 verify(mContext, never()).sendBroadcast(any(Intent.class)); 429 assertEquals("IdleState", getCurrentState().getName()); 430 } 431 432 private void verifyDataSmsIntentBroadcasts(int numPastBroadcasts) { 433 ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); 434 verify(mContext, times(1 + numPastBroadcasts)).sendBroadcast( 435 intentArgumentCaptor.capture()); 436 assertEquals(Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION, 437 intentArgumentCaptor.getAllValues().get(numPastBroadcasts).getAction()); 438 assertEquals("WaitingState", getCurrentState().getName()); 439 440 mContextFixture.sendBroadcastToOrderedBroadcastReceivers(); 441 waitForMs(50); 442 443 assertEquals("IdleState", getCurrentState().getName()); 444 } 445 446 @Test 447 @MediumTest 448 public void testBroadcastSms() { 449 transitionFromStartupToIdle(); 450 451 doReturn(0).when(mInboundSmsTracker).getDestPort(); 452 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, 453 mInboundSmsTracker); 454 waitForMs(100); 455 456 verifyDataSmsIntentBroadcasts(0); 457 458 // send same data sms again, and since it's not text sms it should be broadcast again 459 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, 460 mInboundSmsTracker); 461 waitForMs(100); 462 463 verifyDataSmsIntentBroadcasts(1); 464 } 465 466 @Test 467 @MediumTest 468 public void testInjectSms() { 469 transitionFromStartupToIdle(); 470 471 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, new AsyncResult(null, 472 mSmsMessage, null)); 473 waitForMs(100); 474 475 verifySmsIntentBroadcasts(0); 476 477 // inject same SMS again, verify no broadcasts are sent 478 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, new AsyncResult(null, 479 mSmsMessage, null)); 480 waitForMs(100); 481 482 verify(mContext, times(2)).sendBroadcast(any(Intent.class)); 483 assertEquals("IdleState", getCurrentState().getName()); 484 } 485 486 private void prepareMultiPartSms() { 487 // Part 1 488 mInboundSmsTrackerCVPart1 = new ContentValues(); 489 mInboundSmsTrackerCVPart1.put("destination_port", 1 << 16); 490 mInboundSmsTrackerCVPart1.put("pdu", HexDump.toHexString(mSmsPdu)); 491 mInboundSmsTrackerCVPart1.put("address", "1234567890"); 492 mInboundSmsTrackerCVPart1.put("reference_number", 1); 493 mInboundSmsTrackerCVPart1.put("sequence", 1); 494 mInboundSmsTrackerCVPart1.put("count", 2); 495 mInboundSmsTrackerCVPart1.put("date", System.currentTimeMillis()); 496 mInboundSmsTrackerCVPart1.put("message_body", mMessageBodyPart1); 497 498 doReturn(2).when(mInboundSmsTrackerPart1).getMessageCount(); 499 doReturn(1).when(mInboundSmsTrackerPart1).getReferenceNumber(); 500 doReturn("1234567890").when(mInboundSmsTrackerPart1).getAddress(); 501 doReturn(1).when(mInboundSmsTrackerPart1).getSequenceNumber(); 502 doReturn(1).when(mInboundSmsTrackerPart1).getIndexOffset(); 503 doReturn(-1).when(mInboundSmsTrackerPart1).getDestPort(); 504 doReturn(mMessageBodyPart1).when(mInboundSmsTrackerPart1).getMessageBody(); 505 doReturn(mSmsPdu).when(mInboundSmsTrackerPart1).getPdu(); 506 doReturn(mInboundSmsTrackerCVPart1.get("date")).when(mInboundSmsTrackerPart1). 507 getTimestamp(); 508 doReturn(mInboundSmsTrackerCVPart1).when(mInboundSmsTrackerPart1).getContentValues(); 509 510 // Part 2 511 mInboundSmsTrackerCVPart2 = new ContentValues(); 512 mInboundSmsTrackerCVPart2.put("destination_port", 1 << 16); 513 mInboundSmsTrackerCVPart2.put("pdu", HexDump.toHexString(mSmsPdu)); 514 mInboundSmsTrackerCVPart2.put("address", "1234567890"); 515 mInboundSmsTrackerCVPart2.put("reference_number", 1); 516 mInboundSmsTrackerCVPart2.put("sequence", 2); 517 mInboundSmsTrackerCVPart2.put("count", 2); 518 mInboundSmsTrackerCVPart2.put("date", System.currentTimeMillis()); 519 mInboundSmsTrackerCVPart2.put("message_body", mMessageBodyPart2); 520 521 doReturn(2).when(mInboundSmsTrackerPart2).getMessageCount(); 522 doReturn(1).when(mInboundSmsTrackerPart2).getReferenceNumber(); 523 doReturn("1234567890").when(mInboundSmsTrackerPart2).getAddress(); 524 doReturn(2).when(mInboundSmsTrackerPart2).getSequenceNumber(); 525 doReturn(1).when(mInboundSmsTrackerPart2).getIndexOffset(); 526 doReturn(-1).when(mInboundSmsTrackerPart2).getDestPort(); 527 doReturn(mMessageBodyPart2).when(mInboundSmsTrackerPart2).getMessageBody(); 528 doReturn(mSmsPdu).when(mInboundSmsTrackerPart2).getPdu(); 529 doReturn(mInboundSmsTrackerCVPart2.get("date")).when(mInboundSmsTrackerPart2). 530 getTimestamp(); 531 doReturn(mInboundSmsTrackerCVPart2).when(mInboundSmsTrackerPart2).getContentValues(); 532 } 533 534 @Test 535 @MediumTest 536 public void testMultiPartSms() { 537 transitionFromStartupToIdle(); 538 539 // prepare SMS part 1 and part 2 540 prepareMultiPartSms(); 541 542 mSmsHeader.concatRef = new SmsHeader.ConcatRef(); 543 doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader(); 544 545 doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory) 546 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), 547 anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); 548 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, 549 mSmsMessage, null)); 550 waitForMs(100); 551 552 // State machine should go back to idle and wait for second part 553 assertEquals("IdleState", getCurrentState().getName()); 554 555 doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory) 556 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), 557 anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); 558 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, 559 mSmsMessage, null)); 560 waitForMs(100); 561 562 // verify broadcast intents 563 verifySmsIntentBroadcasts(0); 564 565 // if an additional copy of one of the segments above is received, it should not be kept in 566 // the db and should not be combined with any subsequent messages received from the same 567 // sender 568 569 // additional copy of part 2 of message 570 doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory) 571 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), 572 anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); 573 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, 574 mSmsMessage, null)); 575 waitForMs(100); 576 577 // verify no additional broadcasts sent 578 verify(mContext, times(2)).sendBroadcast(any(Intent.class)); 579 580 // part 1 of new sms recieved from same sender with same parameters, just different 581 // timestamps, should not be combined with the additional part 2 received above 582 583 // call prepareMultiPartSms() to update timestamps 584 prepareMultiPartSms(); 585 586 // part 1 of new sms 587 doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory) 588 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), 589 anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); 590 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, 591 mSmsMessage, null)); 592 waitForMs(100); 593 594 // verify no additional broadcasts sent 595 verify(mContext, times(2)).sendBroadcast(any(Intent.class)); 596 597 assertEquals("IdleState", getCurrentState().getName()); 598 } 599 600 @Test 601 @MediumTest 602 public void testMultiPartIncompleteSms() { 603 /** 604 * Test scenario: 2 messages are received with same address, ref number, count, and 605 * seqNumber, with count = 2 and seqNumber = 1. We should not try to merge these. 606 */ 607 transitionFromStartupToIdle(); 608 609 // prepare SMS part 1 and part 2 610 prepareMultiPartSms(); 611 // change seqNumber in part 2 to 1 612 mInboundSmsTrackerCVPart2.put("sequence", 1); 613 614 mSmsHeader.concatRef = new SmsHeader.ConcatRef(); 615 doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader(); 616 617 doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory) 618 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), 619 anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); 620 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, 621 mSmsMessage, null)); 622 waitForMs(100); 623 624 // State machine should go back to idle and wait for second part 625 assertEquals("IdleState", getCurrentState().getName()); 626 627 doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory) 628 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), 629 anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); 630 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, 631 mSmsMessage, null)); 632 waitForMs(100); 633 634 // verify no broadcasts sent 635 verify(mContext, never()).sendBroadcast(any(Intent.class)); 636 // State machine should go back to idle 637 assertEquals("IdleState", getCurrentState().getName()); 638 } 639 640 @Test 641 @MediumTest 642 public void testMultipartSmsFromBlockedNumber_noBroadcastsSent() { 643 mFakeBlockedNumberContentProvider.mBlockedNumbers.add("1234567890"); 644 645 transitionFromStartupToIdle(); 646 647 // prepare SMS part 1 and part 2 648 prepareMultiPartSms(); 649 650 mSmsHeader.concatRef = new SmsHeader.ConcatRef(); 651 doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader(); 652 doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory) 653 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), 654 anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); 655 656 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, 657 mSmsMessage, null)); 658 waitForMs(100); 659 660 // State machine should go back to idle and wait for second part 661 assertEquals("IdleState", getCurrentState().getName()); 662 663 doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory) 664 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), 665 anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); 666 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, 667 mSmsMessage, null)); 668 waitForMs(100); 669 670 verify(mContext, never()).sendBroadcast(any(Intent.class)); 671 assertEquals("IdleState", getCurrentState().getName()); 672 } 673 674 @Test 675 @MediumTest 676 public void testBroadcastUndeliveredUserLocked() throws Exception { 677 replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null); 678 doReturn(0).when(mInboundSmsTracker).getDestPort(); 679 680 // add a fake entry to db 681 ContentValues rawSms = new ContentValues(); 682 mContentProvider.insert(sRawUri, rawSms); 683 684 // make it a single-part message 685 doReturn(1).when(mInboundSmsTracker).getMessageCount(); 686 687 // user locked 688 UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE); 689 doReturn(false).when(userManager).isUserUnlocked(); 690 691 SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler); 692 693 // verify that a broadcast receiver is registered for current user (user == null) based on 694 // implementation in ContextFixture 695 verify(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq((UserHandle)null), 696 any(IntentFilter.class), eq((String)null), eq((Handler)null)); 697 698 waitForMs(100); 699 700 // verify no broadcasts sent because due to !isUserUnlocked 701 verify(mContext, never()).sendBroadcast(any(Intent.class)); 702 703 // when user unlocks the device, the message in db should be broadcast 704 doReturn(true).when(userManager).isUserUnlocked(); 705 mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED)); 706 waitForMs(100); 707 708 verifyDataSmsIntentBroadcasts(1); 709 } 710 711 @Test 712 @MediumTest 713 public void testBroadcastUndeliveredUserUnlocked() throws Exception { 714 replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null); 715 doReturn(0).when(mInboundSmsTracker).getDestPort(); 716 717 // add a fake entry to db 718 ContentValues rawSms = new ContentValues(); 719 mContentProvider.insert(sRawUri, rawSms); 720 721 // make it a single-part message 722 doReturn(1).when(mInboundSmsTracker).getMessageCount(); 723 724 SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler); 725 waitForMs(100); 726 727 // user is unlocked; intent should be broadcast right away 728 verifyDataSmsIntentBroadcasts(0); 729 } 730 731 @Test 732 @MediumTest 733 public void testBroadcastUndeliveredDeleted() throws Exception { 734 replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null); 735 SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler); 736 doReturn(0).when(mInboundSmsTracker).getDestPort(); 737 738 //add a fake entry to db 739 ContentValues rawSms = new ContentValues(); 740 rawSms.put("deleted", 1); 741 mContentProvider.insert(sRawUri, rawSms); 742 743 //make it a single-part message 744 doReturn(1).when(mInboundSmsTracker).getMessageCount(); 745 746 //when user unlocks the device, broadcast should not be sent for new message 747 mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED)); 748 waitForMs(100); 749 750 verify(mContext, times(1)).sendBroadcast(any(Intent.class)); 751 assertEquals("IdleState", getCurrentState().getName()); 752 753 } 754 755 @Test 756 @MediumTest 757 public void testBroadcastUndeliveredMultiPart() throws Exception { 758 replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null); 759 760 // prepare SMS part 1 and part 2 761 prepareMultiPartSms(); 762 763 //add the 2 SMS parts to db 764 mContentProvider.insert(sRawUri, mInboundSmsTrackerCVPart1); 765 mContentProvider.insert(sRawUri, mInboundSmsTrackerCVPart2); 766 767 //return InboundSmsTracker objects corresponding to the 2 parts 768 doReturn(mInboundSmsTrackerPart1).doReturn(mInboundSmsTrackerPart2). 769 when(mTelephonyComponentFactory).makeInboundSmsTracker(any(Cursor.class), 770 anyBoolean()); 771 772 SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler); 773 waitForMs(100); 774 775 verifySmsIntentBroadcasts(0); 776 } 777} 778