Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2017 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 com.googlecode.android_scripting.facade.telephony;
     18 
     19 import android.app.Activity;
     20 import android.app.PendingIntent;
     21 import android.app.Service;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.net.Uri;
     28 import android.os.Bundle;
     29 import android.provider.Telephony.Sms.Intents;
     30 import android.telephony.SmsCbCmasInfo;
     31 import android.telephony.SmsCbEtwsInfo;
     32 import android.telephony.SmsCbMessage;
     33 import android.telephony.SmsManager;
     34 import android.telephony.SmsMessage;
     35 import android.telephony.SubscriptionManager;
     36 import android.telephony.TelephonyManager;
     37 
     38 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
     39 import com.android.internal.telephony.gsm.SmsCbConstants;
     40 
     41 import com.google.android.mms.ContentType;
     42 import com.google.android.mms.InvalidHeaderValueException;
     43 import com.google.android.mms.pdu.CharacterSets;
     44 import com.google.android.mms.pdu.EncodedStringValue;
     45 import com.google.android.mms.pdu.GenericPdu;
     46 import com.google.android.mms.pdu.PduBody;
     47 import com.google.android.mms.pdu.PduComposer;
     48 import com.google.android.mms.pdu.PduHeaders;
     49 import com.google.android.mms.pdu.PduParser;
     50 import com.google.android.mms.pdu.PduPart;
     51 import com.google.android.mms.pdu.SendConf;
     52 import com.google.android.mms.pdu.SendReq;
     53 import com.googlecode.android_scripting.Log;
     54 import com.googlecode.android_scripting.facade.EventFacade;
     55 import com.googlecode.android_scripting.facade.FacadeManager;
     56 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     57 import com.googlecode.android_scripting.rpc.Rpc;
     58 import com.googlecode.android_scripting.rpc.RpcOptional;
     59 import com.googlecode.android_scripting.rpc.RpcParameter;
     60 
     61 import java.io.File;
     62 import java.io.FileOutputStream;
     63 import java.io.IOException;
     64 import java.lang.reflect.Field;
     65 import java.util.ArrayList;
     66 import java.util.HashMap;
     67 import java.util.List;
     68 
     69 /**
     70  * Exposes SmsManager functionality.
     71  */
     72 public class SmsFacade extends RpcReceiver {
     73 
     74     static final boolean DBG = false;
     75 
     76     private final EventFacade mEventFacade;
     77     private final SmsManager mSms;
     78     private final Context mContext;
     79     private final Service mService;
     80     private BroadcastReceiver mSmsSendListener;
     81     private BroadcastReceiver mSmsIncomingListener;
     82     private int mNumExpectedSentEvents;
     83     private int mNumExpectedDeliveredEvents;
     84     private boolean mListeningIncomingSms;
     85     private IntentFilter mEmergencyCBMessage;
     86     private BroadcastReceiver mGsmEmergencyCBMessageListener;
     87     private BroadcastReceiver mCdmaEmergencyCBMessageListener;
     88     private boolean mGsmEmergencyCBListenerRegistered;
     89     private boolean mCdmaEmergencyCBListenerRegistered;
     90     private boolean mSentReceiversRegistered;
     91     private Object lock = new Object();
     92     private File mMmsSendFile;
     93     private String mPackageName;
     94 
     95     private BroadcastReceiver mMmsSendListener;
     96     private BroadcastReceiver mMmsIncomingListener;
     97     private boolean mListeningIncomingMms;
     98 
     99     TelephonyManager mTelephonyManager;
    100 
    101     private static final String SMS_MESSAGE_STATUS_DELIVERED_ACTION =
    102             "com.googlecode.android_scripting.facade.telephony.SmsFacade.SMS_DELIVERED";
    103     private static final String SMS_MESSAGE_SENT_ACTION =
    104             "com.googlecode.android_scripting.facade.telephony.SmsFacade.SMS_SENT";
    105 
    106     private static final String EMERGENCY_CB_MESSAGE_RECEIVED_ACTION =
    107             "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED";
    108 
    109     private static final String MMS_MESSAGE_SENT_ACTION =
    110             "com.googlecode.android_scripting.facade.telephony.SmsFacade.MMS_SENT";
    111 
    112     private final int MAX_MESSAGE_LENGTH = 160;
    113     private final int INTERNATIONAL_NUMBER_LENGTH = 12;
    114     private final int DOMESTIC_NUMBER_LENGTH = 10;
    115 
    116     private static final String DEFAULT_FROM_PHONE_NUMBER = new String("8675309");
    117 
    118     // Core parameters needed for all types of message
    119     private static final String KEY_MESSAGE_ID = "message_id";
    120     private static final String KEY_MESSAGE = "message";
    121     private static final String KEY_MESSAGE_URI = "message_uri";
    122     private static final String KEY_SUB_PHONE_NUMBER = "sub_phone_number";
    123     private static final String KEY_RECIPIENTS = "recipients";
    124     private static final String KEY_MESSAGE_TEXT = "message_text";
    125     private static final String KEY_SUBJECT_TEXT = "subject_text";
    126 
    127     private final int[] mGsmCbMessageIdList = {
    128             SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING,
    129             SmsCbConstants.MESSAGE_ID_ETWS_TSUNAMI_WARNING,
    130             SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING,
    131             SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE,
    132             SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE,
    133             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL,
    134             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED,
    135             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY,
    136             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED,
    137             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY,
    138             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY,
    139             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED,
    140             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY,
    141             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY,
    142             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST,
    143             SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE
    144     };
    145 
    146     private final int[] mCdmaCbMessageIdList = {
    147             SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
    148             SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
    149             SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT,
    150             SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
    151             SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE
    152     };
    153 
    154     private static HashMap<Integer, String> sSmsSendFailureMap = new HashMap<Integer, String>();
    155     private static HashMap<Integer, String> sMmsSendFailureMap = new HashMap<Integer, String>();
    156     private static HashMap<Integer, String> sMmsSendResponseMap = new HashMap<Integer, String>();
    157 
    158     public SmsFacade(FacadeManager manager) {
    159 
    160         super(manager);
    161         mService = manager.getService();
    162         mContext = mService;
    163         mSms = SmsManager.getDefault();
    164         mEventFacade = manager.getReceiver(EventFacade.class);
    165         mPackageName = mContext.getPackageName();
    166         mSmsSendListener = new SmsSendListener();
    167         mSmsIncomingListener = new SmsIncomingListener();
    168         mNumExpectedSentEvents = 0;
    169         mNumExpectedDeliveredEvents = 0;
    170         mListeningIncomingSms = false;
    171         mGsmEmergencyCBMessageListener = new SmsEmergencyCBMessageListener();
    172         mCdmaEmergencyCBMessageListener = new SmsEmergencyCBMessageListener();
    173         mGsmEmergencyCBListenerRegistered = false;
    174         mCdmaEmergencyCBListenerRegistered = false;
    175         mSentReceiversRegistered = false;
    176 
    177         mMmsIncomingListener = new MmsIncomingListener();
    178         mMmsSendListener = new MmsSendListener();
    179 
    180         mListeningIncomingMms = false;
    181 
    182         IntentFilter smsFilter = new IntentFilter(SMS_MESSAGE_SENT_ACTION);
    183         smsFilter.addAction(SMS_MESSAGE_STATUS_DELIVERED_ACTION);
    184 
    185         IntentFilter mmsFilter = new IntentFilter(MMS_MESSAGE_SENT_ACTION);
    186 
    187         synchronized (lock) {
    188             mService.registerReceiver(mSmsSendListener, smsFilter);
    189             mService.registerReceiver(mMmsSendListener, mmsFilter);
    190             mSentReceiversRegistered = true;
    191         }
    192 
    193         mTelephonyManager =
    194             (TelephonyManager) mService.getSystemService(Context.TELEPHONY_SERVICE);
    195 
    196         try {
    197             Class<?> mSmsClass = mSms.getClass();
    198             Field[] fields = mSmsClass.getFields();
    199             for (Field field : fields) {
    200                 String name = field.getName();
    201                 if (name.startsWith("RESULT_")) {
    202                     sSmsSendFailureMap.put((Integer) field.get(mSmsClass), name);
    203                 } else if (name.startsWith("MMS_ERROR_")) {
    204                     sMmsSendFailureMap.put((Integer) field.get(mSmsClass), name);
    205                 }
    206             }
    207         } catch (Exception e) {
    208             Log.d("SmsFacade error: " + e.toString());
    209         }
    210 
    211         try {
    212             Class<?> mPduHeadersClass = PduHeaders.class;
    213             Field[] fields = mPduHeadersClass.getFields();
    214             for (Field field : fields) {
    215                 String name = field.getName();
    216                 if (name.startsWith("RESPONSE_STATUS_")) {
    217                     sMmsSendResponseMap.put((Integer) field.get(mPduHeadersClass), name);
    218                 }
    219             }
    220         } catch (Exception e) {
    221             Log.d("SmsFacade error: " + e.toString());
    222         }
    223     }
    224 
    225     // FIXME: Move to a utility class
    226     // FIXME: remove the MODE_WORLD_READABLE once we verify the use case
    227     @SuppressWarnings("deprecation")
    228     private boolean writeBytesToFile(String fileName, byte[] pdu) {
    229         FileOutputStream writer = null;
    230         try {
    231             writer = mContext.openFileOutput(fileName, Context.MODE_WORLD_READABLE);
    232             writer.write(pdu);
    233             return true;
    234         } catch (final IOException e) {
    235             return false;
    236         } finally {
    237             if (writer != null) {
    238                 try {
    239                     writer.close();
    240                 } catch (IOException e) {
    241                 }
    242             }
    243         }
    244     }
    245 
    246     // FIXME: Move to a utility class
    247     private boolean writeBytesToCacheFile(String fileName, byte[] pdu) {
    248         mMmsSendFile = new File(mContext.getCacheDir(), fileName);
    249         Log.d(String.format("filename:%s, directory:%s", fileName,
    250                 mContext.getCacheDir().toString()));
    251         FileOutputStream writer = null;
    252         try {
    253             writer = new FileOutputStream(mMmsSendFile);
    254             writer.write(pdu);
    255             return true;
    256         } catch (final IOException e) {
    257             Log.d("writeBytesToCacheFile() failed with " + e.toString());
    258             return false;
    259         } finally {
    260             if (writer != null) {
    261                 try {
    262                     writer.close();
    263                 } catch (IOException e) {
    264                 }
    265             }
    266         }
    267     }
    268 
    269     @Deprecated
    270     @Rpc(description = "Starts tracking incoming SMS.")
    271     public void smsStartTrackingIncomingMessage() {
    272         Log.d("Using Deprecated smsStartTrackingIncomingMessage!");
    273         smsStartTrackingIncomingSmsMessage();
    274     }
    275 
    276     @Rpc(description = "Starts tracking incoming SMS.")
    277     public void smsStartTrackingIncomingSmsMessage() {
    278         mService.registerReceiver(mSmsIncomingListener,
    279                 new IntentFilter(Intents.SMS_RECEIVED_ACTION));
    280         mListeningIncomingSms = true;
    281     }
    282 
    283     @Deprecated
    284     @Rpc(description = "Stops tracking incoming SMS.")
    285     public void smsStopTrackingIncomingMessage() {
    286         Log.d("Using Deprecated smsStopTrackingIncomingMessage!");
    287         smsStopTrackingIncomingSmsMessage();
    288     }
    289 
    290     @Rpc(description = "Stops tracking incoming SMS.")
    291     public void smsStopTrackingIncomingSmsMessage() {
    292         if (mListeningIncomingSms) {
    293             mListeningIncomingSms = false;
    294             try {
    295                 mService.unregisterReceiver(mSmsIncomingListener);
    296             } catch (Exception e) {
    297                 Log.e("Tried to unregister nonexistent SMS Listener!");
    298             }
    299         }
    300     }
    301 
    302     @Rpc(description = "Starts tracking incoming MMS.")
    303     public void smsStartTrackingIncomingMmsMessage() {
    304         IntentFilter mmsReceived = new IntentFilter(Intents.MMS_DOWNLOADED_ACTION);
    305         mmsReceived.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
    306         mmsReceived.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
    307         mService.registerReceiver(mMmsIncomingListener, mmsReceived);
    308         mListeningIncomingMms = true;
    309     }
    310 
    311     @Rpc(description = "Stops tracking incoming MMS.")
    312     public void smsStopTrackingIncomingMmsMessage() {
    313         if (mListeningIncomingMms) {
    314             mListeningIncomingMms = false;
    315             try {
    316                 mService.unregisterReceiver(mMmsIncomingListener);
    317             } catch (Exception e) {
    318                 Log.e("Tried to unregister nonexistent MMS Listener!");
    319             }
    320         }
    321     }
    322 
    323     // Currently requires 'adb shell su root setenforce 0'
    324     @Rpc(description = "Send a multimedia message to a specified number.")
    325     public void smsSendMultimediaMessage(
    326                         @RpcParameter(name = "toPhoneNumber")
    327             String toPhoneNumber,
    328                         @RpcParameter(name = "subject")
    329             String subject,
    330                         @RpcParameter(name = "message")
    331             String message,
    332             @RpcParameter(name = "fromPhoneNumber")
    333             @RpcOptional
    334             String fromPhoneNumber,
    335             @RpcParameter(name = "fileName")
    336             @RpcOptional
    337             String fileName) {
    338 
    339         MmsBuilder mms = new MmsBuilder();
    340 
    341         mms.setToPhoneNumber(toPhoneNumber);
    342         if (fromPhoneNumber == null) {
    343             mTelephonyManager.getLine1Number(); //TODO: b/21592513 - multi-sim awareness
    344         }
    345 
    346         mms.setFromPhoneNumber((fromPhoneNumber != null) ? fromPhoneNumber : DEFAULT_FROM_PHONE_NUMBER);
    347         mms.setSubject(subject);
    348         mms.setDate();
    349         mms.addMessageBody(message);
    350         mms.setMessageClass(MmsBuilder.MESSAGE_CLASS_PERSONAL);
    351         mms.setMessagePriority(MmsBuilder.DEFAULT_PRIORITY);
    352         mms.setDeliveryReport(true);
    353         mms.setReadReport(true);
    354         // Default to 1 week;
    355         mms.setExpirySeconds(MmsBuilder.DEFAULT_EXPIRY_TIME);
    356 
    357         String randomFileName = "mms." + String.valueOf(System.currentTimeMillis()) + ".dat";
    358 
    359         byte[] mmsBytes = mms.build();
    360         if (mmsBytes.length == 0) {
    361             Log.e("Failed to build PDU!");
    362             return;
    363         }
    364 
    365         if (writeBytesToCacheFile(randomFileName, mmsBytes) == false) {
    366             Log.e("Failed to write PDU to file " + randomFileName);
    367             return;
    368         }
    369 
    370         Uri contentUri = (new Uri.Builder())
    371                           .authority(
    372                           "com.googlecode.android_scripting.facade.telephony.MmsFileProvider")
    373                           .path(randomFileName)
    374                           .scheme(ContentResolver.SCHEME_CONTENT)
    375                           .build();
    376 
    377         Bundle actionParameters = new Bundle();
    378         actionParameters.putString(KEY_RECIPIENTS, toPhoneNumber);
    379         actionParameters.putString(KEY_SUBJECT_TEXT, subject);
    380         Uri messageUri = actionParameters.getParcelable(KEY_MESSAGE_URI);
    381 
    382         if (contentUri != null) {
    383             Log.d(String.format("URI String: %s", contentUri.toString()));
    384             mSms.sendMultimediaMessage(mContext, contentUri, null/* locationUrl */,
    385                     null/* configOverrides */,
    386                     createBroadcastPendingIntent(MMS_MESSAGE_SENT_ACTION, messageUri));
    387         }
    388         else {
    389             Log.e("smsSendMultimediaMessage():Content URI String is null");
    390         }
    391     }
    392 
    393     @Rpc(description = "Send a text message to a specified number.")
    394     public void smsSendTextMessage(
    395                         @RpcParameter(name = "phoneNumber")
    396             String phoneNumber,
    397                         @RpcParameter(name = "message")
    398             String message,
    399                         @RpcParameter(name = "deliveryReportRequired")
    400             Boolean deliveryReportRequired) {
    401         int message_length = message.length();
    402         Log.d(String.format("Send SMS message of length %d", message_length));
    403         ArrayList<String> messagesParts = mSms.divideMessage(message);
    404         if (messagesParts.size() > 1) {
    405             mNumExpectedSentEvents = mNumExpectedDeliveredEvents = messagesParts.size();
    406             Log.d(String.format("SMS message of length %d is divided into %d parts",
    407                     message_length, mNumExpectedSentEvents));
    408             ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
    409             ArrayList<PendingIntent> deliveredIntents = new ArrayList<PendingIntent>();
    410             for (int i = 0; i < messagesParts.size(); i++) {
    411                 Bundle actionParameters = new Bundle();
    412                 actionParameters.putString(KEY_RECIPIENTS, phoneNumber);
    413                 Uri messageUri = actionParameters.getParcelable(KEY_MESSAGE_URI);
    414                 sentIntents.add(createBroadcastPendingIntent(SMS_MESSAGE_SENT_ACTION, messageUri));
    415                 if (deliveryReportRequired) {
    416                     deliveredIntents.add(createBroadcastPendingIntent(
    417                             SMS_MESSAGE_STATUS_DELIVERED_ACTION, messageUri));
    418                 }
    419             }
    420             mSms.sendMultipartTextMessage(
    421                     phoneNumber, null, messagesParts,
    422                     sentIntents, deliveryReportRequired ? deliveredIntents : null);
    423         } else {
    424             Log.d(String.format("SMS message of length %s is sent as one part", message_length));
    425             mNumExpectedSentEvents = mNumExpectedDeliveredEvents = 1;
    426             Bundle actionParameters = new Bundle();
    427             actionParameters.putString(KEY_RECIPIENTS, phoneNumber);
    428             Uri messageUri = actionParameters.getParcelable(KEY_MESSAGE_URI);
    429             mSms.sendTextMessage(phoneNumber, null, messagesParts.get(0),
    430                     createBroadcastPendingIntent(SMS_MESSAGE_SENT_ACTION, messageUri),
    431                     deliveryReportRequired ? createBroadcastPendingIntent(
    432                             SMS_MESSAGE_STATUS_DELIVERED_ACTION, messageUri) : null);
    433         }
    434     }
    435 
    436     @Rpc(description = "Retrieves all messages currently stored on ICC.")
    437     public ArrayList<SmsMessage> smsGetAllMessagesFromIcc() {
    438         return mSms.getAllMessagesFromIcc();
    439     }
    440 
    441     @Rpc(description = "Starts tracking GSM Emergency CB Messages.")
    442     public void smsStartTrackingGsmEmergencyCBMessage() {
    443         if (!mGsmEmergencyCBListenerRegistered) {
    444             for (int messageId : mGsmCbMessageIdList) {
    445                 mSms.enableCellBroadcast(
    446                         messageId,
    447                         SmsManager.CELL_BROADCAST_RAN_TYPE_GSM);
    448             }
    449 
    450             mEmergencyCBMessage = new IntentFilter(EMERGENCY_CB_MESSAGE_RECEIVED_ACTION);
    451             mService.registerReceiver(mGsmEmergencyCBMessageListener,
    452                     mEmergencyCBMessage);
    453             mGsmEmergencyCBListenerRegistered = true;
    454         }
    455     }
    456 
    457     @Rpc(description = "Stop tracking GSM Emergency CB Messages")
    458     public void smsStopTrackingGsmEmergencyCBMessage() {
    459         if (mGsmEmergencyCBListenerRegistered) {
    460             mService.unregisterReceiver(mGsmEmergencyCBMessageListener);
    461             mGsmEmergencyCBListenerRegistered = false;
    462             for (int messageId : mGsmCbMessageIdList) {
    463                 mSms.disableCellBroadcast(
    464                         messageId,
    465                         SmsManager.CELL_BROADCAST_RAN_TYPE_GSM);
    466             }
    467         }
    468     }
    469 
    470     @Rpc(description = "Starts tracking CDMA Emergency CB Messages")
    471     public void smsStartTrackingCdmaEmergencyCBMessage() {
    472         if (!mCdmaEmergencyCBListenerRegistered) {
    473             for (int messageId : mCdmaCbMessageIdList) {
    474                 mSms.enableCellBroadcast(
    475                         messageId,
    476                         SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA);
    477             }
    478             mEmergencyCBMessage = new IntentFilter(EMERGENCY_CB_MESSAGE_RECEIVED_ACTION);
    479             mService.registerReceiver(mCdmaEmergencyCBMessageListener,
    480                     mEmergencyCBMessage);
    481             mCdmaEmergencyCBListenerRegistered = true;
    482         }
    483     }
    484 
    485     @Rpc(description = "Stop tracking CDMA Emergency CB Message.")
    486     public void smsStopTrackingCdmaEmergencyCBMessage() {
    487         if (mCdmaEmergencyCBListenerRegistered) {
    488             mService.unregisterReceiver(mCdmaEmergencyCBMessageListener);
    489             mCdmaEmergencyCBListenerRegistered = false;
    490             for (int messageId : mCdmaCbMessageIdList) {
    491                 mSms.disableCellBroadcast(
    492                         messageId,
    493                         SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA);
    494             }
    495         }
    496     }
    497 
    498     private class SmsSendListener extends BroadcastReceiver {
    499         @Override
    500         public void onReceive(Context context, Intent intent) {
    501             Bundle event = new Bundle();
    502             String action = intent.getAction();
    503             int resultCode = getResultCode();
    504             event.putString("ResultCode", Integer.toString(resultCode));
    505             if (SMS_MESSAGE_STATUS_DELIVERED_ACTION.equals(action)) {
    506                 event.putString("Type", "SmsDeliverStatus");
    507                 if (resultCode == Activity.RESULT_OK) {
    508                     if (mNumExpectedDeliveredEvents == 1) {
    509                         Log.d("SMS Message delivered successfully");
    510                         mEventFacade.postEvent(TelephonyConstants.EventSmsDeliverSuccess, event);
    511                     }
    512                     if (mNumExpectedDeliveredEvents > 0) {
    513                         mNumExpectedDeliveredEvents--;
    514                     }
    515                 } else {
    516                     Log.e("SMS Message delivery failed");
    517                     // TODO . Need to find the reason for failure from pdu
    518                     mEventFacade.postEvent(TelephonyConstants.EventSmsDeliverFailure, event);
    519                 }
    520             } else if (SMS_MESSAGE_SENT_ACTION.equals(action)) {
    521                 if (resultCode == Activity.RESULT_OK) {
    522                     if (mNumExpectedSentEvents == 1) {
    523                         event.putString("Type", "SmsSentSuccess");
    524                         event.putString("ResultString", "RESULT_OK");
    525                         Log.d("SMS Message sent successfully");
    526                         mEventFacade.postEvent(TelephonyConstants.EventSmsSentSuccess, event);
    527                     }
    528                     if (mNumExpectedSentEvents > 0) {
    529                         mNumExpectedSentEvents--;
    530                     }
    531                 } else {
    532                     String resultString = getSmsFailureReason(resultCode);
    533                     event.putString("ResultString", resultString);
    534                     Log.e("SMS Message send failed with code " + Integer.toString(
    535                             resultCode) + resultString);
    536                     event.putString("Type", "SmsSentFailure");
    537                     event.putString("Reason", resultString);
    538                     mEventFacade.postEvent(TelephonyConstants.EventSmsSentFailure, event);
    539                 }
    540             }
    541         }
    542     }
    543 
    544     private class SmsIncomingListener extends BroadcastReceiver {
    545         @Override
    546         public void onReceive(Context context, Intent intent) {
    547             Log.d(String.format("SmsIncomingListener Received: %s", intent.toUri(0)));
    548             String action = intent.getAction();
    549             if (Intents.SMS_RECEIVED_ACTION.equals(action)) {
    550                 Log.d("New SMS Received");
    551                 Bundle extras = intent.getExtras();
    552                 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    553                 if (extras != null) {
    554                     Bundle event = new Bundle();
    555                     event.putString("Type", "NewSmsReceived");
    556                     SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);
    557                     StringBuilder smsMsg = new StringBuilder();
    558 
    559                     SmsMessage sms;
    560                     for (int i = 0; i < msgs.length; i++) {
    561                         if (msgs[i] == null) {
    562                             Log.w("SmsMessages received from intent at " + i + " has no body.");
    563                             continue;
    564                         }
    565                         sms = msgs[i];
    566                         String sender = sms.getOriginatingAddress();
    567                         event.putString("Sender", formatPhoneNumber(sender));
    568                         smsMsg.append(sms.getMessageBody());
    569                     }
    570                     event.putString("Text", smsMsg.toString());
    571                     // TODO
    572                     // Need to explore how to get subId information.
    573                     event.putInt("subscriptionId", subId);
    574                     mEventFacade.postEvent(TelephonyConstants.EventSmsReceived, event);
    575                 }
    576             }
    577         }
    578     }
    579 
    580     private class MmsSendListener extends BroadcastReceiver {
    581         @Override
    582         public void onReceive(Context context, Intent intent) {
    583             Bundle event = new Bundle();
    584             event.putString("Type", "MmsDeliverStatus");
    585             String action = intent.getAction();
    586             int resultCode = getResultCode();
    587             event.putString("ResultCode", Integer.toString(resultCode));
    588             String eventType = TelephonyConstants.EventMmsSentFailure;
    589             if (MMS_MESSAGE_SENT_ACTION.equals(action)) {
    590                 if (resultCode == Activity.RESULT_OK) {
    591                     final byte[] response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA);
    592                     event.putString("ResultString", "RESULT_OK");
    593                     if (response != null) {
    594                         boolean shouldParse = mSms.getCarrierConfigValues(
    595                             ).getBoolean(
    596                                 SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
    597                         final GenericPdu pdu = new PduParser(response, shouldParse).parse();
    598                         if (pdu instanceof SendConf) {
    599                             final SendConf sendConf = (SendConf) pdu;
    600                             int responseCode = sendConf.getResponseStatus();
    601                             String responseStatus = getMmsResponseStatus(responseCode);
    602                             event.putString("ResponseStatus", responseStatus);
    603                             if (responseCode == PduHeaders.RESPONSE_STATUS_OK) {
    604                                 Log.d("MMS Message sent successfully");
    605                                 eventType = TelephonyConstants.EventMmsSentSuccess;
    606                             } else {
    607                                 Log.e("MMS sent with error response = " + responseStatus);
    608                                 event.putString("Reason", responseStatus);
    609                             }
    610                         } else {
    611                             Log.e("MMS sent, invalid response");
    612                             event.putString("Reason", "InvalidResponse");
    613                         }
    614                     } else {
    615                         Log.e("MMS sent, empty response");
    616                         event.putString("Reason", "EmptyResponse");
    617                     }
    618                 } else {
    619                     String resultString = getMmsFailureReason(resultCode);
    620                     event.putString("ResultString", resultString);
    621                     Log.e("MMS Message send failed with result code " + Integer.toString(
    622                             resultCode) + resultString);
    623                     event.putString("Reason", resultString);
    624                 }
    625                 event.putString("Type", eventType);
    626                 mEventFacade.postEvent(eventType, event);
    627             } else {
    628                 Log.e("MMS Send Listener Received Invalid Event" + intent.toString());
    629             }
    630         }
    631     }
    632 
    633     // add mms matching after mms message parser is added in sl4a. b/34276948
    634     private class MmsIncomingListener extends BroadcastReceiver {
    635         @Override
    636         public void onReceive(Context context, Intent intent) {
    637             Log.d("MmsIncomingListener Received an Intent " + intent.toString());
    638             String action = intent.getAction();
    639             if (Intents.MMS_DOWNLOADED_ACTION.equals(action)) {
    640                 Log.d("New MMS Downloaded");
    641                 Bundle event = new Bundle();
    642                 event.putString("Type", "NewMmsReceived");
    643                 mEventFacade.postEvent(TelephonyConstants.EventMmsDownloaded, event);
    644             }
    645             else if (Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) {
    646                 Log.d("New Wap Push Received");
    647                 Bundle event = new Bundle();
    648                 event.putString("Type", "NewWapPushReceived");
    649                 mEventFacade.postEvent(TelephonyConstants.EventWapPushReceived, event);
    650             }
    651             else if (Intents.DATA_SMS_RECEIVED_ACTION.equals(action)) {
    652                 Log.d("New Data SMS Received");
    653                 Bundle event = new Bundle();
    654                 event.putString("Type", "NewDataSMSReceived");
    655                 mEventFacade.postEvent(TelephonyConstants.EventDataSmsReceived, event);
    656             }
    657             else {
    658                 Log.e("MmsIncomingListener Received Unexpected Event" + intent.toString());
    659             }
    660         }
    661     }
    662 
    663     String formatPhoneNumber(String phoneNumber) {
    664         String senderNumberStr = null;
    665         int len = phoneNumber.length();
    666         if (len > 0) {
    667             /**
    668              * Currently this incomingNumber modification is specific for US numbers.
    669              */
    670             if ((INTERNATIONAL_NUMBER_LENGTH == len) && ('+' == phoneNumber.charAt(0))) {
    671                 senderNumberStr = phoneNumber.substring(1);
    672             } else if (DOMESTIC_NUMBER_LENGTH == len) {
    673                 senderNumberStr = '1' + phoneNumber;
    674             } else {
    675                 senderNumberStr = phoneNumber;
    676             }
    677         }
    678         return senderNumberStr;
    679     }
    680 
    681     private class SmsEmergencyCBMessageListener extends BroadcastReceiver {
    682         @Override
    683         public void onReceive(Context context, Intent intent) {
    684             if (EMERGENCY_CB_MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) {
    685                 Bundle extras = intent.getExtras();
    686                 if (extras != null) {
    687                     Bundle event = new Bundle();
    688                     String eventName = null;
    689                     SmsCbMessage message = (SmsCbMessage) extras.get("message");
    690                     if (message != null) {
    691                         if (message.isEmergencyMessage()) {
    692                             event.putString("geographicalScope", getGeographicalScope(
    693                                     message.getGeographicalScope()));
    694                             event.putInt("serialNumber", message.getSerialNumber());
    695                             event.putString("location", message.getLocation().toString());
    696                             event.putInt("serviceCategory", message.getServiceCategory());
    697                             event.putString("language", message.getLanguageCode());
    698                             event.putString("message", message.getMessageBody());
    699                             event.putString("priority", getPriority(message.getMessagePriority()));
    700                             if (message.isCmasMessage()) {
    701                                 // CMAS message
    702                                 eventName = TelephonyConstants.EventCmasReceived;
    703                                 event.putString("cmasMessageClass", getCMASMessageClass(
    704                                         message.getCmasWarningInfo().getMessageClass()));
    705                                 event.putString("cmasCategory", getCMASCategory(
    706                                         message.getCmasWarningInfo().getCategory()));
    707                                 event.putString("cmasResponseType", getCMASResponseType(
    708                                         message.getCmasWarningInfo().getResponseType()));
    709                                 event.putString("cmasSeverity", getCMASSeverity(
    710                                         message.getCmasWarningInfo().getSeverity()));
    711                                 event.putString("cmasUrgency", getCMASUrgency(
    712                                         message.getCmasWarningInfo().getUrgency()));
    713                                 event.putString("cmasCertainty", getCMASCertainty(
    714                                         message.getCmasWarningInfo().getCertainty()));
    715                             } else if (message.isEtwsMessage()) {
    716                                 // ETWS message
    717                                 eventName = TelephonyConstants.EventEtwsReceived;
    718                                 event.putString("etwsWarningType", getETWSWarningType(
    719                                         message.getEtwsWarningInfo().getWarningType()));
    720                                 event.putBoolean("etwsIsEmergencyUserAlert",
    721                                         message.getEtwsWarningInfo().isEmergencyUserAlert());
    722                                 event.putBoolean("etwsActivatePopup",
    723                                         message.getEtwsWarningInfo().isPopupAlert());
    724                             } else {
    725                                 Log.d("Received message is not CMAS or ETWS");
    726                             }
    727                             if (eventName != null)
    728                                 mEventFacade.postEvent(eventName, event);
    729                         }
    730                     }
    731                 } else {
    732                     Log.d("Received  Emergency CB without extras");
    733                 }
    734             }
    735         }
    736     }
    737 
    738     private PendingIntent createBroadcastPendingIntent(String intentAction, Uri messageUri) {
    739         Intent intent = new Intent(intentAction, messageUri);
    740         return PendingIntent.getBroadcast(mService, 0, intent, 0);
    741     }
    742 
    743     private static String getSmsFailureReason(int resultCode) {
    744         return sSmsSendFailureMap.get(resultCode);
    745     }
    746 
    747     private static String getMmsFailureReason(int resultCode) {
    748         return sMmsSendFailureMap.get(resultCode);
    749     }
    750 
    751     private static String getMmsResponseStatus(int resultCode) {
    752         return sMmsSendResponseMap.get(resultCode);
    753     }
    754 
    755     private static String getETWSWarningType(int type) {
    756         switch (type) {
    757             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE:
    758                 return "EARTHQUAKE";
    759             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI:
    760                 return "TSUNAMI";
    761             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI:
    762                 return "EARTHQUAKE_AND_TSUNAMI";
    763             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE:
    764                 return "TEST_MESSAGE";
    765             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY:
    766                 return "OTHER_EMERGENCY";
    767         }
    768         return "UNKNOWN";
    769     }
    770 
    771     private static String getCMASMessageClass(int messageclass) {
    772         switch (messageclass) {
    773             case SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT:
    774                 return "PRESIDENTIAL_LEVEL_ALERT";
    775             case SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT:
    776                 return "EXTREME_THREAT";
    777             case SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT:
    778                 return "SEVERE_THREAT";
    779             case SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY:
    780                 return "CHILD_ABDUCTION_EMERGENCY";
    781             case SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST:
    782                 return "REQUIRED_MONTHLY_TEST";
    783             case SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE:
    784                 return "CMAS_EXERCISE";
    785         }
    786         return "UNKNOWN";
    787     }
    788 
    789     private static String getCMASCategory(int category) {
    790         switch (category) {
    791             case SmsCbCmasInfo.CMAS_CATEGORY_GEO:
    792                 return "GEOPHYSICAL";
    793             case SmsCbCmasInfo.CMAS_CATEGORY_MET:
    794                 return "METEOROLOGICAL";
    795             case SmsCbCmasInfo.CMAS_CATEGORY_SAFETY:
    796                 return "SAFETY";
    797             case SmsCbCmasInfo.CMAS_CATEGORY_SECURITY:
    798                 return "SECURITY";
    799             case SmsCbCmasInfo.CMAS_CATEGORY_RESCUE:
    800                 return "RESCUE";
    801             case SmsCbCmasInfo.CMAS_CATEGORY_FIRE:
    802                 return "FIRE";
    803             case SmsCbCmasInfo.CMAS_CATEGORY_HEALTH:
    804                 return "HEALTH";
    805             case SmsCbCmasInfo.CMAS_CATEGORY_ENV:
    806                 return "ENVIRONMENTAL";
    807             case SmsCbCmasInfo.CMAS_CATEGORY_TRANSPORT:
    808                 return "TRANSPORTATION";
    809             case SmsCbCmasInfo.CMAS_CATEGORY_INFRA:
    810                 return "INFRASTRUCTURE";
    811             case SmsCbCmasInfo.CMAS_CATEGORY_CBRNE:
    812                 return "CHEMICAL";
    813             case SmsCbCmasInfo.CMAS_CATEGORY_OTHER:
    814                 return "OTHER";
    815         }
    816         return "UNKNOWN";
    817     }
    818 
    819     private static String getCMASResponseType(int type) {
    820         switch (type) {
    821             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_SHELTER:
    822                 return "SHELTER";
    823             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EVACUATE:
    824                 return "EVACUATE";
    825             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_PREPARE:
    826                 return "PREPARE";
    827             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EXECUTE:
    828                 return "EXECUTE";
    829             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR:
    830                 return "MONITOR";
    831             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_AVOID:
    832                 return "AVOID";
    833             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_ASSESS:
    834                 return "ASSESS";
    835             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_NONE:
    836                 return "NONE";
    837         }
    838         return "UNKNOWN";
    839     }
    840 
    841     private static String getCMASSeverity(int severity) {
    842         switch (severity) {
    843             case SmsCbCmasInfo.CMAS_SEVERITY_EXTREME:
    844                 return "EXTREME";
    845             case SmsCbCmasInfo.CMAS_SEVERITY_SEVERE:
    846                 return "SEVERE";
    847         }
    848         return "UNKNOWN";
    849     }
    850 
    851     private static String getCMASUrgency(int urgency) {
    852         switch (urgency) {
    853             case SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE:
    854                 return "IMMEDIATE";
    855             case SmsCbCmasInfo.CMAS_URGENCY_EXPECTED:
    856                 return "EXPECTED";
    857         }
    858         return "UNKNOWN";
    859     }
    860 
    861     private static String getCMASCertainty(int certainty) {
    862         switch (certainty) {
    863             case SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED:
    864                 return "IMMEDIATE";
    865             case SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY:
    866                 return "LIKELY";
    867         }
    868         return "UNKNOWN";
    869     }
    870 
    871     private static String getGeographicalScope(int scope) {
    872         switch (scope) {
    873             case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
    874                 return "CELL_WIDE_IMMEDIATE";
    875             case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
    876                 return "PLMN_WIDE ";
    877             case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
    878                 return "LA_WIDE";
    879             case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
    880                 return "CELL_WIDE";
    881         }
    882         return "UNKNOWN";
    883     }
    884 
    885     private static String getPriority(int priority) {
    886         switch (priority) {
    887             case SmsCbMessage.MESSAGE_PRIORITY_NORMAL:
    888                 return "NORMAL";
    889             case SmsCbMessage.MESSAGE_PRIORITY_INTERACTIVE:
    890                 return "INTERACTIVE";
    891             case SmsCbMessage.MESSAGE_PRIORITY_URGENT:
    892                 return "URGENT";
    893             case SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY:
    894                 return "EMERGENCY";
    895         }
    896         return "UNKNOWN";
    897     }
    898 
    899     @Override
    900     public void shutdown() {
    901 
    902         smsStopTrackingIncomingSmsMessage();
    903         smsStopTrackingIncomingMmsMessage();
    904         smsStopTrackingGsmEmergencyCBMessage();
    905         smsStopTrackingCdmaEmergencyCBMessage();
    906 
    907         synchronized (lock) {
    908             if (mSentReceiversRegistered) {
    909                 mService.unregisterReceiver(mSmsSendListener);
    910                 mService.unregisterReceiver(mMmsSendListener);
    911                 mSentReceiversRegistered = false;
    912             }
    913         }
    914     }
    915 
    916     private class MmsBuilder {
    917 
    918         public static final String MESSAGE_CLASS_PERSONAL =
    919                 PduHeaders.MESSAGE_CLASS_PERSONAL_STR;
    920 
    921         public static final String MESSAGE_CLASS_ADVERTISEMENT =
    922                 PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR;
    923 
    924         public static final String MESSAGE_CLASS_INFORMATIONAL =
    925                 PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR;
    926 
    927         public static final String MESSAGE_CLASS_AUTO =
    928                 PduHeaders.MESSAGE_CLASS_AUTO_STR;
    929 
    930         public static final int MESSAGE_PRIORITY_LOW = PduHeaders.PRIORITY_LOW;
    931         public static final int MESSAGE_PRIORITY_NORMAL = PduHeaders.PRIORITY_LOW;
    932         public static final int MESSAGE_PRIORITY_HIGH = PduHeaders.PRIORITY_LOW;
    933 
    934         private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 60 * 60;
    935         private static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
    936 
    937         private SendReq mRequest;
    938         private PduBody mBody;
    939 
    940         // FIXME: Eventually this should be exposed as a parameter
    941         private static final String TEMP_CONTENT_FILE_NAME = "text0.txt";
    942 
    943         // Synchronized Multimedia Internet Language
    944         // Fragment for compatibility
    945         private static final String sSmilText =
    946                 "<smil>" +
    947                         "<head>" +
    948                         "<layout>" +
    949                         "<root-layout/>" +
    950                         "<region height=\"100%%\" id=\"Text\" left=\"0%%\"" +
    951                         " top=\"0%%\" width=\"100%%\"/>" +
    952                         "</layout>" +
    953                         "</head>" +
    954                         "<body>" +
    955                         "<par dur=\"8000ms\">" +
    956                         "<text src=\"%s\" region=\"Text\"/>" +
    957                         "</par>" +
    958                         "</body>" +
    959                         "</smil>";
    960 
    961         public MmsBuilder() {
    962             mRequest = new SendReq();
    963             mBody = new PduBody();
    964         }
    965 
    966         public void setFromPhoneNumber(String number) {
    967             mRequest.setFrom(new EncodedStringValue(number));
    968         }
    969 
    970         public void setToPhoneNumber(String number) {
    971             mRequest.setTo(new EncodedStringValue[] {
    972                     new EncodedStringValue(number) });
    973         }
    974 
    975         public void setToPhoneNumbers(List<String> number) {
    976             mRequest.setTo(EncodedStringValue.encodeStrings((String[]) number.toArray()));
    977         }
    978 
    979         public void setSubject(String subject) {
    980             mRequest.setSubject(new EncodedStringValue(subject));
    981         }
    982 
    983         public void setDate() {
    984             setDate(System.currentTimeMillis() / 1000);
    985         }
    986 
    987         public void setDate(long time) {
    988             mRequest.setDate(time);
    989         }
    990 
    991         public void addMessageBody(String message) {
    992             addMessageBody(message, true);
    993         }
    994 
    995         public void setMessageClass(String messageClass) {
    996             mRequest.setMessageClass(messageClass.getBytes());
    997         }
    998 
    999         public void setMessagePriority(int priority) {
   1000             try {
   1001                 mRequest.setPriority(priority);
   1002             } catch (InvalidHeaderValueException e) {
   1003                 Log.e("Invalid Header Value "+e.toString());
   1004             }
   1005         }
   1006 
   1007         public void setDeliveryReport(boolean report) {
   1008             try {
   1009                 mRequest.setDeliveryReport((report) ? PduHeaders.VALUE_YES : PduHeaders.VALUE_NO);
   1010             } catch (InvalidHeaderValueException e) {
   1011                 Log.e("Invalid Header Value "+e.toString());
   1012             }
   1013         }
   1014 
   1015         public void setReadReport(boolean report) {
   1016             try {
   1017                 mRequest.setReadReport((report) ? PduHeaders.VALUE_YES : PduHeaders.VALUE_NO);
   1018             } catch (InvalidHeaderValueException e) {
   1019                 Log.e("Invalid Header Value "+e.toString());
   1020             }
   1021         }
   1022 
   1023         public void setExpirySeconds(int seconds) {
   1024             mRequest.setExpiry(seconds);
   1025         }
   1026 
   1027         public byte[] build() {
   1028             mRequest.setBody(mBody);
   1029 
   1030             int msgSize = 0;
   1031             for (int i = 0; i < mBody.getPartsNum(); i++) {
   1032                 msgSize += mBody.getPart(i).getDataLength();
   1033             }
   1034             mRequest.setMessageSize(msgSize);
   1035 
   1036             return new PduComposer(mContext, mRequest).make();
   1037         }
   1038 
   1039         public void addMessageBody(String message, boolean addSmilFragment) {
   1040             final PduPart part = new PduPart();
   1041             part.setCharset(CharacterSets.UTF_8);
   1042             part.setContentType(ContentType.TEXT_PLAIN.getBytes());
   1043             part.setContentLocation(TEMP_CONTENT_FILE_NAME.getBytes());
   1044             int index = TEMP_CONTENT_FILE_NAME.lastIndexOf(".");
   1045             String contentId = (index == -1) ? TEMP_CONTENT_FILE_NAME
   1046                     : TEMP_CONTENT_FILE_NAME.substring(0, index);
   1047             part.setContentId(contentId.getBytes());
   1048             part.setContentId("txt".getBytes());
   1049             part.setData(message.getBytes());
   1050             mBody.addPart(part);
   1051             if (addSmilFragment) {
   1052                 addSmilTextFragment(TEMP_CONTENT_FILE_NAME);
   1053             }
   1054         }
   1055 
   1056         private void addSmilTextFragment(String contentFilename) {
   1057 
   1058             final String smil = String.format(sSmilText, contentFilename);
   1059             final PduPart smilPart = new PduPart();
   1060             smilPart.setContentId("smil".getBytes());
   1061             smilPart.setContentLocation("smil.xml".getBytes());
   1062             smilPart.setContentType(ContentType.APP_SMIL.getBytes());
   1063             smilPart.setData(smil.getBytes());
   1064             mBody.addPart(0, smilPart);
   1065         }
   1066     }
   1067 
   1068 }
   1069