Home | History | Annotate | Download | only in telephony
      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