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