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.Context; 23 import android.os.AsyncResult; 24 import android.os.Binder; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.os.ServiceManager; 28 import android.telephony.Rlog; 29 import android.util.Log; 30 31 import com.android.internal.telephony.ISms; 32 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; 33 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; 34 import com.android.internal.telephony.uicc.IccConstants; 35 import com.android.internal.telephony.uicc.IccFileHandler; 36 import com.android.internal.util.HexDump; 37 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.List; 41 42 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; 43 import static android.telephony.SmsManager.STATUS_ON_ICC_READ; 44 import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD; 45 46 /** 47 * IccSmsInterfaceManager to provide an inter-process communication to 48 * access Sms in Icc. 49 */ 50 public class IccSmsInterfaceManager extends ISms.Stub { 51 static final String LOG_TAG = "IccSmsInterfaceManager"; 52 static final boolean DBG = true; 53 54 protected final Object mLock = new Object(); 55 protected boolean mSuccess; 56 private List<SmsRawData> mSms; 57 58 private CellBroadcastRangeManager mCellBroadcastRangeManager = 59 new CellBroadcastRangeManager(); 60 private CdmaBroadcastRangeManager mCdmaBroadcastRangeManager = 61 new CdmaBroadcastRangeManager(); 62 63 private static final int EVENT_LOAD_DONE = 1; 64 private static final int EVENT_UPDATE_DONE = 2; 65 protected static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3; 66 protected static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4; 67 private static final int SMS_CB_CODE_SCHEME_MIN = 0; 68 private static final int SMS_CB_CODE_SCHEME_MAX = 255; 69 70 protected PhoneBase mPhone; 71 final protected Context mContext; 72 final protected AppOpsManager mAppOps; 73 protected SMSDispatcher mDispatcher; 74 75 protected Handler mHandler = new Handler() { 76 @Override 77 public void handleMessage(Message msg) { 78 AsyncResult ar; 79 80 switch (msg.what) { 81 case EVENT_UPDATE_DONE: 82 ar = (AsyncResult) msg.obj; 83 synchronized (mLock) { 84 mSuccess = (ar.exception == null); 85 mLock.notifyAll(); 86 } 87 break; 88 case EVENT_LOAD_DONE: 89 ar = (AsyncResult)msg.obj; 90 synchronized (mLock) { 91 if (ar.exception == null) { 92 mSms = buildValidRawData((ArrayList<byte[]>) ar.result); 93 //Mark SMS as read after importing it from card. 94 markMessagesAsRead((ArrayList<byte[]>) ar.result); 95 } else { 96 if (Rlog.isLoggable("SMS", Log.DEBUG)) { 97 log("Cannot load Sms records"); 98 } 99 if (mSms != null) 100 mSms.clear(); 101 } 102 mLock.notifyAll(); 103 } 104 break; 105 case EVENT_SET_BROADCAST_ACTIVATION_DONE: 106 case EVENT_SET_BROADCAST_CONFIG_DONE: 107 ar = (AsyncResult) msg.obj; 108 synchronized (mLock) { 109 mSuccess = (ar.exception == null); 110 mLock.notifyAll(); 111 } 112 break; 113 } 114 } 115 }; 116 117 protected IccSmsInterfaceManager(PhoneBase phone) { 118 mPhone = phone; 119 mContext = phone.getContext(); 120 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 121 mDispatcher = new ImsSMSDispatcher(phone, 122 phone.mSmsStorageMonitor, phone.mSmsUsageMonitor); 123 if (ServiceManager.getService("isms") == null) { 124 ServiceManager.addService("isms", this); 125 } 126 } 127 128 protected void markMessagesAsRead(ArrayList<byte[]> messages) { 129 if (messages == null) { 130 return; 131 } 132 133 //IccFileHandler can be null, if icc card is absent. 134 IccFileHandler fh = mPhone.getIccFileHandler(); 135 if (fh == null) { 136 //shouldn't really happen, as messages are marked as read, only 137 //after importing it from icc. 138 if (Rlog.isLoggable("SMS", Log.DEBUG)) { 139 log("markMessagesAsRead - aborting, no icc card present."); 140 } 141 return; 142 } 143 144 int count = messages.size(); 145 146 for (int i = 0; i < count; i++) { 147 byte[] ba = messages.get(i); 148 if (ba[0] == STATUS_ON_ICC_UNREAD) { 149 int n = ba.length; 150 byte[] nba = new byte[n - 1]; 151 System.arraycopy(ba, 1, nba, 0, n - 1); 152 byte[] record = makeSmsRecordData(STATUS_ON_ICC_READ, nba); 153 fh.updateEFLinearFixed(IccConstants.EF_SMS, i + 1, record, null, null); 154 if (Rlog.isLoggable("SMS", Log.DEBUG)) { 155 log("SMS " + (i + 1) + " marked as read"); 156 } 157 } 158 } 159 } 160 161 protected void updatePhoneObject(PhoneBase phone) { 162 mPhone = phone; 163 mDispatcher.updatePhoneObject(phone); 164 } 165 166 protected void enforceReceiveAndSend(String message) { 167 mContext.enforceCallingPermission( 168 Manifest.permission.RECEIVE_SMS, message); 169 mContext.enforceCallingPermission( 170 Manifest.permission.SEND_SMS, message); 171 } 172 173 /** 174 * Update the specified message on the Icc. 175 * 176 * @param index record index of message to update 177 * @param status new message status (STATUS_ON_ICC_READ, 178 * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, 179 * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE) 180 * @param pdu the raw PDU to store 181 * @return success or not 182 * 183 */ 184 @Override 185 public boolean 186 updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu) { 187 if (DBG) log("updateMessageOnIccEf: index=" + index + 188 " status=" + status + " ==> " + 189 "("+ Arrays.toString(pdu) + ")"); 190 enforceReceiveAndSend("Updating message on Icc"); 191 if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(), 192 callingPackage) != AppOpsManager.MODE_ALLOWED) { 193 return false; 194 } 195 synchronized(mLock) { 196 mSuccess = false; 197 Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); 198 199 if (status == STATUS_ON_ICC_FREE) { 200 // RIL_REQUEST_DELETE_SMS_ON_SIM vs RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM 201 // Special case FREE: call deleteSmsOnSim/Ruim instead of 202 // manipulating the record 203 // Will eventually fail if icc card is not present. 204 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 205 mPhone.mCi.deleteSmsOnSim(index, response); 206 } else { 207 mPhone.mCi.deleteSmsOnRuim(index, response); 208 } 209 } else { 210 //IccFilehandler can be null if ICC card is not present. 211 IccFileHandler fh = mPhone.getIccFileHandler(); 212 if (fh == null) { 213 response.recycle(); 214 return mSuccess; /* is false */ 215 } 216 byte[] record = makeSmsRecordData(status, pdu); 217 fh.updateEFLinearFixed( 218 IccConstants.EF_SMS, 219 index, record, null, response); 220 } 221 try { 222 mLock.wait(); 223 } catch (InterruptedException e) { 224 log("interrupted while trying to update by index"); 225 } 226 } 227 return mSuccess; 228 } 229 230 /** 231 * Copy a raw SMS PDU to the Icc. 232 * 233 * @param pdu the raw PDU to store 234 * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, 235 * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) 236 * @return success or not 237 * 238 */ 239 @Override 240 public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc) { 241 //NOTE smsc not used in RUIM 242 if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " + 243 "pdu=("+ Arrays.toString(pdu) + 244 "), smsc=(" + Arrays.toString(smsc) +")"); 245 enforceReceiveAndSend("Copying message to Icc"); 246 if (mAppOps.noteOp(AppOpsManager.OP_WRITE_ICC_SMS, Binder.getCallingUid(), 247 callingPackage) != AppOpsManager.MODE_ALLOWED) { 248 return false; 249 } 250 synchronized(mLock) { 251 mSuccess = false; 252 Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); 253 254 //RIL_REQUEST_WRITE_SMS_TO_SIM vs RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM 255 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 256 mPhone.mCi.writeSmsToSim(status, IccUtils.bytesToHexString(smsc), 257 IccUtils.bytesToHexString(pdu), response); 258 } else { 259 mPhone.mCi.writeSmsToRuim(status, IccUtils.bytesToHexString(pdu), 260 response); 261 } 262 263 try { 264 mLock.wait(); 265 } catch (InterruptedException e) { 266 log("interrupted while trying to update by index"); 267 } 268 } 269 return mSuccess; 270 } 271 272 /** 273 * Retrieves all messages currently stored on Icc. 274 * 275 * @return list of SmsRawData of all sms on Icc 276 */ 277 @Override 278 public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) { 279 if (DBG) log("getAllMessagesFromEF"); 280 281 mContext.enforceCallingOrSelfPermission( 282 Manifest.permission.RECEIVE_SMS, 283 "Reading messages from Icc"); 284 if (mAppOps.noteOp(AppOpsManager.OP_READ_ICC_SMS, Binder.getCallingUid(), 285 callingPackage) != AppOpsManager.MODE_ALLOWED) { 286 return new ArrayList<SmsRawData>(); 287 } 288 synchronized(mLock) { 289 290 IccFileHandler fh = mPhone.getIccFileHandler(); 291 if (fh == null) { 292 Rlog.e(LOG_TAG, "Cannot load Sms records. No icc card?"); 293 if (mSms != null) { 294 mSms.clear(); 295 return mSms; 296 } 297 } 298 299 Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); 300 fh.loadEFLinearFixedAll(IccConstants.EF_SMS, response); 301 302 try { 303 mLock.wait(); 304 } catch (InterruptedException e) { 305 log("interrupted while trying to load from the Icc"); 306 } 307 } 308 return mSms; 309 } 310 311 /** 312 * Send a data based SMS to a specific application port. 313 * 314 * @param destAddr the address to send the message to 315 * @param scAddr is the service center address or null to use 316 * the current default SMSC 317 * @param destPort the port to deliver the message to 318 * @param data the body of the message to send 319 * @param sentIntent if not NULL this <code>PendingIntent</code> is 320 * broadcast when the message is successfully sent, or failed. 321 * The result code will be <code>Activity.RESULT_OK<code> for success, 322 * or one of these errors:<br> 323 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 324 * <code>RESULT_ERROR_RADIO_OFF</code><br> 325 * <code>RESULT_ERROR_NULL_PDU</code><br> 326 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 327 * the extra "errorCode" containing a radio technology specific value, 328 * generally only useful for troubleshooting.<br> 329 * The per-application based SMS control checks sentIntent. If sentIntent 330 * is NULL the caller will be checked against all unknown applications, 331 * which cause smaller number of SMS to be sent in checking period. 332 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 333 * broadcast when the message is delivered to the recipient. The 334 * raw pdu of the status report is in the extended data ("pdu"). 335 */ 336 @Override 337 public void sendData(String callingPackage, String destAddr, String scAddr, int destPort, 338 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 339 mPhone.getContext().enforceCallingPermission( 340 Manifest.permission.SEND_SMS, 341 "Sending SMS message"); 342 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 343 log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" + 344 destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" + 345 sentIntent + " deliveryIntent=" + deliveryIntent); 346 } 347 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 348 callingPackage) != AppOpsManager.MODE_ALLOWED) { 349 return; 350 } 351 mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent); 352 } 353 354 /** 355 * Send a text based SMS. 356 * 357 * @param destAddr the address to send the message to 358 * @param scAddr is the service center address or null to use 359 * the current default SMSC 360 * @param text the body of the message to send 361 * @param sentIntent if not NULL this <code>PendingIntent</code> is 362 * broadcast when the message is successfully sent, or failed. 363 * The result code will be <code>Activity.RESULT_OK<code> for success, 364 * or one of these errors:<br> 365 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 366 * <code>RESULT_ERROR_RADIO_OFF</code><br> 367 * <code>RESULT_ERROR_NULL_PDU</code><br> 368 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 369 * the extra "errorCode" containing a radio technology specific value, 370 * generally only useful for troubleshooting.<br> 371 * The per-application based SMS control checks sentIntent. If sentIntent 372 * is NULL the caller will be checked against all unknown applications, 373 * which cause smaller number of SMS to be sent in checking period. 374 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 375 * broadcast when the message is delivered to the recipient. The 376 * raw pdu of the status report is in the extended data ("pdu"). 377 */ 378 @Override 379 public void sendText(String callingPackage, String destAddr, String scAddr, 380 String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { 381 mPhone.getContext().enforceCallingPermission( 382 Manifest.permission.SEND_SMS, 383 "Sending SMS message"); 384 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 385 log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr + 386 " text='"+ text + "' sentIntent=" + 387 sentIntent + " deliveryIntent=" + deliveryIntent); 388 } 389 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 390 callingPackage) != AppOpsManager.MODE_ALLOWED) { 391 return; 392 } 393 mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent); 394 } 395 396 /** 397 * Send a multi-part text based SMS. 398 * 399 * @param destAddr the address to send the message to 400 * @param scAddr is the service center address or null to use 401 * the current default SMSC 402 * @param parts an <code>ArrayList</code> of strings that, in order, 403 * comprise the original message 404 * @param sentIntents if not null, an <code>ArrayList</code> of 405 * <code>PendingIntent</code>s (one for each message part) that is 406 * broadcast when the corresponding message part has been sent. 407 * The result code will be <code>Activity.RESULT_OK<code> for success, 408 * or one of these errors: 409 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 410 * <code>RESULT_ERROR_RADIO_OFF</code> 411 * <code>RESULT_ERROR_NULL_PDU</code>. 412 * The per-application based SMS control checks sentIntent. If sentIntent 413 * is NULL the caller will be checked against all unknown applications, 414 * which cause smaller number of SMS to be sent in checking period. 415 * @param deliveryIntents if not null, an <code>ArrayList</code> of 416 * <code>PendingIntent</code>s (one for each message part) that is 417 * broadcast when the corresponding message part has been delivered 418 * to the recipient. The raw pdu of the status report is in the 419 * extended data ("pdu"). 420 */ 421 @Override 422 public void sendMultipartText(String callingPackage, String destAddr, String scAddr, 423 List<String> parts, List<PendingIntent> sentIntents, 424 List<PendingIntent> deliveryIntents) { 425 mPhone.getContext().enforceCallingPermission( 426 Manifest.permission.SEND_SMS, 427 "Sending SMS message"); 428 if (Rlog.isLoggable("SMS", Log.VERBOSE)) { 429 int i = 0; 430 for (String part : parts) { 431 log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr + 432 ", part[" + (i++) + "]=" + part); 433 } 434 } 435 if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 436 callingPackage) != AppOpsManager.MODE_ALLOWED) { 437 return; 438 } 439 mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts, 440 (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents); 441 } 442 443 @Override 444 public int getPremiumSmsPermission(String packageName) { 445 return mDispatcher.getPremiumSmsPermission(packageName); 446 } 447 448 @Override 449 public void setPremiumSmsPermission(String packageName, int permission) { 450 mDispatcher.setPremiumSmsPermission(packageName, permission); 451 } 452 453 /** 454 * create SmsRawData lists from all sms record byte[] 455 * Use null to indicate "free" record 456 * 457 * @param messages List of message records from EF_SMS. 458 * @return SmsRawData list of all in-used records 459 */ 460 protected ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) { 461 int count = messages.size(); 462 ArrayList<SmsRawData> ret; 463 464 ret = new ArrayList<SmsRawData>(count); 465 466 for (int i = 0; i < count; i++) { 467 byte[] ba = messages.get(i); 468 if (ba[0] == STATUS_ON_ICC_FREE) { 469 ret.add(null); 470 } else { 471 ret.add(new SmsRawData(messages.get(i))); 472 } 473 } 474 475 return ret; 476 } 477 478 /** 479 * Generates an EF_SMS record from status and raw PDU. 480 * 481 * @param status Message status. See TS 51.011 10.5.3. 482 * @param pdu Raw message PDU. 483 * @return byte array for the record. 484 */ 485 protected byte[] makeSmsRecordData(int status, byte[] pdu) { 486 byte[] data = new byte[IccConstants.SMS_RECORD_LENGTH]; 487 488 // Status bits for this record. See TS 51.011 10.5.3 489 data[0] = (byte)(status & 7); 490 491 System.arraycopy(pdu, 0, data, 1, pdu.length); 492 493 // Pad out with 0xFF's. 494 for (int j = pdu.length+1; j < IccConstants.SMS_RECORD_LENGTH; j++) { 495 data[j] = -1; 496 } 497 498 return data; 499 } 500 501 public boolean enableCellBroadcast(int messageIdentifier) { 502 return enableCellBroadcastRange(messageIdentifier, messageIdentifier); 503 } 504 505 public boolean disableCellBroadcast(int messageIdentifier) { 506 return disableCellBroadcastRange(messageIdentifier, messageIdentifier); 507 } 508 509 public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) { 510 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 511 return enableGsmBroadcastRange(startMessageId, endMessageId); 512 } else { 513 return enableCdmaBroadcastRange(startMessageId, endMessageId); 514 } 515 } 516 517 public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) { 518 if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) { 519 return disableGsmBroadcastRange(startMessageId, endMessageId); 520 } else { 521 return disableCdmaBroadcastRange(startMessageId, endMessageId); 522 } 523 } 524 525 synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) { 526 if (DBG) log("enableGsmBroadcastRange"); 527 528 Context context = mPhone.getContext(); 529 530 context.enforceCallingPermission( 531 "android.permission.RECEIVE_SMS", 532 "Enabling cell broadcast SMS"); 533 534 String client = context.getPackageManager().getNameForUid( 535 Binder.getCallingUid()); 536 537 if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { 538 log("Failed to add cell broadcast subscription for MID range " + startMessageId 539 + " to " + endMessageId + " from client " + client); 540 return false; 541 } 542 543 if (DBG) 544 log("Added cell broadcast subscription for MID range " + startMessageId 545 + " to " + endMessageId + " from client " + client); 546 547 setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty()); 548 549 return true; 550 } 551 552 synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) { 553 if (DBG) log("disableGsmBroadcastRange"); 554 555 Context context = mPhone.getContext(); 556 557 context.enforceCallingPermission( 558 "android.permission.RECEIVE_SMS", 559 "Disabling cell broadcast SMS"); 560 561 String client = context.getPackageManager().getNameForUid( 562 Binder.getCallingUid()); 563 564 if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { 565 log("Failed to remove cell broadcast subscription for MID range " + startMessageId 566 + " to " + endMessageId + " from client " + client); 567 return false; 568 } 569 570 if (DBG) 571 log("Removed cell broadcast subscription for MID range " + startMessageId 572 + " to " + endMessageId + " from client " + client); 573 574 setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty()); 575 576 return true; 577 } 578 579 synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) { 580 if (DBG) log("enableCdmaBroadcastRange"); 581 582 Context context = mPhone.getContext(); 583 584 context.enforceCallingPermission( 585 "android.permission.RECEIVE_SMS", 586 "Enabling cdma broadcast SMS"); 587 588 String client = context.getPackageManager().getNameForUid( 589 Binder.getCallingUid()); 590 591 if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) { 592 log("Failed to add cdma broadcast subscription for MID range " + startMessageId 593 + " to " + endMessageId + " from client " + client); 594 return false; 595 } 596 597 if (DBG) 598 log("Added cdma broadcast subscription for MID range " + startMessageId 599 + " to " + endMessageId + " from client " + client); 600 601 setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty()); 602 603 return true; 604 } 605 606 synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) { 607 if (DBG) log("disableCdmaBroadcastRange"); 608 609 Context context = mPhone.getContext(); 610 611 context.enforceCallingPermission( 612 "android.permission.RECEIVE_SMS", 613 "Disabling cell broadcast SMS"); 614 615 String client = context.getPackageManager().getNameForUid( 616 Binder.getCallingUid()); 617 618 if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) { 619 log("Failed to remove cdma broadcast subscription for MID range " + startMessageId 620 + " to " + endMessageId + " from client " + client); 621 return false; 622 } 623 624 if (DBG) 625 log("Removed cdma broadcast subscription for MID range " + startMessageId 626 + " to " + endMessageId + " from client " + client); 627 628 setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty()); 629 630 return true; 631 } 632 633 class CellBroadcastRangeManager extends IntRangeManager { 634 private ArrayList<SmsBroadcastConfigInfo> mConfigList = 635 new ArrayList<SmsBroadcastConfigInfo>(); 636 637 /** 638 * Called when the list of enabled ranges has changed. This will be 639 * followed by zero or more calls to {@link #addRange} followed by 640 * a call to {@link #finishUpdate}. 641 */ 642 protected void startUpdate() { 643 mConfigList.clear(); 644 } 645 646 /** 647 * Called after {@link #startUpdate} to indicate a range of enabled 648 * values. 649 * @param startId the first id included in the range 650 * @param endId the last id included in the range 651 */ 652 protected void addRange(int startId, int endId, boolean selected) { 653 mConfigList.add(new SmsBroadcastConfigInfo(startId, endId, 654 SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected)); 655 } 656 657 /** 658 * Called to indicate the end of a range update started by the 659 * previous call to {@link #startUpdate}. 660 * @return true if successful, false otherwise 661 */ 662 protected boolean finishUpdate() { 663 if (mConfigList.isEmpty()) { 664 return true; 665 } else { 666 SmsBroadcastConfigInfo[] configs = 667 mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]); 668 return setCellBroadcastConfig(configs); 669 } 670 } 671 } 672 673 class CdmaBroadcastRangeManager extends IntRangeManager { 674 private ArrayList<CdmaSmsBroadcastConfigInfo> mConfigList = 675 new ArrayList<CdmaSmsBroadcastConfigInfo>(); 676 677 /** 678 * Called when the list of enabled ranges has changed. This will be 679 * followed by zero or more calls to {@link #addRange} followed by a 680 * call to {@link #finishUpdate}. 681 */ 682 protected void startUpdate() { 683 mConfigList.clear(); 684 } 685 686 /** 687 * Called after {@link #startUpdate} to indicate a range of enabled 688 * values. 689 * @param startId the first id included in the range 690 * @param endId the last id included in the range 691 */ 692 protected void addRange(int startId, int endId, boolean selected) { 693 mConfigList.add(new CdmaSmsBroadcastConfigInfo(startId, endId, 694 1, selected)); 695 } 696 697 /** 698 * Called to indicate the end of a range update started by the previous 699 * call to {@link #startUpdate}. 700 * @return true if successful, false otherwise 701 */ 702 protected boolean finishUpdate() { 703 if (mConfigList.isEmpty()) { 704 return true; 705 } else { 706 CdmaSmsBroadcastConfigInfo[] configs = 707 mConfigList.toArray(new CdmaSmsBroadcastConfigInfo[mConfigList.size()]); 708 return setCdmaBroadcastConfig(configs); 709 } 710 } 711 } 712 713 private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) { 714 if (DBG) 715 log("Calling setGsmBroadcastConfig with " + configs.length + " configurations"); 716 717 synchronized (mLock) { 718 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE); 719 720 mSuccess = false; 721 mPhone.mCi.setGsmBroadcastConfig(configs, response); 722 723 try { 724 mLock.wait(); 725 } catch (InterruptedException e) { 726 log("interrupted while trying to set cell broadcast config"); 727 } 728 } 729 730 return mSuccess; 731 } 732 733 private boolean setCellBroadcastActivation(boolean activate) { 734 if (DBG) 735 log("Calling setCellBroadcastActivation(" + activate + ')'); 736 737 synchronized (mLock) { 738 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE); 739 740 mSuccess = false; 741 mPhone.mCi.setGsmBroadcastActivation(activate, response); 742 743 try { 744 mLock.wait(); 745 } catch (InterruptedException e) { 746 log("interrupted while trying to set cell broadcast activation"); 747 } 748 } 749 750 return mSuccess; 751 } 752 753 private boolean setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs) { 754 if (DBG) 755 log("Calling setCdmaBroadcastConfig with " + configs.length + " configurations"); 756 757 synchronized (mLock) { 758 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE); 759 760 mSuccess = false; 761 mPhone.mCi.setCdmaBroadcastConfig(configs, response); 762 763 try { 764 mLock.wait(); 765 } catch (InterruptedException e) { 766 log("interrupted while trying to set cdma broadcast config"); 767 } 768 } 769 770 return mSuccess; 771 } 772 773 private boolean setCdmaBroadcastActivation(boolean activate) { 774 if (DBG) 775 log("Calling setCdmaBroadcastActivation(" + activate + ")"); 776 777 synchronized (mLock) { 778 Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE); 779 780 mSuccess = false; 781 mPhone.mCi.setCdmaBroadcastActivation(activate, response); 782 783 try { 784 mLock.wait(); 785 } catch (InterruptedException e) { 786 log("interrupted while trying to set cdma broadcast activation"); 787 } 788 } 789 790 return mSuccess; 791 } 792 793 protected void log(String msg) { 794 Log.d(LOG_TAG, "[IccSmsInterfaceManager] " + msg); 795 } 796 797 public boolean isImsSmsSupported() { 798 return mDispatcher.isIms(); 799 } 800 801 public String getImsSmsFormat() { 802 return mDispatcher.getImsSmsFormat(); 803 } 804 } 805