• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assume.assumeTrue;
27 
28 import android.Manifest;
29 import android.annotation.Nullable;
30 import android.app.Activity;
31 import android.app.PendingIntent;
32 import android.content.BroadcastReceiver;
33 import android.content.ContentResolver;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.content.pm.PackageManager;
38 import android.net.Uri;
39 import android.os.PersistableBundle;
40 import android.os.SystemClock;
41 import android.platform.test.annotations.AppModeNonSdkSandbox;
42 import android.telephony.CarrierConfigManager;
43 import android.telephony.SmsManager;
44 import android.telephony.SubscriptionManager;
45 import android.telephony.TelephonyManager;
46 import android.telephony.cts.util.DefaultSmsAppHelper;
47 import android.text.TextUtils;
48 import android.util.Log;
49 
50 import com.android.compatibility.common.util.ApiTest;
51 import com.android.compatibility.common.util.ShellIdentityUtils;
52 import com.android.internal.telephony.flags.Flags;
53 
54 import com.google.android.mms.ContentType;
55 import com.google.android.mms.InvalidHeaderValueException;
56 import com.google.android.mms.pdu.CharacterSets;
57 import com.google.android.mms.pdu.EncodedStringValue;
58 import com.google.android.mms.pdu.GenericPdu;
59 import com.google.android.mms.pdu.PduBody;
60 import com.google.android.mms.pdu.PduComposer;
61 import com.google.android.mms.pdu.PduHeaders;
62 import com.google.android.mms.pdu.PduParser;
63 import com.google.android.mms.pdu.PduPart;
64 import com.google.android.mms.pdu.SendConf;
65 import com.google.android.mms.pdu.SendReq;
66 
67 import org.junit.After;
68 import org.junit.AfterClass;
69 import org.junit.Before;
70 import org.junit.BeforeClass;
71 import org.junit.Test;
72 
73 import java.io.File;
74 import java.io.FileOutputStream;
75 import java.io.IOException;
76 import java.util.Random;
77 import java.util.concurrent.CountDownLatch;
78 import java.util.concurrent.TimeUnit;
79 
80 /**
81  * Test sending MMS using {@link android.telephony.SmsManager}.
82  */
83 @AppModeNonSdkSandbox(reason = "SDK sandboxes do not have the required permissions")
84 public class MmsTest {
85     private static final String TAG = "MmsTest";
86 
87     private static final String ACTION_MMS_SENT = "CTS_MMS_SENT_ACTION";
88     private static final String ACTION_MMS_DOWNLOAD = "CTS_MMS_DOWNLOAD_ACTION";
89     public static final String ACTION_WAP_PUSH_DELIVER_DEFAULT_APP =
90             "CTS_WAP_PUSH_DELIVER_DEFAULT_APP_ACTION";
91     private static final long DEFAULT_EXPIRY_TIME = 7 * 24 * 60 * 60;
92     private static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
93     private static final long MESSAGE_ID = 912412L;
94 
95     private static final String SUBJECT = "CTS MMS Test";
96     private static final String MESSAGE_BODY = "CTS MMS test message body";
97     private static final String TEXT_PART_FILENAME = "text_0.txt";
98     private static final String sSmilText =
99             "<smil>" +
100                     "<head>" +
101                         "<layout>" +
102                             "<root-layout/>" +
103                             "<region height=\"100%%\" id=\"Text\" left=\"0%%\" top=\"0%%\" width=\"100%%\"/>" +
104                         "</layout>" +
105                     "</head>" +
106                     "<body>" +
107                         "<par dur=\"8000ms\">" +
108                             "<text src=\"%s\" region=\"Text\"/>" +
109                         "</par>" +
110                     "</body>" +
111             "</smil>";
112 
113     private static final long SENT_TIMEOUT = 1000 * 60 * 5; // 5 minutes
114     private static final long NO_CALLS_TIMEOUT = 1000; // 1 second
115 
116     private static final String PROVIDER_AUTHORITY = "telephonyctstest";
117 
118     private Random mRandom;
119     private SentReceiver mSentReceiver;
120     private SentReceiver mDeliveryReceiver;
121     private TelephonyManager mTelephonyManager;
122     @Nullable private String mOriginalDefaultSmsApp;
123     private static CarrierConfigReceiver sCarrierConfigReceiver;
124 
125     private static class SentReceiver extends BroadcastReceiver {
126         private final Object mLock;
127         private boolean mSuccess;
128         private boolean mDone;
129         private int mExpectedErrorResultCode;
130         private String mAction;
131 
SentReceiver(int expectedErrorResultCode, String action)132         SentReceiver(int expectedErrorResultCode, String action) {
133             mLock = new Object();
134             mSuccess = false;
135             mDone = false;
136             mExpectedErrorResultCode = expectedErrorResultCode;
137             mAction = action;
138         }
139 
140         @Override
onReceive(Context context, Intent intent)141         public void onReceive(Context context, Intent intent) {
142             Log.i(TAG, "onReceive Action " + intent.getAction() + ", mAction " + mAction);
143 
144             switch (intent.getAction()) {
145                 case ACTION_MMS_SENT:
146                     final int resultCode = getResultCode();
147                     if (resultCode == Activity.RESULT_OK) {
148                         final byte[] response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA);
149                         if (response != null) {
150                             final GenericPdu pdu = new PduParser(
151                                     response, shouldParseContentDisposition()).parse();
152                             if (pdu != null && pdu instanceof SendConf) {
153                                 final SendConf sendConf = (SendConf) pdu;
154                                 if (sendConf.getResponseStatus() == PduHeaders.RESPONSE_STATUS_OK) {
155                                     mSuccess = true;
156                                 } else {
157                                     Log.e(TAG,
158                                             "SendConf response status="
159                                                     + sendConf.getResponseStatus());
160                                 }
161                             } else {
162                                 Log.e(TAG, "Not a SendConf: " + (pdu != null
163                                         ? pdu.getClass().getCanonicalName() : "NULL"));
164                             }
165                         } else {
166                             Log.e(TAG, "Empty response");
167                         }
168                     } else {
169                         Log.e(TAG, "Failure result=" + resultCode);
170                         if (resultCode == mExpectedErrorResultCode) {
171                             mSuccess = true;
172                         }
173                         if (resultCode == SmsManager.MMS_ERROR_HTTP_FAILURE) {
174                             final int httpError = intent.getIntExtra(
175                                     SmsManager.EXTRA_MMS_HTTP_STATUS,
176                                     0);
177                             Log.e(TAG, "HTTP failure=" + httpError);
178                         }
179                     }
180                     break;
181                 case ACTION_WAP_PUSH_DELIVER_DEFAULT_APP:
182                     mSuccess = true;
183                     break;
184             }
185 
186             if (intent.getAction().equals(mAction)) {
187                 synchronized (mLock) {
188                     mDone = true;
189                     mLock.notify();
190                 }
191             }
192         }
193 
waitForSuccess(long timeout)194         public boolean waitForSuccess(long timeout) {
195             synchronized(mLock) {
196                 final long startTime = SystemClock.elapsedRealtime();
197                 long waitTime = timeout;
198                 while (!mDone && waitTime > 0) {
199                     try {
200                         mLock.wait(waitTime);
201                     } catch (InterruptedException e) {
202                         // Ignore
203                     }
204                     waitTime = timeout - (SystemClock.elapsedRealtime() - startTime);
205                 }
206                 Log.i(TAG, "Wait for sent: done=" + mDone + ", success=" + mSuccess);
207                 return mDone && mSuccess;
208             }
209         }
210 
verifyNoCalls(long timeout)211         public boolean verifyNoCalls(long timeout) {
212             synchronized (mLock) {
213                 try {
214                     mLock.wait(timeout);
215                 } catch (InterruptedException e) {
216                     // Ignore
217                 }
218                 return (!mDone && !mSuccess);
219             }
220         }
221     }
222 
223     /**
224      * Setup before all tests.
225      */
226     @BeforeClass
beforeAllTests()227     public static void beforeAllTests() {
228         Log.i(TAG, "beforeAllTests");
229         sCarrierConfigReceiver = new CarrierConfigReceiver();
230         IntentFilter filter = new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
231         // ACTION_CARRIER_CONFIG_CHANGED is sticky, so we will get a callback right away.
232         getInstrumentation().getContext().registerReceiver(sCarrierConfigReceiver, filter);
233     }
234 
235     /**
236      * Clean up resources after all tests.
237      */
238     @AfterClass
afterAllTests()239     public static void afterAllTests() {
240         Log.i(TAG, "afterAllTests");
241 
242         // Ensure there are no CarrierConfig overrides.
243         overrideCarrierConfig(SmsManager.getDefaultSmsSubscriptionId(), null);
244         if (sCarrierConfigReceiver != null) {
245             getInstrumentation().getContext().unregisterReceiver(sCarrierConfigReceiver);
246             sCarrierConfigReceiver = null;
247         }
248     }
249 
250     @Before
setUp()251     public void setUp() throws Exception {
252         mRandom = new Random();
253         mTelephonyManager =
254                 (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
255         assumeTrue(getContext().getPackageManager().hasSystemFeature(
256                 PackageManager.FEATURE_TELEPHONY_MESSAGING));
257         mOriginalDefaultSmsApp = DefaultSmsAppHelper.getDefaultSmsApp(getContext());
258         DefaultSmsAppHelper.stopBeingDefaultSmsApp();
259     }
260 
261     @After
tearDown()262     public void tearDown() throws Exception {
263         if (!TextUtils.isEmpty(mOriginalDefaultSmsApp)) {
264             assertTrue(DefaultSmsAppHelper.setDefaultSmsApp(getContext(), mOriginalDefaultSmsApp));
265         }
266     }
267 
268     @Test
269     @ApiTest(apis = "android.telephony.SmsManager#sendMultimediaMessage")
testSendMmsMessage()270     public void testSendMmsMessage() {
271         Log.i("MmsTest", "testSendMmsMessage");
272 
273         // Test non-default SMS app
274         sendMmsMessage(0L /* messageId */, Activity.RESULT_OK, SmsManager.getDefault(), false);
275 
276         // Test default SMS app
277         DefaultSmsAppHelper.ensureDefaultSmsApp();
278         sendMmsMessage(0L /* messageId */, Activity.RESULT_OK, SmsManager.getDefault(), true);
279         DefaultSmsAppHelper.stopBeingDefaultSmsApp();
280     }
281 
282     @Test
283     @ApiTest(apis = "android.telephony.SmsManager#sendMultimediaMessage")
testSendMmsMessageWithInactiveSubscriptionId()284     public void testSendMmsMessageWithInactiveSubscriptionId() {
285         int inactiveSubId = 127;
286 
287         // Test non-default SMS app
288         sendMmsMessage(0L /* messageId */, SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION,
289                 SmsManager.getSmsManagerForSubscriptionId(inactiveSubId), false);
290 
291         // Test default SMS app
292         DefaultSmsAppHelper.ensureDefaultSmsApp();
293         sendMmsMessage(0L /* messageId */, SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION,
294                 SmsManager.getSmsManagerForSubscriptionId(inactiveSubId), true);
295         DefaultSmsAppHelper.stopBeingDefaultSmsApp();
296     }
297 
298     @Test
299     @ApiTest(apis = "android.telephony.SmsManager#sendMultimediaMessage")
testSendMmsMessageWithMmsDisabled()300     public void testSendMmsMessageWithMmsDisabled() {
301         if (!Flags.mmsDisabledError()) {
302             Log.i(TAG, "testSendMmsMessageWithMmsDisabled: mmsDisabledError is not enabled");
303             return;
304         }
305         Log.i(TAG, "testSendMmsMessageWithMmsDisabled");
306 
307         // Disable MMS carrier config
308         PersistableBundle bundle = new PersistableBundle();
309         bundle.putBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED, false);
310         assertTrue(overrideCarrierConfig(SmsManager.getDefaultSmsSubscriptionId(), bundle));
311         assertFalse(doesSupportMMS());
312 
313         // It takes some time for the new carrier config loaded to MmsConfigManager
314         waitFor(TimeUnit.SECONDS.toMillis(1));
315 
316         // Test non-default SMS app
317         sendMmsMessage(0L /* messageId */, SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER,
318                 SmsManager.getDefault(), false);
319 
320         // Test default SMS app
321         DefaultSmsAppHelper.ensureDefaultSmsApp();
322         sendMmsMessage(0L /* messageId */, SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER,
323                 SmsManager.getDefault(), true);
324         DefaultSmsAppHelper.stopBeingDefaultSmsApp();
325 
326         // Restore MMS config
327         if (doesSupportMMS()) {
328             bundle.putBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED, true);
329             assertTrue(overrideCarrierConfig(SmsManager.getDefaultSmsSubscriptionId(), bundle));
330         }
331     }
332 
333     @Test
334     @ApiTest(apis = "android.telephony.SmsManager#sendMultimediaMessage")
testSendMmsMessageWithMessageId()335     public void testSendMmsMessageWithMessageId() {
336         // Test non-default SMS app
337         sendMmsMessage(MESSAGE_ID, Activity.RESULT_OK, SmsManager.getDefault(), false);
338 
339         // Test default SMS app
340         DefaultSmsAppHelper.ensureDefaultSmsApp();
341         sendMmsMessage(MESSAGE_ID, Activity.RESULT_OK, SmsManager.getDefault(), true);
342         DefaultSmsAppHelper.stopBeingDefaultSmsApp();
343     }
344 
sendMmsMessage(long messageId, int expectedErrorResultCode, SmsManager smsManager, boolean defaultSmsApp)345     private void sendMmsMessage(long messageId, int expectedErrorResultCode,
346             SmsManager smsManager, boolean defaultSmsApp) {
347         if (!doesSupportMMS()
348                 && expectedErrorResultCode != SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER) {
349             Log.i(TAG, "testSendMmsMessage skipped: no telephony available or MMS not supported");
350             return;
351         }
352 
353         String selfNumber;
354         getInstrumentation().getUiAutomation()
355                 .adoptShellPermissionIdentity(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
356         try {
357             int subId = mTelephonyManager.getSubscriptionId();
358             SubscriptionManager subscriptionManager = getContext()
359                     .getSystemService(SubscriptionManager.class);
360             selfNumber = subscriptionManager.getPhoneNumber(subId);
361         } finally {
362             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
363         }
364         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
365                 TextUtils.isEmpty(selfNumber));
366 
367         Log.i(TAG, "testSendMmsMessage");
368 
369         final Context context = getContext();
370         // Register sent receiver
371         mSentReceiver = new SentReceiver(expectedErrorResultCode, ACTION_MMS_SENT);
372         context.registerReceiver(mSentReceiver, new IntentFilter(ACTION_MMS_SENT),
373                 Context.RECEIVER_EXPORTED);
374 
375         mDeliveryReceiver = new SentReceiver(expectedErrorResultCode,
376                 ACTION_WAP_PUSH_DELIVER_DEFAULT_APP);
377         context.registerReceiver(mDeliveryReceiver,
378                 new IntentFilter(ACTION_WAP_PUSH_DELIVER_DEFAULT_APP), Context.RECEIVER_EXPORTED);
379 
380         // Create local provider file for sending PDU
381         final String fileName = "send." + String.valueOf(Math.abs(mRandom.nextLong())) + ".dat";
382         final File sendFile = new File(context.getCacheDir(), fileName);
383         final byte[] pdu = buildPdu(context, selfNumber, SUBJECT, MESSAGE_BODY);
384         assertNotNull(pdu);
385         assertTrue(writePdu(sendFile, pdu));
386         final Uri contentUri = (new Uri.Builder())
387                 .authority(PROVIDER_AUTHORITY)
388                 .path(fileName)
389                 .scheme(ContentResolver.SCHEME_CONTENT)
390                 .build();
391         // Send
392         final PendingIntent pendingIntent = PendingIntent.getBroadcast(
393                 context, 0, new Intent(ACTION_MMS_SENT).setPackage(context.getPackageName()),
394                 PendingIntent.FLAG_MUTABLE);
395         if (messageId == 0L) {
396             smsManager.sendMultimediaMessage(context,
397                     contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
398         } else {
399             smsManager.sendMultimediaMessage(context,
400                     contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent,
401                     messageId);
402         }
403         assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT));
404         assertEquals(expectedErrorResultCode, mSentReceiver.getResultCode());
405 
406         if (expectedErrorResultCode == Activity.RESULT_OK) {
407             int carrierId = mTelephonyManager.getSimCarrierId();
408             assertFalse("[RERUN] Carrier [carrier-id: " + carrierId + "] does not support "
409                             + "loop back messages. Use another carrier.",
410                     CarrierCapability.UNSUPPORT_LOOP_BACK_MESSAGES.contains(carrierId));
411         }
412 
413         if (defaultSmsApp && expectedErrorResultCode == Activity.RESULT_OK) {
414             // Default SMS App should receive android.provider.Telephony.WAP_PUSH_DELIVER
415             assertTrue(mDeliveryReceiver.waitForSuccess(SENT_TIMEOUT));
416         } else {
417             // Non-default SMS App should not receive android.provider.Telephony.WAP_PUSH_DELIVER.
418             // Default SMS App will not receive android.provider.Telephony.WAP_PUSH_DELIVER in case
419             // of fail to send a message.
420             assertTrue(mDeliveryReceiver.verifyNoCalls(NO_CALLS_TIMEOUT));
421         }
422         sendFile.delete();
423     }
424 
writePdu(File file, byte[] pdu)425     private static boolean writePdu(File file, byte[] pdu) {
426         FileOutputStream writer = null;
427         try {
428             writer = new FileOutputStream(file);
429             writer.write(pdu);
430             return true;
431         } catch (final IOException e) {
432             return false;
433         } finally {
434             if (writer != null) {
435                 try {
436                     writer.close();
437                 } catch (IOException e) {
438                 }
439             }
440         }
441     }
442 
buildPdu(Context context, String selfNumber, String subject, String text)443     private byte[] buildPdu(Context context, String selfNumber, String subject, String text) {
444         final SendReq req = new SendReq();
445         // From, per spec
446         req.setFrom(new EncodedStringValue(selfNumber));
447         // To
448         final String[] recipients = new String[1];
449         recipients[0] = selfNumber;
450         final EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipients);
451         if (encodedNumbers != null) {
452             req.setTo(encodedNumbers);
453         }
454         // Subject
455         if (!TextUtils.isEmpty(subject)) {
456             req.setSubject(new EncodedStringValue(subject));
457         }
458         // Date
459         req.setDate(System.currentTimeMillis() / 1000);
460         // Body
461         final PduBody body = new PduBody();
462         // Add text part. Always add a smil part for compatibility, without it there
463         // may be issues on some carriers/client apps
464         final int size = addTextPart(body, text, true/* add text smil */);
465         req.setBody(body);
466         // Message size
467         req.setMessageSize(size);
468         // Message class
469         req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
470         // Expiry
471         req.setExpiry(DEFAULT_EXPIRY_TIME);
472         // The following set methods throw InvalidHeaderValueException
473         try {
474             // Priority
475             req.setPriority(DEFAULT_PRIORITY);
476             // Delivery report
477             req.setDeliveryReport(PduHeaders.VALUE_NO);
478             // Read report
479             req.setReadReport(PduHeaders.VALUE_NO);
480         } catch (InvalidHeaderValueException e) {
481             return null;
482         }
483 
484         return new PduComposer(context, req).make();
485     }
486 
addTextPart(PduBody pb, String message, boolean addTextSmil)487     private static int addTextPart(PduBody pb, String message, boolean addTextSmil) {
488         final PduPart part = new PduPart();
489         // Set Charset if it's a text media.
490         part.setCharset(CharacterSets.UTF_8);
491         // Set Content-Type.
492         part.setContentType(ContentType.TEXT_PLAIN.getBytes());
493         // Set Content-Location.
494         part.setContentLocation(TEXT_PART_FILENAME.getBytes());
495         int index = TEXT_PART_FILENAME.lastIndexOf(".");
496         String contentId = (index == -1) ? TEXT_PART_FILENAME
497                 : TEXT_PART_FILENAME.substring(0, index);
498         part.setContentId(contentId.getBytes());
499         part.setData(message.getBytes());
500         pb.addPart(part);
501         if (addTextSmil) {
502             final String smil = String.format(sSmilText, TEXT_PART_FILENAME);
503             addSmilPart(pb, smil);
504         }
505         return part.getData().length;
506     }
507 
addSmilPart(PduBody pb, String smil)508     private static void addSmilPart(PduBody pb, String smil) {
509         final PduPart smilPart = new PduPart();
510         smilPart.setContentId("smil".getBytes());
511         smilPart.setContentLocation("smil.xml".getBytes());
512         smilPart.setContentType(ContentType.APP_SMIL.getBytes());
513         smilPart.setData(smil.getBytes());
514         pb.addPart(0, smilPart);
515     }
516 
shouldParseContentDisposition()517     private static boolean shouldParseContentDisposition() {
518         return SmsManager
519                 .getDefault()
520                 .getCarrierConfigValues()
521                 .getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
522     }
523 
doesSupportMMS()524     private static boolean doesSupportMMS() {
525         return SmsManager
526                 .getDefault()
527                 .getCarrierConfigValues()
528                 .getBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED, true);
529     }
530 
531     @Test
testDownloadMultimediaMessage()532     public void testDownloadMultimediaMessage() {
533         downloadMultimediaMessage(0L /* messageId */);
534     }
535 
536     @Test
testDownloadMultimediaMessageWithMessageId()537     public void testDownloadMultimediaMessageWithMessageId() {
538         downloadMultimediaMessage(MESSAGE_ID);
539     }
540 
downloadMultimediaMessage(long messageId)541     private void downloadMultimediaMessage(long messageId) {
542         if (!doesSupportMMS()) {
543             Log.i(TAG, "testSendMmsMessage skipped: no telephony available or MMS not supported");
544             return;
545         }
546 
547         Log.i(TAG, "testSendMmsMessage");
548         // Prime the MmsService so that MMS config is loaded
549         final SmsManager smsManager = SmsManager.getDefault();
550         smsManager.getCarrierConfigValues();
551         // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
552         waitFor(TimeUnit.SECONDS.toMillis(1));
553 
554         final Context context = getContext();
555         // Create local provider file
556         final String fileName = "download." + String.valueOf(Math.abs(mRandom.nextLong())) + ".dat";
557         final File sendFile = new File(context.getCacheDir(), fileName);
558         final Uri contentUri = (new Uri.Builder())
559                 .authority(PROVIDER_AUTHORITY)
560                 .path(fileName)
561                 .scheme(ContentResolver.SCHEME_CONTENT)
562                 .build();
563 
564         final PendingIntent pendingIntent = PendingIntent.getBroadcast(
565                 context, 0, new Intent(ACTION_MMS_DOWNLOAD).setPackage(context.getPackageName()),
566                 PendingIntent.FLAG_MUTABLE);
567 
568         if (messageId == 0L) {
569             // Verify the downloadMultimediaMessage function without messageId exists. This test
570             // doesn't actually verify downloading is successful, just that the function to
571             // initiate the downloading has been implemented.
572             smsManager.downloadMultimediaMessage(context, "foo/fake", contentUri,
573                     null /* configOverrides */, pendingIntent);
574         } else {
575             // Verify the downloadMultimediaMessage function with messageId exists. This test
576             // doesn't actually verify downloading is successful, just that the function to
577             // initiate the downloading has been implemented.
578             smsManager.downloadMultimediaMessage(context, "foo/fake", contentUri,
579                     null /* configOverrides */, pendingIntent, MESSAGE_ID);
580         }
581     }
582 
583     private abstract static class BaseReceiver extends BroadcastReceiver {
584         protected CountDownLatch mLatch = new CountDownLatch(1);
585 
clearQueue()586         void clearQueue() {
587             mLatch = new CountDownLatch(1);
588         }
589 
waitForChanged()590         boolean waitForChanged() throws Exception {
591             return mLatch.await(5000, TimeUnit.MILLISECONDS);
592         }
593     }
594 
595     private static class CarrierConfigReceiver extends BaseReceiver {
596         private int mSubId;
597 
CarrierConfigReceiver()598         CarrierConfigReceiver() {}
599 
setSubId(int subId)600         public void setSubId(int subId) {
601             mSubId = subId;
602         }
603 
604         @Override
onReceive(Context context, Intent intent)605         public void onReceive(Context context, Intent intent) {
606             if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
607                 int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, -1);
608                 Log.d(TAG, "Carrier config changed for subId=" + subId
609                         + ", mSubId=" + mSubId);
610                 if (mSubId == subId) {
611                     mLatch.countDown();
612                 }
613             }
614         }
615     }
616 
overrideCarrierConfig(int subId, PersistableBundle bundle)617     private static boolean overrideCarrierConfig(int subId, PersistableBundle bundle) {
618         try {
619             CarrierConfigManager carrierConfigManager = getInstrumentation()
620                     .getContext().getSystemService(CarrierConfigManager.class);
621             if (carrierConfigManager == null) {
622                 Log.d(TAG, "CarrierConfigManager is not present on this device.");
623                 return false;
624             }
625             sCarrierConfigReceiver.clearQueue();
626             sCarrierConfigReceiver.setSubId(subId);
627             ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(carrierConfigManager,
628                     (m) -> m.overrideConfig(subId, bundle));
629             return sCarrierConfigReceiver.waitForChanged();
630         } catch (Exception ex) {
631             Log.e(TAG, "overrideCarrierConfig()", ex);
632             return false;
633         }
634     }
635 
waitFor(long timeoutMillis)636     private static void waitFor(long timeoutMillis) {
637         Object delayTimeout = new Object();
638         synchronized (delayTimeout) {
639             try {
640                 delayTimeout.wait(timeoutMillis);
641             } catch (InterruptedException ex) {
642                 // Ignore the exception
643                 Log.d(TAG, "waitFor: delayTimeout ex=" + ex);
644             }
645         }
646     }
647 }
648