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