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 android.telephony;
     18 
     19 import android.app.PendingIntent;
     20 import android.os.RemoteException;
     21 import android.os.ServiceManager;
     22 import android.text.TextUtils;
     23 
     24 import com.android.internal.telephony.EncodeException;
     25 import com.android.internal.telephony.ISms;
     26 import com.android.internal.telephony.IccConstants;
     27 import com.android.internal.telephony.SmsRawData;
     28 
     29 import java.util.ArrayList;
     30 import java.util.Arrays;
     31 import java.util.List;
     32 
     33 /*
     34  * TODO(code review): Curious question... Why are a lot of these
     35  * methods not declared as static, since they do not seem to require
     36  * any local object state?  Assumedly this cannot be changed without
     37  * interfering with the API...
     38  */
     39 
     40 /**
     41  * Manages SMS operations such as sending data, text, and pdu SMS messages.
     42  * Get this object by calling the static method SmsManager.getDefault().
     43  */
     44 public final class SmsManager {
     45     private static SmsManager sInstance;
     46 
     47     /**
     48      * Send a text based SMS.
     49      *
     50      * @param destinationAddress the address to send the message to
     51      * @param scAddress is the service center address or null to use
     52      *  the current default SMSC
     53      * @param text the body of the message to send
     54      * @param sentIntent if not NULL this <code>PendingIntent</code> is
     55      *  broadcast when the message is sucessfully sent, or failed.
     56      *  The result code will be <code>Activity.RESULT_OK<code> for success,
     57      *  or one of these errors:<br>
     58      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
     59      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
     60      *  <code>RESULT_ERROR_NULL_PDU</code><br>
     61      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
     62      *  the extra "errorCode" containing a radio technology specific value,
     63      *  generally only useful for troubleshooting.<br>
     64      *  The per-application based SMS control checks sentIntent. If sentIntent
     65      *  is NULL the caller will be checked against all unknown applications,
     66      *  which cause smaller number of SMS to be sent in checking period.
     67      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
     68      *  broadcast when the message is delivered to the recipient.  The
     69      *  raw pdu of the status report is in the extended data ("pdu").
     70      *
     71      * @throws IllegalArgumentException if destinationAddress or text are empty
     72      */
     73     public void sendTextMessage(
     74             String destinationAddress, String scAddress, String text,
     75             PendingIntent sentIntent, PendingIntent deliveryIntent) {
     76         if (TextUtils.isEmpty(destinationAddress)) {
     77             throw new IllegalArgumentException("Invalid destinationAddress");
     78         }
     79 
     80         if (TextUtils.isEmpty(text)) {
     81             throw new IllegalArgumentException("Invalid message body");
     82         }
     83 
     84         try {
     85             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
     86             if (iccISms != null) {
     87                 iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
     88             }
     89         } catch (RemoteException ex) {
     90             // ignore it
     91         }
     92     }
     93 
     94     /**
     95      * Divide a message text into several fragments, none bigger than
     96      * the maximum SMS message size.
     97      *
     98      * @param text the original message.  Must not be null.
     99      * @return an <code>ArrayList</code> of strings that, in order,
    100      *   comprise the original message
    101      */
    102     public ArrayList<String> divideMessage(String text) {
    103         return SmsMessage.fragmentText(text);
    104     }
    105 
    106     /**
    107      * Send a multi-part text based SMS.  The callee should have already
    108      * divided the message into correctly sized parts by calling
    109      * <code>divideMessage</code>.
    110      *
    111      * @param destinationAddress the address to send the message to
    112      * @param scAddress is the service center address or null to use
    113      *   the current default SMSC
    114      * @param parts an <code>ArrayList</code> of strings that, in order,
    115      *   comprise the original message
    116      * @param sentIntents if not null, an <code>ArrayList</code> of
    117      *   <code>PendingIntent</code>s (one for each message part) that is
    118      *   broadcast when the corresponding message part has been sent.
    119      *   The result code will be <code>Activity.RESULT_OK<code> for success,
    120      *   or one of these errors:<br>
    121      *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    122      *   <code>RESULT_ERROR_RADIO_OFF</code><br>
    123      *   <code>RESULT_ERROR_NULL_PDU</code><br>
    124      *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
    125      *   the extra "errorCode" containing a radio technology specific value,
    126      *   generally only useful for troubleshooting.<br>
    127      *   The per-application based SMS control checks sentIntent. If sentIntent
    128      *   is NULL the caller will be checked against all unknown applicaitons,
    129      *   which cause smaller number of SMS to be sent in checking period.
    130      * @param deliveryIntents if not null, an <code>ArrayList</code> of
    131      *   <code>PendingIntent</code>s (one for each message part) that is
    132      *   broadcast when the corresponding message part has been delivered
    133      *   to the recipient.  The raw pdu of the status report is in the
    134      *   extended data ("pdu").
    135      *
    136      * @throws IllegalArgumentException if destinationAddress or data are empty
    137      */
    138     public void sendMultipartTextMessage(
    139             String destinationAddress, String scAddress, ArrayList<String> parts,
    140             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
    141         if (TextUtils.isEmpty(destinationAddress)) {
    142             throw new IllegalArgumentException("Invalid destinationAddress");
    143         }
    144         if (parts == null || parts.size() < 1) {
    145             throw new IllegalArgumentException("Invalid message body");
    146         }
    147 
    148         if (parts.size() > 1) {
    149             try {
    150                 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    151                 if (iccISms != null) {
    152                     iccISms.sendMultipartText(destinationAddress, scAddress, parts,
    153                             sentIntents, deliveryIntents);
    154                 }
    155             } catch (RemoteException ex) {
    156                 // ignore it
    157             }
    158         } else {
    159             PendingIntent sentIntent = null;
    160             PendingIntent deliveryIntent = null;
    161             if (sentIntents != null && sentIntents.size() > 0) {
    162                 sentIntent = sentIntents.get(0);
    163             }
    164             if (deliveryIntents != null && deliveryIntents.size() > 0) {
    165                 deliveryIntent = deliveryIntents.get(0);
    166             }
    167             sendTextMessage(destinationAddress, scAddress, parts.get(0),
    168                     sentIntent, deliveryIntent);
    169         }
    170     }
    171 
    172     /**
    173      * Send a data based SMS to a specific application port.
    174      *
    175      * @param destinationAddress the address to send the message to
    176      * @param scAddress is the service center address or null to use
    177      *  the current default SMSC
    178      * @param destinationPort the port to deliver the message to
    179      * @param data the body of the message to send
    180      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    181      *  broadcast when the message is sucessfully sent, or failed.
    182      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    183      *  or one of these errors:<br>
    184      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    185      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    186      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    187      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    188      *  the extra "errorCode" containing a radio technology specific value,
    189      *  generally only useful for troubleshooting.<br>
    190      *  The per-application based SMS control checks sentIntent. If sentIntent
    191      *  is NULL the caller will be checked against all unknown applicaitons,
    192      *  which cause smaller number of SMS to be sent in checking period.
    193      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    194      *  broadcast when the message is delivered to the recipient.  The
    195      *  raw pdu of the status report is in the extended data ("pdu").
    196      *
    197      * @throws IllegalArgumentException if destinationAddress or data are empty
    198      */
    199     public void sendDataMessage(
    200             String destinationAddress, String scAddress, short destinationPort,
    201             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    202         if (TextUtils.isEmpty(destinationAddress)) {
    203             throw new IllegalArgumentException("Invalid destinationAddress");
    204         }
    205 
    206         if (data == null || data.length == 0) {
    207             throw new IllegalArgumentException("Invalid message data");
    208         }
    209 
    210         try {
    211             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    212             if (iccISms != null) {
    213                 iccISms.sendData(destinationAddress, scAddress, destinationPort & 0xFFFF,
    214                         data, sentIntent, deliveryIntent);
    215             }
    216         } catch (RemoteException ex) {
    217             // ignore it
    218         }
    219     }
    220 
    221     /**
    222      * Get the default instance of the SmsManager
    223      *
    224      * @return the default instance of the SmsManager
    225      */
    226     public static SmsManager getDefault() {
    227         if (sInstance == null) {
    228             sInstance = new SmsManager();
    229         }
    230         return sInstance;
    231     }
    232 
    233     private SmsManager() {
    234         //nothing
    235     }
    236 
    237     /**
    238      * Copy a raw SMS PDU to the ICC.
    239      * ICC (Integrated Circuit Card) is the card of the device.
    240      * For example, this can be the SIM or USIM for GSM.
    241      *
    242      * @param smsc the SMSC for this message, or NULL for the default SMSC
    243      * @param pdu the raw PDU to store
    244      * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
    245      *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
    246      * @return true for success
    247      *
    248      * {@hide}
    249      */
    250     public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) {
    251         boolean success = false;
    252 
    253         try {
    254             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    255             if (iccISms != null) {
    256                 success = iccISms.copyMessageToIccEf(status, pdu, smsc);
    257             }
    258         } catch (RemoteException ex) {
    259             // ignore it
    260         }
    261 
    262         return success;
    263     }
    264 
    265     /**
    266      * Delete the specified message from the ICC.
    267      * ICC (Integrated Circuit Card) is the card of the device.
    268      * For example, this can be the SIM or USIM for GSM.
    269      *
    270      * @param messageIndex is the record index of the message on ICC
    271      * @return true for success
    272      *
    273      * {@hide}
    274      */
    275     public boolean
    276     deleteMessageFromIcc(int messageIndex) {
    277         boolean success = false;
    278         byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
    279         Arrays.fill(pdu, (byte)0xff);
    280 
    281         try {
    282             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    283             if (iccISms != null) {
    284                 success = iccISms.updateMessageOnIccEf(messageIndex, STATUS_ON_ICC_FREE, pdu);
    285             }
    286         } catch (RemoteException ex) {
    287             // ignore it
    288         }
    289 
    290         return success;
    291     }
    292 
    293     /**
    294      * Update the specified message on the ICC.
    295      * ICC (Integrated Circuit Card) is the card of the device.
    296      * For example, this can be the SIM or USIM for GSM.
    297      *
    298      * @param messageIndex record index of message to update
    299      * @param newStatus new message status (STATUS_ON_ICC_READ,
    300      *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
    301      *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
    302      * @param pdu the raw PDU to store
    303      * @return true for success
    304      *
    305      * {@hide}
    306      */
    307     public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
    308         boolean success = false;
    309 
    310         try {
    311             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    312             if (iccISms != null) {
    313                 success = iccISms.updateMessageOnIccEf(messageIndex, newStatus, pdu);
    314             }
    315         } catch (RemoteException ex) {
    316             // ignore it
    317         }
    318 
    319         return success;
    320     }
    321 
    322     /**
    323      * Retrieves all messages currently stored on ICC.
    324      * ICC (Integrated Circuit Card) is the card of the device.
    325      * For example, this can be the SIM or USIM for GSM.
    326      *
    327      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
    328      *
    329      * {@hide}
    330      */
    331     public ArrayList<SmsMessage> getAllMessagesFromIcc() {
    332         List<SmsRawData> records = null;
    333 
    334         try {
    335             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    336             if (iccISms != null) {
    337                 records = iccISms.getAllMessagesFromIccEf();
    338             }
    339         } catch (RemoteException ex) {
    340             // ignore it
    341         }
    342 
    343         return createMessageListFromRawRecords(records);
    344    }
    345 
    346     /**
    347      * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
    348      * records returned by <code>getAllMessagesFromIcc()</code>
    349      *
    350      * @param records SMS EF records, returned by
    351      *   <code>getAllMessagesFromIcc</code>
    352      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
    353      */
    354     private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
    355         ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
    356         if (records != null) {
    357             int count = records.size();
    358             for (int i = 0; i < count; i++) {
    359                 SmsRawData data = records.get(i);
    360                 // List contains all records, including "free" records (null)
    361                 if (data != null) {
    362                     SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
    363                     if (sms != null) {
    364                         messages.add(sms);
    365                     }
    366                 }
    367             }
    368         }
    369         return messages;
    370     }
    371 
    372     // see SmsMessage.getStatusOnIcc
    373 
    374     /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    375     static public final int STATUS_ON_ICC_FREE      = 0;
    376 
    377     /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    378     static public final int STATUS_ON_ICC_READ      = 1;
    379 
    380     /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    381     static public final int STATUS_ON_ICC_UNREAD    = 3;
    382 
    383     /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    384     static public final int STATUS_ON_ICC_SENT      = 5;
    385 
    386     /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    387     static public final int STATUS_ON_ICC_UNSENT    = 7;
    388 
    389     // SMS send failure result codes
    390 
    391     /** Generic failure cause */
    392     static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
    393     /** Failed because radio was explicitly turned off */
    394     static public final int RESULT_ERROR_RADIO_OFF          = 2;
    395     /** Failed because no pdu provided */
    396     static public final int RESULT_ERROR_NULL_PDU           = 3;
    397     /** Failed because service is currently unavailable */
    398     static public final int RESULT_ERROR_NO_SERVICE         = 4;
    399     /** Failed because we reached the sending queue limit.  {@hide} */
    400     static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
    401     /** Failed because FDN is enabled. {@hide} */
    402     static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
    403 }
    404