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