1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telephony.cts;
18 
19 import static androidx.test.InstrumentationRegistry.getContext;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertNull;
25 import static org.junit.Assert.assertTrue;
26 
27 import android.content.Context;
28 import android.content.pm.PackageManager;
29 import android.telephony.SmsManager;
30 import android.telephony.SmsMessage;
31 import android.telephony.SubscriptionManager;
32 import android.telephony.TelephonyManager;
33 
34 import org.junit.Before;
35 import org.junit.Test;
36 
37 public class SmsMessageTest {
38 
39     private TelephonyManager mTelephonyManager;
40     private PackageManager mPackageManager;
41 
42     private static final String DISPLAY_MESSAGE_BODY = "test subject /test body";
43     private static final String DMB = "{ testBody[^~\\] }";
44     private static final String EMAIL_ADD = "foo@example.com";
45     private static final String EMAIL_FROM = "foo@example.com";
46     private static final String MB = DMB;
47     private static final String MESSAGE_BODY1 = "Test";
48     private static final String MESSAGE_BODY2 = "(Subject)Test";
49     private static final String MESSAGE_BODY3 = "\u2122\u00a9\u00aehello";
50     private static final String MESSAGE_BODY4 = " ";
51     private static final String MESSAGE_BODY5 = " ";
52     private static final String OA = "foo@example.com";
53     private static final String OA1 = "+14154255486";
54     private static final String OA2 = "+15122977683";
55     private static final String OA3 = "_@";
56     private static final String OA4 = "\u0394@";
57     // pseudo subject will always be empty
58     private static final String PSEUDO_SUBJECT = "";
59     private static final String SCA1 = "+16466220020";
60     private static final String SCA2 = "+12063130012";
61     private static final String SCA3 = "+14155551212";
62     private static final String SCA4 = "+14155551212";
63     private static final int NOT_CREATE_FROM_SIM = -1;
64     private static final int NOT_CREATE_FROM_ICC = -1;
65     private static final int PROTOCOL_IDENTIFIER = 0;
66     private static final int SMS_NUMBER1 = 1;
67     private static final int SMS_NUMBER2 = 1;
68     private static final int SMS_NUMBER3 = 1;
69     private static final int STATUS = 0;
70     private static final int STATUS_ON_SIM_DEF = -1;
71     private static final int STATUS_ON_ICC_DEF = -1;
72     private static final int TPLAYER_LENGTH_FOR_PDU = 23;
73     private static final long TIMESTAMP_MILLIS = 1149631383000l;
74     private static final int SEPTETS_SKT = 80;
75     private static final int SEPTETS_KT = 90;
76     private static final String LONG_TEXT_WITH_32BIT_CHARS =
77         "Long dkkshsh jdjsusj kbsksbdf jfkhcu hhdiwoqiwyrygrvn?*?*!\";:'/,."
78         + "__?9#9292736&4;\"$+$+((]\\[\\℅©℅™^®°¥°¥=¢£}}£∆~¶~÷|√×."
79         + " ������������������������������⛪⛲ ";
80     private static final String LONG_TEXT_WITH_FLAGS =
81         "��������������������������������������������������������"
82         + "������������������������������������������������������������";
83 
84     @Before
setUp()85     public void setUp() throws Exception {
86         mTelephonyManager =
87             (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
88         mPackageManager = getContext().getPackageManager();
89     }
90 
91     @Test
testCreateFromPdu()92     public void testCreateFromPdu() throws Exception {
93         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
94                 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)) {
95             // TODO: temp workaround, need to adjust test to use CDMA pdus
96             return;
97         }
98 
99         String pdu = "07916164260220F0040B914151245584F600006060605130308A04D4F29C0E";
100         SmsMessage sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu),
101                 SmsMessage.FORMAT_3GPP);
102         assertEquals(SCA1, sms.getServiceCenterAddress());
103         assertEquals(OA1, sms.getOriginatingAddress());
104         assertEquals(MESSAGE_BODY1, sms.getMessageBody());
105         assertEquals(TPLAYER_LENGTH_FOR_PDU, SmsMessage.getTPLayerLengthForPDU(pdu));
106         int[] result = SmsMessage.calculateLength(sms.getMessageBody(), true);
107         assertEquals(6, result.length);
108         assertEquals(SMS_NUMBER1, result[0]);
109         assertEquals(sms.getMessageBody().length(), result[1]);
110         assertRemaining(sms.getMessageBody().length(), result[2], SmsMessage.MAX_USER_DATA_SEPTETS);
111         assertEquals(SmsMessage.ENCODING_7BIT, result[3]);
112         assertEquals(pdu, toHexString(sms.getPdu()));
113 
114         assertEquals(NOT_CREATE_FROM_SIM, sms.getIndexOnSim());
115         assertEquals(NOT_CREATE_FROM_ICC, sms.getIndexOnIcc());
116         assertEquals(PROTOCOL_IDENTIFIER, sms.getProtocolIdentifier());
117         assertFalse(sms.isEmail());
118         assertFalse(sms.isReplyPathPresent());
119         assertFalse(sms.isStatusReportMessage());
120         assertFalse(sms.isCphsMwiMessage());
121         assertEquals(SmsMessage.MessageClass.UNKNOWN, sms.getMessageClass());
122         assertEquals(STATUS, sms.getStatus());
123         assertEquals(STATUS_ON_SIM_DEF, sms.getStatusOnSim());
124         assertEquals(STATUS_ON_ICC_DEF, sms.getStatusOnIcc());
125         assertEquals(TIMESTAMP_MILLIS, sms.getTimestampMillis());
126 
127         // Test create from null Pdu
128         sms = SmsMessage.createFromPdu(null, SmsMessage.FORMAT_3GPP);
129         assertNull(sms);
130 
131         // Test create from long Pdu
132         pdu = "07912160130310F2040B915121927786F300036060924180008A0DA"
133             + "8695DAC2E8FE9296A794E07";
134         sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
135         assertEquals(SCA2, sms.getServiceCenterAddress());
136         assertEquals(OA2, sms.getOriginatingAddress());
137         assertEquals(MESSAGE_BODY2, sms.getMessageBody());
138         CharSequence msgBody = sms.getMessageBody();
139         result = SmsMessage.calculateLength(msgBody, false);
140         assertEquals(6, result.length);
141         assertEquals(SMS_NUMBER2, result[0]);
142         assertEquals(sms.getMessageBody().length(), result[1]);
143         assertRemaining(sms.getMessageBody().length(), result[2], SmsMessage.MAX_USER_DATA_SEPTETS);
144         assertEquals(SmsMessage.ENCODING_7BIT, result[3]);
145 
146         // Test createFromPdu Ucs to Sms
147         pdu = "07912160130300F4040B914151245584"
148             + "F600087010807121352B10212200A900AE00680065006C006C006F";
149         sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
150         assertEquals(MESSAGE_BODY3, sms.getMessageBody());
151         result = SmsMessage.calculateLength(sms.getMessageBody(), true);
152         assertEquals(6, result.length);
153         assertEquals(SMS_NUMBER3, result[0]);
154         assertEquals(sms.getMessageBody().length(), result[1]);
155         assertRemaining(sms.getMessageBody().length(), result[2], SmsMessage.MAX_USER_DATA_SEPTETS);
156         assertEquals(SmsMessage.ENCODING_7BIT, result[3]);
157     }
158 
assertRemaining(int messageLength, int remaining, int maxChars)159     private void assertRemaining(int messageLength, int remaining, int maxChars) {
160         if (TelephonyUtils.isSkt(mTelephonyManager)) {
161             assertTrue(checkRemaining(SEPTETS_SKT, messageLength, remaining)
162                     || checkRemaining(maxChars, messageLength, remaining));
163         } else if (TelephonyUtils.isKt(mTelephonyManager)) {
164             assertTrue(checkRemaining(SEPTETS_KT, messageLength, remaining)
165                     || checkRemaining(maxChars, messageLength, remaining));
166         } else {
167             assertTrue(checkRemaining(maxChars, messageLength, remaining));
168         }
169     }
170 
checkRemaining(int total, int messageLength, int remaining)171     private boolean checkRemaining(int total, int messageLength, int remaining) {
172         return total - messageLength == remaining;
173     }
174 
175     @Test
testCPHSVoiceMail()176     public void testCPHSVoiceMail() throws Exception {
177         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
178                 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)) {
179             // TODO: temp workaround, need to adjust test to use CDMA pdus
180             return;
181         }
182 
183         // "set MWI flag"
184         String pdu = "07912160130310F20404D0110041006060627171118A0120";
185         SmsMessage sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu),
186                 SmsMessage.FORMAT_3GPP);
187         assertTrue(sms.isReplace());
188         assertEquals(OA3, sms.getOriginatingAddress());
189         assertEquals(MESSAGE_BODY4, sms.getMessageBody());
190         assertTrue(sms.isMWISetMessage());
191 
192         // "clear mwi flag"
193         pdu = "07912160130310F20404D0100041006021924193352B0120";
194         sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
195         assertTrue(sms.isMWIClearMessage());
196 
197         // "clear MWI flag"
198         pdu = "07912160130310F20404D0100041006060627161058A0120";
199         sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
200         assertTrue(sms.isReplace());
201         assertEquals(OA4, sms.getOriginatingAddress());
202         assertEquals(MESSAGE_BODY5, sms.getMessageBody());
203         assertTrue(sms.isMWIClearMessage());
204 
205         // "set MWI flag"
206         pdu = "07912180958750F84401800500C87020026195702B06040102000200";
207         sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
208         assertTrue(sms.isMWISetMessage());
209         assertTrue(sms.isMwiDontStore());
210 
211         // "clear mwi flag"
212         pdu = "07912180958750F84401800500C07020027160112B06040102000000";
213         sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
214 
215         assertTrue(sms.isMWIClearMessage());
216         assertTrue(sms.isMwiDontStore());
217     }
218 
219     @Test
testGetUserData()220     public void testGetUserData() throws Exception {
221         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
222                 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)) {
223             // TODO: temp workaround, need to adjust test to use CDMA pdus
224             return;
225         }
226 
227         String pdu = "07914140279510F6440A8111110301003BF56080207130138A8C0B05040B8423F"
228             + "000032A02010106276170706C69636174696F6E2F766E642E7761702E6D6D732D"
229             + "6D65737361676500AF848D0185B4848C8298524E453955304A6D7135514141426"
230             + "66C414141414D7741414236514141414141008D908918802B3135313232393737"
231             + "3638332F545950453D504C4D4E008A808E022B918805810306977F83687474703"
232             + "A2F2F36";
233         SmsMessage sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu),
234                 SmsMessage.FORMAT_3GPP);
235         byte[] userData = sms.getUserData();
236         assertNotNull(userData);
237     }
238 
239     @Test
testGetSubmitPdu()240     public void testGetSubmitPdu() throws Exception {
241         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
242             return;
243         }
244 
245         SmsMessage.SubmitPdu smsPdu;
246         String scAddress = null, destinationAddress = null;
247         String message = null;
248         boolean statusReportRequested = false;
249 
250         // Null message, null destination
251         smsPdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, message,
252                 statusReportRequested);
253         assertNull(smsPdu);
254 
255         message = "This is a test message";
256 
257         // Non-null message, null destination
258         smsPdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, message,
259                 statusReportRequested);
260         assertNull(smsPdu);
261 
262         if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
263             // TODO: temp workaround, OCTET encoding for EMS not properly supported
264             return;
265         }
266 
267         scAddress = "1650253000";
268         destinationAddress = "18004664411";
269         message = "This is a test message";
270         statusReportRequested = false;
271         smsPdu = SmsMessage.getSubmitPdu(
272                 scAddress, destinationAddress, message, statusReportRequested);
273         assertNotNull(smsPdu);
274 
275         smsPdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, (short)80,
276                 message.getBytes(), statusReportRequested);
277         assertNotNull(smsPdu);
278     }
279 
280     @Test
testEmailGateway()281     public void testEmailGateway() throws Exception {
282         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
283                 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)) {
284             // TODO: temp workaround, need to adjust test to use CDMA pdus
285             return;
286         }
287 
288         String pdu = "07914151551512f204038105f300007011103164638a28e6f71b50c687db" +
289                          "7076d9357eb7412f7a794e07cdeb6275794c07bde8e5391d247e93f3";
290 
291         SmsMessage sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu),
292                 SmsMessage.FORMAT_3GPP);
293         assertEquals(SCA4, sms.getServiceCenterAddress());
294         assertTrue(sms.isEmail());
295         assertEquals(EMAIL_ADD, sms.getEmailFrom());
296         assertEquals(EMAIL_ADD, sms.getDisplayOriginatingAddress());
297         assertEquals(PSEUDO_SUBJECT, sms.getPseudoSubject());
298 
299         assertEquals(DISPLAY_MESSAGE_BODY, sms.getDisplayMessageBody());
300         assertEquals(DISPLAY_MESSAGE_BODY, sms.getEmailBody());
301 
302         pdu = "07914151551512f204038105f400007011103105458a29e6f71b50c687db" +
303                         "7076d9357eb741af0d0a442fcfe9c23739bfe16d289bdee6b5f1813629";
304         sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu), SmsMessage.FORMAT_3GPP);
305         assertEquals(SCA3, sms.getServiceCenterAddress());
306         assertTrue(sms.isEmail());
307         assertEquals(OA, sms.getDisplayOriginatingAddress());
308         assertEquals(EMAIL_FROM, sms.getEmailFrom());
309         assertEquals(DMB, sms.getDisplayMessageBody());
310         assertEquals(MB, sms.getEmailBody());
311     }
312 
313     @Test
testCalculateLength()314     public void testCalculateLength() throws Exception {
315         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
316             return;
317         }
318 
319         int[] result = SmsMessage.calculateLength(LONG_TEXT_WITH_32BIT_CHARS, false);
320         assertEquals(6, result.length);
321         assertEquals(3, result[0]);
322         assertEquals(LONG_TEXT_WITH_32BIT_CHARS.length(), result[1]);
323         // 3 parts, each with (SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER / 2) 16-bit
324         // characters. We need to subtract one because a 32-bit character crosses the
325         // boundary of 2 parts.
326         int preMaxChars = 3 * SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER / 2 - 1;
327         // If EMS is not supported, break down EMS into single segment SMS
328         // and add page info "x/y".
329         // In the case of UCS2 encoding type, we need 8 bytes for this
330         // but we only have 6 bytes from UDH, so truncate the limit for
331         // each segment by 2 bytes (1 char). This log sms has three segments,
332         // so truncate the limit by 3 char in total
333         int maxChars = SmsMessage.hasEmsSupport() ? preMaxChars : preMaxChars - 3;
334         assertRemaining(LONG_TEXT_WITH_32BIT_CHARS.length(), result[2],
335                 maxChars);
336         assertEquals(SmsMessage.ENCODING_16BIT, result[3]);
337     }
338 
339     @Test
testCalculateLengthFlags()340     public void testCalculateLengthFlags() throws Exception {
341         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
342             return;
343         }
344         int[] result = SmsMessage.calculateLength(LONG_TEXT_WITH_FLAGS, false);
345         assertEquals(2, result[0]);
346     }
347 
348     @Test
testGetSmsPdu()349     public void testGetSmsPdu() {
350         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
351             return;
352         }
353 
354         SmsMessage.SubmitPdu smsPdu;
355         String scAddress = null;
356         String destinationAddress = null;
357         String message = null;
358 
359         // Null message, null destination
360         smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
361                 SmsManager.STATUS_ON_ICC_READ,
362                 scAddress, destinationAddress, message, System.currentTimeMillis());
363         assertNull(smsPdu);
364 
365         message = "This is a test message";
366 
367         // Non-null message, null destination
368         smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
369                 SmsManager.STATUS_ON_ICC_READ,
370                 scAddress, destinationAddress, message, System.currentTimeMillis());
371         assertNull(smsPdu);
372 
373         if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
374             // TODO: temp workaround, OCTET encoding for EMS not properly supported
375             return;
376         }
377 
378         scAddress = "1650253000";
379         destinationAddress = "18004664411";
380         message = "This is a test message";
381         smsPdu = SmsMessage.getSmsPdu(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
382                 SmsManager.STATUS_ON_ICC_READ,
383                 scAddress, destinationAddress, message, System.currentTimeMillis());
384         assertNotNull(smsPdu);
385     }
386 
387     private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
388             'A', 'B', 'C', 'D', 'E', 'F' };
389 
toHexString(byte[] array)390     public static String toHexString(byte[] array) {
391         int length = array.length;
392         char[] buf = new char[length * 2];
393 
394         int bufIndex = 0;
395         for (int i = 0 ; i < length; i++)
396         {
397             byte b = array[i];
398             buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
399             buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
400         }
401 
402         return new String(buf);
403     }
404 
toByte(char c)405     private static int toByte(char c) {
406         if (c >= '0' && c <= '9') return (c - '0');
407         if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
408         if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
409 
410         throw new RuntimeException ("Invalid hex char '" + c + "'");
411     }
412 
hexStringToByteArray(String hexString)413     private static byte[] hexStringToByteArray(String hexString) {
414         int length = hexString.length();
415         byte[] buffer = new byte[length / 2];
416 
417         for (int i = 0 ; i < length ; i += 2) {
418             buffer[i / 2] =
419                 (byte)((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i+1)));
420         }
421 
422         return buffer;
423     }
424 }
425