blob: ddabe90be0c82dc1879ef96c1d7a956d8d8d104f [file] [log] [blame]
/*
* 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.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.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
@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);
}
}