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