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