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