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.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.enforceCallingPermission(
    177                 Manifest.permission.RECEIVE_SMS, message);
    178         mContext.enforceCallingPermission(
    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      * Send a data based SMS to a specific application port.
    321      *
    322      * @param destAddr the address to send the message to
    323      * @param scAddr is the service center address or null to use
    324      *  the current default SMSC
    325      * @param destPort the port to deliver the message to
    326      * @param data the body of the message to send
    327      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    328      *  broadcast when the message is successfully sent, or failed.
    329      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    330      *  or one of these errors:<br>
    331      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    332      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    333      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    334      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    335      *  the extra "errorCode" containing a radio technology specific value,
    336      *  generally only useful for troubleshooting.<br>
    337      *  The per-application based SMS control checks sentIntent. If sentIntent
    338      *  is NULL the caller will be checked against all unknown applications,
    339      *  which cause smaller number of SMS to be sent in checking period.
    340      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    341      *  broadcast when the message is delivered to the recipient.  The
    342      *  raw pdu of the status report is in the extended data ("pdu").
    343      */
    344 
    345     public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
    346             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    347         mPhone.getContext().enforceCallingPermission(
    348                 Manifest.permission.SEND_SMS,
    349                 "Sending SMS message");
    350         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
    351             log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
    352                 destPort + " data='"+ HexDump.toHexString(data)  + "' sentIntent=" +
    353                 sentIntent + " deliveryIntent=" + deliveryIntent);
    354         }
    355         if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    356                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    357             return;
    358         }
    359         destAddr = filterDestAddress(destAddr);
    360         mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
    361     }
    362 
    363     /**
    364      * Send a text based SMS.
    365      *
    366      * @param destAddr the address to send the message to
    367      * @param scAddr is the service center address or null to use
    368      *  the current default SMSC
    369      * @param text the body of the message to send
    370      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    371      *  broadcast when the message is successfully sent, or failed.
    372      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    373      *  or one of these errors:<br>
    374      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    375      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    376      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    377      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    378      *  the extra "errorCode" containing a radio technology specific value,
    379      *  generally only useful for troubleshooting.<br>
    380      *  The per-application based SMS control checks sentIntent. If sentIntent
    381      *  is NULL the caller will be checked against all unknown applications,
    382      *  which cause smaller number of SMS to be sent in checking period.
    383      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    384      *  broadcast when the message is delivered to the recipient.  The
    385      *  raw pdu of the status report is in the extended data ("pdu").
    386      */
    387 
    388     public void sendText(String callingPackage, String destAddr, String scAddr,
    389             String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    390         mPhone.getContext().enforceCallingPermission(
    391                 Manifest.permission.SEND_SMS,
    392                 "Sending SMS message");
    393         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
    394             log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
    395                 " text='"+ text + "' sentIntent=" +
    396                 sentIntent + " deliveryIntent=" + deliveryIntent);
    397         }
    398         if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    399                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    400             return;
    401         }
    402         destAddr = filterDestAddress(destAddr);
    403         mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
    404                 null/*messageUri*/, callingPackage);
    405     }
    406 
    407     /**
    408      * Inject an SMS PDU into the android application framework.
    409      *
    410      * @param pdu is the byte array of pdu to be injected into android application framework
    411      * @param format is the format of SMS pdu (3gpp or 3gpp2)
    412      * @param receivedIntent if not NULL this <code>PendingIntent</code> is
    413      *  broadcast when the message is successfully received by the
    414      *  android application framework. This intent is broadcasted at
    415      *  the same time an SMS received from radio is acknowledged back.
    416      */
    417     public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
    418         enforceCarrierPrivilege();
    419         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
    420             log("pdu: " + pdu +
    421                 "\n format=" + format +
    422                 "\n receivedIntent=" + receivedIntent);
    423         }
    424         mDispatcher.injectSmsPdu(pdu, format, receivedIntent);
    425     }
    426 
    427     /**
    428      * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
    429      * This outbound message was handled by the carrier app. If the carrier app fails to send
    430      * this message, it would be resent by PSTN.
    431      *
    432      * @param messageRef the reference number of the SMS message.
    433      * @param success True if and only if the message was sent successfully. If its value is
    434      *  false, this message should be resent via PSTN.
    435      * {@hide}
    436      */
    437     public void updateSmsSendStatus(int messageRef, boolean success) {
    438         enforceCarrierPrivilege();
    439         mDispatcher.updateSmsSendStatus(messageRef, success);
    440     }
    441 
    442     /**
    443      * Send a multi-part text based SMS.
    444      *
    445      * @param destAddr the address to send the message to
    446      * @param scAddr is the service center address or null to use
    447      *   the current default SMSC
    448      * @param parts an <code>ArrayList</code> of strings that, in order,
    449      *   comprise the original message
    450      * @param sentIntents if not null, an <code>ArrayList</code> of
    451      *   <code>PendingIntent</code>s (one for each message part) that is
    452      *   broadcast when the corresponding message part has been sent.
    453      *   The result code will be <code>Activity.RESULT_OK<code> for success,
    454      *   or one of these errors:
    455      *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
    456      *   <code>RESULT_ERROR_RADIO_OFF</code>
    457      *   <code>RESULT_ERROR_NULL_PDU</code>.
    458      *  The per-application based SMS control checks sentIntent. If sentIntent
    459      *  is NULL the caller will be checked against all unknown applications,
    460      *  which cause smaller number of SMS to be sent in checking period.
    461      * @param deliveryIntents if not null, an <code>ArrayList</code> of
    462      *   <code>PendingIntent</code>s (one for each message part) that is
    463      *   broadcast when the corresponding message part has been delivered
    464      *   to the recipient.  The raw pdu of the status report is in the
    465      *   extended data ("pdu").
    466      */
    467 
    468     public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
    469             List<String> parts, List<PendingIntent> sentIntents,
    470             List<PendingIntent> deliveryIntents) {
    471         mPhone.getContext().enforceCallingPermission(
    472                 Manifest.permission.SEND_SMS,
    473                 "Sending SMS message");
    474         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
    475             int i = 0;
    476             for (String part : parts) {
    477                 log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
    478                         ", part[" + (i++) + "]=" + part);
    479             }
    480         }
    481         if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
    482                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
    483             return;
    484         }
    485 
    486         if (parts.size() > 1 && parts.size() < 10 && !SmsMessage.hasEmsSupport()) {
    487             for (int i = 0; i < parts.size(); i++) {
    488                 // If EMS is not supported, we have to break down EMS into single segment SMS
    489                 // and add page info " x/y".
    490                 String singlePart = parts.get(i);
    491                 if (SmsMessage.shouldAppendPageNumberAsPrefix()) {
    492                     singlePart = String.valueOf(i + 1) + '/' + parts.size() + ' ' + singlePart;
    493                 } else {
    494                     singlePart = singlePart.concat(' ' + String.valueOf(i + 1) + '/' + parts.size());
    495                 }
    496 
    497                 PendingIntent singleSentIntent = null;
    498                 if (sentIntents != null && sentIntents.size() > i) {
    499                     singleSentIntent = sentIntents.get(i);
    500                 }
    501 
    502                 PendingIntent singleDeliveryIntent = null;
    503                 if (deliveryIntents != null && deliveryIntents.size() > i) {
    504                     singleDeliveryIntent = deliveryIntents.get(i);
    505                 }
    506 
    507                 mDispatcher.sendText(destAddr, scAddr, singlePart,
    508                         singleSentIntent, singleDeliveryIntent,
    509                         null/*messageUri*/, callingPackage);
    510             }
    511             return;
    512         }
    513 
    514         mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
    515                 (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents,
    516                 null/*messageUri*/, callingPackage);
    517     }
    518 
    519 
    520     public int getPremiumSmsPermission(String packageName) {
    521         return mDispatcher.getPremiumSmsPermission(packageName);
    522     }
    523 
    524 
    525     public void setPremiumSmsPermission(String packageName, int permission) {
    526         mDispatcher.setPremiumSmsPermission(packageName, permission);
    527     }
    528 
    529     /**
    530      * create SmsRawData lists from all sms record byte[]
    531      * Use null to indicate "free" record
    532      *
    533      * @param messages List of message records from EF_SMS.
    534      * @return SmsRawData list of all in-used records
    535      */
    536     protected ArrayList<SmsRawData> buildValidRawData(ArrayList<byte[]> messages) {
    537         int count = messages.size();
    538         ArrayList<SmsRawData> ret;
    539 
    540         ret = new ArrayList<SmsRawData>(count);
    541 
    542         for (int i = 0; i < count; i++) {
    543             byte[] ba = messages.get(i);
    544             if (ba[0] == STATUS_ON_ICC_FREE) {
    545                 ret.add(null);
    546             } else {
    547                 ret.add(new SmsRawData(messages.get(i)));
    548             }
    549         }
    550 
    551         return ret;
    552     }
    553 
    554     /**
    555      * Generates an EF_SMS record from status and raw PDU.
    556      *
    557      * @param status Message status.  See TS 51.011 10.5.3.
    558      * @param pdu Raw message PDU.
    559      * @return byte array for the record.
    560      */
    561     protected byte[] makeSmsRecordData(int status, byte[] pdu) {
    562         byte[] data;
    563         if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
    564             data = new byte[IccConstants.SMS_RECORD_LENGTH];
    565         } else {
    566             data = new byte[IccConstants.CDMA_SMS_RECORD_LENGTH];
    567         }
    568 
    569         // Status bits for this record.  See TS 51.011 10.5.3
    570         data[0] = (byte)(status & 7);
    571 
    572         System.arraycopy(pdu, 0, data, 1, pdu.length);
    573 
    574         // Pad out with 0xFF's.
    575         for (int j = pdu.length+1; j < data.length; j++) {
    576             data[j] = -1;
    577         }
    578 
    579         return data;
    580     }
    581 
    582     public boolean enableCellBroadcast(int messageIdentifier) {
    583         return enableCellBroadcastRange(messageIdentifier, messageIdentifier);
    584     }
    585 
    586     public boolean disableCellBroadcast(int messageIdentifier) {
    587         return disableCellBroadcastRange(messageIdentifier, messageIdentifier);
    588     }
    589 
    590     public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
    591         if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
    592             return enableGsmBroadcastRange(startMessageId, endMessageId);
    593         } else {
    594             return enableCdmaBroadcastRange(startMessageId, endMessageId);
    595         }
    596     }
    597 
    598     public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
    599         if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
    600             return disableGsmBroadcastRange(startMessageId, endMessageId);
    601         } else {
    602             return disableCdmaBroadcastRange(startMessageId, endMessageId);
    603         }
    604     }
    605 
    606     synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) {
    607         if (DBG) log("enableGsmBroadcastRange");
    608 
    609         Context context = mPhone.getContext();
    610 
    611         context.enforceCallingPermission(
    612                 "android.permission.RECEIVE_SMS",
    613                 "Enabling cell broadcast SMS");
    614 
    615         String client = context.getPackageManager().getNameForUid(
    616                 Binder.getCallingUid());
    617 
    618         if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
    619             log("Failed to add cell broadcast subscription for MID range " + startMessageId
    620                     + " to " + endMessageId + " from client " + client);
    621             return false;
    622         }
    623 
    624         if (DBG)
    625             log("Added cell broadcast subscription for MID range " + startMessageId
    626                     + " to " + endMessageId + " from client " + client);
    627 
    628         setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
    629 
    630         return true;
    631     }
    632 
    633     synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) {
    634         if (DBG) log("disableGsmBroadcastRange");
    635 
    636         Context context = mPhone.getContext();
    637 
    638         context.enforceCallingPermission(
    639                 "android.permission.RECEIVE_SMS",
    640                 "Disabling cell broadcast SMS");
    641 
    642         String client = context.getPackageManager().getNameForUid(
    643                 Binder.getCallingUid());
    644 
    645         if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
    646             log("Failed to remove cell broadcast subscription for MID range " + startMessageId
    647                     + " to " + endMessageId + " from client " + client);
    648             return false;
    649         }
    650 
    651         if (DBG)
    652             log("Removed cell broadcast subscription for MID range " + startMessageId
    653                     + " to " + endMessageId + " from client " + client);
    654 
    655         setCellBroadcastActivation(!mCellBroadcastRangeManager.isEmpty());
    656 
    657         return true;
    658     }
    659 
    660     synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) {
    661         if (DBG) log("enableCdmaBroadcastRange");
    662 
    663         Context context = mPhone.getContext();
    664 
    665         context.enforceCallingPermission(
    666                 "android.permission.RECEIVE_SMS",
    667                 "Enabling cdma broadcast SMS");
    668 
    669         String client = context.getPackageManager().getNameForUid(
    670                 Binder.getCallingUid());
    671 
    672         if (!mCdmaBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
    673             log("Failed to add cdma broadcast subscription for MID range " + startMessageId
    674                     + " to " + endMessageId + " from client " + client);
    675             return false;
    676         }
    677 
    678         if (DBG)
    679             log("Added cdma broadcast subscription for MID range " + startMessageId
    680                     + " to " + endMessageId + " from client " + client);
    681 
    682         setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
    683 
    684         return true;
    685     }
    686 
    687     synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) {
    688         if (DBG) log("disableCdmaBroadcastRange");
    689 
    690         Context context = mPhone.getContext();
    691 
    692         context.enforceCallingPermission(
    693                 "android.permission.RECEIVE_SMS",
    694                 "Disabling cell broadcast SMS");
    695 
    696         String client = context.getPackageManager().getNameForUid(
    697                 Binder.getCallingUid());
    698 
    699         if (!mCdmaBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
    700             log("Failed to remove cdma broadcast subscription for MID range " + startMessageId
    701                     + " to " + endMessageId + " from client " + client);
    702             return false;
    703         }
    704 
    705         if (DBG)
    706             log("Removed cdma broadcast subscription for MID range " + startMessageId
    707                     + " to " + endMessageId + " from client " + client);
    708 
    709         setCdmaBroadcastActivation(!mCdmaBroadcastRangeManager.isEmpty());
    710 
    711         return true;
    712     }
    713 
    714     class CellBroadcastRangeManager extends IntRangeManager {
    715         private ArrayList<SmsBroadcastConfigInfo> mConfigList =
    716                 new ArrayList<SmsBroadcastConfigInfo>();
    717 
    718         /**
    719          * Called when the list of enabled ranges has changed. This will be
    720          * followed by zero or more calls to {@link #addRange} followed by
    721          * a call to {@link #finishUpdate}.
    722          */
    723         protected void startUpdate() {
    724             mConfigList.clear();
    725         }
    726 
    727         /**
    728          * Called after {@link #startUpdate} to indicate a range of enabled
    729          * values.
    730          * @param startId the first id included in the range
    731          * @param endId the last id included in the range
    732          */
    733         protected void addRange(int startId, int endId, boolean selected) {
    734             mConfigList.add(new SmsBroadcastConfigInfo(startId, endId,
    735                         SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected));
    736         }
    737 
    738         /**
    739          * Called to indicate the end of a range update started by the
    740          * previous call to {@link #startUpdate}.
    741          * @return true if successful, false otherwise
    742          */
    743         protected boolean finishUpdate() {
    744             if (mConfigList.isEmpty()) {
    745                 return true;
    746             } else {
    747                 SmsBroadcastConfigInfo[] configs =
    748                         mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]);
    749                 return setCellBroadcastConfig(configs);
    750             }
    751         }
    752     }
    753 
    754     class CdmaBroadcastRangeManager extends IntRangeManager {
    755         private ArrayList<CdmaSmsBroadcastConfigInfo> mConfigList =
    756                 new ArrayList<CdmaSmsBroadcastConfigInfo>();
    757 
    758         /**
    759          * Called when the list of enabled ranges has changed. This will be
    760          * followed by zero or more calls to {@link #addRange} followed by a
    761          * call to {@link #finishUpdate}.
    762          */
    763         protected void startUpdate() {
    764             mConfigList.clear();
    765         }
    766 
    767         /**
    768          * Called after {@link #startUpdate} to indicate a range of enabled
    769          * values.
    770          * @param startId the first id included in the range
    771          * @param endId the last id included in the range
    772          */
    773         protected void addRange(int startId, int endId, boolean selected) {
    774             mConfigList.add(new CdmaSmsBroadcastConfigInfo(startId, endId,
    775                     1, selected));
    776         }
    777 
    778         /**
    779          * Called to indicate the end of a range update started by the previous
    780          * call to {@link #startUpdate}.
    781          * @return true if successful, false otherwise
    782          */
    783         protected boolean finishUpdate() {
    784             if (mConfigList.isEmpty()) {
    785                 return true;
    786             } else {
    787                 CdmaSmsBroadcastConfigInfo[] configs =
    788                         mConfigList.toArray(new CdmaSmsBroadcastConfigInfo[mConfigList.size()]);
    789                 return setCdmaBroadcastConfig(configs);
    790             }
    791         }
    792     }
    793 
    794     private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
    795         if (DBG)
    796             log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
    797 
    798         synchronized (mLock) {
    799             Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
    800 
    801             mSuccess = false;
    802             mPhone.mCi.setGsmBroadcastConfig(configs, response);
    803 
    804             try {
    805                 mLock.wait();
    806             } catch (InterruptedException e) {
    807                 log("interrupted while trying to set cell broadcast config");
    808             }
    809         }
    810 
    811         return mSuccess;
    812     }
    813 
    814     private boolean setCellBroadcastActivation(boolean activate) {
    815         if (DBG)
    816             log("Calling setCellBroadcastActivation(" + activate + ')');
    817 
    818         synchronized (mLock) {
    819             Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
    820 
    821             mSuccess = false;
    822             mPhone.mCi.setGsmBroadcastActivation(activate, response);
    823 
    824             try {
    825                 mLock.wait();
    826             } catch (InterruptedException e) {
    827                 log("interrupted while trying to set cell broadcast activation");
    828             }
    829         }
    830 
    831         return mSuccess;
    832     }
    833 
    834     private boolean setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs) {
    835         if (DBG)
    836             log("Calling setCdmaBroadcastConfig with " + configs.length + " configurations");
    837 
    838         synchronized (mLock) {
    839             Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
    840 
    841             mSuccess = false;
    842             mPhone.mCi.setCdmaBroadcastConfig(configs, response);
    843 
    844             try {
    845                 mLock.wait();
    846             } catch (InterruptedException e) {
    847                 log("interrupted while trying to set cdma broadcast config");
    848             }
    849         }
    850 
    851         return mSuccess;
    852     }
    853 
    854     private boolean setCdmaBroadcastActivation(boolean activate) {
    855         if (DBG)
    856             log("Calling setCdmaBroadcastActivation(" + activate + ")");
    857 
    858         synchronized (mLock) {
    859             Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
    860 
    861             mSuccess = false;
    862             mPhone.mCi.setCdmaBroadcastActivation(activate, response);
    863 
    864             try {
    865                 mLock.wait();
    866             } catch (InterruptedException e) {
    867                 log("interrupted while trying to set cdma broadcast activation");
    868             }
    869         }
    870 
    871         return mSuccess;
    872     }
    873 
    874     protected void log(String msg) {
    875         Log.d(LOG_TAG, "[IccSmsInterfaceManager] " + msg);
    876     }
    877 
    878     public boolean isImsSmsSupported() {
    879         return mDispatcher.isIms();
    880     }
    881 
    882     public String getImsSmsFormat() {
    883         return mDispatcher.getImsSmsFormat();
    884     }
    885 
    886     public void sendStoredText(String callingPkg, Uri messageUri, String scAddress,
    887             PendingIntent sentIntent, PendingIntent deliveryIntent) {
    888         mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,
    889                 "Sending SMS message");
    890         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
    891             log("sendStoredText: scAddr=" + scAddress + " messageUri=" + messageUri
    892                     + " sentIntent=" + sentIntent + " deliveryIntent=" + deliveryIntent);
    893         }
    894         if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg)
    895                 != AppOpsManager.MODE_ALLOWED) {
    896             return;
    897         }
    898         final ContentResolver resolver = mPhone.getContext().getContentResolver();
    899         if (!isFailedOrDraft(resolver, messageUri)) {
    900             Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: not FAILED or DRAFT message");
    901             returnUnspecifiedFailure(sentIntent);
    902             return;
    903         }
    904         final String[] textAndAddress = loadTextAndAddress(resolver, messageUri);
    905         if (textAndAddress == null) {
    906             Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredText: can not load text");
    907             returnUnspecifiedFailure(sentIntent);
    908             return;
    909         }
    910         textAndAddress[1] = filterDestAddress(textAndAddress[1]);
    911         mDispatcher.sendText(textAndAddress[1], scAddress, textAndAddress[0],
    912                 sentIntent, deliveryIntent, messageUri, callingPkg);
    913     }
    914 
    915     public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress,
    916             List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
    917         mPhone.getContext().enforceCallingPermission(Manifest.permission.SEND_SMS,
    918                 "Sending SMS message");
    919         if (mAppOps.noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), callingPkg)
    920                 != AppOpsManager.MODE_ALLOWED) {
    921             return;
    922         }
    923         final ContentResolver resolver = mPhone.getContext().getContentResolver();
    924         if (!isFailedOrDraft(resolver, messageUri)) {
    925             Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: "
    926                     + "not FAILED or DRAFT message");
    927             returnUnspecifiedFailure(sentIntents);
    928             return;
    929         }
    930         final String[] textAndAddress = loadTextAndAddress(resolver, messageUri);
    931         if (textAndAddress == null) {
    932             Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: can not load text");
    933             returnUnspecifiedFailure(sentIntents);
    934             return;
    935         }
    936         final ArrayList<String> parts = SmsManager.getDefault().divideMessage(textAndAddress[0]);
    937         if (parts == null || parts.size() < 1) {
    938             Log.e(LOG_TAG, "[IccSmsInterfaceManager]sendStoredMultipartText: can not divide text");
    939             returnUnspecifiedFailure(sentIntents);
    940             return;
    941         }
    942 
    943         if (parts.size() > 1 && parts.size() < 10 && !SmsMessage.hasEmsSupport()) {
    944             for (int i = 0; i < parts.size(); i++) {
    945                 // If EMS is not supported, we have to break down EMS into single segment SMS
    946                 // and add page info " x/y".
    947                 String singlePart = parts.get(i);
    948                 if (SmsMessage.shouldAppendPageNumberAsPrefix()) {
    949                     singlePart = String.valueOf(i + 1) + '/' + parts.size() + ' ' + singlePart;
    950                 } else {
    951                     singlePart = singlePart.concat(' ' + String.valueOf(i + 1) + '/' + parts.size());
    952                 }
    953 
    954                 PendingIntent singleSentIntent = null;
    955                 if (sentIntents != null && sentIntents.size() > i) {
    956                     singleSentIntent = sentIntents.get(i);
    957                 }
    958 
    959                 PendingIntent singleDeliveryIntent = null;
    960                 if (deliveryIntents != null && deliveryIntents.size() > i) {
    961                     singleDeliveryIntent = deliveryIntents.get(i);
    962                 }
    963 
    964                 mDispatcher.sendText(textAndAddress[1], scAddress, singlePart,
    965                         singleSentIntent, singleDeliveryIntent, messageUri, callingPkg);
    966             }
    967             return;
    968         }
    969 
    970         textAndAddress[1] = filterDestAddress(textAndAddress[1]);
    971         mDispatcher.sendMultipartText(
    972                 textAndAddress[1], // destAddress
    973                 scAddress,
    974                 parts,
    975                 (ArrayList<PendingIntent>) sentIntents,
    976                 (ArrayList<PendingIntent>) deliveryIntents,
    977                 messageUri,
    978                 callingPkg);
    979     }
    980 
    981     private boolean isFailedOrDraft(ContentResolver resolver, Uri messageUri) {
    982         // Clear the calling identity and query the database using the phone user id
    983         // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
    984         // between the calling uid and the package uid
    985         final long identity = Binder.clearCallingIdentity();
    986         Cursor cursor = null;
    987         try {
    988             cursor = resolver.query(
    989                     messageUri,
    990                     new String[]{ Telephony.Sms.TYPE },
    991                     null/*selection*/,
    992                     null/*selectionArgs*/,
    993                     null/*sortOrder*/);
    994             if (cursor != null && cursor.moveToFirst()) {
    995                 final int type = cursor.getInt(0);
    996                 return type == Telephony.Sms.MESSAGE_TYPE_DRAFT
    997                         || type == Telephony.Sms.MESSAGE_TYPE_FAILED;
    998             }
    999         } catch (SQLiteException e) {
   1000             Log.e(LOG_TAG, "[IccSmsInterfaceManager]isFailedOrDraft: query message type failed", e);
   1001         } finally {
   1002             if (cursor != null) {
   1003                 cursor.close();
   1004             }
   1005             Binder.restoreCallingIdentity(identity);
   1006         }
   1007         return false;
   1008     }
   1009 
   1010     // Return an array including both the SMS text (0) and address (1)
   1011     private String[] loadTextAndAddress(ContentResolver resolver, Uri messageUri) {
   1012         // Clear the calling identity and query the database using the phone user id
   1013         // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
   1014         // between the calling uid and the package uid
   1015         final long identity = Binder.clearCallingIdentity();
   1016         Cursor cursor = null;
   1017         try {
   1018             cursor = resolver.query(
   1019                     messageUri,
   1020                     new String[]{
   1021                             Telephony.Sms.BODY,
   1022                             Telephony.Sms.ADDRESS
   1023                     },
   1024                     null/*selection*/,
   1025                     null/*selectionArgs*/,
   1026                     null/*sortOrder*/);
   1027             if (cursor != null && cursor.moveToFirst()) {
   1028                 return new String[]{ cursor.getString(0), cursor.getString(1) };
   1029             }
   1030         } catch (SQLiteException e) {
   1031             Log.e(LOG_TAG, "[IccSmsInterfaceManager]loadText: query message text failed", e);
   1032         } finally {
   1033             if (cursor != null) {
   1034                 cursor.close();
   1035             }
   1036             Binder.restoreCallingIdentity(identity);
   1037         }
   1038         return null;
   1039     }
   1040 
   1041     private void returnUnspecifiedFailure(PendingIntent pi) {
   1042         if (pi != null) {
   1043             try {
   1044                 pi.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
   1045             } catch (PendingIntent.CanceledException e) {
   1046                 // ignore
   1047             }
   1048         }
   1049     }
   1050 
   1051     private void returnUnspecifiedFailure(List<PendingIntent> pis) {
   1052         if (pis == null) {
   1053             return;
   1054         }
   1055         for (PendingIntent pi : pis) {
   1056             returnUnspecifiedFailure(pi);
   1057         }
   1058     }
   1059 
   1060     private void enforceCarrierPrivilege() {
   1061         UiccController controller = UiccController.getInstance();
   1062         if (controller == null || controller.getUiccCard() == null) {
   1063             throw new SecurityException("No Carrier Privilege: No UICC");
   1064         }
   1065         if (controller.getUiccCard().getCarrierPrivilegeStatusForCurrentTransaction(
   1066                 mContext.getPackageManager()) !=
   1067                     TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
   1068             throw new SecurityException("No Carrier Privilege.");
   1069         }
   1070     }
   1071 
   1072     private String filterDestAddress(String destAddr) {
   1073         String result  = null;
   1074         result = SmsNumberUtils.filterDestAddr(mPhone, destAddr);
   1075         return result != null ? result : destAddr;
   1076     }
   1077 
   1078 }
   1079