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