1/* 2 * Copyright (C) 2008 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 17package com.android.internal.telephony; 18 19import android.Manifest; 20import android.app.AppOpsManager; 21import android.app.PendingIntent; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.database.Cursor; 25import android.database.sqlite.SQLiteException; 26import android.net.Uri; 27import android.os.AsyncResult; 28import android.os.Binder; 29import android.os.Handler; 30import android.os.Message; 31import android.os.UserManager; 32import android.provider.Telephony; 33import android.telephony.Rlog; 34import android.telephony.SmsManager; 35import android.telephony.SmsMessage; 36import android.util.Log; 37 38import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; 39import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; 40import com.android.internal.telephony.uicc.IccConstants; 41import com.android.internal.telephony.uicc.IccFileHandler; 42import com.android.internal.telephony.uicc.IccUtils; 43import com.android.internal.telephony.uicc.UiccController; 44import com.android.internal.util.HexDump; 45 46import java.util.ArrayList; 47import java.util.Arrays; 48import java.util.List; 49 50import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; 51import static android.telephony.SmsManager.STATUS_ON_ICC_READ; 52import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD; 53 54import android.telephony.TelephonyManager; 55 56/** 57 * IccSmsInterfaceManager to provide an inter-process communication to 58 * access Sms in Icc. 59 */ 60public class IccSmsInterfaceManager { 61 static final String LOG_TAG = "IccSmsInterfaceManager"; 62 static final boolean DBG = true; 63 64 protected final Object mLock = new Object(); 65 protected boolean mSuccess; 66 private List<SmsRawData> mSms; 67 68 private CellBroadcastRangeManager mCellBroadcastRangeManager = 69 new CellBroadcastRangeManager(); 70 private CdmaBroadcastRangeManager mCdmaBroadcastRangeManager = 71 new CdmaBroadcastRangeManager(); 72 73 private static final int EVENT_LOAD_DONE = 1; 74 private static final int EVENT_UPDATE_DONE = 2; 75 protected static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3; 76 protected static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4; 77 private static final int SMS_CB_CODE_SCHEME_MIN = 0; 78 private static final int SMS_CB_CODE_SCHEME_MAX = 255; 79 80 protected Phone mPhone; 81 final protected Context mContext; 82 final protected AppOpsManager mAppOps; 83 final private UserManager mUserManager; 84 protected SMSDispatcher mDispatcher; 85 86 protected Handler mHandler = new Handler() { 87 @Override 88 public void handleMessage(Message msg) { 89 AsyncResult ar; 90 91 switch (msg.what) { 92 case EVENT_UPDATE_DONE: 93 ar = (AsyncResult) msg.obj; 94 synchronized (mLock) { 95 mSuccess = (ar.exception == null); 96 mLock.notifyAll(); 97 } 98 break; 99 case EVENT_LOAD_DONE: 100 ar = (AsyncResult)msg.obj; 101 synchronized (mLock) { 102 if (ar.exception == null) { 103 mSms = buildValidRawData((ArrayList<byte[]>) ar.result); 104 //Mark SMS as read after importing it from card. 105 markMessagesAsRead((ArrayList<byte[]>) ar.result); 106 } else { 107 if (Rlog.isLoggable("SMS", Log.DEBUG)) { 108 log("Cannot load Sms records"); 109 } 110 mSms = null; 111 } 112 mLock.notifyAll(); 113 } 114 break; 115 case EVENT_SET_BROADCAST_ACTIVATION_DONE: 116 case EVENT_SET_BROADCAST_CONFIG_DONE: 117 ar = (AsyncResult) msg.obj; 118 synchronized (mLock) { 119 mSuccess = (ar.exception == null); 120 mLock.notifyAll(); 121 } 122 break; 123 } 124 } 125 }; 126 127 protected IccSmsInterfaceManager(Phone phone) { 128 mPhone = phone; 129 mContext = phone.getContext(); 130 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 131 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 132 mDispatcher = new ImsSMSDispatcher(phone, 133 phone.mSmsStorageMonitor, phone.mSmsUsageMonitor); 134 } 135 136 protected void markMessagesAsRead(ArrayList<byte[]> messages) { 137 if (messages == null) { 138 return; 139 } 140 141 //IccFileHandler can be null, if icc card is absent. 142 IccFileHandler fh = mPhone.getIccFileHandler(); 143 if (fh == null) { 144 //shouldn't really happen, as messages are marked as read, only 145 //after importing it from icc. 146 if (Rlog.isLoggable("SMS", Log.DEBUG)) { 147 log("markMessagesAsRead - aborting, no icc card present."); 148 } 149 return; 150 } 151 152 int count = messages.size(); 153 154 for (int i = 0; i < count; i++) { 155 byte[] ba = messages.get(i); 156 if (ba[0] == STATUS_ON_ICC_UNREAD) { 157 int n = ba.length; 158 byte[] nba = new byte[n - 1]; 159 System.arraycopy(ba, 1, nba, 0, n - 1); 160 byte[] record = makeSmsRecordData(STATUS_ON_ICC_READ, nba); 161 fh.updateEFLinearFixed(IccConstants.EF_SMS, i + 1, record, null, null); 162 if (Rlog.isLoggable("SMS", Log.DEBUG)) { 163 log("SMS " + (i + 1) + " marked as read"); 164 } 165 } 166 } 167 } 168 169 protected void updatePhoneObject(Phone phone) { 170 mPhone = phone; 171 mDispatcher.updatePhoneObject(phone); 172 } 173 174 protected void enforceReceiveAndSend(String message) { 175 mContext.enforceCallingOrSelfPermission( 176 Manifest.permission.RECEIVE_SMS, message); 177 mContext.enforceCallingOrSelfPermission( 178 Manifest.permission.SEND_SMS, message); 179 } 180 181 /** 182 * Update the specified message on the Icc. 183 * 184 * @param index record index of message to update 185 * @param status new message status (STATUS_ON_ICC_READ, 186 * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, 187 * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE) 188 * @param pdu the raw PDU to store 189 * @return success or not 190 * 191 */ 192 193 public boolean 194 updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu) { 195 if (DBG) log("updateMessageOnIccEf: index=" + index + 196 " status=" + status + " ==> " + 197 "("+ Arrays.toString(pdu) + ")"); 198 enforceReceiveAndSend("Updating message on Icc"); 199 if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(), 200 callingPackage) != AppOpsManager.MODE_ALLOWED) { 201 return false; 202 } 203 synchronized(mLock) { 204 mSuccess = false; 205 Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); 206 207 if (status == STATUS_ON_ICC_FREE) { 208 // RIL_REQUEST_DELETE_SMS_ON_SIM vs RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM 209 // Special case FREE: call deleteSmsOnSim/Ruim instead of 210 // manipulating the record 211 // Will eventually fail if icc card is not present. 212 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 213 mPhone.mCi.deleteSmsOnSim(index, response); 214 } else { 215 mPhone.mCi.deleteSmsOnRuim(index, response); 216 } 217 } else { 218 //IccFilehandler can be null if ICC card is not present. 219 IccFileHandler fh = mPhone.getIccFileHandler(); 220 if (fh == null) { 221 response.recycle(); 222 return mSuccess; /* is false */ 223 } 224 byte[] record = makeSmsRecordData(status, pdu); 225 fh.updateEFLinearFixed( 226 IccConstants.EF_SMS, 227 index, record, null, response); 228 } 229 try { 230 mLock.wait(); 231 } catch (InterruptedException e) { 232 log("interrupted while trying to update by index"); 233 } 234 } 235 return mSuccess; 236 } 237 238 /** 239 * Copy a raw SMS PDU to the Icc. 240 * 241 * @param pdu the raw PDU to store 242 * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, 243 * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) 244 * @return success or not 245 * 246 */ 247 public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc) { 248 //NOTE smsc not used in RUIM 249 if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " + 250 "pdu=("+ Arrays.toString(pdu) + 251 "), smsc=(" + Arrays.toString(smsc) +")"); 252 enforceReceiveAndSend("Copying message to Icc"); 253 if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(), 254 callingPackage) != AppOpsManager.MODE_ALLOWED) { 255 return false; 256 } 257 synchronized(mLock) { 258 mSuccess = false; 259 Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); 260 261 //RIL_REQUEST_WRITE_SMS_TO_SIM vs RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM 262 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 263 mPhone.mCi.writeSmsToSim(status, IccUtils.bytesToHexString(smsc), 264 IccUtils.bytesToHexString(pdu), response); 265 } else { 266 mPhone.mCi.writeSmsToRuim(status, IccUtils.bytesToHexString(pdu), 267 response); 268 } 269 270 try { 271 mLock.wait(); 272 } catch (InterruptedException e) { 273 log("interrupted while trying to update by index"); 274 } 275 } 276 return mSuccess; 277 } 278 279 /** 280 * Retrieves all messages currently stored on Icc. 281 * 282 * @return list of SmsRawData of all sms on Icc 283 */ 284 285 public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) { 286 if (DBG) log("getAllMessagesFromEF"); 287 288 mContext.enforceCallingOrSelfPermission( 289 Manifest.permission.RECEIVE_SMS, 290 "Reading messages from Icc"); 291 if (mAppOps.noteOp(AppOpsManager.OP_READ_ICC_SMS, Binder.getCallingUid(), 292 callingPackage) != AppOpsManager.MODE_ALLOWED) { 293 return new ArrayList<SmsRawData>(); 294 } 295 synchronized(mLock) { 296 297 IccFileHandler fh = mPhone.getIccFileHandler(); 298 if (fh == null) { 299 Rlog.e(LOG_TAG, "Cannot load Sms records. No icc card?"); 300 mSms = null; 301 return mSms; 302 } 303 304 Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); 305 fh.loadEFLinearFixedAll(IccConstants.EF_SMS, response); 306 307 try { 308 mLock.wait(); 309 } catch (InterruptedException e) { 310 log("interrupted while trying to load from the Icc"); 311 } 312 } 313 return mSms; 314 } 315 316 /** 317 * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}. 318 * This method checks if the calling package or itself has the permission to send the data sms. 319 */ 320 public void sendDataWithSelfPermissions(String callingPackage, String destAddr, String scAddr, 321 int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 322 mPhone.getContext().enforceCallingOrSelfPermission( 323 Manifest.permission.SEND_SMS, 324 "Sending SMS message"); 325 sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent, 326 deliveryIntent); 327 } 328 329 /** 330 * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}. 331 * This method checks only if the calling package has the permission to send the data sms. 332 */ 333 public void sendData(String callingPackage, String destAddr, String scAddr, int destPort, 334 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 335 mPhone.getContext().enforceCallingPermission( 336 Manifest.permission.SEND_SMS, 337 "Sending SMS message"); 338 sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent, 339 deliveryIntent); 340 } 341 342 /** 343 * Send a data based SMS to a specific application port. 344 * 345 * @param destAddr the address to send the message to 346 * @param scAddr is the service center address or null to use 347 * the current default SMSC 348 * @param destPort the port to deliver the message to 349 * @param data the body of the message to send 350 * @param sentIntent if not NULL this <code>PendingIntent</code> is 351 * broadcast when the message is successfully sent, or failed. 352 * The result code will be <code>Activity.RESULT_OK<code> for success, 353 * or one of these errors:<br> 354 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 355 * <code>RESULT_ERROR_RADIO_OFF</code><br> 356 * <code>RESULT_ERROR_NULL_PDU</code><br> 357 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 358 * the extra "errorCode" containing a radio technology specific value, 359 * generally only useful for troubleshooting.<br> 360 * The per-application based SMS control checks sentIntent. If sentIntent 361 * is NULL the caller will be checked against all unknown applications, 362 * which cause smaller number of SMS to be sent in checking period. 363 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 364 * broadcast when the message is delivered to the recipient. The 365 * raw pdu of the status report is in the extended data ("pdu"). 366 */ 367 368 private void sendDataInternal(String callingPackage, String destAddr, String scAddr, 369 int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 370 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 371 log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" + 372 destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" + 373 sentIntent + " deliveryIntent=" + deliveryIntent); 374 } 375 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 376 callingPackage) != AppOpsManager.MODE_ALLOWED) { 377 return; 378 } 379 destAddr = filterDestAddress(destAddr); 380 mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); 381 } 382 383 /** 384 * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}. 385 * This method checks only if the calling package has the permission to send the sms. 386 */ 387 public void sendText(String callingPackage, String destAddr, String scAddr, 388 String text, PendingIntent sentIntent, PendingIntent deliveryIntent, 389 boolean persistMessageForNonDefaultSmsApp) { 390 mPhone.getContext().enforceCallingPermission( 391 Manifest.permission.SEND_SMS, 392 "Sending SMS message"); 393 sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, 394 persistMessageForNonDefaultSmsApp); 395 } 396 397 /** 398 * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}. 399 * This method checks if the calling package or itself has the permission to send the sms. 400 */ 401 public void sendTextWithSelfPermissions(String callingPackage, String destAddr, String scAddr, 402 String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { 403 mPhone.getContext().enforceCallingOrSelfPermission( 404 Manifest.permission.SEND_SMS, 405 "Sending SMS message"); 406 sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent, 407 true /* persistMessageForNonDefaultSmsApp */); 408 } 409 410 /** 411 * Send a text based SMS. 412 * 413 * @param destAddr the address to send the message to 414 * @param scAddr is the service center address or null to use 415 * the current default SMSC 416 * @param text the body of the message to send 417 * @param sentIntent if not NULL this <code>PendingIntent</code> is 418 * broadcast when the message is successfully sent, or failed. 419 * The result code will be <code>Activity.RESULT_OK<code> for success, 420 * or one of these errors:<br> 421 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 422 * <code>RESULT_ERROR_RADIO_OFF</code><br> 423 * <code>RESULT_ERROR_NULL_PDU</code><br> 424 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 425 * the extra "errorCode" containing a radio technology specific value, 426 * generally only useful for troubleshooting.<br> 427 * The per-application based SMS control checks sentIntent. If sentIntent 428 * is NULL the caller will be checked against all unknown applications, 429 * which cause smaller number of SMS to be sent in checking period. 430 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 431 * broadcast when the message is delivered to the recipient. The 432 * raw pdu of the status report is in the extended data ("pdu"). 433 */ 434 435 private void sendTextInternal(String callingPackage, String destAddr, String scAddr, 436 String text, PendingIntent sentIntent, PendingIntent deliveryIntent, 437 boolean persistMessageForNonDefaultSmsApp) { 438 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 439 log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr + 440 " text='"+ text + "' sentIntent=" + 441 sentIntent + " deliveryIntent=" + deliveryIntent); 442 } 443 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 444 callingPackage) != AppOpsManager.MODE_ALLOWED) { 445 return; 446 } 447 if (!persistMessageForNonDefaultSmsApp) { 448 // Only allow carrier app to skip auto message persistence. 449 enforceCarrierPrivilege(); 450 } 451 destAddr = filterDestAddress(destAddr); 452 mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, 453 null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp); 454 } 455 456 /** 457 * Inject an SMS PDU into the android application framework. 458 * 459 * @param pdu is the byte array of pdu to be injected into android application framework 460 * @param format is the format of SMS pdu (3gpp or 3gpp2) 461 * @param receivedIntent if not NULL this <code>PendingIntent</code> is 462 * broadcast when the message is successfully received by the 463 * android application framework. This intent is broadcasted at 464 * the same time an SMS received from radio is acknowledged back. 465 */ 466 public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { 467 enforceCarrierPrivilege(); 468 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 469 log("pdu: " + pdu + 470 "\n format=" + format + 471 "\n receivedIntent=" + receivedIntent); 472 } 473 mDispatcher.injectSmsPdu(pdu, format, receivedIntent); 474 } 475 476 /** 477 * Send a multi-part text based SMS. 478 * 479 * @param destAddr the address to send the message to 480 * @param scAddr is the service center address or null to use 481 * the current default SMSC 482 * @param parts an <code>ArrayList</code> of strings that, in order, 483 * comprise the original message 484 * @param sentIntents if not null, an <code>ArrayList</code> of 485 * <code>PendingIntent</code>s (one for each message part) that is 486 * broadcast when the corresponding message part has been sent. 487 * The result code will be <code>Activity.RESULT_OK<code> for success, 488 * or one of these errors: 489 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 490 * <code>RESULT_ERROR_RADIO_OFF</code> 491 * <code>RESULT_ERROR_NULL_PDU</code>. 492 * The per-application based SMS control checks sentIntent. If sentIntent 493 * is NULL the caller will be checked against all unknown applications, 494 * which cause smaller number of SMS to be sent in checking period. 495 * @param deliveryIntents if not null, an <code>ArrayList</code> of 496 * <code>PendingIntent</code>s (one for each message part) that is 497 * broadcast when the corresponding message part has been delivered 498 * to the recipient. The raw pdu of the status report is in the 499 * extended data ("pdu"). 500 */ 501 502 public void sendMultipartText(String callingPackage, String destAddr, String scAddr, 503 List<String> parts, List<PendingIntent> sentIntents, 504 List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) { 505 mPhone.getContext().enforceCallingPermission( 506 Manifest.permission.SEND_SMS, 507 "Sending SMS message"); 508 if (!persistMessageForNonDefaultSmsApp) { 509 // Only allow carrier app to skip auto message persistence. 510 enforceCarrierPrivilege(); 511 } 512 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 513 int i = 0; 514 for (String part : parts) { 515 log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr + 516 ", part[" + (i++) + "]=" + part); 517 } 518 } 519 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 520 callingPackage) != AppOpsManager.MODE_ALLOWED) { 521 return; 522 } 523 524 destAddr = filterDestAddress(destAddr); 525 526 if (parts.size() > 1 && parts.size() < 10 && !SmsMessage.hasEmsSupport()) { 527 for (int i = 0; i < parts.size(); i++) { 528 // If EMS is not supported, we have to break down EMS into single segment SMS 529 // and add page info " x/y". 530 String singlePart = parts.get(i); 531 if (SmsMessage.shouldAppendPageNumberAsPrefix()) { 532 singlePart = String.valueOf(i + 1) + '/' + parts.size() + ' ' + singlePart; 533 } else { 534 singlePart = singlePart.concat(' ' + String.valueOf(i + 1) + '/' + parts.size()); 535 } 536 537 PendingIntent singleSentIntent = null; 538 if (sentIntents != null && sentIntents.size() > i) { 539 singleSentIntent = sentIntents.get(i); 540 } 541 542 PendingIntent singleDeliveryIntent = null; 543 if (deliveryIntents != null && deliveryIntents.size() > i) { 544 singleDeliveryIntent = deliveryIntents.get(i); 545 } 546 547 mDispatcher.sendText(destAddr, scAddr, singlePart, 548 singleSentIntent, singleDeliveryIntent, 549 null/*messageUri*/, callingPackage, 550 persistMessageForNonDefaultSmsApp); 551 } 552 return; 553 } 554 555 mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts, 556 (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents, 557 null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp); 558 } 559 560 561 public int getPremiumSmsPermission(String packageName) { 562 return mDispatcher.getPremiumSmsPermission(packageName); 563 } 564 565 566 public void setPremiumSmsPermission(String packageName, int permission) { 567 mDispatcher.setPremiumSmsPermission(packageName, permission); 568 } 569 570 /** 571 * create SmsRawData lists from all sms record byte[] 572 * Use null to indicate "free" record 573 * 574 * @param messages List of message records from EF_SMS. 575 * @return SmsRawData list of all in-used records 576 */ 577 protected ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) { 578 int count = messages.size(); 579 ArrayList<SmsRawData> ret; 580 581 ret = new ArrayList<SmsRawData>(count); 582 583 for (int i = 0; i < count; i++) { 584 byte[] ba = messages.get(i); 585 if (ba[0] == STATUS_ON_ICC_FREE) { 586 ret.add(null); 587 } else { 588 ret.add(new SmsRawData(messages.get(i))); 589 } 590 } 591 592 return ret; 593 } 594 595 /** 596 * Generates an EF_SMS record from status and raw PDU. 597 * 598 * @param status Message status. See TS 51.011 10.5.3. 599 * @param pdu Raw message PDU. 600 * @return byte array for the record. 601 */ 602 protected byte[] makeSmsRecordData(int status, byte[] pdu) { 603 byte[] data; 604 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 605 data = new byte[IccConstants.SMS_RECORD_LENGTH]; 606 } else { 607 data = new byte[IccConstants.CDMA_SMS_RECORD_LENGTH]; 608 } 609 610 // Status bits for this record. See TS 51.011 10.5.3 611 data[0] = (byte)(status & 7); 612 613 System.arraycopy(pdu, 0, data, 1, pdu.length); 614 615 // Pad out with 0xFF's. 616 for (int j = pdu.length+1; j < data.length; j++) { 617 data[j] = -1; 618 } 619 620 return data; 621 } 622 623 public boolean enableCellBroadcast(int messageIdentifier, int ranType) { 624 return enableCellBroadcastRange(messageIdentifier, messageIdentifier, ranType); 625 } 626 627 public boolean disableCellBroadcast(int messageIdentifier, int ranType) { 628 return disableCellBroadcastRange(messageIdentifier, messageIdentifier, ranType); 629 } 630 631 public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) { 632 if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_GSM) { 633 return enableGsmBroadcastRange(startMessageId, endMessageId); 634 } else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA) { 635 return enableCdmaBroadcastRange(startMessageId, endMessageId); 636 } else { 637 throw new IllegalArgumentException("Not a supportted RAN Type"); 638 } 639 } 640 641 public boolean disableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) { 642 if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_GSM ) { 643 return disableGsmBroadcastRange(startMessageId, endMessageId); 644 } else if (ranType == SmsManager.CELL_BROADCAST_RAN_TYPE_CDMA) { 645 return disableCdmaBroadcastRange(startMessageId, endMessageId); 646 } else { 647 throw new IllegalArgumentException("Not a supportted RAN Type"); 648 } 649 } 650 651 synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) { 652 653 Context context = mPhone.getContext(); 654 655 context.enforceCallingPermission( 656 "android.permission.RECEIVE_SMS", 657 "Enabling cell broadcast SMS"); 658 659 String client = context.getPackageManager().getNameForUid( 660 Binder.getCallingUid()); 661 662 if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { 663 log("Failed to add GSM cell broadcast subscription for MID range " + startMessageId 664 + " to " + endMessageId + " from client " + client); 665 return false; 666 } 667 668 if (DBG) 669 log("Added GSM cell broadcast subscription for MID range " + startMessageId 670 + " to " + endMessageId + " from client " + client); 671 672 setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty()); 673 674 return true; 675 } 676 677 synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) { 678 679 Context context = mPhone.getContext(); 680 681 context.enforceCallingPermission( 682 "android.permission.RECEIVE_SMS", 683 "Disabling cell broadcast SMS"); 684 685 String client = context.getPackageManager().getNameForUid( 686 Binder.getCallingUid()); 687 688 if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { 689 log("Failed to remove GSM cell broadcast subscription for MID range " + startMessageId 690 + " to " + endMessageId + " from client " + client); 691 return false; 692 } 693 694 if (DBG) 695 log("Removed GSM cell broadcast subscription for MID range " + startMessageId 696 + " to " + endMessageId + " from client " + client); 697 698 setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty()); 699 700 return true; 701 } 702 703 synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) { 704 705 Context context = mPhone.getContext(); 706 707 context.enforceCallingPermission( 708 "android.permission.RECEIVE_SMS", 709 "Enabling cdma broadcast SMS"); 710 711 String client = context.getPackageManager().getNameForUid( 712 Binder.getCallingUid()); 713 714 if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { 715 log("Failed to add cdma broadcast subscription for MID range " + startMessageId 716 + " to " + endMessageId + " from client " + client); 717 return false; 718 } 719 720 if (DBG) 721 log("Added cdma broadcast subscription for MID range " + startMessageId 722 + " to " + endMessageId + " from client " + client); 723 724 setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty()); 725 726 return true; 727 } 728 729 synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) { 730 731 Context context = mPhone.getContext(); 732 733 context.enforceCallingPermission( 734 "android.permission.RECEIVE_SMS", 735 "Disabling cell broadcast SMS"); 736 737 String client = context.getPackageManager().getNameForUid( 738 Binder.getCallingUid()); 739 740 if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { 741 log("Failed to remove cdma broadcast subscription for MID range " + startMessageId 742 + " to " + endMessageId + " from client " + client); 743 return false; 744 } 745 746 if (DBG) 747 log("Removed cdma broadcast subscription for MID range " + startMessageId 748 + " to " + endMessageId + " from client " + client); 749 750 setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty()); 751 752 return true; 753 } 754 755 class CellBroadcastRangeManager extends IntRangeManager { 756 private ArrayList<SmsBroadcastConfigInfo> mConfigList = 757 new ArrayList<SmsBroadcastConfigInfo>(); 758 759 /** 760 * Called when the list of enabled ranges has changed. This will be 761 * followed by zero or more calls to {@link #addRange} followed by 762 * a call to {@link #finishUpdate}. 763 */ 764 protected void startUpdate() { 765 mConfigList.clear(); 766 } 767 768 /** 769 * Called after {@link #startUpdate} to indicate a range of enabled 770 * values. 771 * @param startId the first id included in the range 772 * @param endId the last id included in the range 773 */ 774 protected void addRange(int startId, int endId, boolean selected) { 775 mConfigList.add(new SmsBroadcastConfigInfo(startId, endId, 776 SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected)); 777 } 778 779 /** 780 * Called to indicate the end of a range update started by the 781 * previous call to {@link #startUpdate}. 782 * @return true if successful, false otherwise 783 */ 784 protected boolean finishUpdate() { 785 if (mConfigList.isEmpty()) { 786 return true; 787 } else { 788 SmsBroadcastConfigInfo[] configs = 789 mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]); 790 return setCellBroadcastConfig(configs); 791 } 792 } 793 } 794 795 class CdmaBroadcastRangeManager extends IntRangeManager { 796 private ArrayList<CdmaSmsBroadcastConfigInfo> mConfigList = 797 new ArrayList<CdmaSmsBroadcastConfigInfo>(); 798 799 /** 800 * Called when the list of enabled ranges has changed. This will be 801 * followed by zero or more calls to {@link #addRange} followed by a 802 * call to {@link #finishUpdate}. 803 */ 804 protected void startUpdate() { 805 mConfigList.clear(); 806 } 807 808 /** 809 * Called after {@link #startUpdate} to indicate a range of enabled 810 * values. 811 * @param startId the first id included in the range 812 * @param endId the last id included in the range 813 */ 814 protected void addRange(int startId, int endId, boolean selected) { 815 mConfigList.add(new CdmaSmsBroadcastConfigInfo(startId, endId, 816 1, selected)); 817 } 818 819 /** 820 * Called to indicate the end of a range update started by the previous 821 * call to {@link #startUpdate}. 822 * @return true if successful, false otherwise 823 */ 824 protected boolean finishUpdate() { 825 if (mConfigList.isEmpty()) { 826 return true; 827 } else { 828 CdmaSmsBroadcastConfigInfo[] configs = 829 mConfigList.toArray(new CdmaSmsBroadcastConfigInfo[mConfigList.size()]); 830 return setCdmaBroadcastConfig(configs); 831 } 832 } 833 } 834 835 private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) { 836 if (DBG) 837 log("Calling setGsmBroadcastConfig with " + configs.length + " configurations"); 838 839 synchronized (mLock) { 840 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE); 841 842 mSuccess = false; 843 mPhone.mCi.setGsmBroadcastConfig(configs, response); 844 845 try { 846 mLock.wait(); 847 } catch (InterruptedException e) { 848 log("interrupted while trying to set cell broadcast config"); 849 } 850 } 851 852 return mSuccess; 853 } 854 855 private boolean setCellBroadcastActivation(boolean activate) { 856 if (DBG) 857 log("Calling setCellBroadcastActivation(" + activate + ')'); 858 859 synchronized (mLock) { 860 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE); 861 862 mSuccess = false; 863 mPhone.mCi.setGsmBroadcastActivation(activate, response); 864 865 try { 866 mLock.wait(); 867 } catch (InterruptedException e) { 868 log("interrupted while trying to set cell broadcast activation"); 869 } 870 } 871 872 return mSuccess; 873 } 874 875 private boolean setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs) { 876 if (DBG) 877 log("Calling setCdmaBroadcastConfig with " + configs.length + " configurations"); 878 879 synchronized (mLock) { 880 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE); 881 882 mSuccess = false; 883 mPhone.mCi.setCdmaBroadcastConfig(configs, response); 884 885 try { 886 mLock.wait(); 887 } catch (InterruptedException e) { 888 log("interrupted while trying to set cdma broadcast config"); 889 } 890 } 891 892 return mSuccess; 893 } 894 895 private boolean setCdmaBroadcastActivation(boolean activate) { 896 if (DBG) 897 log("Calling setCdmaBroadcastActivation(" + activate + ")"); 898 899 synchronized (mLock) { 900 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE); 901 902 mSuccess = false; 903 mPhone.mCi.setCdmaBroadcastActivation(activate, response); 904 905 try { 906 mLock.wait(); 907 } catch (InterruptedException e) { 908 log("interrupted while trying to set cdma broadcast activation"); 909 } 910 } 911 912 return mSuccess; 913 } 914 915 protected void log(String msg) { 916 Log.d(LOG_TAG, "[IccSmsInterfaceManager] " + msg); 917 } 918 919 public boolean isImsSmsSupported() { 920 return mDispatcher.isIms(); 921 } 922 923 public String getImsSmsFormat() { 924 return mDispatcher.getImsSmsFormat(); 925 } 926 927 public void sendStoredText(String callingPkg, Uri messageUri, String scAddress, 928 PendingIntent sentIntent, PendingIntent deliveryIntent) { 929 mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS, 930 "Sending SMS message"); 931 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 932 log("sendStoredText: scAddr=" + scAddress + " messageUri=" + messageUri 933 + " sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent); 934 } 935 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) 936 != AppOpsManager.MODE_ALLOWED) { 937 return; 938 } 939 final ContentResolver resolver = mPhone.getContext().getContentResolver(); 940 if (!isFailedOrDraft(resolver, messageUri)) { 941 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: not FAILED or DRAFT message"); 942 returnUnspecifiedFailure(sentIntent); 943 return; 944 } 945 final String[] textAndAddress = loadTextAndAddress(resolver, messageUri); 946 if (textAndAddress == null) { 947 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: can not load text"); 948 returnUnspecifiedFailure(sentIntent); 949 return; 950 } 951 textAndAddress[1] = filterDestAddress(textAndAddress[1]); 952 mDispatcher.sendText(textAndAddress[1], scAddress, textAndAddress[0], 953 sentIntent, deliveryIntent, messageUri, callingPkg, 954 true /* persistMessageForNonDefaultSmsApp */); 955 } 956 957 public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress, 958 List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { 959 mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS, 960 "Sending SMS message"); 961 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) 962 != AppOpsManager.MODE_ALLOWED) { 963 return; 964 } 965 final ContentResolver resolver = mPhone.getContext().getContentResolver(); 966 if (!isFailedOrDraft(resolver, messageUri)) { 967 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: " 968 + "not FAILED or DRAFT message"); 969 returnUnspecifiedFailure(sentIntents); 970 return; 971 } 972 final String[] textAndAddress = loadTextAndAddress(resolver, messageUri); 973 if (textAndAddress == null) { 974 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: can not load text"); 975 returnUnspecifiedFailure(sentIntents); 976 return; 977 } 978 final ArrayList<String> parts = SmsManager.getDefault().divideMessage(textAndAddress[0]); 979 if (parts == null || parts.size() < 1) { 980 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: can not divide text"); 981 returnUnspecifiedFailure(sentIntents); 982 return; 983 } 984 985 textAndAddress[1] = filterDestAddress(textAndAddress[1]); 986 987 if (parts.size() > 1 && parts.size() < 10 && !SmsMessage.hasEmsSupport()) { 988 for (int i = 0; i < parts.size(); i++) { 989 // If EMS is not supported, we have to break down EMS into single segment SMS 990 // and add page info " x/y". 991 String singlePart = parts.get(i); 992 if (SmsMessage.shouldAppendPageNumberAsPrefix()) { 993 singlePart = String.valueOf(i + 1) + '/' + parts.size() + ' ' + singlePart; 994 } else { 995 singlePart = singlePart.concat(' ' + String.valueOf(i + 1) + '/' + parts.size()); 996 } 997 998 PendingIntent singleSentIntent = null; 999 if (sentIntents != null && sentIntents.size() > i) { 1000 singleSentIntent = sentIntents.get(i); 1001 } 1002 1003 PendingIntent singleDeliveryIntent = null; 1004 if (deliveryIntents != null && deliveryIntents.size() > i) { 1005 singleDeliveryIntent = deliveryIntents.get(i); 1006 } 1007 1008 mDispatcher.sendText(textAndAddress[1], scAddress, singlePart, 1009 singleSentIntent, singleDeliveryIntent, messageUri, callingPkg, 1010 true /* persistMessageForNonDefaultSmsApp */); 1011 } 1012 return; 1013 } 1014 1015 mDispatcher.sendMultipartText( 1016 textAndAddress[1], // destAddress 1017 scAddress, 1018 parts, 1019 (ArrayList<PendingIntent>) sentIntents, 1020 (ArrayList<PendingIntent>) deliveryIntents, 1021 messageUri, 1022 callingPkg, 1023 true /* persistMessageForNonDefaultSmsApp */); 1024 } 1025 1026 private boolean isFailedOrDraft(ContentResolver resolver, Uri messageUri) { 1027 // Clear the calling identity and query the database using the phone user id 1028 // Otherwise the AppOps check in TelephonyProvider would complain about mismatch 1029 // between the calling uid and the package uid 1030 final long identity = Binder.clearCallingIdentity(); 1031 Cursor cursor = null; 1032 try { 1033 cursor = resolver.query( 1034 messageUri, 1035 new String[]{ Telephony.Sms.TYPE }, 1036 null/*selection*/, 1037 null/*selectionArgs*/, 1038 null/*sortOrder*/); 1039 if (cursor != null && cursor.moveToFirst()) { 1040 final int type = cursor.getInt(0); 1041 return type == Telephony.Sms.MESSAGE_TYPE_DRAFT 1042 || type == Telephony.Sms.MESSAGE_TYPE_FAILED; 1043 } 1044 } catch (SQLiteException e) { 1045 Log.e(LOG_TAG, "[IccSmsInterfaceManager]isFailedOrDraft: query message type failed", e); 1046 } finally { 1047 if (cursor != null) { 1048 cursor.close(); 1049 } 1050 Binder.restoreCallingIdentity(identity); 1051 } 1052 return false; 1053 } 1054 1055 // Return an array including both the SMS text (0) and address (1) 1056 private String[] loadTextAndAddress(ContentResolver resolver, Uri messageUri) { 1057 // Clear the calling identity and query the database using the phone user id 1058 // Otherwise the AppOps check in TelephonyProvider would complain about mismatch 1059 // between the calling uid and the package uid 1060 final long identity = Binder.clearCallingIdentity(); 1061 Cursor cursor = null; 1062 try { 1063 cursor = resolver.query( 1064 messageUri, 1065 new String[]{ 1066 Telephony.Sms.BODY, 1067 Telephony.Sms.ADDRESS 1068 }, 1069 null/*selection*/, 1070 null/*selectionArgs*/, 1071 null/*sortOrder*/); 1072 if (cursor != null && cursor.moveToFirst()) { 1073 return new String[]{ cursor.getString(0), cursor.getString(1) }; 1074 } 1075 } catch (SQLiteException e) { 1076 Log.e(LOG_TAG, "[IccSmsInterfaceManager]loadText: query message text failed", e); 1077 } finally { 1078 if (cursor != null) { 1079 cursor.close(); 1080 } 1081 Binder.restoreCallingIdentity(identity); 1082 } 1083 return null; 1084 } 1085 1086 private void returnUnspecifiedFailure(PendingIntent pi) { 1087 if (pi != null) { 1088 try { 1089 pi.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); 1090 } catch (PendingIntent.CanceledException e) { 1091 // ignore 1092 } 1093 } 1094 } 1095 1096 private void returnUnspecifiedFailure(List<PendingIntent> pis) { 1097 if (pis == null) { 1098 return; 1099 } 1100 for (PendingIntent pi : pis) { 1101 returnUnspecifiedFailure(pi); 1102 } 1103 } 1104 1105 private void enforceCarrierPrivilege() { 1106 UiccController controller = UiccController.getInstance(); 1107 if (controller == null || controller.getUiccCard(mPhone.getPhoneId()) == null) { 1108 throw new SecurityException("No Carrier Privilege: No UICC"); 1109 } 1110 if (controller.getUiccCard(mPhone.getPhoneId()).getCarrierPrivilegeStatusForCurrentTransaction( 1111 mContext.getPackageManager()) != 1112 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { 1113 throw new SecurityException("No Carrier Privilege."); 1114 } 1115 } 1116 1117 private String filterDestAddress(String destAddr) { 1118 String result = null; 1119 result = SmsNumberUtils.filterDestAddr(mPhone, destAddr); 1120 return result != null ? result : destAddr; 1121 } 1122 1123} 1124