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.ISms;
     25 import com.android.internal.telephony.IccConstants;
     26 import com.android.internal.telephony.SmsRawData;
     27 
     28 import java.util.ArrayList;
     29 import java.util.Arrays;
     30 import java.util.List;
     31 
     32 /*
     33  * TODO(code review): Curious question... Why are a lot of these
     34  * methods not declared as static, since they do not seem to require
     35  * any local object state?  Presumably this cannot be changed without
     36  * interfering with the API...
     37  */
     38 
     39 /**
     40  * Manages SMS operations such as sending data, text, and pdu SMS messages.
     41  * Get this object by calling the static method SmsManager.getDefault().
     42  */
     43 public final class SmsManager {
     44     /** Singleton object constructed during class initialization. */
     45     private static final SmsManager sInstance = new SmsManager();
     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 successfully 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 applications,
    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 successfully 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 applications,
    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         return sInstance;
    228     }
    229 
    230     private SmsManager() {
    231         //nothing
    232     }
    233 
    234     /**
    235      * Copy a raw SMS PDU to the ICC.
    236      * ICC (Integrated Circuit Card) is the card of the device.
    237      * For example, this can be the SIM or USIM for GSM.
    238      *
    239      * @param smsc the SMSC for this message, or NULL for the default SMSC
    240      * @param pdu the raw PDU to store
    241      * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
    242      *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
    243      * @return true for success
    244      *
    245      * {@hide}
    246      */
    247     public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) {
    248         boolean success = false;
    249 
    250         try {
    251             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    252             if (iccISms != null) {
    253                 success = iccISms.copyMessageToIccEf(status, pdu, smsc);
    254             }
    255         } catch (RemoteException ex) {
    256             // ignore it
    257         }
    258 
    259         return success;
    260     }
    261 
    262     /**
    263      * Delete the specified message from the ICC.
    264      * ICC (Integrated Circuit Card) is the card of the device.
    265      * For example, this can be the SIM or USIM for GSM.
    266      *
    267      * @param messageIndex is the record index of the message on ICC
    268      * @return true for success
    269      *
    270      * {@hide}
    271      */
    272     public boolean
    273     deleteMessageFromIcc(int messageIndex) {
    274         boolean success = false;
    275         byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
    276         Arrays.fill(pdu, (byte)0xff);
    277 
    278         try {
    279             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    280             if (iccISms != null) {
    281                 success = iccISms.updateMessageOnIccEf(messageIndex, STATUS_ON_ICC_FREE, pdu);
    282             }
    283         } catch (RemoteException ex) {
    284             // ignore it
    285         }
    286 
    287         return success;
    288     }
    289 
    290     /**
    291      * Update the specified message on the ICC.
    292      * ICC (Integrated Circuit Card) is the card of the device.
    293      * For example, this can be the SIM or USIM for GSM.
    294      *
    295      * @param messageIndex record index of message to update
    296      * @param newStatus new message status (STATUS_ON_ICC_READ,
    297      *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
    298      *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
    299      * @param pdu the raw PDU to store
    300      * @return true for success
    301      *
    302      * {@hide}
    303      */
    304     public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
    305         boolean success = false;
    306 
    307         try {
    308             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    309             if (iccISms != null) {
    310                 success = iccISms.updateMessageOnIccEf(messageIndex, newStatus, pdu);
    311             }
    312         } catch (RemoteException ex) {
    313             // ignore it
    314         }
    315 
    316         return success;
    317     }
    318 
    319     /**
    320      * Retrieves all messages currently stored on ICC.
    321      * ICC (Integrated Circuit Card) is the card of the device.
    322      * For example, this can be the SIM or USIM for GSM.
    323      *
    324      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
    325      *
    326      * {@hide}
    327      */
    328     public static ArrayList<SmsMessage> getAllMessagesFromIcc() {
    329         List<SmsRawData> records = null;
    330 
    331         try {
    332             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    333             if (iccISms != null) {
    334                 records = iccISms.getAllMessagesFromIccEf();
    335             }
    336         } catch (RemoteException ex) {
    337             // ignore it
    338         }
    339 
    340         return createMessageListFromRawRecords(records);
    341     }
    342 
    343     /**
    344      * Enable reception of cell broadcast (SMS-CB) messages with the given
    345      * message identifier. Note that if two different clients enable the same
    346      * message identifier, they must both disable it for the device to stop
    347      * receiving those messages. All received messages will be broadcast in an
    348      * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
    349      * Note: This call is blocking, callers may want to avoid calling it from
    350      * the main thread of an application.
    351      *
    352      * @param messageIdentifier Message identifier as specified in TS 23.041
    353      * @return true if successful, false otherwise
    354      * @see #disableCellBroadcast(int)
    355      *
    356      * {@hide}
    357      */
    358     public boolean enableCellBroadcast(int messageIdentifier) {
    359         boolean success = false;
    360 
    361         try {
    362             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    363             if (iccISms != null) {
    364                 success = iccISms.enableCellBroadcast(messageIdentifier);
    365             }
    366         } catch (RemoteException ex) {
    367             // ignore it
    368         }
    369 
    370         return success;
    371     }
    372 
    373     /**
    374      * Disable reception of cell broadcast (SMS-CB) messages with the given
    375      * message identifier. Note that if two different clients enable the same
    376      * message identifier, they must both disable it for the device to stop
    377      * receiving those messages.
    378      * Note: This call is blocking, callers may want to avoid calling it from
    379      * the main thread of an application.
    380      *
    381      * @param messageIdentifier Message identifier as specified in TS 23.041
    382      * @return true if successful, false otherwise
    383      *
    384      * @see #enableCellBroadcast(int)
    385      *
    386      * {@hide}
    387      */
    388     public boolean disableCellBroadcast(int messageIdentifier) {
    389         boolean success = false;
    390 
    391         try {
    392             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    393             if (iccISms != null) {
    394                 success = iccISms.disableCellBroadcast(messageIdentifier);
    395             }
    396         } catch (RemoteException ex) {
    397             // ignore it
    398         }
    399 
    400         return success;
    401     }
    402 
    403     /**
    404      * Enable reception of cell broadcast (SMS-CB) messages with the given
    405      * message identifier range. Note that if two different clients enable the same
    406      * message identifier, they must both disable it for the device to stop
    407      * receiving those messages. All received messages will be broadcast in an
    408      * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
    409      * Note: This call is blocking, callers may want to avoid calling it from
    410      * the main thread of an application.
    411      *
    412      * @param startMessageId first message identifier as specified in TS 23.041
    413      * @param endMessageId last message identifier as specified in TS 23.041
    414      * @return true if successful, false otherwise
    415      * @see #disableCellBroadcastRange(int, int)
    416      *
    417      * {@hide}
    418      */
    419     public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
    420         boolean success = false;
    421 
    422         try {
    423             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    424             if (iccISms != null) {
    425                 success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId);
    426             }
    427         } catch (RemoteException ex) {
    428             // ignore it
    429         }
    430 
    431         return success;
    432     }
    433 
    434     /**
    435      * Disable reception of cell broadcast (SMS-CB) messages with the given
    436      * message identifier range. Note that if two different clients enable the same
    437      * message identifier, they must both disable it for the device to stop
    438      * receiving those messages.
    439      * Note: This call is blocking, callers may want to avoid calling it from
    440      * the main thread of an application.
    441      *
    442      * @param startMessageId first message identifier as specified in TS 23.041
    443      * @param endMessageId last message identifier as specified in TS 23.041
    444      * @return true if successful, false otherwise
    445      *
    446      * @see #enableCellBroadcastRange(int, int)
    447      *
    448      * {@hide}
    449      */
    450     public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
    451         boolean success = false;
    452 
    453         try {
    454             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    455             if (iccISms != null) {
    456                 success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId);
    457             }
    458         } catch (RemoteException ex) {
    459             // ignore it
    460         }
    461 
    462         return success;
    463     }
    464 
    465     /**
    466      * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
    467      * records returned by <code>getAllMessagesFromIcc()</code>
    468      *
    469      * @param records SMS EF records, returned by
    470      *   <code>getAllMessagesFromIcc</code>
    471      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
    472      */
    473     private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
    474         ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
    475         if (records != null) {
    476             int count = records.size();
    477             for (int i = 0; i < count; i++) {
    478                 SmsRawData data = records.get(i);
    479                 // List contains all records, including "free" records (null)
    480                 if (data != null) {
    481                     SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
    482                     if (sms != null) {
    483                         messages.add(sms);
    484                     }
    485                 }
    486             }
    487         }
    488         return messages;
    489     }
    490 
    491     // see SmsMessage.getStatusOnIcc
    492 
    493     /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    494     static public final int STATUS_ON_ICC_FREE      = 0;
    495 
    496     /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    497     static public final int STATUS_ON_ICC_READ      = 1;
    498 
    499     /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    500     static public final int STATUS_ON_ICC_UNREAD    = 3;
    501 
    502     /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    503     static public final int STATUS_ON_ICC_SENT      = 5;
    504 
    505     /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    506     static public final int STATUS_ON_ICC_UNSENT    = 7;
    507 
    508     // SMS send failure result codes
    509 
    510     /** Generic failure cause */
    511     static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
    512     /** Failed because radio was explicitly turned off */
    513     static public final int RESULT_ERROR_RADIO_OFF          = 2;
    514     /** Failed because no pdu provided */
    515     static public final int RESULT_ERROR_NULL_PDU           = 3;
    516     /** Failed because service is currently unavailable */
    517     static public final int RESULT_ERROR_NO_SERVICE         = 4;
    518     /** Failed because we reached the sending queue limit.  {@hide} */
    519     static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
    520     /** Failed because FDN is enabled. {@hide} */
    521     static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
    522 }
    523