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 17 package com.android.internal.telephony; 18 19 import android.Manifest; 20 import android.app.AppOpsManager; 21 import android.app.PendingIntent; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.database.Cursor; 25 import android.database.sqlite.SQLiteException; 26 import android.net.Uri; 27 import android.os.AsyncResult; 28 import android.os.Binder; 29 import android.os.Handler; 30 import android.os.Message; 31 import android.os.UserManager; 32 import android.provider.Telephony; 33 import android.telephony.Rlog; 34 import android.telephony.SmsManager; 35 import android.telephony.SmsMessage; 36 import android.util.Log; 37 38 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; 39 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; 40 import com.android.internal.telephony.uicc.IccConstants; 41 import com.android.internal.telephony.uicc.IccFileHandler; 42 import com.android.internal.telephony.uicc.UiccController; 43 import com.android.internal.telephony.SmsNumberUtils; 44 import com.android.internal.util.HexDump; 45 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.List; 49 50 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; 51 import static android.telephony.SmsManager.STATUS_ON_ICC_READ; 52 import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD; 53 54 import android.telephony.TelephonyManager; 55 56 /** 57 * IccSmsInterfaceManager to provide an inter-process communication to 58 * access Sms in Icc. 59 */ 60 public 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.enforceCallingPermission( 177 Manifest.permission.RECEIVE_SMS, message); 178 mContext.enforceCallingPermission( 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 * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary. 429 * This outbound message was handled by the carrier app. If the carrier app fails to send 430 * this message, it would be resent by PSTN. 431 * 432 * @param messageRef the reference number of the SMS message. 433 * @param success True if and only if the message was sent successfully. If its value is 434 * false, this message should be resent via PSTN. 435 * {@hide} 436 */ 437 public void updateSmsSendStatus(int messageRef, boolean success) { 438 enforceCarrierPrivilege(); 439 mDispatcher.updateSmsSendStatus(messageRef, success); 440 } 441 442 /** 443 * Send a multi-part text based SMS. 444 * 445 * @param destAddr the address to send the message to 446 * @param scAddr is the service center address or null to use 447 * the current default SMSC 448 * @param parts an <code>ArrayList</code> of strings that, in order, 449 * comprise the original message 450 * @param sentIntents if not null, an <code>ArrayList</code> of 451 * <code>PendingIntent</code>s (one for each message part) that is 452 * broadcast when the corresponding message part has been sent. 453 * The result code will be <code>Activity.RESULT_OK<code> for success, 454 * or one of these errors: 455 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 456 * <code>RESULT_ERROR_RADIO_OFF</code> 457 * <code>RESULT_ERROR_NULL_PDU</code>. 458 * The per-application based SMS control checks sentIntent. If sentIntent 459 * is NULL the caller will be checked against all unknown applications, 460 * which cause smaller number of SMS to be sent in checking period. 461 * @param deliveryIntents if not null, an <code>ArrayList</code> of 462 * <code>PendingIntent</code>s (one for each message part) that is 463 * broadcast when the corresponding message part has been delivered 464 * to the recipient. The raw pdu of the status report is in the 465 * extended data ("pdu"). 466 */ 467 468 public void sendMultipartText(String callingPackage, String destAddr, String scAddr, 469 List<String> parts, List<PendingIntent> sentIntents, 470 List<PendingIntent> deliveryIntents) { 471 mPhone.getContext().enforceCallingPermission( 472 Manifest.permission.SEND_SMS, 473 "Sending SMS message"); 474 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 475 int i = 0; 476 for (String part : parts) { 477 log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr + 478 ", part[" + (i++) + "]=" + part); 479 } 480 } 481 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 482 callingPackage) != AppOpsManager.MODE_ALLOWED) { 483 return; 484 } 485 486 if (parts.size() > 1 && parts.size() < 10 && !SmsMessage.hasEmsSupport()) { 487 for (int i = 0; i < parts.size(); i++) { 488 // If EMS is not supported, we have to break down EMS into single segment SMS 489 // and add page info " x/y". 490 String singlePart = parts.get(i); 491 if (SmsMessage.shouldAppendPageNumberAsPrefix()) { 492 singlePart = String.valueOf(i + 1) + '/' + parts.size() + ' ' + singlePart; 493 } else { 494 singlePart = singlePart.concat(' ' + String.valueOf(i + 1) + '/' + parts.size()); 495 } 496 497 PendingIntent singleSentIntent = null; 498 if (sentIntents != null && sentIntents.size() > i) { 499 singleSentIntent = sentIntents.get(i); 500 } 501 502 PendingIntent singleDeliveryIntent = null; 503 if (deliveryIntents != null && deliveryIntents.size() > i) { 504 singleDeliveryIntent = deliveryIntents.get(i); 505 } 506 507 mDispatcher.sendText(destAddr, scAddr, singlePart, 508 singleSentIntent, singleDeliveryIntent, 509 null/*messageUri*/, callingPackage); 510 } 511 return; 512 } 513 514 mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts, 515 (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents, 516 null/*messageUri*/, callingPackage); 517 } 518 519 520 public int getPremiumSmsPermission(String packageName) { 521 return mDispatcher.getPremiumSmsPermission(packageName); 522 } 523 524 525 public void setPremiumSmsPermission(String packageName, int permission) { 526 mDispatcher.setPremiumSmsPermission(packageName, permission); 527 } 528 529 /** 530 * create SmsRawData lists from all sms record byte[] 531 * Use null to indicate "free" record 532 * 533 * @param messages List of message records from EF_SMS. 534 * @return SmsRawData list of all in-used records 535 */ 536 protected ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) { 537 int count = messages.size(); 538 ArrayList<SmsRawData> ret; 539 540 ret = new ArrayList<SmsRawData>(count); 541 542 for (int i = 0; i < count; i++) { 543 byte[] ba = messages.get(i); 544 if (ba[0] == STATUS_ON_ICC_FREE) { 545 ret.add(null); 546 } else { 547 ret.add(new SmsRawData(messages.get(i))); 548 } 549 } 550 551 return ret; 552 } 553 554 /** 555 * Generates an EF_SMS record from status and raw PDU. 556 * 557 * @param status Message status. See TS 51.011 10.5.3. 558 * @param pdu Raw message PDU. 559 * @return byte array for the record. 560 */ 561 protected byte[] makeSmsRecordData(int status, byte[] pdu) { 562 byte[] data; 563 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 564 data = new byte[IccConstants.SMS_RECORD_LENGTH]; 565 } else { 566 data = new byte[IccConstants.CDMA_SMS_RECORD_LENGTH]; 567 } 568 569 // Status bits for this record. See TS 51.011 10.5.3 570 data[0] = (byte)(status & 7); 571 572 System.arraycopy(pdu, 0, data, 1, pdu.length); 573 574 // Pad out with 0xFF's. 575 for (int j = pdu.length+1; j < data.length; j++) { 576 data[j] = -1; 577 } 578 579 return data; 580 } 581 582 public boolean enableCellBroadcast(int messageIdentifier) { 583 return enableCellBroadcastRange(messageIdentifier, messageIdentifier); 584 } 585 586 public boolean disableCellBroadcast(int messageIdentifier) { 587 return disableCellBroadcastRange(messageIdentifier, messageIdentifier); 588 } 589 590 public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) { 591 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 592 return enableGsmBroadcastRange(startMessageId, endMessageId); 593 } else { 594 return enableCdmaBroadcastRange(startMessageId, endMessageId); 595 } 596 } 597 598 public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) { 599 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 600 return disableGsmBroadcastRange(startMessageId, endMessageId); 601 } else { 602 return disableCdmaBroadcastRange(startMessageId, endMessageId); 603 } 604 } 605 606 synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) { 607 if (DBG) log("enableGsmBroadcastRange"); 608 609 Context context = mPhone.getContext(); 610 611 context.enforceCallingPermission( 612 "android.permission.RECEIVE_SMS", 613 "Enabling cell broadcast SMS"); 614 615 String client = context.getPackageManager().getNameForUid( 616 Binder.getCallingUid()); 617 618 if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { 619 log("Failed to add cell broadcast subscription for MID range " + startMessageId 620 + " to " + endMessageId + " from client " + client); 621 return false; 622 } 623 624 if (DBG) 625 log("Added cell broadcast subscription for MID range " + startMessageId 626 + " to " + endMessageId + " from client " + client); 627 628 setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty()); 629 630 return true; 631 } 632 633 synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) { 634 if (DBG) log("disableGsmBroadcastRange"); 635 636 Context context = mPhone.getContext(); 637 638 context.enforceCallingPermission( 639 "android.permission.RECEIVE_SMS", 640 "Disabling cell broadcast SMS"); 641 642 String client = context.getPackageManager().getNameForUid( 643 Binder.getCallingUid()); 644 645 if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { 646 log("Failed to remove cell broadcast subscription for MID range " + startMessageId 647 + " to " + endMessageId + " from client " + client); 648 return false; 649 } 650 651 if (DBG) 652 log("Removed cell broadcast subscription for MID range " + startMessageId 653 + " to " + endMessageId + " from client " + client); 654 655 setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty()); 656 657 return true; 658 } 659 660 synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) { 661 if (DBG) log("enableCdmaBroadcastRange"); 662 663 Context context = mPhone.getContext(); 664 665 context.enforceCallingPermission( 666 "android.permission.RECEIVE_SMS", 667 "Enabling cdma broadcast SMS"); 668 669 String client = context.getPackageManager().getNameForUid( 670 Binder.getCallingUid()); 671 672 if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { 673 log("Failed to add cdma broadcast subscription for MID range " + startMessageId 674 + " to " + endMessageId + " from client " + client); 675 return false; 676 } 677 678 if (DBG) 679 log("Added cdma broadcast subscription for MID range " + startMessageId 680 + " to " + endMessageId + " from client " + client); 681 682 setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty()); 683 684 return true; 685 } 686 687 synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) { 688 if (DBG) log("disableCdmaBroadcastRange"); 689 690 Context context = mPhone.getContext(); 691 692 context.enforceCallingPermission( 693 "android.permission.RECEIVE_SMS", 694 "Disabling cell broadcast SMS"); 695 696 String client = context.getPackageManager().getNameForUid( 697 Binder.getCallingUid()); 698 699 if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { 700 log("Failed to remove cdma broadcast subscription for MID range " + startMessageId 701 + " to " + endMessageId + " from client " + client); 702 return false; 703 } 704 705 if (DBG) 706 log("Removed cdma broadcast subscription for MID range " + startMessageId 707 + " to " + endMessageId + " from client " + client); 708 709 setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty()); 710 711 return true; 712 } 713 714 class CellBroadcastRangeManager extends IntRangeManager { 715 private ArrayList<SmsBroadcastConfigInfo> mConfigList = 716 new ArrayList<SmsBroadcastConfigInfo>(); 717 718 /** 719 * Called when the list of enabled ranges has changed. This will be 720 * followed by zero or more calls to {@link #addRange} followed by 721 * a call to {@link #finishUpdate}. 722 */ 723 protected void startUpdate() { 724 mConfigList.clear(); 725 } 726 727 /** 728 * Called after {@link #startUpdate} to indicate a range of enabled 729 * values. 730 * @param startId the first id included in the range 731 * @param endId the last id included in the range 732 */ 733 protected void addRange(int startId, int endId, boolean selected) { 734 mConfigList.add(new SmsBroadcastConfigInfo(startId, endId, 735 SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected)); 736 } 737 738 /** 739 * Called to indicate the end of a range update started by the 740 * previous call to {@link #startUpdate}. 741 * @return true if successful, false otherwise 742 */ 743 protected boolean finishUpdate() { 744 if (mConfigList.isEmpty()) { 745 return true; 746 } else { 747 SmsBroadcastConfigInfo[] configs = 748 mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]); 749 return setCellBroadcastConfig(configs); 750 } 751 } 752 } 753 754 class CdmaBroadcastRangeManager extends IntRangeManager { 755 private ArrayList<CdmaSmsBroadcastConfigInfo> mConfigList = 756 new ArrayList<CdmaSmsBroadcastConfigInfo>(); 757 758 /** 759 * Called when the list of enabled ranges has changed. This will be 760 * followed by zero or more calls to {@link #addRange} followed by a 761 * call to {@link #finishUpdate}. 762 */ 763 protected void startUpdate() { 764 mConfigList.clear(); 765 } 766 767 /** 768 * Called after {@link #startUpdate} to indicate a range of enabled 769 * values. 770 * @param startId the first id included in the range 771 * @param endId the last id included in the range 772 */ 773 protected void addRange(int startId, int endId, boolean selected) { 774 mConfigList.add(new CdmaSmsBroadcastConfigInfo(startId, endId, 775 1, selected)); 776 } 777 778 /** 779 * Called to indicate the end of a range update started by the previous 780 * call to {@link #startUpdate}. 781 * @return true if successful, false otherwise 782 */ 783 protected boolean finishUpdate() { 784 if (mConfigList.isEmpty()) { 785 return true; 786 } else { 787 CdmaSmsBroadcastConfigInfo[] configs = 788 mConfigList.toArray(new CdmaSmsBroadcastConfigInfo[mConfigList.size()]); 789 return setCdmaBroadcastConfig(configs); 790 } 791 } 792 } 793 794 private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) { 795 if (DBG) 796 log("Calling setGsmBroadcastConfig with " + configs.length + " configurations"); 797 798 synchronized (mLock) { 799 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE); 800 801 mSuccess = false; 802 mPhone.mCi.setGsmBroadcastConfig(configs, response); 803 804 try { 805 mLock.wait(); 806 } catch (InterruptedException e) { 807 log("interrupted while trying to set cell broadcast config"); 808 } 809 } 810 811 return mSuccess; 812 } 813 814 private boolean setCellBroadcastActivation(boolean activate) { 815 if (DBG) 816 log("Calling setCellBroadcastActivation(" + activate + ')'); 817 818 synchronized (mLock) { 819 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE); 820 821 mSuccess = false; 822 mPhone.mCi.setGsmBroadcastActivation(activate, response); 823 824 try { 825 mLock.wait(); 826 } catch (InterruptedException e) { 827 log("interrupted while trying to set cell broadcast activation"); 828 } 829 } 830 831 return mSuccess; 832 } 833 834 private boolean setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs) { 835 if (DBG) 836 log("Calling setCdmaBroadcastConfig with " + configs.length + " configurations"); 837 838 synchronized (mLock) { 839 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE); 840 841 mSuccess = false; 842 mPhone.mCi.setCdmaBroadcastConfig(configs, response); 843 844 try { 845 mLock.wait(); 846 } catch (InterruptedException e) { 847 log("interrupted while trying to set cdma broadcast config"); 848 } 849 } 850 851 return mSuccess; 852 } 853 854 private boolean setCdmaBroadcastActivation(boolean activate) { 855 if (DBG) 856 log("Calling setCdmaBroadcastActivation(" + activate + ")"); 857 858 synchronized (mLock) { 859 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE); 860 861 mSuccess = false; 862 mPhone.mCi.setCdmaBroadcastActivation(activate, response); 863 864 try { 865 mLock.wait(); 866 } catch (InterruptedException e) { 867 log("interrupted while trying to set cdma broadcast activation"); 868 } 869 } 870 871 return mSuccess; 872 } 873 874 protected void log(String msg) { 875 Log.d(LOG_TAG, "[IccSmsInterfaceManager] " + msg); 876 } 877 878 public boolean isImsSmsSupported() { 879 return mDispatcher.isIms(); 880 } 881 882 public String getImsSmsFormat() { 883 return mDispatcher.getImsSmsFormat(); 884 } 885 886 public void sendStoredText(String callingPkg, Uri messageUri, String scAddress, 887 PendingIntent sentIntent, PendingIntent deliveryIntent) { 888 mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS, 889 "Sending SMS message"); 890 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 891 log("sendStoredText: scAddr=" + scAddress + " messageUri=" + messageUri 892 + " sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent); 893 } 894 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) 895 != AppOpsManager.MODE_ALLOWED) { 896 return; 897 } 898 final ContentResolver resolver = mPhone.getContext().getContentResolver(); 899 if (!isFailedOrDraft(resolver, messageUri)) { 900 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: not FAILED or DRAFT message"); 901 returnUnspecifiedFailure(sentIntent); 902 return; 903 } 904 final String[] textAndAddress = loadTextAndAddress(resolver, messageUri); 905 if (textAndAddress == null) { 906 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: can not load text"); 907 returnUnspecifiedFailure(sentIntent); 908 return; 909 } 910 textAndAddress[1] = filterDestAddress(textAndAddress[1]); 911 mDispatcher.sendText(textAndAddress[1], scAddress, textAndAddress[0], 912 sentIntent, deliveryIntent, messageUri, callingPkg); 913 } 914 915 public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress, 916 List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { 917 mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS, 918 "Sending SMS message"); 919 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg) 920 != AppOpsManager.MODE_ALLOWED) { 921 return; 922 } 923 final ContentResolver resolver = mPhone.getContext().getContentResolver(); 924 if (!isFailedOrDraft(resolver, messageUri)) { 925 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: " 926 + "not FAILED or DRAFT message"); 927 returnUnspecifiedFailure(sentIntents); 928 return; 929 } 930 final String[] textAndAddress = loadTextAndAddress(resolver, messageUri); 931 if (textAndAddress == null) { 932 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: can not load text"); 933 returnUnspecifiedFailure(sentIntents); 934 return; 935 } 936 final ArrayList<String> parts = SmsManager.getDefault().divideMessage(textAndAddress[0]); 937 if (parts == null || parts.size() < 1) { 938 Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: can not divide text"); 939 returnUnspecifiedFailure(sentIntents); 940 return; 941 } 942 943 if (parts.size() > 1 && parts.size() < 10 && !SmsMessage.hasEmsSupport()) { 944 for (int i = 0; i < parts.size(); i++) { 945 // If EMS is not supported, we have to break down EMS into single segment SMS 946 // and add page info " x/y". 947 String singlePart = parts.get(i); 948 if (SmsMessage.shouldAppendPageNumberAsPrefix()) { 949 singlePart = String.valueOf(i + 1) + '/' + parts.size() + ' ' + singlePart; 950 } else { 951 singlePart = singlePart.concat(' ' + String.valueOf(i + 1) + '/' + parts.size()); 952 } 953 954 PendingIntent singleSentIntent = null; 955 if (sentIntents != null && sentIntents.size() > i) { 956 singleSentIntent = sentIntents.get(i); 957 } 958 959 PendingIntent singleDeliveryIntent = null; 960 if (deliveryIntents != null && deliveryIntents.size() > i) { 961 singleDeliveryIntent = deliveryIntents.get(i); 962 } 963 964 mDispatcher.sendText(textAndAddress[1], scAddress, singlePart, 965 singleSentIntent, singleDeliveryIntent, messageUri, callingPkg); 966 } 967 return; 968 } 969 970 textAndAddress[1] = filterDestAddress(textAndAddress[1]); 971 mDispatcher.sendMultipartText( 972 textAndAddress[1], // destAddress 973 scAddress, 974 parts, 975 (ArrayList<PendingIntent>) sentIntents, 976 (ArrayList<PendingIntent>) deliveryIntents, 977 messageUri, 978 callingPkg); 979 } 980 981 private boolean isFailedOrDraft(ContentResolver resolver, Uri messageUri) { 982 // Clear the calling identity and query the database using the phone user id 983 // Otherwise the AppOps check in TelephonyProvider would complain about mismatch 984 // between the calling uid and the package uid 985 final long identity = Binder.clearCallingIdentity(); 986 Cursor cursor = null; 987 try { 988 cursor = resolver.query( 989 messageUri, 990 new String[]{ Telephony.Sms.TYPE }, 991 null/*selection*/, 992 null/*selectionArgs*/, 993 null/*sortOrder*/); 994 if (cursor != null && cursor.moveToFirst()) { 995 final int type = cursor.getInt(0); 996 return type == Telephony.Sms.MESSAGE_TYPE_DRAFT 997 || type == Telephony.Sms.MESSAGE_TYPE_FAILED; 998 } 999 } catch (SQLiteException e) { 1000 Log.e(LOG_TAG, "[IccSmsInterfaceManager]isFailedOrDraft: query message type failed", e); 1001 } finally { 1002 if (cursor != null) { 1003 cursor.close(); 1004 } 1005 Binder.restoreCallingIdentity(identity); 1006 } 1007 return false; 1008 } 1009 1010 // Return an array including both the SMS text (0) and address (1) 1011 private String[] loadTextAndAddress(ContentResolver resolver, Uri messageUri) { 1012 // Clear the calling identity and query the database using the phone user id 1013 // Otherwise the AppOps check in TelephonyProvider would complain about mismatch 1014 // between the calling uid and the package uid 1015 final long identity = Binder.clearCallingIdentity(); 1016 Cursor cursor = null; 1017 try { 1018 cursor = resolver.query( 1019 messageUri, 1020 new String[]{ 1021 Telephony.Sms.BODY, 1022 Telephony.Sms.ADDRESS 1023 }, 1024 null/*selection*/, 1025 null/*selectionArgs*/, 1026 null/*sortOrder*/); 1027 if (cursor != null && cursor.moveToFirst()) { 1028 return new String[]{ cursor.getString(0), cursor.getString(1) }; 1029 } 1030 } catch (SQLiteException e) { 1031 Log.e(LOG_TAG, "[IccSmsInterfaceManager]loadText: query message text failed", e); 1032 } finally { 1033 if (cursor != null) { 1034 cursor.close(); 1035 } 1036 Binder.restoreCallingIdentity(identity); 1037 } 1038 return null; 1039 } 1040 1041 private void returnUnspecifiedFailure(PendingIntent pi) { 1042 if (pi != null) { 1043 try { 1044 pi.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); 1045 } catch (PendingIntent.CanceledException e) { 1046 // ignore 1047 } 1048 } 1049 } 1050 1051 private void returnUnspecifiedFailure(List<PendingIntent> pis) { 1052 if (pis == null) { 1053 return; 1054 } 1055 for (PendingIntent pi : pis) { 1056 returnUnspecifiedFailure(pi); 1057 } 1058 } 1059 1060 private void enforceCarrierPrivilege() { 1061 UiccController controller = UiccController.getInstance(); 1062 if (controller == null || controller.getUiccCard() == null) { 1063 throw new SecurityException("No Carrier Privilege: No UICC"); 1064 } 1065 if (controller.getUiccCard().getCarrierPrivilegeStatusForCurrentTransaction( 1066 mContext.getPackageManager()) != 1067 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { 1068 throw new SecurityException("No Carrier Privilege."); 1069 } 1070 } 1071 1072 private String filterDestAddress(String destAddr) { 1073 String result = null; 1074 result = SmsNumberUtils.filterDestAddr(mPhone, destAddr); 1075 return result != null ? result : destAddr; 1076 } 1077 1078 } 1079