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 
SmsFacade(FacadeManager manager)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")
writeBytesToFile(String fileName, byte[] pdu)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
writeBytesToCacheFile(String fileName, byte[] pdu)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.")
smsStartTrackingIncomingMessage()271     public void smsStartTrackingIncomingMessage() {
272         Log.d("Using Deprecated smsStartTrackingIncomingMessage!");
273         smsStartTrackingIncomingSmsMessage();
274     }
275 
276     @Rpc(description = "Starts tracking incoming SMS.")
smsStartTrackingIncomingSmsMessage()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.")
smsStopTrackingIncomingMessage()285     public void smsStopTrackingIncomingMessage() {
286         Log.d("Using Deprecated smsStopTrackingIncomingMessage!");
287         smsStopTrackingIncomingSmsMessage();
288     }
289 
290     @Rpc(description = "Stops tracking incoming SMS.")
smsStopTrackingIncomingSmsMessage()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.")
smsStartTrackingIncomingMmsMessage()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.")
smsStopTrackingIncomingMmsMessage()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.")
smsSendMultimediaMessage( @pcParametername = "toPhoneNumber") String toPhoneNumber, @RpcParameter(name = "subject") String subject, @RpcParameter(name = "message") String message, @RpcParameter(name = "fromPhoneNumber") @RpcOptional String fromPhoneNumber, @RpcParameter(name = "fileName") @RpcOptional String fileName)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.")
smsSendTextMessage( @pcParametername = "phoneNumber") String phoneNumber, @RpcParameter(name = "message") String message, @RpcParameter(name = "deliveryReportRequired") Boolean deliveryReportRequired)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.")
smsGetAllMessagesFromIcc()437     public ArrayList<SmsMessage> smsGetAllMessagesFromIcc() {
438         return mSms.getAllMessagesFromIcc();
439     }
440 
441     @Rpc(description = "Starts tracking GSM Emergency CB Messages.")
smsStartTrackingGsmEmergencyCBMessage()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")
smsStopTrackingGsmEmergencyCBMessage()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")
smsStartTrackingCdmaEmergencyCBMessage()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.")
smsStopTrackingCdmaEmergencyCBMessage()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
onReceive(Context context, Intent intent)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
onReceive(Context context, Intent intent)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
onReceive(Context context, Intent intent)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
onReceive(Context context, Intent intent)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 
formatPhoneNumber(String phoneNumber)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
onReceive(Context context, Intent intent)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 
createBroadcastPendingIntent(String intentAction, Uri messageUri)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 
getSmsFailureReason(int resultCode)743     private static String getSmsFailureReason(int resultCode) {
744         return sSmsSendFailureMap.get(resultCode);
745     }
746 
getMmsFailureReason(int resultCode)747     private static String getMmsFailureReason(int resultCode) {
748         return sMmsSendFailureMap.get(resultCode);
749     }
750 
getMmsResponseStatus(int resultCode)751     private static String getMmsResponseStatus(int resultCode) {
752         return sMmsSendResponseMap.get(resultCode);
753     }
754 
getETWSWarningType(int type)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 
getCMASMessageClass(int messageclass)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 
getCMASCategory(int category)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 
getCMASResponseType(int type)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 
getCMASSeverity(int severity)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 
getCMASUrgency(int urgency)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 
getCMASCertainty(int certainty)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 
getGeographicalScope(int scope)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 
getPriority(int priority)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
shutdown()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 
MmsBuilder()961         public MmsBuilder() {
962             mRequest = new SendReq();
963             mBody = new PduBody();
964         }
965 
setFromPhoneNumber(String number)966         public void setFromPhoneNumber(String number) {
967             mRequest.setFrom(new EncodedStringValue(number));
968         }
969 
setToPhoneNumber(String number)970         public void setToPhoneNumber(String number) {
971             mRequest.setTo(new EncodedStringValue[] {
972                     new EncodedStringValue(number) });
973         }
974 
setToPhoneNumbers(List<String> number)975         public void setToPhoneNumbers(List<String> number) {
976             mRequest.setTo(EncodedStringValue.encodeStrings((String[]) number.toArray()));
977         }
978 
setSubject(String subject)979         public void setSubject(String subject) {
980             mRequest.setSubject(new EncodedStringValue(subject));
981         }
982 
setDate()983         public void setDate() {
984             setDate(System.currentTimeMillis() / 1000);
985         }
986 
setDate(long time)987         public void setDate(long time) {
988             mRequest.setDate(time);
989         }
990 
addMessageBody(String message)991         public void addMessageBody(String message) {
992             addMessageBody(message, true);
993         }
994 
setMessageClass(String messageClass)995         public void setMessageClass(String messageClass) {
996             mRequest.setMessageClass(messageClass.getBytes());
997         }
998 
setMessagePriority(int priority)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 
setDeliveryReport(boolean report)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 
setReadReport(boolean report)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 
setExpirySeconds(int seconds)1023         public void setExpirySeconds(int seconds) {
1024             mRequest.setExpiry(seconds);
1025         }
1026 
build()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 
addMessageBody(String message, boolean addSmilFragment)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 
addSmilTextFragment(String contentFilename)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