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