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             String action = intent.getAction();
    548             if (Intents.SMS_RECEIVED_ACTION.equals(action)) {
    549                 Log.d("New SMS Received");
    550                 Bundle extras = intent.getExtras();
    551                 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    552                 if (extras != null) {
    553                     Bundle event = new Bundle();
    554                     event.putString("Type", "NewSmsReceived");
    555                     SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);
    556                     StringBuilder smsMsg = new StringBuilder();
    557 
    558                     SmsMessage sms = msgs[0];
    559                     String sender = sms.getOriginatingAddress();
    560                     event.putString("Sender", formatPhoneNumber(sender));
    561 
    562                     for (int i = 0; i < msgs.length; i++) {
    563                         sms = msgs[i];
    564                         smsMsg.append(sms.getMessageBody());
    565                     }
    566                     event.putString("Text", smsMsg.toString());
    567                     // TODO
    568                     // Need to explore how to get subId information.
    569                     event.putInt("subscriptionId", subId);
    570                     mEventFacade.postEvent(TelephonyConstants.EventSmsReceived, event);
    571                 }
    572             }
    573         }
    574     }
    575 
    576     private class MmsSendListener extends BroadcastReceiver {
    577         @Override
    578         public void onReceive(Context context, Intent intent) {
    579             Bundle event = new Bundle();
    580             event.putString("Type", "MmsDeliverStatus");
    581             String action = intent.getAction();
    582             int resultCode = getResultCode();
    583             event.putString("ResultCode", Integer.toString(resultCode));
    584             String eventType = TelephonyConstants.EventMmsSentFailure;
    585             if (MMS_MESSAGE_SENT_ACTION.equals(action)) {
    586                 if (resultCode == Activity.RESULT_OK) {
    587                     final byte[] response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA);
    588                     event.putString("ResultString", "RESULT_OK");
    589                     if (response != null) {
    590                         boolean shouldParse = mSms.getCarrierConfigValues(
    591                             ).getBoolean(
    592                                 SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
    593                         final GenericPdu pdu = new PduParser(response, shouldParse).parse();
    594                         if (pdu instanceof SendConf) {
    595                             final SendConf sendConf = (SendConf) pdu;
    596                             int responseCode = sendConf.getResponseStatus();
    597                             String responseStatus = getMmsResponseStatus(responseCode);
    598                             event.putString("ResponseStatus", responseStatus);
    599                             if (responseCode == PduHeaders.RESPONSE_STATUS_OK) {
    600                                 Log.d("MMS Message sent successfully");
    601                                 eventType = TelephonyConstants.EventMmsSentSuccess;
    602                             } else {
    603                                 Log.e("MMS sent with error response = " + responseStatus);
    604                                 event.putString("Reason", responseStatus);
    605                             }
    606                         } else {
    607                             Log.e("MMS sent, invalid response");
    608                             event.putString("Reason", "InvalidResponse");
    609                         }
    610                     } else {
    611                         Log.e("MMS sent, empty response");
    612                         event.putString("Reason", "EmptyResponse");
    613                     }
    614                 } else {
    615                     String resultString = getMmsFailureReason(resultCode);
    616                     event.putString("ResultString", resultString);
    617                     Log.e("MMS Message send failed with result code " + Integer.toString(
    618                             resultCode) + resultString);
    619                     event.putString("Reason", resultString);
    620                 }
    621                 event.putString("Type", eventType);
    622                 mEventFacade.postEvent(eventType, event);
    623             } else {
    624                 Log.e("MMS Send Listener Received Invalid Event" + intent.toString());
    625             }
    626         }
    627     }
    628 
    629     // add mms matching after mms message parser is added in sl4a. b/34276948
    630     private class MmsIncomingListener extends BroadcastReceiver {
    631         @Override
    632         public void onReceive(Context context, Intent intent) {
    633             Log.d("MmsIncomingListener Received an Intent " + intent.toString());
    634             String action = intent.getAction();
    635             if (Intents.MMS_DOWNLOADED_ACTION.equals(action)) {
    636                 Log.d("New MMS Downloaded");
    637                 Bundle event = new Bundle();
    638                 event.putString("Type", "NewMmsReceived");
    639                 mEventFacade.postEvent(TelephonyConstants.EventMmsDownloaded, event);
    640             }
    641             else if (Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) {
    642                 Log.d("New Wap Push Received");
    643                 Bundle event = new Bundle();
    644                 event.putString("Type", "NewWapPushReceived");
    645                 mEventFacade.postEvent(TelephonyConstants.EventWapPushReceived, event);
    646             }
    647             else if (Intents.DATA_SMS_RECEIVED_ACTION.equals(action)) {
    648                 Log.d("New Data SMS Received");
    649                 Bundle event = new Bundle();
    650                 event.putString("Type", "NewDataSMSReceived");
    651                 mEventFacade.postEvent(TelephonyConstants.EventDataSmsReceived, event);
    652             }
    653             else {
    654                 Log.e("MmsIncomingListener Received Unexpected Event" + intent.toString());
    655             }
    656         }
    657     }
    658 
    659     String formatPhoneNumber(String phoneNumber) {
    660         String senderNumberStr = null;
    661         int len = phoneNumber.length();
    662         if (len > 0) {
    663             /**
    664              * Currently this incomingNumber modification is specific for US numbers.
    665              */
    666             if ((INTERNATIONAL_NUMBER_LENGTH == len) && ('+' == phoneNumber.charAt(0))) {
    667                 senderNumberStr = phoneNumber.substring(1);
    668             } else if (DOMESTIC_NUMBER_LENGTH == len) {
    669                 senderNumberStr = '1' + phoneNumber;
    670             } else {
    671                 senderNumberStr = phoneNumber;
    672             }
    673         }
    674         return senderNumberStr;
    675     }
    676 
    677     private class SmsEmergencyCBMessageListener extends BroadcastReceiver {
    678         @Override
    679         public void onReceive(Context context, Intent intent) {
    680             if (EMERGENCY_CB_MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) {
    681                 Bundle extras = intent.getExtras();
    682                 if (extras != null) {
    683                     Bundle event = new Bundle();
    684                     String eventName = null;
    685                     SmsCbMessage message = (SmsCbMessage) extras.get("message");
    686                     if (message != null) {
    687                         if (message.isEmergencyMessage()) {
    688                             event.putString("geographicalScope", getGeographicalScope(
    689                                     message.getGeographicalScope()));
    690                             event.putInt("serialNumber", message.getSerialNumber());
    691                             event.putString("location", message.getLocation().toString());
    692                             event.putInt("serviceCategory", message.getServiceCategory());
    693                             event.putString("language", message.getLanguageCode());
    694                             event.putString("message", message.getMessageBody());
    695                             event.putString("priority", getPriority(message.getMessagePriority()));
    696                             if (message.isCmasMessage()) {
    697                                 // CMAS message
    698                                 eventName = TelephonyConstants.EventCmasReceived;
    699                                 event.putString("cmasMessageClass", getCMASMessageClass(
    700                                         message.getCmasWarningInfo().getMessageClass()));
    701                                 event.putString("cmasCategory", getCMASCategory(
    702                                         message.getCmasWarningInfo().getCategory()));
    703                                 event.putString("cmasResponseType", getCMASResponseType(
    704                                         message.getCmasWarningInfo().getResponseType()));
    705                                 event.putString("cmasSeverity", getCMASSeverity(
    706                                         message.getCmasWarningInfo().getSeverity()));
    707                                 event.putString("cmasUrgency", getCMASUrgency(
    708                                         message.getCmasWarningInfo().getUrgency()));
    709                                 event.putString("cmasCertainty", getCMASCertainty(
    710                                         message.getCmasWarningInfo().getCertainty()));
    711                             } else if (message.isEtwsMessage()) {
    712                                 // ETWS message
    713                                 eventName = TelephonyConstants.EventEtwsReceived;
    714                                 event.putString("etwsWarningType", getETWSWarningType(
    715                                         message.getEtwsWarningInfo().getWarningType()));
    716                                 event.putBoolean("etwsIsEmergencyUserAlert",
    717                                         message.getEtwsWarningInfo().isEmergencyUserAlert());
    718                                 event.putBoolean("etwsActivatePopup",
    719                                         message.getEtwsWarningInfo().isPopupAlert());
    720                             } else {
    721                                 Log.d("Received message is not CMAS or ETWS");
    722                             }
    723                             if (eventName != null)
    724                                 mEventFacade.postEvent(eventName, event);
    725                         }
    726                     }
    727                 } else {
    728                     Log.d("Received  Emergency CB without extras");
    729                 }
    730             }
    731         }
    732     }
    733 
    734     private PendingIntent createBroadcastPendingIntent(String intentAction, Uri messageUri) {
    735         Intent intent = new Intent(intentAction, messageUri);
    736         return PendingIntent.getBroadcast(mService, 0, intent, 0);
    737     }
    738 
    739     private static String getSmsFailureReason(int resultCode) {
    740         return sSmsSendFailureMap.get(resultCode);
    741     }
    742 
    743     private static String getMmsFailureReason(int resultCode) {
    744         return sMmsSendFailureMap.get(resultCode);
    745     }
    746 
    747     private static String getMmsResponseStatus(int resultCode) {
    748         return sMmsSendResponseMap.get(resultCode);
    749     }
    750 
    751     private static String getETWSWarningType(int type) {
    752         switch (type) {
    753             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE:
    754                 return "EARTHQUAKE";
    755             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI:
    756                 return "TSUNAMI";
    757             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI:
    758                 return "EARTHQUAKE_AND_TSUNAMI";
    759             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE:
    760                 return "TEST_MESSAGE";
    761             case SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY:
    762                 return "OTHER_EMERGENCY";
    763         }
    764         return "UNKNOWN";
    765     }
    766 
    767     private static String getCMASMessageClass(int messageclass) {
    768         switch (messageclass) {
    769             case SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT:
    770                 return "PRESIDENTIAL_LEVEL_ALERT";
    771             case SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT:
    772                 return "EXTREME_THREAT";
    773             case SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT:
    774                 return "SEVERE_THREAT";
    775             case SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY:
    776                 return "CHILD_ABDUCTION_EMERGENCY";
    777             case SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST:
    778                 return "REQUIRED_MONTHLY_TEST";
    779             case SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE:
    780                 return "CMAS_EXERCISE";
    781         }
    782         return "UNKNOWN";
    783     }
    784 
    785     private static String getCMASCategory(int category) {
    786         switch (category) {
    787             case SmsCbCmasInfo.CMAS_CATEGORY_GEO:
    788                 return "GEOPHYSICAL";
    789             case SmsCbCmasInfo.CMAS_CATEGORY_MET:
    790                 return "METEOROLOGICAL";
    791             case SmsCbCmasInfo.CMAS_CATEGORY_SAFETY:
    792                 return "SAFETY";
    793             case SmsCbCmasInfo.CMAS_CATEGORY_SECURITY:
    794                 return "SECURITY";
    795             case SmsCbCmasInfo.CMAS_CATEGORY_RESCUE:
    796                 return "RESCUE";
    797             case SmsCbCmasInfo.CMAS_CATEGORY_FIRE:
    798                 return "FIRE";
    799             case SmsCbCmasInfo.CMAS_CATEGORY_HEALTH:
    800                 return "HEALTH";
    801             case SmsCbCmasInfo.CMAS_CATEGORY_ENV:
    802                 return "ENVIRONMENTAL";
    803             case SmsCbCmasInfo.CMAS_CATEGORY_TRANSPORT:
    804                 return "TRANSPORTATION";
    805             case SmsCbCmasInfo.CMAS_CATEGORY_INFRA:
    806                 return "INFRASTRUCTURE";
    807             case SmsCbCmasInfo.CMAS_CATEGORY_CBRNE:
    808                 return "CHEMICAL";
    809             case SmsCbCmasInfo.CMAS_CATEGORY_OTHER:
    810                 return "OTHER";
    811         }
    812         return "UNKNOWN";
    813     }
    814 
    815     private static String getCMASResponseType(int type) {
    816         switch (type) {
    817             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_SHELTER:
    818                 return "SHELTER";
    819             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EVACUATE:
    820                 return "EVACUATE";
    821             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_PREPARE:
    822                 return "PREPARE";
    823             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_EXECUTE:
    824                 return "EXECUTE";
    825             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR:
    826                 return "MONITOR";
    827             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_AVOID:
    828                 return "AVOID";
    829             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_ASSESS:
    830                 return "ASSESS";
    831             case SmsCbCmasInfo.CMAS_RESPONSE_TYPE_NONE:
    832                 return "NONE";
    833         }
    834         return "UNKNOWN";
    835     }
    836 
    837     private static String getCMASSeverity(int severity) {
    838         switch (severity) {
    839             case SmsCbCmasInfo.CMAS_SEVERITY_EXTREME:
    840                 return "EXTREME";
    841             case SmsCbCmasInfo.CMAS_SEVERITY_SEVERE:
    842                 return "SEVERE";
    843         }
    844         return "UNKNOWN";
    845     }
    846 
    847     private static String getCMASUrgency(int urgency) {
    848         switch (urgency) {
    849             case SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE:
    850                 return "IMMEDIATE";
    851             case SmsCbCmasInfo.CMAS_URGENCY_EXPECTED:
    852                 return "EXPECTED";
    853         }
    854         return "UNKNOWN";
    855     }
    856 
    857     private static String getCMASCertainty(int certainty) {
    858         switch (certainty) {
    859             case SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED:
    860                 return "IMMEDIATE";
    861             case SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY:
    862                 return "LIKELY";
    863         }
    864         return "UNKNOWN";
    865     }
    866 
    867     private static String getGeographicalScope(int scope) {
    868         switch (scope) {
    869             case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
    870                 return "CELL_WIDE_IMMEDIATE";
    871             case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
    872                 return "PLMN_WIDE ";
    873             case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
    874                 return "LA_WIDE";
    875             case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
    876                 return "CELL_WIDE";
    877         }
    878         return "UNKNOWN";
    879     }
    880 
    881     private static String getPriority(int priority) {
    882         switch (priority) {
    883             case SmsCbMessage.MESSAGE_PRIORITY_NORMAL:
    884                 return "NORMAL";
    885             case SmsCbMessage.MESSAGE_PRIORITY_INTERACTIVE:
    886                 return "INTERACTIVE";
    887             case SmsCbMessage.MESSAGE_PRIORITY_URGENT:
    888                 return "URGENT";
    889             case SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY:
    890                 return "EMERGENCY";
    891         }
    892         return "UNKNOWN";
    893     }
    894 
    895     @Override
    896     public void shutdown() {
    897 
    898         smsStopTrackingIncomingSmsMessage();
    899         smsStopTrackingIncomingMmsMessage();
    900         smsStopTrackingGsmEmergencyCBMessage();
    901         smsStopTrackingCdmaEmergencyCBMessage();
    902 
    903         synchronized (lock) {
    904             if (mSentReceiversRegistered) {
    905                 mService.unregisterReceiver(mSmsSendListener);
    906                 mService.unregisterReceiver(mMmsSendListener);
    907                 mSentReceiversRegistered = false;
    908             }
    909         }
    910     }
    911 
    912     private class MmsBuilder {
    913 
    914         public static final String MESSAGE_CLASS_PERSONAL =
    915                 PduHeaders.MESSAGE_CLASS_PERSONAL_STR;
    916 
    917         public static final String MESSAGE_CLASS_ADVERTISEMENT =
    918                 PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR;
    919 
    920         public static final String MESSAGE_CLASS_INFORMATIONAL =
    921                 PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR;
    922 
    923         public static final String MESSAGE_CLASS_AUTO =
    924                 PduHeaders.MESSAGE_CLASS_AUTO_STR;
    925 
    926         public static final int MESSAGE_PRIORITY_LOW = PduHeaders.PRIORITY_LOW;
    927         public static final int MESSAGE_PRIORITY_NORMAL = PduHeaders.PRIORITY_LOW;
    928         public static final int MESSAGE_PRIORITY_HIGH = PduHeaders.PRIORITY_LOW;
    929 
    930         private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 60 * 60;
    931         private static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
    932 
    933         private SendReq mRequest;
    934         private PduBody mBody;
    935 
    936         // FIXME: Eventually this should be exposed as a parameter
    937         private static final String TEMP_CONTENT_FILE_NAME = "text0.txt";
    938 
    939         // Synchronized Multimedia Internet Language
    940         // Fragment for compatibility
    941         private static final String sSmilText =
    942                 "<smil>" +
    943                         "<head>" +
    944                         "<layout>" +
    945                         "<root-layout/>" +
    946                         "<region height=\"100%%\" id=\"Text\" left=\"0%%\"" +
    947                         " top=\"0%%\" width=\"100%%\"/>" +
    948                         "</layout>" +
    949                         "</head>" +
    950                         "<body>" +
    951                         "<par dur=\"8000ms\">" +
    952                         "<text src=\"%s\" region=\"Text\"/>" +
    953                         "</par>" +
    954                         "</body>" +
    955                         "</smil>";
    956 
    957         public MmsBuilder() {
    958             mRequest = new SendReq();
    959             mBody = new PduBody();
    960         }
    961 
    962         public void setFromPhoneNumber(String number) {
    963             mRequest.setFrom(new EncodedStringValue(number));
    964         }
    965 
    966         public void setToPhoneNumber(String number) {
    967             mRequest.setTo(new EncodedStringValue[] {
    968                     new EncodedStringValue(number) });
    969         }
    970 
    971         public void setToPhoneNumbers(List<String> number) {
    972             mRequest.setTo(EncodedStringValue.encodeStrings((String[]) number.toArray()));
    973         }
    974 
    975         public void setSubject(String subject) {
    976             mRequest.setSubject(new EncodedStringValue(subject));
    977         }
    978 
    979         public void setDate() {
    980             setDate(System.currentTimeMillis() / 1000);
    981         }
    982 
    983         public void setDate(long time) {
    984             mRequest.setDate(time);
    985         }
    986 
    987         public void addMessageBody(String message) {
    988             addMessageBody(message, true);
    989         }
    990 
    991         public void setMessageClass(String messageClass) {
    992             mRequest.setMessageClass(messageClass.getBytes());
    993         }
    994 
    995         public void setMessagePriority(int priority) {
    996             try {
    997                 mRequest.setPriority(priority);
    998             } catch (InvalidHeaderValueException e) {
    999                 Log.e("Invalid Header Value "+e.toString());
   1000             }
   1001         }
   1002 
   1003         public void setDeliveryReport(boolean report) {
   1004             try {
   1005                 mRequest.setDeliveryReport((report) ? PduHeaders.VALUE_YES : PduHeaders.VALUE_NO);
   1006             } catch (InvalidHeaderValueException e) {
   1007                 Log.e("Invalid Header Value "+e.toString());
   1008             }
   1009         }
   1010 
   1011         public void setReadReport(boolean report) {
   1012             try {
   1013                 mRequest.setReadReport((report) ? PduHeaders.VALUE_YES : PduHeaders.VALUE_NO);
   1014             } catch (InvalidHeaderValueException e) {
   1015                 Log.e("Invalid Header Value "+e.toString());
   1016             }
   1017         }
   1018 
   1019         public void setExpirySeconds(int seconds) {
   1020             mRequest.setExpiry(seconds);
   1021         }
   1022 
   1023         public byte[] build() {
   1024             mRequest.setBody(mBody);
   1025 
   1026             int msgSize = 0;
   1027             for (int i = 0; i < mBody.getPartsNum(); i++) {
   1028                 msgSize += mBody.getPart(i).getDataLength();
   1029             }
   1030             mRequest.setMessageSize(msgSize);
   1031 
   1032             return new PduComposer(mContext, mRequest).make();
   1033         }
   1034 
   1035         public void addMessageBody(String message, boolean addSmilFragment) {
   1036             final PduPart part = new PduPart();
   1037             part.setCharset(CharacterSets.UTF_8);
   1038             part.setContentType(ContentType.TEXT_PLAIN.getBytes());
   1039             part.setContentLocation(TEMP_CONTENT_FILE_NAME.getBytes());
   1040             int index = TEMP_CONTENT_FILE_NAME.lastIndexOf(".");
   1041             String contentId = (index == -1) ? TEMP_CONTENT_FILE_NAME
   1042                     : TEMP_CONTENT_FILE_NAME.substring(0, index);
   1043             part.setContentId(contentId.getBytes());
   1044             part.setContentId("txt".getBytes());
   1045             part.setData(message.getBytes());
   1046             mBody.addPart(part);
   1047             if (addSmilFragment) {
   1048                 addSmilTextFragment(TEMP_CONTENT_FILE_NAME);
   1049             }
   1050         }
   1051 
   1052         private void addSmilTextFragment(String contentFilename) {
   1053 
   1054             final String smil = String.format(sSmilText, contentFilename);
   1055             final PduPart smilPart = new PduPart();
   1056             smilPart.setContentId("smil".getBytes());
   1057             smilPart.setContentLocation("smil.xml".getBytes());
   1058             smilPart.setContentType(ContentType.APP_SMIL.getBytes());
   1059             smilPart.setData(smil.getBytes());
   1060             mBody.addPart(0, smilPart);
   1061         }
   1062     }
   1063 
   1064 }
   1065