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