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