GsmSMSDispatcher.java revision 1a87ab3d7170d618f048c4f5af8c7504a587aaa5
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.net.Uri;
24import android.os.AsyncResult;
25import android.os.Message;
26import android.provider.Telephony.Sms;
27import android.provider.Telephony.Sms.Intents;
28import android.telephony.Rlog;
29import android.telephony.ServiceState;
30
31import com.android.internal.telephony.GsmAlphabet;
32import com.android.internal.telephony.ImsSMSDispatcher;
33import com.android.internal.telephony.InboundSmsHandler;
34import com.android.internal.telephony.Phone;
35import com.android.internal.telephony.SMSDispatcher;
36import com.android.internal.telephony.SmsConstants;
37import com.android.internal.telephony.SmsHeader;
38import com.android.internal.telephony.SmsUsageMonitor;
39import com.android.internal.telephony.uicc.IccRecords;
40import com.android.internal.telephony.uicc.IccUtils;
41import com.android.internal.telephony.uicc.UiccCardApplication;
42import com.android.internal.telephony.uicc.UiccController;
43
44import java.util.HashMap;
45import java.util.concurrent.atomic.AtomicBoolean;
46import java.util.concurrent.atomic.AtomicInteger;
47import java.util.concurrent.atomic.AtomicReference;
48
49public final class GsmSMSDispatcher extends SMSDispatcher {
50    private static final String TAG = "GsmSMSDispatcher";
51    private static final boolean VDBG = false;
52    protected UiccController mUiccController = null;
53    private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
54    private AtomicReference<UiccCardApplication> mUiccApplication =
55            new AtomicReference<UiccCardApplication>();
56    private GsmInboundSmsHandler mGsmInboundSmsHandler;
57
58    /** Status report received */
59    private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
60
61    public GsmSMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor,
62            ImsSMSDispatcher imsSMSDispatcher,
63            GsmInboundSmsHandler gsmInboundSmsHandler) {
64        super(phone, usageMonitor, imsSMSDispatcher);
65        mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
66        mGsmInboundSmsHandler = gsmInboundSmsHandler;
67        mUiccController = UiccController.getInstance();
68        mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
69        Rlog.d(TAG, "GsmSMSDispatcher created");
70    }
71
72    @Override
73    public void dispose() {
74        super.dispose();
75        mCi.unSetOnSmsStatus(this);
76        mUiccController.unregisterForIccChanged(this);
77    }
78
79    @Override
80    protected String getFormat() {
81        return SmsConstants.FORMAT_3GPP;
82    }
83
84    /**
85     * Handles 3GPP format-specific events coming from the phone stack.
86     * Other events are handled by {@link SMSDispatcher#handleMessage}.
87     *
88     * @param msg the message to handle
89     */
90    @Override
91    public void handleMessage(Message msg) {
92        switch (msg.what) {
93        case EVENT_NEW_SMS_STATUS_REPORT:
94            handleStatusReport((AsyncResult) msg.obj);
95            break;
96
97        case EVENT_NEW_ICC_SMS:
98        // pass to InboundSmsHandler to process
99        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj);
100        break;
101
102        case EVENT_ICC_CHANGED:
103            onUpdateIccAvailability();
104            break;
105
106        default:
107            super.handleMessage(msg);
108        }
109    }
110
111    /**
112     * Called when a status report is received.  This should correspond to
113     * a previously successful SEND.
114     *
115     * @param ar AsyncResult passed into the message handler.  ar.result should
116     *           be a String representing the status report PDU, as ASCII hex.
117     */
118    private void handleStatusReport(AsyncResult ar) {
119        String pduString = (String) ar.result;
120        SmsMessage sms = SmsMessage.newFromCDS(pduString);
121
122        if (sms != null) {
123            int tpStatus = sms.getStatus();
124            int messageRef = sms.mMessageRef;
125            for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
126                SmsTracker tracker = deliveryPendingList.get(i);
127                if (tracker.mMessageRef == messageRef) {
128                    // Found it.  Remove from list and broadcast.
129                    if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
130                       deliveryPendingList.remove(i);
131                       // Update the message status (COMPLETE or FAILED)
132                       tracker.updateSentMessageStatus(mContext, tpStatus);
133                    }
134                    PendingIntent intent = tracker.mDeliveryIntent;
135                    Intent fillIn = new Intent();
136                    fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
137                    fillIn.putExtra("format", getFormat());
138                    try {
139                        intent.send(mContext, Activity.RESULT_OK, fillIn);
140                    } catch (CanceledException ex) {}
141
142                    // Only expect to see one tracker matching this messageref
143                    break;
144                }
145            }
146        }
147        mCi.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
148    }
149
150    /** {@inheritDoc} */
151    @Override
152    protected void sendData(String destAddr, String scAddr, int destPort,
153            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
154        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
155                scAddr, destAddr, destPort, data, (deliveryIntent != null));
156        if (pdu != null) {
157            HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
158            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
159                    null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
160                    false /*isText*/, true /*persistMessage*/);
161
162            String carrierPackage = getCarrierAppPackageName();
163            if (carrierPackage != null) {
164                Rlog.d(TAG, "Found carrier package.");
165                DataSmsSender smsSender = new DataSmsSender(tracker);
166                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
167            } else {
168                Rlog.v(TAG, "No carrier package.");
169                sendRawPdu(tracker);
170            }
171        } else {
172            Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
173        }
174    }
175
176    /** {@inheritDoc} */
177    @Override
178    protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
179            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
180            boolean persistMessage) {
181        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
182                scAddr, destAddr, text, (deliveryIntent != null));
183        if (pdu != null) {
184            HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
185            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
186                    messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/,
187                    persistMessage);
188
189            String carrierPackage = getCarrierAppPackageName();
190            if (carrierPackage != null) {
191                Rlog.d(TAG, "Found carrier package.");
192                TextSmsSender smsSender = new TextSmsSender(tracker);
193                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
194            } else {
195                Rlog.v(TAG, "No carrier package.");
196                sendRawPdu(tracker);
197            }
198        } else {
199            Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
200        }
201    }
202
203    /** {@inheritDoc} */
204    @Override
205    protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
206        throw new IllegalStateException("This method must be called only on ImsSMSDispatcher");
207    }
208
209    /** {@inheritDoc} */
210    @Override
211    protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
212            boolean use7bitOnly) {
213        return SmsMessage.calculateLength(messageBody, use7bitOnly);
214    }
215
216    /** {@inheritDoc} */
217    @Override
218    protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
219            String message, SmsHeader smsHeader, int encoding,
220            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
221            AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
222            String fullMessageText) {
223        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
224                message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
225                encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
226        if (pdu != null) {
227            HashMap map =  getSmsTrackerMap(destinationAddress, scAddress,
228                    message, pdu);
229            return getSmsTracker(map, sentIntent,
230                    deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
231                    smsHeader, !lastPart, fullMessageText, true /*isText*/,
232                    false /*persistMessage*/);
233        } else {
234            Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
235            return null;
236        }
237    }
238
239    @Override
240    protected void sendSubmitPdu(SmsTracker tracker) {
241        sendRawPdu(tracker);
242    }
243
244    /** {@inheritDoc} */
245    @Override
246    protected void sendSms(SmsTracker tracker) {
247        HashMap<String, Object> map = tracker.mData;
248
249        byte pdu[] = (byte[]) map.get("pdu");
250
251        if (tracker.mRetryCount > 0) {
252            Rlog.d(TAG, "sendSms: "
253                    + " mRetryCount=" + tracker.mRetryCount
254                    + " mMessageRef=" + tracker.mMessageRef
255                    + " SS=" + mPhone.getServiceState().getState());
256
257            // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
258            //   TP-RD (bit 2) is 1 for retry
259            //   and TP-MR is set to previously failed sms TP-MR
260            if (((0x01 & pdu[0]) == 0x01)) {
261                pdu[0] |= 0x04; // TP-RD
262                pdu[1] = (byte) tracker.mMessageRef; // TP-MR
263            }
264        }
265        Rlog.d(TAG, "sendSms: "
266                + " isIms()=" + isIms()
267                + " mRetryCount=" + tracker.mRetryCount
268                + " mImsRetry=" + tracker.mImsRetry
269                + " mMessageRef=" + tracker.mMessageRef
270                + " SS=" + mPhone.getServiceState().getState());
271
272        sendSmsByPstn(tracker);
273    }
274
275    /** {@inheritDoc} */
276    @Override
277    protected void sendSmsByPstn(SmsTracker tracker) {
278        int ss = mPhone.getServiceState().getState();
279        // if sms over IMS is not supported on data and voice is not available...
280        if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
281            tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
282            return;
283        }
284
285        HashMap<String, Object> map = tracker.mData;
286
287        byte smsc[] = (byte[]) map.get("smsc");
288        byte[] pdu = (byte[]) map.get("pdu");
289        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
290
291        // sms over gsm is used:
292        //   if sms over IMS is not supported AND
293        //   this is not a retry case after sms over IMS failed
294        //     indicated by mImsRetry > 0
295        if (0 == tracker.mImsRetry && !isIms()) {
296            if (tracker.mRetryCount > 0) {
297                // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
298                //   TP-RD (bit 2) is 1 for retry
299                //   and TP-MR is set to previously failed sms TP-MR
300                if (((0x01 & pdu[0]) == 0x01)) {
301                    pdu[0] |= 0x04; // TP-RD
302                    pdu[1] = (byte) tracker.mMessageRef; // TP-MR
303                }
304            }
305            if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
306                mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
307                        IccUtils.bytesToHexString(pdu), reply);
308            } else {
309                mCi.sendSMS(IccUtils.bytesToHexString(smsc),
310                        IccUtils.bytesToHexString(pdu), reply);
311            }
312        } else {
313            mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
314                    IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
315                    tracker.mMessageRef, reply);
316            // increment it here, so in case of SMS_FAIL_RETRY over IMS
317            // next retry will be sent using IMS request again.
318            tracker.mImsRetry++;
319        }
320    }
321
322    protected UiccCardApplication getUiccCardApplication() {
323            Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId()
324                    + " slotId = " + mPhone.getPhoneId());
325                return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
326                        UiccController.APP_FAM_3GPP);
327    }
328
329    private void onUpdateIccAvailability() {
330        if (mUiccController == null ) {
331            return;
332        }
333
334        UiccCardApplication newUiccApplication = getUiccCardApplication();
335
336        UiccCardApplication app = mUiccApplication.get();
337        if (app != newUiccApplication) {
338            if (app != null) {
339                Rlog.d(TAG, "Removing stale icc objects.");
340                if (mIccRecords.get() != null) {
341                    mIccRecords.get().unregisterForNewSms(this);
342                }
343                mIccRecords.set(null);
344                mUiccApplication.set(null);
345            }
346            if (newUiccApplication != null) {
347                Rlog.d(TAG, "New Uicc application found");
348                mUiccApplication.set(newUiccApplication);
349                mIccRecords.set(newUiccApplication.getIccRecords());
350                if (mIccRecords.get() != null) {
351                    mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null);
352                }
353            }
354        }
355    }
356}
357