GsmSMSDispatcher.java revision 64c499113a758cf80cddfd4d0183f944a1a6645a
1/*
2 * Copyright (C) 2006 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 com.android.internal.telephony.gsm;
18
19import android.app.Activity;
20import android.app.PendingIntent;
21import android.app.PendingIntent.CanceledException;
22import android.content.Intent;
23import android.os.AsyncResult;
24import android.os.Message;
25import android.telephony.ServiceState;
26import android.util.Config;
27import android.util.Log;
28
29import com.android.internal.telephony.IccUtils;
30import com.android.internal.telephony.gsm.SmsMessage;
31import com.android.internal.telephony.SMSDispatcher;
32import com.android.internal.telephony.SmsHeader;
33import com.android.internal.telephony.SmsMessageBase;
34
35import java.util.ArrayList;
36import java.util.HashMap;
37
38
39final class GsmSMSDispatcher extends SMSDispatcher {
40    private static final String TAG = "GSM";
41
42    private GSMPhone mGsmPhone;
43
44    GsmSMSDispatcher(GSMPhone phone) {
45        super(phone);
46        mGsmPhone = phone;
47    }
48
49    /**
50     * Called when a status report is received.  This should correspond to
51     * a previously successful SEND.
52     *
53     * @param ar AsyncResult passed into the message handler.  ar.result should
54     *           be a String representing the status report PDU, as ASCII hex.
55     */
56    protected void handleStatusReport(AsyncResult ar) {
57        String pduString = (String) ar.result;
58        SmsMessage sms = SmsMessage.newFromCDS(pduString);
59
60        if (sms != null) {
61            int messageRef = sms.messageRef;
62            for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
63                SmsTracker tracker = deliveryPendingList.get(i);
64                if (tracker.mMessageRef == messageRef) {
65                    // Found it.  Remove from list and broadcast.
66                    deliveryPendingList.remove(i);
67                    PendingIntent intent = tracker.mDeliveryIntent;
68                    Intent fillIn = new Intent();
69                    fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
70                    try {
71                        intent.send(mContext, Activity.RESULT_OK, fillIn);
72                    } catch (CanceledException ex) {}
73
74                    // Only expect to see one tracker matching this messageref
75                    break;
76                }
77            }
78        }
79
80        if (mCm != null) {
81            mCm.acknowledgeLastIncomingSMS(true, null);
82        }
83    }
84
85
86    /**
87     * Dispatches an incoming SMS messages.
88     *
89     * @param sms the incoming message from the phone
90     */
91    protected void dispatchMessage(SmsMessageBase smsb) {
92
93        // If sms is null, means there was a parsing error.
94        // TODO: Should NAK this.
95        if (smsb == null) {
96            return;
97        }
98        SmsMessage sms = (SmsMessage) smsb;
99        boolean handled = false;
100
101        // Special case the message waiting indicator messages
102        if (sms.isMWISetMessage()) {
103            mGsmPhone.updateMessageWaitingIndicator(true);
104            handled |= sms.isMwiDontStore();
105            if (Config.LOGD) {
106                Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled);
107            }
108        } else if (sms.isMWIClearMessage()) {
109            mGsmPhone.updateMessageWaitingIndicator(false);
110            handled |= sms.isMwiDontStore();
111            if (Config.LOGD) {
112                Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled);
113            }
114        }
115
116        if (handled) return;
117
118        SmsHeader smsHeader = sms.getUserDataHeader();
119         // See if message is partial or port addressed.
120        if ((smsHeader == null) || (smsHeader.concatRef == null)) {
121            // Message is not partial (not part of concatenated sequence).
122            byte[][] pdus = new byte[1][];
123            pdus[0] = sms.getPdu();
124
125            if (smsHeader.portAddrs != null) {
126                if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
127                    mWapPush.dispatchWapPdu(sms.getUserData());
128                }
129                // The message was sent to a port, so concoct a URI for it.
130                dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort);
131            } else {
132                // Normal short and non-port-addressed message, dispatch it.
133                dispatchPdus(pdus);
134            }
135        } else {
136            // Process the message part.
137            processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs);
138        }
139    }
140
141    /** {@inheritDoc} */
142    protected void sendMultipartText(String destinationAddress, String scAddress,
143            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
144            ArrayList<PendingIntent> deliveryIntents) {
145
146        int refNumber = getNextConcatenatedRef() & 0x00FF;
147
148        for (int i = 0, msgCount = parts.size(); i < msgCount; i++) {
149            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
150            concatRef.refNumber = refNumber;
151            concatRef.seqNumber = i + 1;  // 1-based sequence
152            concatRef.msgCount = msgCount;
153            concatRef.isEightBits = false;
154            SmsHeader smsHeader = new SmsHeader();
155            smsHeader.concatRef = concatRef;
156
157            PendingIntent sentIntent = null;
158            if (sentIntents != null && sentIntents.size() > i) {
159                sentIntent = sentIntents.get(i);
160            }
161
162            PendingIntent deliveryIntent = null;
163            if (deliveryIntents != null && deliveryIntents.size() > i) {
164                deliveryIntent = deliveryIntents.get(i);
165            }
166
167            SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
168                    parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader));
169
170            sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
171        }
172    }
173
174    /**
175     * Send a multi-part text based SMS which already passed SMS control check.
176     *
177     * It is the working function for sendMultipartText().
178     *
179     * @param destinationAddress the address to send the message to
180     * @param scAddress is the service center address or null to use
181     *   the current default SMSC
182     * @param parts an <code>ArrayList</code> of strings that, in order,
183     *   comprise the original message
184     * @param sentIntents if not null, an <code>ArrayList</code> of
185     *   <code>PendingIntent</code>s (one for each message part) that is
186     *   broadcast when the corresponding message part has been sent.
187     *   The result code will be <code>Activity.RESULT_OK<code> for success,
188     *   or one of these errors:
189     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
190     *   <code>RESULT_ERROR_RADIO_OFF</code>
191     *   <code>RESULT_ERROR_NULL_PDU</code>.
192     * @param deliveryIntents if not null, an <code>ArrayList</code> of
193     *   <code>PendingIntent</code>s (one for each message part) that is
194     *   broadcast when the corresponding message part has been delivered
195     *   to the recipient.  The raw pdu of the status report is in the
196     *   extended data ("pdu").
197     */
198    private void sendMultipartTextWithPermit(String destinationAddress,
199            String scAddress, ArrayList<String> parts,
200            ArrayList<PendingIntent> sentIntents,
201            ArrayList<PendingIntent> deliveryIntents) {
202
203        // check if in service
204        int ss = mPhone.getServiceState().getState();
205        if (ss != ServiceState.STATE_IN_SERVICE) {
206            for (int i = 0, count = parts.size(); i < count; i++) {
207                PendingIntent sentIntent = null;
208                if (sentIntents != null && sentIntents.size() > i) {
209                    sentIntent = sentIntents.get(i);
210                }
211                SmsTracker tracker = SmsTrackerFactory(null, sentIntent, null);
212                handleNotInService(ss, tracker);
213            }
214            return;
215        }
216
217        int refNumber = getNextConcatenatedRef() & 0x00FF;
218
219        for (int i = 0, msgCount = parts.size(); i < msgCount; i++) {
220            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
221            concatRef.refNumber = refNumber;
222            concatRef.seqNumber = i + 1;  // 1-based sequence
223            concatRef.msgCount = msgCount;
224            concatRef.isEightBits = false;
225            SmsHeader smsHeader = new SmsHeader();
226            smsHeader.concatRef = concatRef;
227
228            PendingIntent sentIntent = null;
229            if (sentIntents != null && sentIntents.size() > i) {
230                sentIntent = sentIntents.get(i);
231            }
232
233            PendingIntent deliveryIntent = null;
234            if (deliveryIntents != null && deliveryIntents.size() > i) {
235                deliveryIntent = deliveryIntents.get(i);
236            }
237
238            SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
239                    parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader));
240
241            HashMap<String, Object> map = new HashMap<String, Object>();
242            map.put("smsc", pdus.encodedScAddress);
243            map.put("pdu", pdus.encodedMessage);
244
245            SmsTracker tracker =  SmsTrackerFactory(map, sentIntent, deliveryIntent);
246            sendSms(tracker);
247        }
248    }
249
250    /** {@inheritDoc} */
251    protected void sendSms(SmsTracker tracker) {
252        HashMap map = tracker.mData;
253
254        byte smsc[] = (byte[]) map.get("smsc");
255        byte pdu[] = (byte[]) map.get("pdu");
256
257        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
258        mCm.sendSMS(IccUtils.bytesToHexString(smsc),
259                IccUtils.bytesToHexString(pdu), reply);
260    }
261
262    /**
263     * Send the multi-part SMS based on multipart Sms tracker
264     *
265     * @param tracker holds the multipart Sms tracker ready to be sent
266     */
267    protected void sendMultipartSms (SmsTracker tracker) {
268        ArrayList<String> parts;
269        ArrayList<PendingIntent> sentIntents;
270        ArrayList<PendingIntent> deliveryIntents;
271
272        HashMap map = tracker.mData;
273
274        String destinationAddress = (String) map.get("destination");
275        String scAddress = (String) map.get("scaddress");
276
277        parts = (ArrayList<String>) map.get("parts");
278        sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
279        deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
280
281        sendMultipartTextWithPermit(destinationAddress,
282                scAddress, parts, sentIntents, deliveryIntents);
283
284    }
285
286    /** {@inheritDoc} */
287    protected void acknowledgeLastIncomingSms(boolean success, Message response){
288        // FIXME unit test leaves cm == null. this should change
289        if (mCm != null) {
290            mCm.acknowledgeLastIncomingSMS(success, response);
291        }
292    }
293
294    /** {@inheritDoc} */
295    protected void activateCellBroadcastSms(int activate, Message response) {
296        // Unless CBS is implemented for GSM, this point should be unreachable.
297        Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
298        response.recycle();
299    }
300
301    /** {@inheritDoc} */
302    protected void getCellBroadcastSmsConfig(Message response){
303        // Unless CBS is implemented for GSM, this point should be unreachable.
304        Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
305        response.recycle();
306    }
307
308    /** {@inheritDoc} */
309    protected  void setCellBroadcastConfig(int[] configValuesArray, Message response) {
310        // Unless CBS is implemented for GSM, this point should be unreachable.
311        Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
312        response.recycle();
313    }
314
315}
316