• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import static androidx.test.InstrumentationRegistry.getInstrumentation;
21 
22 import static com.android.compatibility.common.util.BlockedNumberUtil.deleteBlockedNumber;
23 import static com.android.compatibility.common.util.BlockedNumberUtil.insertBlockedNumber;
24 
25 import static org.hamcrest.Matchers.anyOf;
26 import static org.hamcrest.Matchers.emptyString;
27 import static org.hamcrest.Matchers.equalTo;
28 import static org.hamcrest.Matchers.greaterThan;
29 import static org.hamcrest.Matchers.startsWith;
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertFalse;
32 import static org.junit.Assert.assertNotEquals;
33 import static org.junit.Assert.assertNotNull;
34 import static org.junit.Assert.assertThat;
35 import static org.junit.Assert.assertTrue;
36 import static org.junit.Assert.fail;
37 import static org.junit.Assume.assumeTrue;
38 
39 import android.app.AppOpsManager;
40 import android.app.PendingIntent;
41 import android.app.UiAutomation;
42 import android.app.role.RoleManager;
43 import android.content.BroadcastReceiver;
44 import android.content.ComponentName;
45 import android.content.ContentResolver;
46 import android.content.ContentValues;
47 import android.content.Context;
48 import android.content.Intent;
49 import android.content.IntentFilter;
50 import android.content.pm.PackageManager;
51 import android.net.Uri;
52 import android.os.AsyncTask;
53 import android.os.Bundle;
54 import android.os.ParcelFileDescriptor;
55 import android.os.RemoteCallback;
56 import android.os.SystemClock;
57 import android.provider.Telephony;
58 import android.telephony.SmsCbMessage;
59 import android.telephony.SmsManager;
60 import android.telephony.SmsMessage;
61 import android.telephony.TelephonyManager;
62 import android.telephony.cdma.CdmaSmsCbProgramData;
63 import android.text.TextUtils;
64 import android.util.Log;
65 
66 import androidx.test.InstrumentationRegistry;
67 
68 import org.junit.After;
69 import org.junit.Before;
70 import org.junit.Test;
71 
72 import java.io.BufferedReader;
73 import java.io.FileInputStream;
74 import java.io.IOException;
75 import java.io.InputStream;
76 import java.io.InputStreamReader;
77 import java.nio.charset.StandardCharsets;
78 import java.util.ArrayList;
79 import java.util.Date;
80 import java.util.List;
81 import java.util.concurrent.Callable;
82 import java.util.concurrent.CompletableFuture;
83 import java.util.concurrent.TimeUnit;
84 
85 /**
86  * Tests for {@link android.telephony.SmsManager}.
87  *
88  * Structured so tests can be reused to test {@link android.telephony.gsm.SmsManager}
89  */
90 public class SmsManagerTest {
91 
92     private static final String TAG = "SmsManagerTest";
93     private static final String LONG_TEXT =
94         "This is a very long text. This text should be broken into three " +
95         "separate messages.This is a very long text. This text should be broken into " +
96         "three separate messages.This is a very long text. This text should be broken " +
97         "into three separate messages.This is a very long text. This text should be " +
98         "broken into three separate messages.";;
99     private static final String LONG_TEXT_WITH_32BIT_CHARS =
100         "Long dkkshsh jdjsusj kbsksbdf jfkhcu hhdiwoqiwyrygrvn?*?*!\";:'/,."
101         + "__?9#9292736&4;\"$+$+((]\\[\\℅©℅™^®°¥°¥=¢£}}£∆~¶~÷|√×."
102         + " ������������������������������⛪⛲ ";
103 
104     private static final String SMS_SEND_ACTION = "CTS_SMS_SEND_ACTION";
105     private static final String SMS_DELIVERY_ACTION = "CTS_SMS_DELIVERY_ACTION";
106     private static final String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";
107     public static final String SMS_DELIVER_DEFAULT_APP_ACTION = "CTS_SMS_DELIVERY_ACTION_DEFAULT_APP";
108     public static final String LEGACY_SMS_APP = "android.telephony.cts.sms23";
109     public static final String MODERN_SMS_APP = "android.telephony.cts.sms";
110     private static final String SMS_RETRIEVER_APP = "android.telephony.cts.smsretriever";
111     private static final String SMS_RETRIEVER_ACTION = "CTS_SMS_RETRIEVER_ACTION";
112     private static final String FINANCIAL_SMS_APP = "android.telephony.cts.financialsms";
113 
114     private TelephonyManager mTelephonyManager;
115     private String mDestAddr;
116     private String mText;
117     private SmsBroadcastReceiver mSendReceiver;
118     private SmsBroadcastReceiver mDeliveryReceiver;
119     private SmsBroadcastReceiver mDataSmsReceiver;
120     private SmsBroadcastReceiver mSmsDeliverReceiver;
121     private SmsBroadcastReceiver mSmsReceivedReceiver;
122     private SmsBroadcastReceiver mSmsRetrieverReceiver;
123     private PendingIntent mSentIntent;
124     private PendingIntent mDeliveredIntent;
125     private Intent mSendIntent;
126     private Intent mDeliveryIntent;
127     private Context mContext;
128     private Uri mBlockedNumberUri;
129     private boolean mTestAppSetAsDefaultSmsApp;
130     private boolean mDeliveryReportSupported;
131     private static boolean mReceivedDataSms;
132     private static String mReceivedText;
133     private static boolean sHasShellPermissionIdentity = false;
134     private static long sMessageId = 0L;
135 
136     private static final int TIME_OUT = 1000 * 60 * 10;
137     private static final int NO_CALLS_TIMEOUT_MILLIS = 1000; // 1 second
138 
139     @Before
setUp()140     public void setUp() throws Exception {
141         assumeTrue(getContext().getPackageManager().hasSystemFeature(
142                 PackageManager.FEATURE_TELEPHONY_MESSAGING));
143 
144         mContext = getContext();
145         mTelephonyManager =
146             (TelephonyManager) getContext().getSystemService(
147                     Context.TELEPHONY_SERVICE);
148         mDestAddr = mTelephonyManager.getLine1Number();
149         mText = "This is a test message";
150 
151         // exclude the networks that don't support SMS delivery report
152         String mccmnc = mTelephonyManager.getSimOperator();
153         mDeliveryReportSupported = !(CarrierCapability.NO_DELIVERY_REPORTS.contains(mccmnc));
154 
155         // register receivers
156         mSendIntent = new Intent(SMS_SEND_ACTION);
157         mDeliveryIntent = new Intent(SMS_DELIVERY_ACTION);
158 
159         IntentFilter sendIntentFilter = new IntentFilter(SMS_SEND_ACTION);
160         IntentFilter deliveryIntentFilter = new IntentFilter(SMS_DELIVERY_ACTION);
161         IntentFilter dataSmsReceivedIntentFilter = new IntentFilter(DATA_SMS_RECEIVED_ACTION);
162         IntentFilter smsDeliverIntentFilter = new IntentFilter(SMS_DELIVER_DEFAULT_APP_ACTION);
163         IntentFilter smsReceivedIntentFilter =
164                 new IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION);
165         IntentFilter smsRetrieverIntentFilter = new IntentFilter(SMS_RETRIEVER_ACTION);
166         dataSmsReceivedIntentFilter.addDataScheme("sms");
167         dataSmsReceivedIntentFilter.addDataAuthority("localhost", "19989");
168 
169         mSendReceiver = new SmsBroadcastReceiver(SMS_SEND_ACTION);
170         mDeliveryReceiver = new SmsBroadcastReceiver(SMS_DELIVERY_ACTION);
171         mDataSmsReceiver = new SmsBroadcastReceiver(DATA_SMS_RECEIVED_ACTION);
172         mSmsDeliverReceiver = new SmsBroadcastReceiver(SMS_DELIVER_DEFAULT_APP_ACTION);
173         mSmsReceivedReceiver = new SmsBroadcastReceiver(Telephony.Sms.Intents.SMS_RECEIVED_ACTION);
174         mSmsRetrieverReceiver = new SmsBroadcastReceiver(SMS_RETRIEVER_ACTION);
175 
176         mContext.registerReceiver(mSendReceiver, sendIntentFilter);
177         mContext.registerReceiver(mDeliveryReceiver, deliveryIntentFilter);
178         mContext.registerReceiver(mDataSmsReceiver, dataSmsReceivedIntentFilter);
179         mContext.registerReceiver(mSmsDeliverReceiver, smsDeliverIntentFilter);
180         mContext.registerReceiver(mSmsReceivedReceiver, smsReceivedIntentFilter);
181         mContext.registerReceiver(mSmsRetrieverReceiver, smsRetrieverIntentFilter);
182     }
183 
184     @After
tearDown()185     public void tearDown() throws Exception {
186         if (mBlockedNumberUri != null) {
187             unblockNumber(mBlockedNumberUri);
188             mBlockedNumberUri = null;
189         }
190         if (mTestAppSetAsDefaultSmsApp) {
191             setDefaultSmsApp(false);
192         }
193 
194         // unregister receivers
195         if (mSendReceiver != null) {
196             mContext.unregisterReceiver(mSendReceiver);
197         }
198         if (mDeliveryReceiver != null) {
199             mContext.unregisterReceiver(mDeliveryReceiver);
200         }
201         if (mDataSmsReceiver != null) {
202             mContext.unregisterReceiver(mDataSmsReceiver);
203         }
204         if (mSmsDeliverReceiver != null) {
205             mContext.unregisterReceiver(mSmsDeliverReceiver);
206         }
207         if (mSmsReceivedReceiver != null) {
208             mContext.unregisterReceiver(mSmsReceivedReceiver);
209         }
210         if (mSmsRetrieverReceiver != null) {
211             mContext.unregisterReceiver(mSmsRetrieverReceiver);
212         }
213     }
214 
215     @Test
testDivideMessage()216     public void testDivideMessage() {
217         ArrayList<String> dividedMessages = divideMessage(LONG_TEXT);
218         assertNotNull(dividedMessages);
219         if (TelephonyUtils.isSkt(mTelephonyManager)) {
220             assertTrue(isComplete(dividedMessages, 5, LONG_TEXT)
221                     || isComplete(dividedMessages, 3, LONG_TEXT));
222         } else if (TelephonyUtils.isKt(mTelephonyManager)) {
223             assertTrue(isComplete(dividedMessages, 4, LONG_TEXT)
224                     || isComplete(dividedMessages, 3, LONG_TEXT));
225         } else {
226             assertTrue(isComplete(dividedMessages, 3, LONG_TEXT));
227         }
228     }
229 
230     @Test
testDivideUnicodeMessage()231     public void testDivideUnicodeMessage() {
232         ArrayList<String> dividedMessages = divideMessage(LONG_TEXT_WITH_32BIT_CHARS);
233         assertNotNull(dividedMessages);
234         assertTrue(isComplete(dividedMessages, 3, LONG_TEXT_WITH_32BIT_CHARS));
235         for (String messagePiece : dividedMessages) {
236             assertFalse(Character.isHighSurrogate(
237                     messagePiece.charAt(messagePiece.length() - 1)));
238         }
239     }
240 
isComplete(List<String> dividedMessages, int numParts, String longText)241     private boolean isComplete(List<String> dividedMessages, int numParts, String longText) {
242         if (dividedMessages.size() != numParts) {
243             return false;
244         }
245 
246         String actualMessage = "";
247         for (int i = 0; i < numParts; i++) {
248             actualMessage += dividedMessages.get(i);
249         }
250         return longText.equals(actualMessage);
251     }
252 
253     @Test
testSmsRetriever()254     public void testSmsRetriever() throws Exception {
255         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
256                 TextUtils.isEmpty(mDestAddr));
257 
258         String mccmnc = mTelephonyManager.getSimOperator();
259         init();
260 
261         CompletableFuture<Bundle> callbackResult = new CompletableFuture<>();
262 
263         mContext.startActivity(new Intent()
264                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
265                 .setComponent(new ComponentName(
266                         SMS_RETRIEVER_APP, SMS_RETRIEVER_APP + ".MainActivity"))
267                 .putExtra("callback", new RemoteCallback(callbackResult::complete)));
268 
269 
270         Bundle bundle = callbackResult.get(200, TimeUnit.SECONDS);
271         String token = bundle.getString("token");
272         assertThat(bundle.getString("class"), startsWith(SMS_RETRIEVER_APP));
273         assertNotNull(token);
274 
275         String composedText = "testprefix1" + mText + token;
276         sendTextMessage(mDestAddr, composedText, null, null);
277 
278         assertTrue("[RERUN] SMS retriever message not received. Check signal.",
279                 mSmsRetrieverReceiver.waitForCalls(1, TIME_OUT));
280     }
281 
sendAndReceiveSms(boolean addMessageId)282     private void sendAndReceiveSms(boolean addMessageId) throws Exception {
283         // send single text sms
284         init();
285         if (addMessageId) {
286             long fakeMessageId = 19812L;
287             sendTextMessageWithMessageId(mDestAddr,
288                     String.valueOf(SystemClock.elapsedRealtimeNanos()), mSentIntent,
289                     mDeliveredIntent, fakeMessageId);
290         } else {
291             sendTextMessage(mDestAddr, String.valueOf(SystemClock.elapsedRealtimeNanos()),
292                     mSentIntent, mDeliveredIntent);
293         }
294         assertTrue("[RERUN] Could not send SMS. Check signal.",
295                 mSendReceiver.waitForCalls(1, TIME_OUT));
296         if (mDeliveryReportSupported) {
297             assertTrue("[RERUN] SMS message delivery notification not received. Check signal.",
298                     mDeliveryReceiver.waitForCalls(1, TIME_OUT));
299         }
300         // non-default app should receive only SMS_RECEIVED_ACTION
301         assertTrue(mSmsReceivedReceiver.waitForCalls(1, TIME_OUT));
302         // Received SMS should always contain a generated messageId
303         assertNotEquals(0L, sMessageId);
304         assertTrue(mSmsDeliverReceiver.waitForCalls(0, 0));
305     }
306 
sendAndReceiveMultipartSms(String mccmnc, boolean addMessageId)307     private void sendAndReceiveMultipartSms(String mccmnc, boolean addMessageId) throws Exception {
308         sMessageId = 0L;
309         int numPartsSent = sendMultipartTextMessageIfSupported(mccmnc, addMessageId);
310         if (numPartsSent > 0) {
311             assertTrue("[RERUN] Could not send multi part SMS. Check signal.",
312                     mSendReceiver.waitForCalls(numPartsSent, TIME_OUT));
313             if (mDeliveryReportSupported) {
314                 assertTrue("[RERUN] Multi part SMS message delivery notification not received. "
315                         + "Check signal.", mDeliveryReceiver.waitForCalls(numPartsSent, TIME_OUT));
316             }
317             // non-default app should receive only SMS_RECEIVED_ACTION
318             assertTrue(mSmsReceivedReceiver.waitForCalls(1, TIME_OUT));
319             assertTrue(mSmsDeliverReceiver.waitForCalls(0, 0));
320             // Received SMS should contain a generated messageId
321             assertNotEquals(0L, sMessageId);
322         } else {
323             // This GSM network doesn't support Multipart SMS message.
324             // Skip the test.
325         }
326     }
327 
sendDataSms(String mccmnc)328     private void sendDataSms(String mccmnc) throws Exception {
329         if (sendDataMessageIfSupported(mccmnc)) {
330             assertTrue("[RERUN] Could not send data SMS. Check signal.",
331                     mSendReceiver.waitForCalls(1, TIME_OUT));
332             if (mDeliveryReportSupported) {
333                 assertTrue("[RERUN] Data SMS message delivery notification not received. " +
334                         "Check signal.", mDeliveryReceiver.waitForCalls(1, TIME_OUT));
335             }
336             mDataSmsReceiver.waitForCalls(1, TIME_OUT);
337             assertTrue("[RERUN] Data SMS message not received. Check signal.", mReceivedDataSms);
338             assertEquals(mReceivedText, mText);
339         } else {
340             // This GSM network doesn't support Data(binary) SMS message.
341             // Skip the test.
342         }
343     }
344 
345     @Test(timeout = 10 * 60 * 1000)
testSendAndReceiveMessages()346     public void testSendAndReceiveMessages() throws Exception {
347         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
348                 TextUtils.isEmpty(mDestAddr));
349 
350         String mccmnc = mTelephonyManager.getSimOperator();
351 
352         // send/receive single text sms with and without messageId
353         sendAndReceiveSms(/* addMessageId= */ true);
354         sendAndReceiveSms(/* addMessageId= */ false);
355 
356         // due to permission restrictions, currently there is no way to make this test app the
357         // default SMS app
358 
359         if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
360             // TODO: temp workaround, OCTET encoding for EMS not properly supported
361             return;
362         }
363 
364         // send/receive data sms
365         sendDataSms(mccmnc);
366 
367         // send/receive multi part text sms with and without messageId
368         sendAndReceiveMultipartSms(mccmnc, /* addMessageId= */ true);
369         sendAndReceiveMultipartSms(mccmnc, /* addMessageId= */ false);
370     }
371 
372     @Test
testSmsBlocking()373     public void testSmsBlocking() throws Exception {
374         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
375                 TextUtils.isEmpty(mDestAddr));
376 
377         // disable suppressing blocking.
378         TelephonyUtils.endBlockSuppression(getInstrumentation());
379 
380         String mccmnc = mTelephonyManager.getSimOperator();
381         // Setting default SMS App is needed to be able to block numbers.
382         setDefaultSmsApp(true);
383         blockNumber(mDestAddr);
384 
385         // single-part SMS blocking
386         init();
387         sendTextMessage(mDestAddr, String.valueOf(SystemClock.elapsedRealtimeNanos()),
388                 mSentIntent, mDeliveredIntent);
389         assertTrue("[RERUN] Could not send SMS. Check signal.",
390                 mSendReceiver.waitForCalls(1, TIME_OUT));
391         assertTrue("Expected no messages to be received due to number blocking.",
392                 mSmsReceivedReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
393         assertTrue("Expected no messages to be delivered due to number blocking.",
394                 mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
395 
396         // send data sms
397         if (!sendDataMessageIfSupported(mccmnc)) {
398             assertTrue("[RERUN] Could not send data SMS. Check signal.",
399                     mSendReceiver.waitForCalls(1, TIME_OUT));
400             if (mDeliveryReportSupported) {
401                 assertTrue("[RERUN] Data SMS message delivery notification not received. " +
402                         "Check signal.", mDeliveryReceiver.waitForCalls(1, TIME_OUT));
403             }
404             assertTrue("Expected no messages to be delivered due to number blocking.",
405                     mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
406         } else {
407             // This GSM network doesn't support Data(binary) SMS message.
408             // Skip the test.
409         }
410 
411         // multi-part SMS blocking
412         int numPartsSent = sendMultipartTextMessageIfSupported(mccmnc, /* addMessageId= */ false);
413         if (numPartsSent > 0) {
414             assertTrue("[RERUN] Could not send multi part SMS. Check signal.",
415                     mSendReceiver.waitForCalls(numPartsSent, TIME_OUT));
416 
417             assertTrue("Expected no messages to be received due to number blocking.",
418                     mSmsReceivedReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
419             assertTrue("Expected no messages to be delivered due to number blocking.",
420                     mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
421         } else {
422             // This GSM network doesn't support Multipart SMS message.
423             // Skip the test.
424         }
425     }
426 
427     @Test
testGetSmsMessagesForFinancialAppPermissionRequestedNotGranted()428     public void testGetSmsMessagesForFinancialAppPermissionRequestedNotGranted() throws Exception {
429         CompletableFuture<Bundle> callbackResult = new CompletableFuture<>();
430 
431         mContext.startActivity(new Intent()
432                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
433                 .setComponent(new ComponentName(FINANCIAL_SMS_APP, FINANCIAL_SMS_APP + ".MainActivity"))
434                 .putExtra("callback", new RemoteCallback(callbackResult::complete)));
435 
436         Bundle bundle = callbackResult.get(500, TimeUnit.SECONDS);
437 
438         assertThat(bundle.getString("class"), startsWith(FINANCIAL_SMS_APP));
439         assertThat(bundle.getInt("rowNum"), equalTo(-1));
440     }
441 
442     @Test
testGetSmsMessagesForFinancialAppPermissionRequestedGranted()443     public void testGetSmsMessagesForFinancialAppPermissionRequestedGranted() throws Exception {
444         CompletableFuture<Bundle> callbackResult = new CompletableFuture<>();
445         String ctsPackageName = getInstrumentation().getContext().getPackageName();
446 
447         executeWithShellPermissionIdentity(() -> {
448             setModeForOps(FINANCIAL_SMS_APP,
449                     AppOpsManager.MODE_ALLOWED,
450                     AppOpsManager.OPSTR_SMS_FINANCIAL_TRANSACTIONS);
451             });
452         mContext.startActivity(new Intent()
453                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
454                 .setComponent(new ComponentName(FINANCIAL_SMS_APP, FINANCIAL_SMS_APP + ".MainActivity"))
455                 .putExtra("callback", new RemoteCallback(callbackResult::complete)));
456 
457 
458         Bundle bundle = callbackResult.get(500, TimeUnit.SECONDS);
459 
460         assertThat(bundle.getString("class"), startsWith(FINANCIAL_SMS_APP));
461         assertThat(bundle.getInt("rowNum"), equalTo(-1));
462     }
463 
464     @Test
testSmsNotPersisted_failsWithoutCarrierPermissions()465     public void testSmsNotPersisted_failsWithoutCarrierPermissions() throws Exception {
466         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
467                 TextUtils.isEmpty(mDestAddr));
468 
469         try {
470             getSmsManager().sendTextMessageWithoutPersisting(mDestAddr, null /*scAddress */,
471                     mDestAddr, mSentIntent, mDeliveredIntent);
472             fail("We should get a SecurityException due to not having carrier privileges");
473         } catch (SecurityException e) {
474             // Success
475         }
476     }
477 
478     @Test
testContentProviderAccessRestriction()479     public void testContentProviderAccessRestriction() throws Exception {
480         Uri dummySmsUri = null;
481         Context context = getInstrumentation().getContext();
482         ContentResolver contentResolver = context.getContentResolver();
483         int originalWriteSmsMode = -1;
484         String ctsPackageName = context.getPackageName();
485         try {
486             // Insert some test sms
487             originalWriteSmsMode = context.getSystemService(AppOpsManager.class)
488                     .unsafeCheckOpNoThrow(AppOpsManager.OPSTR_WRITE_SMS,
489                             getPackageUid(ctsPackageName), ctsPackageName);
490             setModeForOps(ctsPackageName,
491                     AppOpsManager.MODE_ALLOWED, AppOpsManager.OPSTR_WRITE_SMS);
492             ContentValues contentValues = new ContentValues();
493             contentValues.put(Telephony.TextBasedSmsColumns.ADDRESS, "addr");
494             contentValues.put(Telephony.TextBasedSmsColumns.READ, 1);
495             contentValues.put(Telephony.TextBasedSmsColumns.SUBJECT, "subj");
496             contentValues.put(Telephony.TextBasedSmsColumns.BODY, "created_at_"
497                     + new Date().toString().replace(" ", "_"));
498 
499             dummySmsUri = contentResolver.insert(Telephony.Sms.CONTENT_URI, contentValues);
500             assertNotNull("Failed to insert test sms", dummySmsUri);
501             assertNotEquals("Failed to insert test sms", "0", dummySmsUri.getLastPathSegment());
502             testSmsAccessAboutDefaultApp(LEGACY_SMS_APP);
503             testSmsAccessAboutDefaultApp(MODERN_SMS_APP);
504         } finally {
505             if (dummySmsUri != null && !"/0".equals(dummySmsUri.getLastPathSegment())) {
506                 final Uri finalDummySmsUri = dummySmsUri;
507                 executeWithShellPermissionIdentity(() -> contentResolver.delete(finalDummySmsUri,
508                         null, null));
509             }
510             if (originalWriteSmsMode >= 0) {
511                 int finalOriginalWriteSmsMode = originalWriteSmsMode;
512                 executeWithShellPermissionIdentity(() ->
513                         setModeForOps(ctsPackageName,
514                                 finalOriginalWriteSmsMode, AppOpsManager.OPSTR_WRITE_SMS));
515             }
516         }
517     }
518 
testSmsAccessAboutDefaultApp(String pkg)519     private void testSmsAccessAboutDefaultApp(String pkg)
520             throws Exception {
521         String originalSmsApp = getSmsApp();
522         assertNotEquals(pkg, originalSmsApp);
523         assertCanAccessSms(pkg);
524         try {
525             setSmsApp(pkg);
526             assertCanAccessSms(pkg);
527         } finally {
528             resetReadWriteSmsAppOps(pkg);
529             setSmsApp(originalSmsApp);
530         }
531     }
532 
resetReadWriteSmsAppOps(String pkg)533     private void resetReadWriteSmsAppOps(String pkg) throws Exception {
534         setModeForOps(pkg, AppOpsManager.MODE_DEFAULT,
535                 AppOpsManager.OPSTR_READ_SMS, AppOpsManager.OPSTR_WRITE_SMS);
536     }
537 
setModeForOps(String pkg, int mode, String... ops)538     private void setModeForOps(String pkg, int mode, String... ops) throws Exception {
539         // We cannot reset these app ops to DEFAULT via current API, so we reset them manually here
540         // temporarily as we will rewrite how the default SMS app is setup later.
541         executeWithShellPermissionIdentity(() -> {
542             int uid = getPackageUid(pkg);
543             AppOpsManager appOpsManager =
544                     getInstrumentation().getContext().getSystemService(AppOpsManager.class);
545             for (String op : ops) {
546                 appOpsManager.setUidMode(op, uid, mode);
547             }
548         });
549     }
550 
getPackageUid(String pkg)551     private int getPackageUid(String pkg) throws PackageManager.NameNotFoundException {
552         return getInstrumentation().getContext().getPackageManager().getPackageUid(pkg, 0);
553     }
554 
getSmsApp()555     private String getSmsApp() throws Exception {
556         return executeWithShellPermissionIdentity(() -> getInstrumentation()
557                 .getContext()
558                 .getSystemService(RoleManager.class)
559                 .getRoleHolders(RoleManager.ROLE_SMS)
560                 .get(0));
561     }
562 
setSmsApp(String pkg)563     private void setSmsApp(String pkg) throws Exception {
564         executeWithShellPermissionIdentity(() -> {
565             Context context = getInstrumentation().getContext();
566             RoleManager roleManager = context.getSystemService(RoleManager.class);
567             CompletableFuture<Boolean> result = new CompletableFuture<>();
568             if (roleManager.getRoleHoldersAsUser(RoleManager.ROLE_SMS,
569                     context.getUser()).contains(pkg)) {
570                 result.complete(true);
571             } else {
572                 roleManager.addRoleHolderAsUser(RoleManager.ROLE_SMS, pkg,
573                         RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, context.getUser(),
574                         AsyncTask.THREAD_POOL_EXECUTOR, result::complete);
575             }
576             assertTrue(result.get(5, TimeUnit.SECONDS));
577         });
578     }
579 
executeWithShellPermissionIdentity(Callable<T> callable)580     private <T> T executeWithShellPermissionIdentity(Callable<T> callable) throws Exception {
581         if (sHasShellPermissionIdentity) {
582             return callable.call();
583         }
584         UiAutomation uiAutomation = getInstrumentation().getUiAutomation(
585                 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
586         uiAutomation.adoptShellPermissionIdentity();
587         try {
588             sHasShellPermissionIdentity = true;
589             return callable.call();
590         } finally {
591             uiAutomation.dropShellPermissionIdentity();
592             sHasShellPermissionIdentity = false;
593         }
594     }
595 
executeWithShellPermissionIdentity(RunnableWithException runnable)596     private void executeWithShellPermissionIdentity(RunnableWithException runnable)
597             throws Exception {
598         executeWithShellPermissionIdentity(() -> {
599             runnable.run();
600             return null;
601         });
602     }
603 
604     private interface RunnableWithException {
run()605         void run() throws Exception;
606     }
607 
assertCanAccessSms(String pkg)608     private void assertCanAccessSms(String pkg) throws Exception {
609         CompletableFuture<Bundle> callbackResult = new CompletableFuture<>();
610         mContext.startActivity(new Intent()
611                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
612                 .setComponent(new ComponentName(pkg, pkg + ".MainActivity"))
613                 .putExtra("callback", new RemoteCallback(callbackResult::complete)));
614 
615         Bundle bundle = callbackResult.get(20, TimeUnit.SECONDS);
616 
617         assertThat(bundle.getString("class"), startsWith(pkg));
618         assertThat(bundle.getString("exceptionMessage"), anyOf(equalTo(null), emptyString()));
619         assertThat(bundle.getInt("queryCount"), greaterThan(0));
620     }
621 
init()622     private void init() {
623         mSendReceiver.reset();
624         mDeliveryReceiver.reset();
625         mDataSmsReceiver.reset();
626         mSmsDeliverReceiver.reset();
627         mSmsReceivedReceiver.reset();
628         mSmsRetrieverReceiver.reset();
629         mReceivedDataSms = false;
630         sMessageId = 0L;
631         mSentIntent = PendingIntent.getBroadcast(mContext, 0, mSendIntent,
632                 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
633         mDeliveredIntent = PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent,
634                 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
635     }
636 
637     /**
638      * Returns the number of parts sent in the message. If Multi-part SMS is not supported,
639      * returns 0.
640      */
sendMultipartTextMessageIfSupported(String mccmnc, boolean addMessageId)641     private int sendMultipartTextMessageIfSupported(String mccmnc, boolean addMessageId) {
642         int numPartsSent = 0;
643         if (!CarrierCapability.UNSUPPORT_MULTIPART_SMS_MESSAGES.contains(mccmnc)) {
644             init();
645             ArrayList<String> parts = divideMessage(LONG_TEXT);
646             numPartsSent = parts.size();
647             ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
648             ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();
649             for (int i = 0; i < numPartsSent; i++) {
650                 sentIntents.add(PendingIntent.getBroadcast(mContext, 0, mSendIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED));
651                 deliveryIntents.add(PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent, PendingIntent.FLAG_MUTABLE_UNAUDITED));
652             }
653             sendMultiPartTextMessage(mDestAddr, parts, sentIntents, deliveryIntents, addMessageId);
654         }
655         return numPartsSent;
656     }
657 
sendDataMessageIfSupported(String mccmnc)658     private boolean sendDataMessageIfSupported(String mccmnc) {
659         if (!CarrierCapability.UNSUPPORT_DATA_SMS_MESSAGES.contains(mccmnc)) {
660             byte[] data = mText.getBytes();
661             short port = 19989;
662 
663             init();
664             sendDataMessage(mDestAddr, port, data, mSentIntent, mDeliveredIntent);
665             return true;
666         }
667         return false;
668     }
669 
670     @Test
testGetDefault()671     public void testGetDefault() {
672         assertNotNull(getSmsManager());
673     }
674 
675     @Test
testGetSetSmscAddress()676     public void testGetSetSmscAddress() {
677         String smsc = null;
678         try {
679             smsc = getSmsManager().getSmscAddress();
680             fail("SmsManager.getSmscAddress() should throw a SecurityException");
681         } catch (SecurityException e) {
682             // expected
683         }
684 
685         InstrumentationRegistry.getInstrumentation().getUiAutomation()
686                 .adoptShellPermissionIdentity("android.permission.READ_PRIVILEGED_PHONE_STATE");
687         try {
688             smsc = getSmsManager().getSmscAddress();
689         } catch (SecurityException se) {
690             fail("Caller with READ_PRIVILEGED_PHONE_STATE should be able to call API");
691         } finally {
692             InstrumentationRegistry.getInstrumentation().getUiAutomation()
693                     .dropShellPermissionIdentity();
694         }
695 
696         try {
697             getSmsManager().setSmscAddress(smsc);
698             fail("SmsManager.setSmscAddress() should throw a SecurityException");
699         } catch (SecurityException e) {
700             // expected
701         }
702 
703         InstrumentationRegistry.getInstrumentation().getUiAutomation()
704                 .adoptShellPermissionIdentity("android.permission.MODIFY_PHONE_STATE");
705         try {
706             getSmsManager().setSmscAddress(smsc);
707         } catch (SecurityException se) {
708             fail("Caller with MODIFY_PHONE_STATE should be able to call API");
709         } finally {
710             InstrumentationRegistry.getInstrumentation().getUiAutomation()
711                     .dropShellPermissionIdentity();
712         }
713     }
714 
715     @Test
testGetPremiumSmsConsent()716     public void testGetPremiumSmsConsent() {
717         try {
718             getSmsManager().getPremiumSmsConsent("fake package name");
719             fail("SmsManager.getPremiumSmsConsent() should throw a SecurityException");
720         } catch (SecurityException e) {
721             // expected
722         }
723 
724         InstrumentationRegistry.getInstrumentation().getUiAutomation()
725                 .adoptShellPermissionIdentity("android.permission.READ_PRIVILEGED_PHONE_STATE");
726         try {
727             getSmsManager().getPremiumSmsConsent("fake package name");
728             fail("Caller with permission but only phone/system uid is allowed");
729         } catch (SecurityException se) {
730             // expected
731         } finally {
732             InstrumentationRegistry.getInstrumentation().getUiAutomation()
733                     .dropShellPermissionIdentity();
734         }
735     }
736 
737     @Test
testSetPremiumSmsConsent()738     public void testSetPremiumSmsConsent() {
739         try {
740             getSmsManager().setPremiumSmsConsent("fake package name", 0);
741             fail("SmsManager.setPremiumSmsConsent() should throw a SecurityException");
742         } catch (SecurityException e) {
743             // expected
744         }
745 
746         InstrumentationRegistry.getInstrumentation().getUiAutomation()
747                 .adoptShellPermissionIdentity("android.permission.MODIFY_PHONE_STATE");
748         try {
749             getSmsManager().setPremiumSmsConsent("fake package name", 0);
750             fail("Caller with permission but only phone/system uid is allowed");
751         } catch (SecurityException se) {
752             // expected
753         } finally {
754             InstrumentationRegistry.getInstrumentation().getUiAutomation()
755                     .dropShellPermissionIdentity();
756         }
757     }
758 
759     /**
760      * Verify that SmsManager.getSmsCapacityOnIcc requires Permission.
761      * <p>
762      * Requires Permission:
763      * {@link android.Manifest.permission#READ_PHONE_STATE}.
764      */
765     @Test
testGetSmsCapacityOnIcc()766     public void testGetSmsCapacityOnIcc() {
767         try {
768             getSmsManager().getSmsCapacityOnIcc();
769         } catch (SecurityException e) {
770             fail("Caller with READ_PHONE_STATE should be able to call API");
771         }
772     }
773 
774     @Test
testDisableCellBroadcastRange()775     public void testDisableCellBroadcastRange() {
776         try {
777             int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP;
778             executeWithShellPermissionIdentity(() -> {
779                 getSmsManager().disableCellBroadcastRange(
780                         CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
781                         CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
782                         ranType);
783             });
784         } catch (Exception e) {
785             // expected
786         }
787     }
788 
789     @Test
testEnableCellBroadcastRange()790     public void testEnableCellBroadcastRange() {
791         try {
792             int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP;
793             executeWithShellPermissionIdentity(() -> {
794                 getSmsManager().enableCellBroadcastRange(
795                         CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
796                         CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
797                         ranType);
798             });
799         } catch (Exception e) {
800             // expected
801         }
802     }
803 
804     @Test
testResetAllCellBroadcastRanges()805     public void testResetAllCellBroadcastRanges() {
806         try {
807             executeWithShellPermissionIdentity(() -> {
808                 getSmsManager().resetAllCellBroadcastRanges();
809             });
810         } catch (Exception e) {
811             // expected
812         }
813     }
814 
815     @Test
testCreateForSubscriptionId()816     public void testCreateForSubscriptionId() {
817         int testSubId = 123;
818         SmsManager smsManager = mContext.getSystemService(SmsManager.class)
819                 .createForSubscriptionId(testSubId);
820         assertEquals("getSubscriptionId() should be " + testSubId, testSubId,
821                 smsManager.getSubscriptionId());
822     }
823 
divideMessage(String text)824     protected ArrayList<String> divideMessage(String text) {
825         return getSmsManager().divideMessage(text);
826     }
827 
getSmsManager()828     private android.telephony.SmsManager getSmsManager() {
829         return android.telephony.SmsManager.getDefault();
830     }
831 
sendMultiPartTextMessage(String destAddr, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents, boolean addMessageId)832     protected void sendMultiPartTextMessage(String destAddr, ArrayList<String> parts,
833             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
834             boolean addMessageId) {
835         if (addMessageId) {
836             long fakeMessageId = 1278;
837             getSmsManager().sendMultipartTextMessage(destAddr, null, parts, sentIntents,
838                     deliveryIntents, fakeMessageId);
839         } else if (mContext.getOpPackageName() != null) {
840             getSmsManager().sendMultipartTextMessage(destAddr, null, parts, sentIntents,
841                     deliveryIntents, mContext.getOpPackageName(), mContext.getAttributionTag());
842         } else {
843             getSmsManager().sendMultipartTextMessage(destAddr, null, parts, sentIntents,
844                     deliveryIntents);
845         }
846     }
847 
sendDataMessage(String destAddr,short port, byte[] data, PendingIntent sentIntent, PendingIntent deliveredIntent)848     protected void sendDataMessage(String destAddr,short port, byte[] data, PendingIntent sentIntent, PendingIntent deliveredIntent) {
849         getSmsManager().sendDataMessage(destAddr, null, port, data, sentIntent, deliveredIntent);
850     }
851 
sendTextMessage(String destAddr, String text, PendingIntent sentIntent, PendingIntent deliveredIntent)852     protected void sendTextMessage(String destAddr, String text, PendingIntent sentIntent,
853             PendingIntent deliveredIntent) {
854         getSmsManager().sendTextMessage(destAddr, null, text, sentIntent, deliveredIntent);
855     }
856 
sendTextMessageWithMessageId(String destAddr, String text, PendingIntent sentIntent, PendingIntent deliveredIntent, long messageId)857     protected void sendTextMessageWithMessageId(String destAddr, String text,
858             PendingIntent sentIntent, PendingIntent deliveredIntent, long messageId) {
859         getSmsManager().sendTextMessage(destAddr, null, text, sentIntent, deliveredIntent,
860                 messageId);
861     }
862 
blockNumber(String number)863     private void blockNumber(String number) {
864         mBlockedNumberUri = insertBlockedNumber(mContext, number);
865         if (mBlockedNumberUri == null) {
866             fail("Failed to insert into blocked number provider.");
867         }
868     }
869 
unblockNumber(Uri uri)870     private void unblockNumber(Uri uri) {
871         deleteBlockedNumber(mContext, uri);
872     }
873 
setDefaultSmsApp(boolean setToSmsApp)874     private void setDefaultSmsApp(boolean setToSmsApp)
875             throws Exception {
876         String command = String.format(
877                 "appops set --user 0 %s WRITE_SMS %s",
878                 mContext.getPackageName(),
879                 setToSmsApp ? "allow" : "default");
880         assertTrue("Setting default SMS app failed : " + setToSmsApp,
881                 executeShellCommand(command).isEmpty());
882         mTestAppSetAsDefaultSmsApp = setToSmsApp;
883     }
884 
executeShellCommand(String command)885     private String executeShellCommand(String command)
886             throws IOException {
887         ParcelFileDescriptor pfd =
888                 getInstrumentation().getUiAutomation().executeShellCommand(command);
889         BufferedReader br = null;
890         try (InputStream in = new FileInputStream(pfd.getFileDescriptor());) {
891             br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
892             String str;
893             StringBuilder out = new StringBuilder();
894             while ((str = br.readLine()) != null) {
895                 out.append(str);
896             }
897             return out.toString();
898         } finally {
899             if (br != null) {
900                 br.close();
901             }
902         }
903     }
904 
905     private static class SmsBroadcastReceiver extends BroadcastReceiver {
906         private int mCalls;
907         private int mExpectedCalls;
908         private String mAction;
909         private Object mLock;
910 
SmsBroadcastReceiver(String action)911         SmsBroadcastReceiver(String action) {
912             mAction = action;
913             reset();
914             mLock = new Object();
915         }
916 
reset()917         void reset() {
918             mExpectedCalls = Integer.MAX_VALUE;
919             mCalls = 0;
920         }
921 
922         @Override
onReceive(Context context, Intent intent)923         public void onReceive(Context context, Intent intent) {
924             if(mAction.equals(DATA_SMS_RECEIVED_ACTION)){
925                 StringBuilder sb = new StringBuilder();
926                 Bundle bundle = intent.getExtras();
927                 if (bundle != null) {
928                     Object[] obj = (Object[]) bundle.get("pdus");
929                     String format = bundle.getString("format");
930                     SmsMessage[] message = new SmsMessage[obj.length];
931                     for (int i = 0; i < obj.length; i++) {
932                         message[i] = SmsMessage.createFromPdu((byte[]) obj[i], format);
933                     }
934 
935                     for (SmsMessage currentMessage : message) {
936                         byte[] binaryContent = currentMessage.getUserData();
937                         String readableContent = new String(binaryContent);
938                         sb.append(readableContent);
939                     }
940                 }
941                 mReceivedDataSms = true;
942                 mReceivedText=sb.toString();
943             }
944             if (mAction.equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
945                 sMessageId = intent.getLongExtra("messageId", 0L);
946             }
947             Log.i(TAG, "onReceive " + intent.getAction() + " mAction " + mAction);
948             if (intent.getAction().equals(mAction)) {
949                 synchronized (mLock) {
950                     mCalls += 1;
951                     mLock.notify();
952                 }
953             }
954         }
955 
verifyNoCalls(long timeout)956         private boolean verifyNoCalls(long timeout) throws InterruptedException {
957             synchronized(mLock) {
958                 mLock.wait(timeout);
959                 return mCalls == 0;
960             }
961         }
962 
waitForCalls(int expectedCalls, long timeout)963         public boolean waitForCalls(int expectedCalls, long timeout) throws InterruptedException {
964             synchronized(mLock) {
965                 mExpectedCalls = expectedCalls;
966                 long startTime = SystemClock.elapsedRealtime();
967 
968                 while (mCalls < mExpectedCalls) {
969                     long waitTime = timeout - (SystemClock.elapsedRealtime() - startTime);
970                     if (waitTime > 0) {
971                         mLock.wait(waitTime);
972                     } else {
973                         return false;  // timed out
974                     }
975                 }
976                 return true;  // success
977             }
978         }
979     }
980 }
981