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