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