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.assertNull;
35 import static org.junit.Assert.assertThat;
36 import static org.junit.Assert.assertTrue;
37 import static org.junit.Assert.fail;
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.database.CursorWindow;
52 import android.net.Uri;
53 import android.os.AsyncTask;
54 import android.os.Bundle;
55 import android.os.ParcelFileDescriptor;
56 import android.os.RemoteCallback;
57 import android.os.SystemClock;
58 import android.provider.Telephony;
59 import android.telephony.SmsManager;
60 import android.telephony.SmsCbMessage;
61 import android.telephony.SmsMessage;
62 import android.telephony.TelephonyManager;
63 import android.telephony.cdma.CdmaSmsCbProgramData;
64 import android.text.TextUtils;
65 import android.util.Log;
66 
67 import org.junit.After;
68 import org.junit.Before;
69 import org.junit.Test;
70 
71 import java.io.BufferedReader;
72 import java.io.FileInputStream;
73 import java.io.IOException;
74 import java.io.InputStream;
75 import java.io.InputStreamReader;
76 import java.nio.charset.StandardCharsets;
77 import java.util.ArrayList;
78 import java.util.Date;
79 import java.util.List;
80 import java.util.concurrent.Callable;
81 import java.util.concurrent.CompletableFuture;
82 import java.util.concurrent.CountDownLatch;
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 PackageManager mPackageManager;
116     private String mDestAddr;
117     private String mText;
118     private SmsBroadcastReceiver mSendReceiver;
119     private SmsBroadcastReceiver mDeliveryReceiver;
120     private SmsBroadcastReceiver mDataSmsReceiver;
121     private SmsBroadcastReceiver mSmsDeliverReceiver;
122     private SmsBroadcastReceiver mSmsReceivedReceiver;
123     private SmsBroadcastReceiver mSmsRetrieverReceiver;
124     private PendingIntent mSentIntent;
125     private PendingIntent mDeliveredIntent;
126     private Intent mSendIntent;
127     private Intent mDeliveryIntent;
128     private Context mContext;
129     private Uri mBlockedNumberUri;
130     private boolean mTestAppSetAsDefaultSmsApp;
131     private boolean mDeliveryReportSupported;
132     private static boolean mReceivedDataSms;
133     private static String mReceivedText;
134     private static boolean sHasShellPermissionIdentity = false;
135 
136     private static final int TIME_OUT = 1000 * 60 * 4;
137     private static final int NO_CALLS_TIMEOUT_MILLIS = 1000; // 1 second
138 
139     @Before
setUp()140     public void setUp() throws Exception {
141         mContext = getContext();
142         mTelephonyManager =
143             (TelephonyManager) getContext().getSystemService(
144                     Context.TELEPHONY_SERVICE);
145         mPackageManager = mContext.getPackageManager();
146         mDestAddr = mTelephonyManager.getLine1Number();
147         mText = "This is a test message";
148 
149         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
150             mDeliveryReportSupported = false;
151         } else {
152             // exclude the networks that don't support SMS delivery report
153             String mccmnc = mTelephonyManager.getSimOperator();
154             mDeliveryReportSupported = !(CarrierCapability.NO_DELIVERY_REPORTS.contains(mccmnc));
155         }
156     }
157 
158     @After
tearDown()159     public void tearDown() throws Exception {
160         if (mBlockedNumberUri != null) {
161             unblockNumber(mBlockedNumberUri);
162             mBlockedNumberUri = null;
163         }
164         if (mTestAppSetAsDefaultSmsApp) {
165             setDefaultSmsApp(false);
166         }
167     }
168 
169     @Test
testDivideMessage()170     public void testDivideMessage() {
171         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
172             return;
173         }
174         ArrayList<String> dividedMessages = divideMessage(LONG_TEXT);
175         assertNotNull(dividedMessages);
176         if (TelephonyUtils.isSkt(mTelephonyManager)) {
177             assertTrue(isComplete(dividedMessages, 5, LONG_TEXT)
178                     || isComplete(dividedMessages, 3, LONG_TEXT));
179         } else if (TelephonyUtils.isKt(mTelephonyManager)) {
180             assertTrue(isComplete(dividedMessages, 4, LONG_TEXT)
181                     || isComplete(dividedMessages, 3, LONG_TEXT));
182         } else {
183             assertTrue(isComplete(dividedMessages, 3, LONG_TEXT));
184         }
185     }
186 
187     @Test
testDivideUnicodeMessage()188     public void testDivideUnicodeMessage() {
189         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
190             return;
191         }
192         ArrayList<String> dividedMessages = divideMessage(LONG_TEXT_WITH_32BIT_CHARS);
193         assertNotNull(dividedMessages);
194         assertTrue(isComplete(dividedMessages, 3, LONG_TEXT_WITH_32BIT_CHARS));
195         for (String messagePiece : dividedMessages) {
196             assertFalse(Character.isHighSurrogate(
197                     messagePiece.charAt(messagePiece.length() - 1)));
198         }
199     }
200 
isComplete(List<String> dividedMessages, int numParts, String longText)201     private boolean isComplete(List<String> dividedMessages, int numParts, String longText) {
202         if (dividedMessages.size() != numParts) {
203             return false;
204         }
205 
206         String actualMessage = "";
207         for (int i = 0; i < numParts; i++) {
208             actualMessage += dividedMessages.get(i);
209         }
210         return longText.equals(actualMessage);
211     }
212 
213     @Test
testSmsRetriever()214     public void testSmsRetriever() throws Exception {
215         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
216             return;
217         }
218 
219         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
220                 TextUtils.isEmpty(mDestAddr));
221 
222         String mccmnc = mTelephonyManager.getSimOperator();
223         setupBroadcastReceivers();
224         init();
225 
226         CompletableFuture<Bundle> callbackResult = new CompletableFuture<>();
227 
228         mContext.startActivity(new Intent()
229                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
230                 .setComponent(new ComponentName(
231                         SMS_RETRIEVER_APP, SMS_RETRIEVER_APP + ".MainActivity"))
232                 .putExtra("callback", new RemoteCallback(callbackResult::complete)));
233 
234 
235         Bundle bundle = callbackResult.get(200, TimeUnit.SECONDS);
236         String token = bundle.getString("token");
237         assertThat(bundle.getString("class"), startsWith(SMS_RETRIEVER_APP));
238         assertNotNull(token);
239 
240         String composedText = "testprefix1" + mText + token;
241         sendTextMessage(mDestAddr, composedText, null, null);
242 
243         assertTrue("[RERUN] SMS retriever message not received. Check signal.",
244                 mSmsRetrieverReceiver.waitForCalls(1, TIME_OUT));
245     }
246 
247     @Test
testSendAndReceiveMessages()248     public void testSendAndReceiveMessages() throws Exception {
249         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
250             return;
251         }
252 
253         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
254                 TextUtils.isEmpty(mDestAddr));
255 
256         String mccmnc = mTelephonyManager.getSimOperator();
257         setupBroadcastReceivers();
258 
259         // send single text sms
260         init();
261         sendTextMessage(mDestAddr, mDestAddr, mSentIntent, mDeliveredIntent);
262         assertTrue("[RERUN] Could not send SMS. Check signal.",
263                 mSendReceiver.waitForCalls(1, TIME_OUT));
264         if (mDeliveryReportSupported) {
265             assertTrue("[RERUN] SMS message delivery notification not received. Check signal.",
266                     mDeliveryReceiver.waitForCalls(1, TIME_OUT));
267         }
268         // non-default app should receive only SMS_RECEIVED_ACTION
269         assertTrue(mSmsReceivedReceiver.waitForCalls(1, TIME_OUT));
270         assertTrue(mSmsDeliverReceiver.waitForCalls(0, 0));
271 
272         // due to permission restrictions, currently there is no way to make this test app the
273         // default SMS app
274 
275         if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
276             // TODO: temp workaround, OCTET encoding for EMS not properly supported
277             return;
278         }
279 
280         // send data sms
281         if (sendDataMessageIfSupported(mccmnc)) {
282             assertTrue("[RERUN] Could not send data SMS. Check signal.",
283                     mSendReceiver.waitForCalls(1, TIME_OUT));
284             if (mDeliveryReportSupported) {
285                 assertTrue("[RERUN] Data SMS message delivery notification not received. " +
286                         "Check signal.", mDeliveryReceiver.waitForCalls(1, TIME_OUT));
287             }
288             mDataSmsReceiver.waitForCalls(1, TIME_OUT);
289             assertTrue("[RERUN] Data SMS message not received. Check signal.", mReceivedDataSms);
290             assertEquals(mReceivedText, mText);
291         } else {
292             // This GSM network doesn't support Data(binary) SMS message.
293             // Skip the test.
294         }
295 
296         // send multi parts text sms
297         int numPartsSent = sendMultipartTextMessageIfSupported(mccmnc);
298         if (numPartsSent > 0) {
299             assertTrue("[RERUN] Could not send multi part SMS. Check signal.",
300                     mSendReceiver.waitForCalls(numPartsSent, TIME_OUT));
301             if (mDeliveryReportSupported) {
302                 assertTrue("[RERUN] Multi part SMS message delivery notification not received. " +
303                         "Check signal.", mDeliveryReceiver.waitForCalls(numPartsSent, TIME_OUT));
304             }
305             // non-default app should receive only SMS_RECEIVED_ACTION
306             assertTrue(mSmsReceivedReceiver.waitForCalls(1, TIME_OUT));
307             assertTrue(mSmsDeliverReceiver.waitForCalls(0, 0));
308         } else {
309             // This GSM network doesn't support Multipart SMS message.
310             // Skip the test.
311         }
312     }
313 
314     @Test
testSmsBlocking()315     public void testSmsBlocking() throws Exception {
316         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
317             return;
318         }
319 
320         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
321                 TextUtils.isEmpty(mDestAddr));
322 
323         // disable suppressing blocking.
324         TelephonyUtils.endBlockSuppression(getInstrumentation());
325 
326         String mccmnc = mTelephonyManager.getSimOperator();
327         // Setting default SMS App is needed to be able to block numbers.
328         setDefaultSmsApp(true);
329         blockNumber(mDestAddr);
330         setupBroadcastReceivers();
331 
332         // single-part SMS blocking
333         init();
334         sendTextMessage(mDestAddr, mDestAddr, mSentIntent, mDeliveredIntent);
335         assertTrue("[RERUN] Could not send SMS. Check signal.",
336                 mSendReceiver.waitForCalls(1, TIME_OUT));
337         assertTrue("Expected no messages to be received due to number blocking.",
338                 mSmsReceivedReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
339         assertTrue("Expected no messages to be delivered due to number blocking.",
340                 mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
341 
342         // send data sms
343         if (!sendDataMessageIfSupported(mccmnc)) {
344             assertTrue("[RERUN] Could not send data SMS. Check signal.",
345                     mSendReceiver.waitForCalls(1, TIME_OUT));
346             if (mDeliveryReportSupported) {
347                 assertTrue("[RERUN] Data SMS message delivery notification not received. " +
348                         "Check signal.", mDeliveryReceiver.waitForCalls(1, TIME_OUT));
349             }
350             assertTrue("Expected no messages to be delivered due to number blocking.",
351                     mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
352         } else {
353             // This GSM network doesn't support Data(binary) SMS message.
354             // Skip the test.
355         }
356 
357         // multi-part SMS blocking
358         int numPartsSent = sendMultipartTextMessageIfSupported(mccmnc);
359         if (numPartsSent > 0) {
360             assertTrue("[RERUN] Could not send multi part SMS. Check signal.",
361                     mSendReceiver.waitForCalls(numPartsSent, TIME_OUT));
362 
363             assertTrue("Expected no messages to be received due to number blocking.",
364                     mSmsReceivedReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
365             assertTrue("Expected no messages to be delivered due to number blocking.",
366                     mSmsDeliverReceiver.verifyNoCalls(NO_CALLS_TIMEOUT_MILLIS));
367         } else {
368             // This GSM network doesn't support Multipart SMS message.
369             // Skip the test.
370         }
371     }
372 
373     @Test
testGetSmsMessagesForFinancialAppPermissionRequestedNotGranted()374     public void testGetSmsMessagesForFinancialAppPermissionRequestedNotGranted() throws Exception {
375         CompletableFuture<Bundle> callbackResult = new CompletableFuture<>();
376 
377         mContext.startActivity(new Intent()
378                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
379                 .setComponent(new ComponentName(FINANCIAL_SMS_APP, FINANCIAL_SMS_APP + ".MainActivity"))
380                 .putExtra("callback", new RemoteCallback(callbackResult::complete)));
381 
382         Bundle bundle = callbackResult.get(500, TimeUnit.SECONDS);
383 
384         assertThat(bundle.getString("class"), startsWith(FINANCIAL_SMS_APP));
385         assertThat(bundle.getInt("rowNum"), equalTo(-1));
386     }
387 
388     @Test
testGetSmsMessagesForFinancialAppPermissionRequestedGranted()389     public void testGetSmsMessagesForFinancialAppPermissionRequestedGranted() throws Exception {
390         CompletableFuture<Bundle> callbackResult = new CompletableFuture<>();
391         String ctsPackageName = getInstrumentation().getContext().getPackageName();
392 
393         executeWithShellPermissionIdentity(() -> {
394             setModeForOps(FINANCIAL_SMS_APP,
395                     AppOpsManager.MODE_ALLOWED,
396                     AppOpsManager.OPSTR_SMS_FINANCIAL_TRANSACTIONS);
397             });
398         mContext.startActivity(new Intent()
399                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
400                 .setComponent(new ComponentName(FINANCIAL_SMS_APP, FINANCIAL_SMS_APP + ".MainActivity"))
401                 .putExtra("callback", new RemoteCallback(callbackResult::complete)));
402 
403 
404         Bundle bundle = callbackResult.get(500, TimeUnit.SECONDS);
405 
406         assertThat(bundle.getString("class"), startsWith(FINANCIAL_SMS_APP));
407         assertThat(bundle.getInt("rowNum"), equalTo(-1));
408     }
409 
410     @Test
testSmsNotPersisted_failsWithoutCarrierPermissions()411     public void testSmsNotPersisted_failsWithoutCarrierPermissions() throws Exception {
412         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
413             return;
414         }
415 
416         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
417                 TextUtils.isEmpty(mDestAddr));
418 
419         try {
420             getSmsManager().sendTextMessageWithoutPersisting(mDestAddr, null /*scAddress */,
421                     mDestAddr, mSentIntent, mDeliveredIntent);
422             fail("We should get a SecurityException due to not having carrier privileges");
423         } catch (SecurityException e) {
424             // Success
425         }
426     }
427 
428     @Test
testContentProviderAccessRestriction()429     public void testContentProviderAccessRestriction() throws Exception {
430         Uri dummySmsUri = null;
431         Context context = getInstrumentation().getContext();
432         ContentResolver contentResolver = context.getContentResolver();
433         int originalWriteSmsMode = -1;
434         String ctsPackageName = context.getPackageName();
435         try {
436             // Insert some dummy sms
437             originalWriteSmsMode = context.getSystemService(AppOpsManager.class)
438                     .unsafeCheckOpNoThrow(AppOpsManager.OPSTR_WRITE_SMS,
439                             getPackageUid(ctsPackageName), ctsPackageName);
440             dummySmsUri = executeWithShellPermissionIdentity(() -> {
441                 setModeForOps(ctsPackageName,
442                         AppOpsManager.MODE_ALLOWED, AppOpsManager.OPSTR_WRITE_SMS);
443                 ContentValues contentValues = new ContentValues();
444                 contentValues.put(Telephony.TextBasedSmsColumns.ADDRESS, "addr");
445                 contentValues.put(Telephony.TextBasedSmsColumns.READ, 1);
446                 contentValues.put(Telephony.TextBasedSmsColumns.SUBJECT, "subj");
447                 contentValues.put(Telephony.TextBasedSmsColumns.BODY, "created_at_" +
448                         new Date().toString().replace(" ", "_"));
449                 return contentResolver.insert(Telephony.Sms.CONTENT_URI, contentValues);
450             });
451             assertNotNull("Failed to insert dummy sms", dummySmsUri);
452             assertNotEquals("Failed to insert dummy sms", dummySmsUri.getLastPathSegment(), "0");
453             testSmsAccessAboutDefaultApp(LEGACY_SMS_APP);
454             testSmsAccessAboutDefaultApp(MODERN_SMS_APP);
455         } finally {
456             if (dummySmsUri != null && !"/0".equals(dummySmsUri.getLastPathSegment())) {
457                 final Uri finalDummySmsUri = dummySmsUri;
458                 executeWithShellPermissionIdentity(() -> contentResolver.delete(finalDummySmsUri,
459                         null, null));
460             }
461             if (originalWriteSmsMode >= 0) {
462                 int finalOriginalWriteSmsMode = originalWriteSmsMode;
463                 executeWithShellPermissionIdentity(() ->
464                         setModeForOps(ctsPackageName,
465                                 finalOriginalWriteSmsMode, AppOpsManager.OPSTR_WRITE_SMS));
466             }
467         }
468     }
469 
testSmsAccessAboutDefaultApp(String pkg)470     private void testSmsAccessAboutDefaultApp(String pkg)
471             throws Exception {
472         String originalSmsApp = getSmsApp();
473         assertNotEquals(pkg, originalSmsApp);
474         assertCanAccessSms(pkg);
475         try {
476             setSmsApp(pkg);
477             assertCanAccessSms(pkg);
478         } finally {
479             resetReadWriteSmsAppOps(pkg);
480             setSmsApp(originalSmsApp);
481         }
482     }
483 
resetReadWriteSmsAppOps(String pkg)484     private void resetReadWriteSmsAppOps(String pkg) throws Exception {
485         setModeForOps(pkg, AppOpsManager.MODE_DEFAULT,
486                 AppOpsManager.OPSTR_READ_SMS, AppOpsManager.OPSTR_WRITE_SMS);
487     }
488 
setModeForOps(String pkg, int mode, String... ops)489     private void setModeForOps(String pkg, int mode, String... ops) throws Exception {
490         // We cannot reset these app ops to DEFAULT via current API, so we reset them manually here
491         // temporarily as we will rewrite how the default SMS app is setup later.
492         executeWithShellPermissionIdentity(() -> {
493             int uid = getPackageUid(pkg);
494             AppOpsManager appOpsManager =
495                     getInstrumentation().getContext().getSystemService(AppOpsManager.class);
496             for (String op : ops) {
497                 appOpsManager.setUidMode(op, uid, mode);
498             }
499         });
500     }
501 
getPackageUid(String pkg)502     private int getPackageUid(String pkg) throws PackageManager.NameNotFoundException {
503         return getInstrumentation().getContext().getPackageManager().getPackageUid(pkg, 0);
504     }
505 
getSmsApp()506     private String getSmsApp() throws Exception {
507         return executeWithShellPermissionIdentity(() -> getInstrumentation()
508                 .getContext()
509                 .getSystemService(RoleManager.class)
510                 .getRoleHolders(RoleManager.ROLE_SMS)
511                 .get(0));
512     }
513 
setSmsApp(String pkg)514     private void setSmsApp(String pkg) throws Exception {
515         executeWithShellPermissionIdentity(() -> {
516             Context context = getInstrumentation().getContext();
517             CompletableFuture<Boolean> result = new CompletableFuture<>();
518             context.getSystemService(RoleManager.class).addRoleHolderAsUser(
519                     RoleManager.ROLE_SMS, pkg, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP,
520                     context.getUser(), AsyncTask.THREAD_POOL_EXECUTOR, result::complete);
521             assertTrue(result.get(5, TimeUnit.SECONDS));
522         });
523     }
524 
executeWithShellPermissionIdentity(Callable<T> callable)525     private <T> T executeWithShellPermissionIdentity(Callable<T> callable) throws Exception {
526         if (sHasShellPermissionIdentity) {
527             return callable.call();
528         }
529         UiAutomation uiAutomation = getInstrumentation().getUiAutomation(
530                 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
531         uiAutomation.adoptShellPermissionIdentity();
532         try {
533             sHasShellPermissionIdentity = true;
534             return callable.call();
535         } finally {
536             uiAutomation.dropShellPermissionIdentity();
537             sHasShellPermissionIdentity = false;
538         }
539     }
540 
executeWithShellPermissionIdentity(RunnableWithException runnable)541     private void executeWithShellPermissionIdentity(RunnableWithException runnable)
542             throws Exception {
543         executeWithShellPermissionIdentity(() -> {
544             runnable.run();
545             return null;
546         });
547     }
548 
549     private interface RunnableWithException {
run()550         void run() throws Exception;
551     }
552 
assertCanAccessSms(String pkg)553     private void assertCanAccessSms(String pkg) throws Exception {
554         CompletableFuture<Bundle> callbackResult = new CompletableFuture<>();
555         mContext.startActivity(new Intent()
556                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
557                 .setComponent(new ComponentName(pkg, pkg + ".MainActivity"))
558                 .putExtra("callback", new RemoteCallback(callbackResult::complete)));
559 
560         Bundle bundle = callbackResult.get(20, TimeUnit.SECONDS);
561 
562         assertThat(bundle.getString("class"), startsWith(pkg));
563         assertThat(bundle.getString("exceptionMessage"), anyOf(equalTo(null), emptyString()));
564         assertThat(bundle.getInt("queryCount"), greaterThan(0));
565     }
566 
init()567     private void init() {
568         mSendReceiver.reset();
569         mDeliveryReceiver.reset();
570         mDataSmsReceiver.reset();
571         mSmsDeliverReceiver.reset();
572         mSmsReceivedReceiver.reset();
573         mSmsRetrieverReceiver.reset();
574         mReceivedDataSms = false;
575         mSentIntent = PendingIntent.getBroadcast(mContext, 0, mSendIntent,
576                 PendingIntent.FLAG_ONE_SHOT);
577         mDeliveredIntent = PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent,
578                 PendingIntent.FLAG_ONE_SHOT);
579     }
580 
setupBroadcastReceivers()581     private void setupBroadcastReceivers() {
582         mSendIntent = new Intent(SMS_SEND_ACTION);
583         mDeliveryIntent = new Intent(SMS_DELIVERY_ACTION);
584 
585         IntentFilter sendIntentFilter = new IntentFilter(SMS_SEND_ACTION);
586         IntentFilter deliveryIntentFilter = new IntentFilter(SMS_DELIVERY_ACTION);
587         IntentFilter dataSmsReceivedIntentFilter = new IntentFilter(DATA_SMS_RECEIVED_ACTION);
588         IntentFilter smsDeliverIntentFilter = new IntentFilter(SMS_DELIVER_DEFAULT_APP_ACTION);
589         IntentFilter smsReceivedIntentFilter =
590                 new IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION);
591         IntentFilter smsRetrieverIntentFilter = new IntentFilter(SMS_RETRIEVER_ACTION);
592         dataSmsReceivedIntentFilter.addDataScheme("sms");
593         dataSmsReceivedIntentFilter.addDataAuthority("localhost", "19989");
594 
595         mSendReceiver = new SmsBroadcastReceiver(SMS_SEND_ACTION);
596         mDeliveryReceiver = new SmsBroadcastReceiver(SMS_DELIVERY_ACTION);
597         mDataSmsReceiver = new SmsBroadcastReceiver(DATA_SMS_RECEIVED_ACTION);
598         mSmsDeliverReceiver = new SmsBroadcastReceiver(SMS_DELIVER_DEFAULT_APP_ACTION);
599         mSmsReceivedReceiver = new SmsBroadcastReceiver(Telephony.Sms.Intents.SMS_RECEIVED_ACTION);
600         mSmsRetrieverReceiver = new SmsBroadcastReceiver(SMS_RETRIEVER_ACTION);
601 
602         mContext.registerReceiver(mSendReceiver, sendIntentFilter);
603         mContext.registerReceiver(mDeliveryReceiver, deliveryIntentFilter);
604         mContext.registerReceiver(mDataSmsReceiver, dataSmsReceivedIntentFilter);
605         mContext.registerReceiver(mSmsDeliverReceiver, smsDeliverIntentFilter);
606         mContext.registerReceiver(mSmsReceivedReceiver, smsReceivedIntentFilter);
607         mContext.registerReceiver(mSmsRetrieverReceiver, smsRetrieverIntentFilter);
608     }
609 
610     /**
611      * Returns the number of parts sent in the message. If Multi-part SMS is not supported,
612      * returns 0.
613      */
sendMultipartTextMessageIfSupported(String mccmnc)614     private int sendMultipartTextMessageIfSupported(String mccmnc) {
615         int numPartsSent = 0;
616         if (!CarrierCapability.UNSUPPORT_MULTIPART_SMS_MESSAGES.contains(mccmnc)) {
617             init();
618             ArrayList<String> parts = divideMessage(LONG_TEXT);
619             numPartsSent = parts.size();
620             ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
621             ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>();
622             for (int i = 0; i < numPartsSent; i++) {
623                 sentIntents.add(PendingIntent.getBroadcast(mContext, 0, mSendIntent, 0));
624                 deliveryIntents.add(PendingIntent.getBroadcast(mContext, 0, mDeliveryIntent, 0));
625             }
626             sendMultiPartTextMessage(mDestAddr, parts, sentIntents, deliveryIntents);
627         }
628         return numPartsSent;
629     }
630 
sendDataMessageIfSupported(String mccmnc)631     private boolean sendDataMessageIfSupported(String mccmnc) {
632         if (!CarrierCapability.UNSUPPORT_DATA_SMS_MESSAGES.contains(mccmnc)) {
633             byte[] data = mText.getBytes();
634             short port = 19989;
635 
636             init();
637             sendDataMessage(mDestAddr, port, data, mSentIntent, mDeliveredIntent);
638             return true;
639         }
640         return false;
641     }
642 
643     @Test
testGetDefault()644     public void testGetDefault() {
645         assertNotNull(getSmsManager());
646     }
647 
648     @Test
testGetSmscAddress()649     public void testGetSmscAddress() {
650         try {
651             getSmsManager().getSmscAddress();
652             fail("SmsManager.getSmscAddress() should throw a SecurityException");
653         } catch (SecurityException e) {
654             // expected
655         }
656     }
657 
658     @Test
testSetSmscAddress()659     public void testSetSmscAddress() {
660         try {
661             getSmsManager().setSmscAddress("fake smsc");
662             fail("SmsManager.setSmscAddress() should throw a SecurityException");
663         } catch (SecurityException e) {
664             // expected
665         }
666     }
667 
668     @Test
testDisableCellBroadcastRange()669     public void testDisableCellBroadcastRange() {
670         try {
671             int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP;
672             executeWithShellPermissionIdentity(() -> {
673                 getSmsManager().disableCellBroadcastRange(
674                         CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
675                         CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
676                         ranType);
677             });
678         } catch (Exception e) {
679             // expected
680         }
681     }
682 
683     @Test
testEnableCellBroadcastRange()684     public void testEnableCellBroadcastRange() {
685         try {
686             int ranType = SmsCbMessage.MESSAGE_FORMAT_3GPP;
687             executeWithShellPermissionIdentity(() -> {
688                 getSmsManager().enableCellBroadcastRange(
689                         CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
690                         CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT,
691                         ranType);
692             });
693         } catch (Exception e) {
694             // expected
695         }
696     }
697 
divideMessage(String text)698     protected ArrayList<String> divideMessage(String text) {
699         return getSmsManager().divideMessage(text);
700     }
701 
getSmsManager()702     private android.telephony.SmsManager getSmsManager() {
703         return android.telephony.SmsManager.getDefault();
704     }
705 
sendMultiPartTextMessage(String destAddr, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents)706     protected void sendMultiPartTextMessage(String destAddr, ArrayList<String> parts,
707             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
708         if (mContext.getOpPackageName() != null) {
709             getSmsManager().sendMultipartTextMessage(destAddr, null, parts, sentIntents,
710                     deliveryIntents, mContext.getOpPackageName());
711         } else {
712             getSmsManager().sendMultipartTextMessage(destAddr, null, parts, sentIntents,
713                     deliveryIntents);
714         }
715     }
716 
sendDataMessage(String destAddr,short port, byte[] data, PendingIntent sentIntent, PendingIntent deliveredIntent)717     protected void sendDataMessage(String destAddr,short port, byte[] data, PendingIntent sentIntent, PendingIntent deliveredIntent) {
718         getSmsManager().sendDataMessage(destAddr, null, port, data, sentIntent, deliveredIntent);
719     }
720 
sendTextMessage(String destAddr, String text, PendingIntent sentIntent, PendingIntent deliveredIntent)721     protected void sendTextMessage(String destAddr, String text, PendingIntent sentIntent, PendingIntent deliveredIntent) {
722         getSmsManager().sendTextMessage(destAddr, null, text, sentIntent, deliveredIntent);
723     }
724 
blockNumber(String number)725     private void blockNumber(String number) {
726         mBlockedNumberUri = insertBlockedNumber(mContext, number);
727         if (mBlockedNumberUri == null) {
728             fail("Failed to insert into blocked number provider.");
729         }
730     }
731 
unblockNumber(Uri uri)732     private void unblockNumber(Uri uri) {
733         deleteBlockedNumber(mContext, uri);
734     }
735 
setDefaultSmsApp(boolean setToSmsApp)736     private void setDefaultSmsApp(boolean setToSmsApp)
737             throws Exception {
738         String command = String.format(
739                 "appops set --user 0 %s WRITE_SMS %s",
740                 mContext.getPackageName(),
741                 setToSmsApp ? "allow" : "default");
742         assertTrue("Setting default SMS app failed : " + setToSmsApp,
743                 executeShellCommand(command).isEmpty());
744         mTestAppSetAsDefaultSmsApp = setToSmsApp;
745     }
746 
executeShellCommand(String command)747     private String executeShellCommand(String command)
748             throws IOException {
749         ParcelFileDescriptor pfd =
750                 getInstrumentation().getUiAutomation().executeShellCommand(command);
751         BufferedReader br = null;
752         try (InputStream in = new FileInputStream(pfd.getFileDescriptor());) {
753             br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
754             String str;
755             StringBuilder out = new StringBuilder();
756             while ((str = br.readLine()) != null) {
757                 out.append(str);
758             }
759             return out.toString();
760         } finally {
761             if (br != null) {
762                 br.close();
763             }
764         }
765     }
766 
767     private static class SmsBroadcastReceiver extends BroadcastReceiver {
768         private int mCalls;
769         private int mExpectedCalls;
770         private String mAction;
771         private Object mLock;
772 
SmsBroadcastReceiver(String action)773         SmsBroadcastReceiver(String action) {
774             mAction = action;
775             reset();
776             mLock = new Object();
777         }
778 
reset()779         void reset() {
780             mExpectedCalls = Integer.MAX_VALUE;
781             mCalls = 0;
782         }
783 
784         @Override
onReceive(Context context, Intent intent)785         public void onReceive(Context context, Intent intent) {
786             if(mAction.equals(DATA_SMS_RECEIVED_ACTION)){
787                 StringBuilder sb = new StringBuilder();
788                 Bundle bundle = intent.getExtras();
789                 if (bundle != null) {
790                     Object[] obj = (Object[]) bundle.get("pdus");
791                     String format = bundle.getString("format");
792                     SmsMessage[] message = new SmsMessage[obj.length];
793                     for (int i = 0; i < obj.length; i++) {
794                         message[i] = SmsMessage.createFromPdu((byte[]) obj[i], format);
795                     }
796 
797                     for (SmsMessage currentMessage : message) {
798                         byte[] binaryContent = currentMessage.getUserData();
799                         String readableContent = new String(binaryContent);
800                         sb.append(readableContent);
801                     }
802                 }
803                 mReceivedDataSms = true;
804                 mReceivedText=sb.toString();
805             }
806             Log.i(TAG, "onReceive " + intent.getAction());
807             if (intent.getAction().equals(mAction)) {
808                 synchronized (mLock) {
809                     mCalls += 1;
810                     mLock.notify();
811                 }
812             }
813         }
814 
verifyNoCalls(long timeout)815         private boolean verifyNoCalls(long timeout) throws InterruptedException {
816             synchronized(mLock) {
817                 mLock.wait(timeout);
818                 return mCalls == 0;
819             }
820         }
821 
waitForCalls(int expectedCalls, long timeout)822         public boolean waitForCalls(int expectedCalls, long timeout) throws InterruptedException {
823             synchronized(mLock) {
824                 mExpectedCalls = expectedCalls;
825                 long startTime = SystemClock.elapsedRealtime();
826 
827                 while (mCalls < mExpectedCalls) {
828                     long waitTime = timeout - (SystemClock.elapsedRealtime() - startTime);
829                     if (waitTime > 0) {
830                         mLock.wait(waitTime);
831                     } else {
832                         return false;  // timed out
833                     }
834                 }
835                 return true;  // success
836             }
837         }
838     }
839 }
840