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.ActivityThread;
     20 import android.app.PendingIntent;
     21 import android.os.RemoteException;
     22 import android.os.ServiceManager;
     23 import android.text.TextUtils;
     24 
     25 import com.android.internal.telephony.ISms;
     26 import com.android.internal.telephony.SmsRawData;
     27 import com.android.internal.telephony.uicc.IccConstants;
     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?  Presumably 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     /** Singleton object constructed during class initialization. */
     46     private static final SmsManager sInstance = new SmsManager();
     47 
     48     /**
     49      * Send a text based SMS.
     50      *
     51      * @param destinationAddress the address to send the message to
     52      * @param scAddress is the service center address or null to use
     53      *  the current default SMSC
     54      * @param text the body of the message to send
     55      * @param sentIntent if not NULL this <code>PendingIntent</code> is
     56      *  broadcast when the message is successfully sent, or failed.
     57      *  The result code will be <code>Activity.RESULT_OK</code> for success,
     58      *  or one of these errors:<br>
     59      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
     60      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
     61      *  <code>RESULT_ERROR_NULL_PDU</code><br>
     62      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
     63      *  the extra "errorCode" containing a radio technology specific value,
     64      *  generally only useful for troubleshooting.<br>
     65      *  The per-application based SMS control checks sentIntent. If sentIntent
     66      *  is NULL the caller will be checked against all unknown applications,
     67      *  which cause smaller number of SMS to be sent in checking period.
     68      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
     69      *  broadcast when the message is delivered to the recipient.  The
     70      *  raw pdu of the status report is in the extended data ("pdu").
     71      *
     72      * @throws IllegalArgumentException if destinationAddress or text are empty
     73      */
     74     public void sendTextMessage(
     75             String destinationAddress, String scAddress, String text,
     76             PendingIntent sentIntent, PendingIntent deliveryIntent) {
     77         if (TextUtils.isEmpty(destinationAddress)) {
     78             throw new IllegalArgumentException("Invalid destinationAddress");
     79         }
     80 
     81         if (TextUtils.isEmpty(text)) {
     82             throw new IllegalArgumentException("Invalid message body");
     83         }
     84 
     85         try {
     86             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
     87             if (iccISms != null) {
     88                 iccISms.sendText(ActivityThread.currentPackageName(), destinationAddress,
     89                         scAddress, text, sentIntent, deliveryIntent);
     90             }
     91         } catch (RemoteException ex) {
     92             // ignore it
     93         }
     94     }
     95 
     96     /**
     97      * Divide a message text into several fragments, none bigger than
     98      * the maximum SMS message size.
     99      *
    100      * @param text the original message.  Must not be null.
    101      * @return an <code>ArrayList</code> of strings that, in order,
    102      *   comprise the original message
    103      *
    104      * @throws IllegalArgumentException if text is null
    105      */
    106     public ArrayList<String> divideMessage(String text) {
    107         if (null == text) {
    108             throw new IllegalArgumentException("text is null");
    109         }
    110         return SmsMessage.fragmentText(text);
    111     }
    112 
    113     /**
    114      * Send a multi-part text based SMS.  The callee should have already
    115      * divided the message into correctly sized parts by calling
    116      * <code>divideMessage</code>.
    117      *
    118      * @param destinationAddress the address to send the message to
    119      * @param scAddress is the service center address or null to use
    120      *   the current default SMSC
    121      * @param parts an <code>ArrayList</code> of strings that, in order,
    122      *   comprise the original message
    123      * @param sentIntents if not null, an <code>ArrayList</code> of
    124      *   <code>PendingIntent</code>s (one for each message part) that is
    125      *   broadcast when the corresponding message part has been sent.
    126      *   The result code will be <code>Activity.RESULT_OK</code> for success,
    127      *   or one of these errors:<br>
    128      *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    129      *   <code>RESULT_ERROR_RADIO_OFF</code><br>
    130      *   <code>RESULT_ERROR_NULL_PDU</code><br>
    131      *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
    132      *   the extra "errorCode" containing a radio technology specific value,
    133      *   generally only useful for troubleshooting.<br>
    134      *   The per-application based SMS control checks sentIntent. If sentIntent
    135      *   is NULL the caller will be checked against all unknown applications,
    136      *   which cause smaller number of SMS to be sent in checking period.
    137      * @param deliveryIntents if not null, an <code>ArrayList</code> of
    138      *   <code>PendingIntent</code>s (one for each message part) that is
    139      *   broadcast when the corresponding message part has been delivered
    140      *   to the recipient.  The raw pdu of the status report is in the
    141      *   extended data ("pdu").
    142      *
    143      * @throws IllegalArgumentException if destinationAddress or data are empty
    144      */
    145     public void sendMultipartTextMessage(
    146             String destinationAddress, String scAddress, ArrayList<String> parts,
    147             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
    148         if (TextUtils.isEmpty(destinationAddress)) {
    149             throw new IllegalArgumentException("Invalid destinationAddress");
    150         }
    151         if (parts == null || parts.size() < 1) {
    152             throw new IllegalArgumentException("Invalid message body");
    153         }
    154 
    155         if (parts.size() > 1) {
    156             try {
    157                 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    158                 if (iccISms != null) {
    159                     iccISms.sendMultipartText(ActivityThread.currentPackageName(),
    160                             destinationAddress, scAddress, parts,
    161                             sentIntents, deliveryIntents);
    162                 }
    163             } catch (RemoteException ex) {
    164                 // ignore it
    165             }
    166         } else {
    167             PendingIntent sentIntent = null;
    168             PendingIntent deliveryIntent = null;
    169             if (sentIntents != null && sentIntents.size() > 0) {
    170                 sentIntent = sentIntents.get(0);
    171             }
    172             if (deliveryIntents != null && deliveryIntents.size() > 0) {
    173                 deliveryIntent = deliveryIntents.get(0);
    174             }
    175             sendTextMessage(destinationAddress, scAddress, parts.get(0),
    176                     sentIntent, deliveryIntent);
    177         }
    178     }
    179 
    180     /**
    181      * Send a data based SMS to a specific application port.
    182      *
    183      * @param destinationAddress the address to send the message to
    184      * @param scAddress is the service center address or null to use
    185      *  the current default SMSC
    186      * @param destinationPort the port to deliver the message to
    187      * @param data the body of the message to send
    188      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    189      *  broadcast when the message is successfully sent, or failed.
    190      *  The result code will be <code>Activity.RESULT_OK</code> for success,
    191      *  or one of these errors:<br>
    192      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    193      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    194      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    195      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    196      *  the extra "errorCode" containing a radio technology specific value,
    197      *  generally only useful for troubleshooting.<br>
    198      *  The per-application based SMS control checks sentIntent. If sentIntent
    199      *  is NULL the caller will be checked against all unknown applications,
    200      *  which cause smaller number of SMS to be sent in checking period.
    201      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    202      *  broadcast when the message is delivered to the recipient.  The
    203      *  raw pdu of the status report is in the extended data ("pdu").
    204      *
    205      * @throws IllegalArgumentException if destinationAddress or data are empty
    206      */
    207     public void sendDataMessage(
    208             String destinationAddress, String scAddress, short destinationPort,
    209             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    210         if (TextUtils.isEmpty(destinationAddress)) {
    211             throw new IllegalArgumentException("Invalid destinationAddress");
    212         }
    213 
    214         if (data == null || data.length == 0) {
    215             throw new IllegalArgumentException("Invalid message data");
    216         }
    217 
    218         try {
    219             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    220             if (iccISms != null) {
    221                 iccISms.sendData(ActivityThread.currentPackageName(),
    222                         destinationAddress, scAddress, destinationPort & 0xFFFF,
    223                         data, sentIntent, deliveryIntent);
    224             }
    225         } catch (RemoteException ex) {
    226             // ignore it
    227         }
    228     }
    229 
    230     /**
    231      * Get the default instance of the SmsManager
    232      *
    233      * @return the default instance of the SmsManager
    234      */
    235     public static SmsManager getDefault() {
    236         return sInstance;
    237     }
    238 
    239     private SmsManager() {
    240         //nothing
    241     }
    242 
    243     /**
    244      * Copy a raw SMS PDU to the ICC.
    245      * ICC (Integrated Circuit Card) is the card of the device.
    246      * For example, this can be the SIM or USIM for GSM.
    247      *
    248      * @param smsc the SMSC for this message, or NULL for the default SMSC
    249      * @param pdu the raw PDU to store
    250      * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
    251      *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
    252      * @return true for success
    253      *
    254      * @throws IllegalArgumentException if pdu is NULL
    255      * {@hide}
    256      */
    257     public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) {
    258         boolean success = false;
    259 
    260         if (null == pdu) {
    261             throw new IllegalArgumentException("pdu is NULL");
    262         }
    263         try {
    264             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    265             if (iccISms != null) {
    266                 success = iccISms.copyMessageToIccEf(ActivityThread.currentPackageName(),
    267                         status, pdu, smsc);
    268             }
    269         } catch (RemoteException ex) {
    270             // ignore it
    271         }
    272 
    273         return success;
    274     }
    275 
    276     /**
    277      * Delete the specified message from the ICC.
    278      * ICC (Integrated Circuit Card) is the card of the device.
    279      * For example, this can be the SIM or USIM for GSM.
    280      *
    281      * @param messageIndex is the record index of the message on ICC
    282      * @return true for success
    283      *
    284      * {@hide}
    285      */
    286     public boolean
    287     deleteMessageFromIcc(int messageIndex) {
    288         boolean success = false;
    289         byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
    290         Arrays.fill(pdu, (byte)0xff);
    291 
    292         try {
    293             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    294             if (iccISms != null) {
    295                 success = iccISms.updateMessageOnIccEf(ActivityThread.currentPackageName(),
    296                         messageIndex, STATUS_ON_ICC_FREE, pdu);
    297             }
    298         } catch (RemoteException ex) {
    299             // ignore it
    300         }
    301 
    302         return success;
    303     }
    304 
    305     /**
    306      * Update the specified message on the ICC.
    307      * ICC (Integrated Circuit Card) is the card of the device.
    308      * For example, this can be the SIM or USIM for GSM.
    309      *
    310      * @param messageIndex record index of message to update
    311      * @param newStatus new message status (STATUS_ON_ICC_READ,
    312      *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
    313      *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
    314      * @param pdu the raw PDU to store
    315      * @return true for success
    316      *
    317      * {@hide}
    318      */
    319     public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
    320         boolean success = false;
    321 
    322         try {
    323             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    324             if (iccISms != null) {
    325                 success = iccISms.updateMessageOnIccEf(ActivityThread.currentPackageName(),
    326                         messageIndex, newStatus, pdu);
    327             }
    328         } catch (RemoteException ex) {
    329             // ignore it
    330         }
    331 
    332         return success;
    333     }
    334 
    335     /**
    336      * Retrieves all messages currently stored on ICC.
    337      * ICC (Integrated Circuit Card) is the card of the device.
    338      * For example, this can be the SIM or USIM for GSM.
    339      *
    340      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
    341      *
    342      * {@hide}
    343      */
    344     public static ArrayList<SmsMessage> getAllMessagesFromIcc() {
    345         List<SmsRawData> records = null;
    346 
    347         try {
    348             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    349             if (iccISms != null) {
    350                 records = iccISms.getAllMessagesFromIccEf(ActivityThread.currentPackageName());
    351             }
    352         } catch (RemoteException ex) {
    353             // ignore it
    354         }
    355 
    356         return createMessageListFromRawRecords(records);
    357     }
    358 
    359     /**
    360      * Enable reception of cell broadcast (SMS-CB) messages with the given
    361      * message identifier. Note that if two different clients enable the same
    362      * message identifier, they must both disable it for the device to stop
    363      * receiving those messages. All received messages will be broadcast in an
    364      * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
    365      * Note: This call is blocking, callers may want to avoid calling it from
    366      * the main thread of an application.
    367      *
    368      * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
    369      * or C.R1001-G (3GPP2)
    370      * @return true if successful, false otherwise
    371      * @see #disableCellBroadcast(int)
    372      *
    373      * {@hide}
    374      */
    375     public boolean enableCellBroadcast(int messageIdentifier) {
    376         boolean success = false;
    377 
    378         try {
    379             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    380             if (iccISms != null) {
    381                 success = iccISms.enableCellBroadcast(messageIdentifier);
    382             }
    383         } catch (RemoteException ex) {
    384             // ignore it
    385         }
    386 
    387         return success;
    388     }
    389 
    390     /**
    391      * Disable reception of cell broadcast (SMS-CB) messages with the given
    392      * message identifier. Note that if two different clients enable the same
    393      * message identifier, they must both disable it for the device to stop
    394      * receiving those messages.
    395      * Note: This call is blocking, callers may want to avoid calling it from
    396      * the main thread of an application.
    397      *
    398      * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
    399      * or C.R1001-G (3GPP2)
    400      * @return true if successful, false otherwise
    401      *
    402      * @see #enableCellBroadcast(int)
    403      *
    404      * {@hide}
    405      */
    406     public boolean disableCellBroadcast(int messageIdentifier) {
    407         boolean success = false;
    408 
    409         try {
    410             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    411             if (iccISms != null) {
    412                 success = iccISms.disableCellBroadcast(messageIdentifier);
    413             }
    414         } catch (RemoteException ex) {
    415             // ignore it
    416         }
    417 
    418         return success;
    419     }
    420 
    421     /**
    422      * Enable reception of cell broadcast (SMS-CB) messages with the given
    423      * message identifier range. Note that if two different clients enable the same
    424      * message identifier, they must both disable it for the device to stop
    425      * receiving those messages. All received messages will be broadcast in an
    426      * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
    427      * Note: This call is blocking, callers may want to avoid calling it from
    428      * the main thread of an application.
    429      *
    430      * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
    431      * or C.R1001-G (3GPP2)
    432      * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
    433      * or C.R1001-G (3GPP2)
    434      * @return true if successful, false otherwise
    435      * @see #disableCellBroadcastRange(int, int)
    436      *
    437      * @throws IllegalArgumentException if endMessageId < startMessageId
    438      * {@hide}
    439      */
    440     public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
    441         boolean success = false;
    442 
    443         if (endMessageId < startMessageId) {
    444             throw new IllegalArgumentException("endMessageId < startMessageId");
    445         }
    446         try {
    447             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    448             if (iccISms != null) {
    449                 success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId);
    450             }
    451         } catch (RemoteException ex) {
    452             // ignore it
    453         }
    454 
    455         return success;
    456     }
    457 
    458     /**
    459      * Disable reception of cell broadcast (SMS-CB) messages with the given
    460      * message identifier range. Note that if two different clients enable the same
    461      * message identifier, they must both disable it for the device to stop
    462      * receiving those messages.
    463      * Note: This call is blocking, callers may want to avoid calling it from
    464      * the main thread of an application.
    465      *
    466      * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
    467      * or C.R1001-G (3GPP2)
    468      * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
    469      * or C.R1001-G (3GPP2)
    470      * @return true if successful, false otherwise
    471      *
    472      * @see #enableCellBroadcastRange(int, int)
    473      *
    474      * @throws IllegalArgumentException if endMessageId < startMessageId
    475      * {@hide}
    476      */
    477     public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
    478         boolean success = false;
    479 
    480         if (endMessageId < startMessageId) {
    481             throw new IllegalArgumentException("endMessageId < startMessageId");
    482         }
    483         try {
    484             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
    485             if (iccISms != null) {
    486                 success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId);
    487             }
    488         } catch (RemoteException ex) {
    489             // ignore it
    490         }
    491 
    492         return success;
    493     }
    494 
    495     /**
    496      * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
    497      * records returned by <code>getAllMessagesFromIcc()</code>
    498      *
    499      * @param records SMS EF records, returned by
    500      *   <code>getAllMessagesFromIcc</code>
    501      * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
    502      */
    503     private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
    504         ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
    505         if (records != null) {
    506             int count = records.size();
    507             for (int i = 0; i < count; i++) {
    508                 SmsRawData data = records.get(i);
    509                 // List contains all records, including "free" records (null)
    510                 if (data != null) {
    511                     SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
    512                     if (sms != null) {
    513                         messages.add(sms);
    514                     }
    515                 }
    516             }
    517         }
    518         return messages;
    519     }
    520 
    521     // see SmsMessage.getStatusOnIcc
    522 
    523     /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    524     static public final int STATUS_ON_ICC_FREE      = 0;
    525 
    526     /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    527     static public final int STATUS_ON_ICC_READ      = 1;
    528 
    529     /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    530     static public final int STATUS_ON_ICC_UNREAD    = 3;
    531 
    532     /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    533     static public final int STATUS_ON_ICC_SENT      = 5;
    534 
    535     /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
    536     static public final int STATUS_ON_ICC_UNSENT    = 7;
    537 
    538     // SMS send failure result codes
    539 
    540     /** Generic failure cause */
    541     static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
    542     /** Failed because radio was explicitly turned off */
    543     static public final int RESULT_ERROR_RADIO_OFF          = 2;
    544     /** Failed because no pdu provided */
    545     static public final int RESULT_ERROR_NULL_PDU           = 3;
    546     /** Failed because service is currently unavailable */
    547     static public final int RESULT_ERROR_NO_SERVICE         = 4;
    548     /** Failed because we reached the sending queue limit.  {@hide} */
    549     static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
    550     /** Failed because FDN is enabled. {@hide} */
    551     static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
    552 }
    553