/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.gsm; import android.content.BroadcastReceiver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.net.Uri; import android.os.AsyncResult; import android.os.Handler; import android.os.HandlerThread; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Postsubmit; import android.provider.Telephony; import android.test.AssertionFailedError; import android.test.mock.MockContentResolver; import android.test.suitebuilder.annotation.MediumTest; import com.android.internal.telephony.FakeSmsContentProvider; import com.android.internal.telephony.InboundSmsHandler; import com.android.internal.telephony.InboundSmsTracker; import com.android.internal.telephony.SmsBroadcastUndelivered; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsStorageMonitor; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.TelephonyTestUtils; import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; import com.android.internal.util.HexDump; import com.android.internal.util.IState; import com.android.internal.util.StateMachine; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.lang.reflect.Method; import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class GsmInboundSmsHandlerTest extends TelephonyTest { @Mock private SmsStorageMonitor mSmsStorageMonitor; @Mock private android.telephony.SmsMessage mSmsMessage; @Mock private SmsMessage mGsmSmsMessage; @Mock private SmsHeader mSmsHeader; @Mock private InboundSmsTracker mInboundSmsTrackerPart1; @Mock private InboundSmsTracker mInboundSmsTrackerPart2; @Mock private CdmaInboundSmsHandler mCdmaInboundSmsHandler; private GsmInboundSmsHandler mGsmInboundSmsHandler; private GsmInboundSmsHandlerTestHandler mGsmInboundSmsHandlerTestHandler; private FakeSmsContentProvider mContentProvider; private static final String RAW_TABLE_NAME = "raw"; private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, RAW_TABLE_NAME); private ContentValues mInboundSmsTrackerCV = new ContentValues(); // For multi-part SMS private ContentValues mInboundSmsTrackerCVPart1; private ContentValues mInboundSmsTrackerCVPart2; private String mMessageBody = "This is the message body of a single-part message"; private String mMessageBodyPart1 = "This is the first part of a multi-part message"; private String mMessageBodyPart2 = "This is the second part of a multi-part message"; byte[] mSmsPdu = new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF}; private class GsmInboundSmsHandlerTestHandler extends HandlerThread { private GsmInboundSmsHandlerTestHandler(String name) { super(name); } @Override public void onLooperPrepared() { mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(mContext, mSmsStorageMonitor, mPhone); setReady(true); } } private IState getCurrentState() { try { Method method = StateMachine.class.getDeclaredMethod("getCurrentState"); method.setAccessible(true); return (IState) method.invoke(mGsmInboundSmsHandler); } catch (Exception e) { fail(e.toString()); return null; } } @Before public void setUp() throws Exception { super.setUp("GsmInboundSmsHandlerTest"); doReturn(true).when(mTelephonyManager).getSmsReceiveCapableForPhone(anyInt(), anyBoolean()); doReturn(true).when(mSmsStorageMonitor).isStorageAvailable(); UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE); doReturn(true).when(userManager).isUserUnlocked(); try { doReturn(new int[]{UserHandle.USER_SYSTEM}).when(mIActivityManager).getRunningUserIds(); } catch (RemoteException re) { fail("Unexpected RemoteException: " + re.getStackTrace()); } mSmsMessage.mWrappedSmsMessage = mGsmSmsMessage; mInboundSmsTrackerCV.put("destination_port", 1 << 16); mInboundSmsTrackerCV.put("pdu", HexDump.toHexString(mSmsPdu)); mInboundSmsTrackerCV.put("address", "1234567890"); mInboundSmsTrackerCV.put("reference_number", 1); mInboundSmsTrackerCV.put("sequence", 1); mInboundSmsTrackerCV.put("count", 1); mInboundSmsTrackerCV.put("date", System.currentTimeMillis()); mInboundSmsTrackerCV.put("message_body", mMessageBody); doReturn(1).when(mInboundSmsTracker).getMessageCount(); doReturn(1).when(mInboundSmsTracker).getReferenceNumber(); doReturn("1234567890").when(mInboundSmsTracker).getAddress(); doReturn(1).when(mInboundSmsTracker).getSequenceNumber(); doReturn(1).when(mInboundSmsTracker).getIndexOffset(); doReturn(-1).when(mInboundSmsTracker).getDestPort(); doReturn(mMessageBody).when(mInboundSmsTracker).getMessageBody(); doReturn(mSmsPdu).when(mInboundSmsTracker).getPdu(); doReturn(mInboundSmsTrackerCV.get("date")).when(mInboundSmsTracker).getTimestamp(); doReturn(mInboundSmsTrackerCV).when(mInboundSmsTracker).getContentValues(); mContentProvider = new FakeSmsContentProvider(); ((MockContentResolver)mContext.getContentResolver()).addProvider( Telephony.Sms.CONTENT_URI.getAuthority(), mContentProvider); mGsmInboundSmsHandlerTestHandler = new GsmInboundSmsHandlerTestHandler(TAG); mGsmInboundSmsHandlerTestHandler.start(); waitUntilReady(); } @After public void tearDown() throws Exception { // wait for wakelock to be released; timeout at 10s int i = 0; while (mGsmInboundSmsHandler.getWakeLock().isHeld() && i < 100) { waitForMs(100); i++; } assertFalse(mGsmInboundSmsHandler.getWakeLock().isHeld()); mGsmInboundSmsHandler = null; mContentProvider.shutdown(); mGsmInboundSmsHandlerTestHandler.quitSafely(); super.tearDown(); } private void transitionFromStartupToIdle() { // verify initially in StartupState assertEquals("StartupState", getCurrentState().getName()); // trigger transition to IdleState mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS); waitForMs(50); assertEquals("IdleState", getCurrentState().getName()); } private void verifySmsIntentBroadcasts(int numPastBroadcasts) { ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(1 + numPastBroadcasts)).sendBroadcast( intentArgumentCaptor.capture()); assertEquals(Telephony.Sms.Intents.SMS_DELIVER_ACTION, intentArgumentCaptor.getAllValues().get(numPastBroadcasts).getAction()); assertEquals("WaitingState", getCurrentState().getName()); mContextFixture.sendBroadcastToOrderedBroadcastReceivers(); intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(2 + numPastBroadcasts)).sendBroadcast( intentArgumentCaptor.capture()); assertEquals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION, intentArgumentCaptor.getAllValues().get(numPastBroadcasts + 1).getAction()); assertEquals("WaitingState", getCurrentState().getName()); mContextFixture.sendBroadcastToOrderedBroadcastReceivers(); waitForMs(50); assertEquals("IdleState", getCurrentState().getName()); } @Test public void testAquireWakeLock() { int oldWakeLockCount = 0; int newWakeLockCount = 0; try { oldWakeLockCount = mGsmInboundSmsHandler.getWakelockCount(); callMethodWithNoArgs("acquireWakeLock"); newWakeLockCount = mGsmInboundSmsHandler.getWakelockCount(); assertEquals(1, newWakeLockCount - oldWakeLockCount); callMethodWithNoArgs("decrementWakeLock"); newWakeLockCount = mGsmInboundSmsHandler.getWakelockCount(); assertEquals(0, newWakeLockCount - oldWakeLockCount); oldWakeLockCount = mGsmInboundSmsHandler.getWakelockCount(); callMethodWithNoArgs("acquireWakeLock"); newWakeLockCount = mGsmInboundSmsHandler.getWakelockCount(); assertEquals(1, newWakeLockCount - oldWakeLockCount); callMethodWithNoArgs("releaseWakeLock"); newWakeLockCount = mGsmInboundSmsHandler.getWakelockCount(); assertEquals(0, newWakeLockCount); } catch(Exception e) { fail(e.toString()); } } private void callMethodWithNoArgs(String method) throws Exception { Method m; m = mGsmInboundSmsHandler.getClass().getSuperclass().getDeclaredMethod(method); m.setAccessible(true); m.invoke(mGsmInboundSmsHandler); } @Test @MediumTest public void testNewSms() { transitionFromStartupToIdle(); // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); verifySmsIntentBroadcasts(0); // send same SMS again, verify no broadcasts are sent mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); verify(mContext, times(2)).sendBroadcast(any(Intent.class)); assertEquals("IdleState", getCurrentState().getName()); } @Test @MediumTest public void testNewSmsFromBlockedNumber_noBroadcastsSent() { String blockedNumber = "123456789"; doReturn(blockedNumber).when(mInboundSmsTracker).getAddress(); mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber); transitionFromStartupToIdle(); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); verify(mContext, never()).sendBroadcast(any(Intent.class)); assertEquals("IdleState", getCurrentState().getName()); } private void verifyDataSmsIntentBroadcasts(int numPastBroadcasts) { ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(1 + numPastBroadcasts)).sendBroadcast( intentArgumentCaptor.capture()); assertEquals(Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION, intentArgumentCaptor.getAllValues().get(numPastBroadcasts).getAction()); assertEquals("WaitingState", getCurrentState().getName()); mContextFixture.sendBroadcastToOrderedBroadcastReceivers(); waitForMs(50); assertEquals("IdleState", getCurrentState().getName()); } @Test @MediumTest public void testBroadcastSms() { transitionFromStartupToIdle(); doReturn(0).when(mInboundSmsTracker).getDestPort(); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, mInboundSmsTracker); waitForMs(100); verifyDataSmsIntentBroadcasts(0); // send same data sms again, and since it's not text sms it should be broadcast again mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, mInboundSmsTracker); waitForMs(100); verifyDataSmsIntentBroadcasts(1); } @Test @MediumTest public void testInjectSms() { transitionFromStartupToIdle(); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); verifySmsIntentBroadcasts(0); // inject same SMS again, verify no broadcasts are sent mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); verify(mContext, times(2)).sendBroadcast(any(Intent.class)); assertEquals("IdleState", getCurrentState().getName()); } private void prepareMultiPartSms() { // Part 1 mInboundSmsTrackerCVPart1 = new ContentValues(); mInboundSmsTrackerCVPart1.put("destination_port", 1 << 16); mInboundSmsTrackerCVPart1.put("pdu", HexDump.toHexString(mSmsPdu)); mInboundSmsTrackerCVPart1.put("address", "1234567890"); mInboundSmsTrackerCVPart1.put("reference_number", 1); mInboundSmsTrackerCVPart1.put("sequence", 1); mInboundSmsTrackerCVPart1.put("count", 2); mInboundSmsTrackerCVPart1.put("date", System.currentTimeMillis()); mInboundSmsTrackerCVPart1.put("message_body", mMessageBodyPart1); doReturn(2).when(mInboundSmsTrackerPart1).getMessageCount(); doReturn(1).when(mInboundSmsTrackerPart1).getReferenceNumber(); doReturn("1234567890").when(mInboundSmsTrackerPart1).getAddress(); doReturn(1).when(mInboundSmsTrackerPart1).getSequenceNumber(); doReturn(1).when(mInboundSmsTrackerPart1).getIndexOffset(); doReturn(-1).when(mInboundSmsTrackerPart1).getDestPort(); doReturn(mMessageBodyPart1).when(mInboundSmsTrackerPart1).getMessageBody(); doReturn(mSmsPdu).when(mInboundSmsTrackerPart1).getPdu(); doReturn(mInboundSmsTrackerCVPart1.get("date")).when(mInboundSmsTrackerPart1). getTimestamp(); doReturn(mInboundSmsTrackerCVPart1).when(mInboundSmsTrackerPart1).getContentValues(); // Part 2 mInboundSmsTrackerCVPart2 = new ContentValues(); mInboundSmsTrackerCVPart2.put("destination_port", 1 << 16); mInboundSmsTrackerCVPart2.put("pdu", HexDump.toHexString(mSmsPdu)); mInboundSmsTrackerCVPart2.put("address", "1234567890"); mInboundSmsTrackerCVPart2.put("reference_number", 1); mInboundSmsTrackerCVPart2.put("sequence", 2); mInboundSmsTrackerCVPart2.put("count", 2); mInboundSmsTrackerCVPart2.put("date", System.currentTimeMillis()); mInboundSmsTrackerCVPart2.put("message_body", mMessageBodyPart2); doReturn(2).when(mInboundSmsTrackerPart2).getMessageCount(); doReturn(1).when(mInboundSmsTrackerPart2).getReferenceNumber(); doReturn("1234567890").when(mInboundSmsTrackerPart2).getAddress(); doReturn(2).when(mInboundSmsTrackerPart2).getSequenceNumber(); doReturn(1).when(mInboundSmsTrackerPart2).getIndexOffset(); doReturn(-1).when(mInboundSmsTrackerPart2).getDestPort(); doReturn(mMessageBodyPart2).when(mInboundSmsTrackerPart2).getMessageBody(); doReturn(mSmsPdu).when(mInboundSmsTrackerPart2).getPdu(); doReturn(mInboundSmsTrackerCVPart2.get("date")).when(mInboundSmsTrackerPart2). getTimestamp(); doReturn(mInboundSmsTrackerCVPart2).when(mInboundSmsTrackerPart2).getContentValues(); } @Postsubmit @Test @MediumTest public void testMultiPartSms() { transitionFromStartupToIdle(); // prepare SMS part 1 and part 2 prepareMultiPartSms(); mSmsHeader.concatRef = new SmsHeader.ConcatRef(); doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader(); doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory) .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); // State machine should go back to idle and wait for second part assertEquals("IdleState", getCurrentState().getName()); doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory) .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); // verify broadcast intents verifySmsIntentBroadcasts(0); // if an additional copy of one of the segments above is received, it should not be kept in // the db and should not be combined with any subsequent messages received from the same // sender // additional copy of part 2 of message doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory) .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); // verify no additional broadcasts sent verify(mContext, times(2)).sendBroadcast(any(Intent.class)); // part 1 of new sms recieved from same sender with same parameters, just different // timestamps, should not be combined with the additional part 2 received above // call prepareMultiPartSms() to update timestamps prepareMultiPartSms(); // part 1 of new sms doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory) .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); // verify no additional broadcasts sent verify(mContext, times(2)).sendBroadcast(any(Intent.class)); assertEquals("IdleState", getCurrentState().getName()); } @Test @MediumTest public void testMultiPartIncompleteSms() { /** * Test scenario: 2 messages are received with same address, ref number, count, and * seqNumber, with count = 2 and seqNumber = 1. We should not try to merge these. */ transitionFromStartupToIdle(); // prepare SMS part 1 and part 2 prepareMultiPartSms(); // change seqNumber in part 2 to 1 mInboundSmsTrackerCVPart2.put("sequence", 1); doReturn(1).when(mInboundSmsTrackerPart2).getSequenceNumber(); mSmsHeader.concatRef = new SmsHeader.ConcatRef(); doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader(); doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory) .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); // State machine should go back to idle and wait for second part assertEquals("IdleState", getCurrentState().getName()); doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory) .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); // verify no broadcasts sent verify(mContext, never()).sendBroadcast(any(Intent.class)); // verify there's only 1 of the segments in the db (other should be discarded as dup) assertEquals(1, mContentProvider.getNumRows()); // State machine should go back to idle assertEquals("IdleState", getCurrentState().getName()); } @Test @MediumTest public void testMultipartSmsFromBlockedNumber_noBroadcastsSent() { mFakeBlockedNumberContentProvider.mBlockedNumbers.add("1234567890"); transitionFromStartupToIdle(); // prepare SMS part 1 and part 2 prepareMultiPartSms(); mSmsHeader.concatRef = new SmsHeader.ConcatRef(); doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader(); doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory) .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); // State machine should go back to idle and wait for second part assertEquals("IdleState", getCurrentState().getName()); doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory) .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString()); mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null, mSmsMessage, null)); waitForMs(100); verify(mContext, never()).sendBroadcast(any(Intent.class)); assertEquals("IdleState", getCurrentState().getName()); } @Test @MediumTest public void testBroadcastUndeliveredUserLocked() throws Exception { replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null); doReturn(0).when(mInboundSmsTracker).getDestPort(); // add a fake entry to db mContentProvider.insert(sRawUri, mInboundSmsTrackerCV); // make it a single-part message doReturn(1).when(mInboundSmsTracker).getMessageCount(); // user locked UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE); doReturn(false).when(userManager).isUserUnlocked(); SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler); // verify that a broadcast receiver is registered for current user (user == null) based on // implementation in ContextFixture verify(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq((UserHandle)null), any(IntentFilter.class), eq((String)null), eq((Handler)null)); waitForMs(100); // verify no broadcasts sent because due to !isUserUnlocked verify(mContext, never()).sendBroadcast(any(Intent.class)); // when user unlocks the device, the message in db should be broadcast doReturn(true).when(userManager).isUserUnlocked(); mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED)); waitForMs(100); verifyDataSmsIntentBroadcasts(1); } @Test @MediumTest public void testBroadcastUndeliveredUserUnlocked() throws Exception { replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null); doReturn(0).when(mInboundSmsTracker).getDestPort(); // add a fake entry to db mContentProvider.insert(sRawUri, mInboundSmsTrackerCV); // make it a single-part message doReturn(1).when(mInboundSmsTracker).getMessageCount(); SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler); waitForMs(100); // user is unlocked; intent should be broadcast right away verifyDataSmsIntentBroadcasts(0); } @Test @MediumTest public void testBroadcastUndeliveredDeleted() throws Exception { replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null); SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler); doReturn(0).when(mInboundSmsTracker).getDestPort(); //add a fake entry to db ContentValues rawSms = new ContentValues(); rawSms.put("deleted", 1); mContentProvider.insert(sRawUri, rawSms); //make it a single-part message doReturn(1).when(mInboundSmsTracker).getMessageCount(); //when user unlocks the device, broadcast should not be sent for new message mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED)); waitForMs(100); verify(mContext, times(1)).sendBroadcast(any(Intent.class)); assertEquals("IdleState", getCurrentState().getName()); } @Postsubmit @Test @MediumTest public void testBroadcastUndeliveredMultiPart() throws Exception { replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null); // prepare SMS part 1 and part 2 prepareMultiPartSms(); //add the 2 SMS parts to db mContentProvider.insert(sRawUri, mInboundSmsTrackerCVPart1); mContentProvider.insert(sRawUri, mInboundSmsTrackerCVPart2); //return InboundSmsTracker objects corresponding to the 2 parts doReturn(mInboundSmsTrackerPart1).doReturn(mInboundSmsTrackerPart2). when(mTelephonyComponentFactory).makeInboundSmsTracker(any(Cursor.class), anyBoolean()); SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler); waitForMs(100); verifySmsIntentBroadcasts(0); } }