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