Home | History | Annotate | Download | only in cts
      1 package android.location.cts;
      2 
      3 import android.app.Activity;
      4 import android.app.PendingIntent;
      5 import android.content.BroadcastReceiver;
      6 import android.content.ContentResolver;
      7 import android.content.Context;
      8 import android.content.Intent;
      9 import android.content.IntentFilter;
     10 import android.content.pm.PackageManager;
     11 import android.net.Uri;
     12 import android.os.SystemClock;
     13 import android.telephony.SmsManager;
     14 import android.telephony.TelephonyManager;
     15 import android.test.AndroidTestCase;
     16 import android.text.TextUtils;
     17 import android.util.Log;
     18 
     19 import com.google.android.mms.ContentType;
     20 import com.google.android.mms.InvalidHeaderValueException;
     21 import com.google.android.mms.pdu.CharacterSets;
     22 import com.google.android.mms.pdu.EncodedStringValue;
     23 import com.google.android.mms.pdu.GenericPdu;
     24 import com.google.android.mms.pdu.PduBody;
     25 import com.google.android.mms.pdu.PduComposer;
     26 import com.google.android.mms.pdu.PduHeaders;
     27 import com.google.android.mms.pdu.PduParser;
     28 import com.google.android.mms.pdu.PduPart;
     29 import com.google.android.mms.pdu.SendConf;
     30 import com.google.android.mms.pdu.SendReq;
     31 
     32 import java.io.File;
     33 import java.io.FileOutputStream;
     34 import java.io.IOException;
     35 import java.util.Random;
     36 import java.util.concurrent.CountDownLatch;
     37 import java.util.concurrent.TimeUnit;
     38 
     39 /**
     40  * Test sending SMS and MMS using {@link android.telephony.SmsManager}.
     41  */
     42 public class EmergencyCallMessageTest extends GnssTestCase {
     43 
     44     private static final String TAG = "EmergencyCallMSGTest";
     45 
     46     private static final String ACTION_MMS_SENT = "CTS_MMS_SENT_ACTION";
     47     private static final long DEFAULT_EXPIRY_TIME_SECS = TimeUnit.DAYS.toSeconds(7);
     48     private static final long MMS_CONFIG_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(1);
     49     private static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
     50     private static final short DEFAULT_DATA_SMS_PORT = 8091;
     51     private static final String PHONE_NUMBER_KEY = "android.cts.emergencycall.phonenumber";
     52     private static final String SUBJECT = "CTS Emergency Call MMS Test";
     53     private static final String MMS_MESSAGE_BODY = "CTS Emergency Call MMS test message body";
     54     private static final String SMS_MESSAGE_BODY = "CTS Emergency Call Sms test message body";
     55     private static final String SMS_DATA_MESSAGE_BODY =
     56         "CTS Emergency Call Sms data test message body";
     57     private static final String TEXT_PART_FILENAME = "text_0.txt";
     58     private static final String SMIL_TEXT =
     59             "<smil>" +
     60                     "<head>" +
     61                         "<layout>" +
     62                             "<root-layout/>" +
     63                             "<region height=\"100%%\" id=\"Text\" left=\"0%%\" top=\"0%%\" width=\"100%%\"/>" +
     64                         "</layout>" +
     65                     "</head>" +
     66                     "<body>" +
     67                         "<par dur=\"8000ms\">" +
     68                             "<text src=\"%s\" region=\"Text\"/>" +
     69                         "</par>" +
     70                     "</body>" +
     71             "</smil>";
     72 
     73     private static final long SENT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5); // 5 minutes
     74 
     75     private static final String PROVIDER_AUTHORITY = "emergencycallverifier";
     76 
     77     private Random mRandom;
     78     private SentReceiver mSentReceiver;
     79     private TelephonyManager mTelephonyManager;
     80     private PackageManager mPackageManager;
     81 
     82     private static class SentReceiver extends BroadcastReceiver {
     83         private boolean mSuccess;
     84         private boolean mDone;
     85         private final CountDownLatch mLatch;
     86         public SentReceiver() {
     87             mLatch =  new CountDownLatch(1);
     88             mSuccess = false;
     89             mDone = false;
     90         }
     91 
     92         @Override
     93         public void onReceive(Context context, Intent intent) {
     94             Log.i(TAG, "Action " + intent.getAction());
     95             if (!ACTION_MMS_SENT.equals(intent.getAction())) {
     96                 return;
     97             }
     98             final int resultCode = getResultCode();
     99             if (resultCode == Activity.RESULT_OK) {
    100                 final byte[] response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA);
    101                 if (response != null) {
    102                     final GenericPdu pdu = new PduParser(
    103                             response, shouldParseContentDisposition()).parse();
    104                     if (pdu != null && pdu instanceof SendConf) {
    105                         final SendConf sendConf = (SendConf) pdu;
    106                         if (sendConf.getResponseStatus() == PduHeaders.RESPONSE_STATUS_OK) {
    107                             mSuccess = true;
    108                         } else {
    109                             Log.e(TAG, "SendConf response status=" + sendConf.getResponseStatus());
    110                         }
    111                     } else {
    112                         Log.e(TAG, "Not a SendConf: " +
    113                                 (pdu != null ? pdu.getClass().getCanonicalName() : "NULL"));
    114                     }
    115                 } else {
    116                     Log.e(TAG, "Empty response");
    117                 }
    118             } else {
    119                 Log.e(TAG, "Failure result=" + resultCode);
    120                 if (resultCode == SmsManager.MMS_ERROR_HTTP_FAILURE) {
    121                     final int httpError = intent.getIntExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, 0);
    122                     Log.e(TAG, "HTTP failure=" + httpError);
    123                 }
    124             }
    125             mDone = true;
    126             mLatch.countDown();
    127         }
    128 
    129         public boolean waitForSuccess(long timeoutMs) throws Exception {
    130             mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
    131             Log.i(TAG, "Wait for sent: done=" + mDone + ", success=" + mSuccess);
    132             return mDone && mSuccess;
    133         }
    134     }
    135 
    136     @Override
    137     protected void setUp() throws Exception {
    138         super.setUp();
    139 
    140         mRandom = new Random(System.currentTimeMillis());
    141         mTelephonyManager =
    142                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    143         mPackageManager = mContext.getPackageManager();
    144     }
    145 
    146     public void testSendSmsMessage() {
    147         // this test is only for cts verifier
    148         if (!isCtsVerifierTest()) {
    149             return;
    150         }
    151         SmsManager smsManager = SmsManager.getDefault();
    152         final String selfNumber = getPhoneNumber(mContext);
    153         smsManager.sendTextMessage(selfNumber, null, SMS_MESSAGE_BODY, null, null);
    154     }
    155 
    156     public void testSendSmsDataMessage() {
    157         // this test is only for cts verifier
    158         if (!isCtsVerifierTest()) {
    159             return;
    160         }
    161         SmsManager smsManager = SmsManager.getDefault();
    162         final String selfNumber = getPhoneNumber(mContext);
    163         smsManager.sendDataMessage(selfNumber, null, DEFAULT_DATA_SMS_PORT,
    164             SMS_DATA_MESSAGE_BODY.getBytes(), null, null);
    165     }
    166 
    167     public void testSendMmsMessage() throws Exception {
    168         // this test is only for cts verifier
    169         if (!isCtsVerifierTest()) {
    170             return;
    171         }
    172         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
    173              || !doesSupportMMS()) {
    174             Log.i(TAG, "testSendMmsMessage skipped: no telephony available or MMS not supported");
    175             return;
    176         }
    177 
    178         Log.i(TAG, "testSendMmsMessage");
    179         // Prime the MmsService so that MMS config is loaded
    180         final SmsManager smsManager = SmsManager.getDefault();
    181         // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
    182         try {
    183             Thread.sleep(MMS_CONFIG_DELAY_MILLIS);
    184         } catch (InterruptedException e) {
    185             // Ignore
    186         }
    187 
    188         final Context context = getContext();
    189         // Register sent receiver
    190         mSentReceiver = new SentReceiver();
    191         context.registerReceiver(mSentReceiver, new IntentFilter(ACTION_MMS_SENT));
    192         // Create local provider file for sending PDU
    193         final String fileName = "send." + String.valueOf(Math.abs(mRandom.nextLong())) + ".dat";
    194         final File sendFile = new File(context.getCacheDir(), fileName);
    195         final String selfNumber = getPhoneNumber(context);
    196         assertTrue(!TextUtils.isEmpty(selfNumber));
    197         final byte[] pdu = buildPdu(context, selfNumber, SUBJECT, MMS_MESSAGE_BODY);
    198         assertNotNull(pdu);
    199         assertTrue(writePdu(sendFile, pdu));
    200         final Uri contentUri = (new Uri.Builder())
    201                 .authority(PROVIDER_AUTHORITY)
    202                 .path(fileName)
    203                 .scheme(ContentResolver.SCHEME_CONTENT)
    204                 .build();
    205         // Send
    206         final PendingIntent pendingIntent = PendingIntent.getBroadcast(
    207                 context, 0, new Intent(ACTION_MMS_SENT), 0);
    208         smsManager.sendMultimediaMessage(context,
    209                 contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
    210         assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT_MILLIS));
    211         sendFile.delete();
    212     }
    213 
    214     private static boolean writePdu(File file, byte[] pdu) {
    215         FileOutputStream writer = null;
    216         try {
    217             writer = new FileOutputStream(file);
    218             writer.write(pdu);
    219             return true;
    220         } catch (final IOException e) {
    221             String stackTrace = Log.getStackTraceString(e);
    222             Log.i(TAG, stackTrace);
    223             return false;
    224         } finally {
    225             if (writer != null) {
    226                 try {
    227                     writer.close();
    228                 } catch (IOException e) {
    229                 }
    230             }
    231         }
    232     }
    233 
    234     private static byte[] buildPdu(Context context, String selfNumber, String subject,
    235        String text) {
    236         final SendReq req = new SendReq();
    237         // From, per spec
    238         req.setFrom(new EncodedStringValue(selfNumber));
    239         // To
    240         final String[] recipients = new String[1];
    241         recipients[0] = selfNumber;
    242         final EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipients);
    243         if (encodedNumbers != null) {
    244             req.setTo(encodedNumbers);
    245         }
    246         // Subject
    247         if (!TextUtils.isEmpty(subject)) {
    248             req.setSubject(new EncodedStringValue(subject));
    249         }
    250         // Date
    251         req.setDate(System.currentTimeMillis() / 1000);
    252         // Body
    253         final PduBody body = new PduBody();
    254         // Add text part. Always add a smil part for compatibility, without it there
    255         // may be issues on some carriers/client apps
    256         final int size = addTextPart(body, text, true/* add text smil */);
    257         req.setBody(body);
    258         // Message size
    259         req.setMessageSize(size);
    260         // Message class
    261         req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
    262         // Expiry
    263         req.setExpiry(DEFAULT_EXPIRY_TIME_SECS);
    264         // The following set methods throw InvalidHeaderValueException
    265         try {
    266             // Priority
    267             req.setPriority(DEFAULT_PRIORITY);
    268             // Delivery report
    269             req.setDeliveryReport(PduHeaders.VALUE_NO);
    270             // Read report
    271             req.setReadReport(PduHeaders.VALUE_NO);
    272         } catch (InvalidHeaderValueException e) {
    273             return null;
    274         }
    275 
    276         return new PduComposer(context, req).make();
    277     }
    278 
    279     private static int addTextPart(PduBody pb, String message, boolean addTextSmil) {
    280         final PduPart part = new PduPart();
    281         // Set Charset if it's a text media.
    282         part.setCharset(CharacterSets.UTF_8);
    283         // Set Content-Type.
    284         part.setContentType(ContentType.TEXT_PLAIN.getBytes());
    285         // Set Content-Location.
    286         part.setContentLocation(TEXT_PART_FILENAME.getBytes());
    287         int index = TEXT_PART_FILENAME.lastIndexOf(".");
    288         String contentId = (index == -1) ? TEXT_PART_FILENAME
    289                 : TEXT_PART_FILENAME.substring(0, index);
    290         part.setContentId(contentId.getBytes());
    291         part.setData(message.getBytes());
    292         pb.addPart(part);
    293         if (addTextSmil) {
    294             final String smil = String.format(SMIL_TEXT, TEXT_PART_FILENAME);
    295             addSmilPart(pb, smil);
    296         }
    297         return part.getData().length;
    298     }
    299 
    300     private static void addSmilPart(PduBody pb, String smil) {
    301         final PduPart smilPart = new PduPart();
    302         smilPart.setContentId("smil".getBytes());
    303         smilPart.setContentLocation("smil.xml".getBytes());
    304         smilPart.setContentType(ContentType.APP_SMIL.getBytes());
    305         smilPart.setData(smil.getBytes());
    306         pb.addPart(0, smilPart);
    307     }
    308 
    309     private static String getPhoneNumber(Context context) {
    310         final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
    311                 Context.TELEPHONY_SERVICE);
    312         String phoneNumber = telephonyManager.getLine1Number();
    313         if (phoneNumber.trim().isEmpty()) {
    314             phoneNumber = System.getProperty(PHONE_NUMBER_KEY);
    315         }
    316         return phoneNumber;
    317     }
    318 
    319     private static boolean shouldParseContentDisposition() {
    320         return SmsManager
    321                 .getDefault()
    322                 .getCarrierConfigValues()
    323                 .getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
    324     }
    325 
    326     private static boolean doesSupportMMS() {
    327         return SmsManager
    328                 .getDefault()
    329                 .getCarrierConfigValues()
    330                 .getBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED, true);
    331     }
    332 
    333 }