GsmSMSDispatcher.java revision b29851580bba4a13ddbf7a534d8b09295eb2c60f
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;
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*/);
161            sendRawPdu(tracker);
162        } else {
163            Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
164        }
165    }
166
167    /** {@inheritDoc} */
168    @Override
169    protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
170            PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
171        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
172                scAddr, destAddr, text, (deliveryIntent != null));
173        if (pdu != null) {
174            if (messageUri == null) {
175                messageUri = writeOutboxMessage(
176                        getSubId(),
177                        destAddr,
178                        text,
179                        deliveryIntent != null,
180                        callingPkg);
181            } else {
182                moveToOutbox(getSubId(), messageUri, callingPkg);
183            }
184            HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
185            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
186                    messageUri);
187            sendRawPdu(tracker);
188        } else {
189            Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
190        }
191    }
192
193    /** {@inheritDoc} */
194    @Override
195    protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
196        throw new IllegalStateException("This method must be called only on ImsSMSDispatcher");
197    }
198
199    /** {@inheritDoc} */
200    @Override
201    protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
202            boolean use7bitOnly) {
203        return SmsMessage.calculateLength(messageBody, use7bitOnly);
204    }
205
206    /** {@inheritDoc} */
207    @Override
208    protected void sendNewSubmitPdu(String destinationAddress, String scAddress,
209            String message, SmsHeader smsHeader, int encoding,
210            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
211            AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri) {
212        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
213                message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
214                encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
215        if (pdu != null) {
216            HashMap map =  getSmsTrackerMap(destinationAddress, scAddress,
217                    message, pdu);
218            SmsTracker tracker = getSmsTracker(map, sentIntent,
219                    deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri);
220            sendRawPdu(tracker);
221        } else {
222            Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
223        }
224    }
225
226    /** {@inheritDoc} */
227    @Override
228    protected void sendSms(SmsTracker tracker) {
229        HashMap<String, Object> map = tracker.mData;
230
231        byte pdu[] = (byte[]) map.get("pdu");
232
233        if (tracker.mRetryCount > 0) {
234            Rlog.d(TAG, "sendSms: "
235                    + " mRetryCount=" + tracker.mRetryCount
236                    + " mMessageRef=" + tracker.mMessageRef
237                    + " SS=" + mPhone.getServiceState().getState());
238
239            // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
240            //   TP-RD (bit 2) is 1 for retry
241            //   and TP-MR is set to previously failed sms TP-MR
242            if (((0x01 & pdu[0]) == 0x01)) {
243                pdu[0] |= 0x04; // TP-RD
244                pdu[1] = (byte) tracker.mMessageRef; // TP-MR
245            }
246        }
247        Rlog.d(TAG, "sendSms: "
248                + " isIms()=" + isIms()
249                + " mRetryCount=" + tracker.mRetryCount
250                + " mImsRetry=" + tracker.mImsRetry
251                + " mMessageRef=" + tracker.mMessageRef
252                + " SS=" + mPhone.getServiceState().getState());
253
254        // FIX this once the carrier app and SIM restricted API is finalized.
255        // We should direct the intent to only the default carrier app.
256
257        // Send SMS via the carrier app.
258        BroadcastReceiver resultReceiver = new SMSDispatcherReceiver(tracker);
259
260        Intent intent = new Intent(Intents.SMS_SEND_ACTION);
261        intent.putExtra("pdu", pdu);
262        intent.putExtra("smsc", (byte[]) map.get("smsc"));
263        intent.putExtra("format", getFormat());
264        intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
265        Rlog.d(TAG, "Sending SMS by carrier app.");
266
267        mContext.sendOrderedBroadcast(intent, android.Manifest.permission.RECEIVE_SMS,
268                                      AppOpsManager.OP_RECEIVE_SMS, resultReceiver,
269                                      null, Activity.RESULT_CANCELED, null, null);
270    }
271
272    /** {@inheritDoc} */
273    @Override
274    protected void sendSmsByPstn(SmsTracker tracker) {
275        HashMap<String, Object> map = tracker.mData;
276
277        byte smsc[] = (byte[]) map.get("smsc");
278        byte[] pdu = (byte[]) map.get("pdu");
279        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
280
281        // sms over gsm is used:
282        //   if sms over IMS is not supported AND
283        //   this is not a retry case after sms over IMS failed
284        //     indicated by mImsRetry > 0
285        if (0 == tracker.mImsRetry && !isIms()) {
286            if (tracker.mRetryCount > 0) {
287                // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
288                //   TP-RD (bit 2) is 1 for retry
289                //   and TP-MR is set to previously failed sms TP-MR
290                if (((0x01 & pdu[0]) == 0x01)) {
291                    pdu[0] |= 0x04; // TP-RD
292                    pdu[1] = (byte) tracker.mMessageRef; // TP-MR
293                }
294            }
295            mCi.sendSMS(IccUtils.bytesToHexString(smsc),
296                    IccUtils.bytesToHexString(pdu), reply);
297        } else {
298            mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
299                    IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
300                    tracker.mMessageRef, reply);
301            // increment it here, so in case of SMS_FAIL_RETRY over IMS
302            // next retry will be sent using IMS request again.
303            tracker.mImsRetry++;
304        }
305    }
306
307    /** {@inheritDoc} */
308    @Override
309    protected void updateSmsSendStatus(int messageRef, boolean success) {
310        // This function should be defined in ImsDispatcher.
311        Rlog.e(TAG, "updateSmsSendStatus should never be called from here!");
312    }
313
314    protected UiccCardApplication getUiccCardApplication() {
315            Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId()
316                    + " slotId = " + mPhone.getPhoneId());
317                return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
318                        UiccController.APP_FAM_3GPP);
319    }
320
321    private void onUpdateIccAvailability() {
322        if (mUiccController == null ) {
323            return;
324        }
325
326        UiccCardApplication newUiccApplication = getUiccCardApplication();
327
328        UiccCardApplication app = mUiccApplication.get();
329        if (app != newUiccApplication) {
330            if (app != null) {
331                Rlog.d(TAG, "Removing stale icc objects.");
332                if (mIccRecords.get() != null) {
333                    mIccRecords.get().unregisterForNewSms(this);
334                }
335                mIccRecords.set(null);
336                mUiccApplication.set(null);
337            }
338            if (newUiccApplication != null) {
339                Rlog.d(TAG, "New Uicc application found");
340                mUiccApplication.set(newUiccApplication);
341                mIccRecords.set(newUiccApplication.getIccRecords());
342                if (mIccRecords.get() != null) {
343                    mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null);
344                }
345            }
346        }
347    }
348}
349