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