]> nv-tegra.nvidia Code Review - android/platform/frameworks/opt/telephony.git/blob - src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
b18fa4a898b5009416d5d6da0cb6c23ba32de91c
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / gsm / GsmSMSDispatcher.java
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
17 package com.android.internal.telephony.gsm;
18
19 import android.app.Activity;
20 import android.app.PendingIntent;
21 import android.app.PendingIntent.CanceledException;
22 import android.content.Intent;
23 import android.net.Uri;
24 import android.os.AsyncResult;
25 import android.os.Message;
26 import android.provider.Telephony.Sms;
27 import android.provider.Telephony.Sms.Intents;
28 import android.telephony.Rlog;
29 import android.telephony.ServiceState;
30
31 import com.android.internal.telephony.GsmAlphabet;
32 import com.android.internal.telephony.ImsSMSDispatcher;
33 import com.android.internal.telephony.InboundSmsHandler;
34 import com.android.internal.telephony.PhoneBase;
35 import com.android.internal.telephony.SMSDispatcher;
36 import com.android.internal.telephony.SmsConstants;
37 import com.android.internal.telephony.SmsHeader;
38 import com.android.internal.telephony.SmsUsageMonitor;
39 import com.android.internal.telephony.uicc.IccRecords;
40 import com.android.internal.telephony.uicc.IccUtils;
41 import com.android.internal.telephony.uicc.UiccCardApplication;
42 import com.android.internal.telephony.uicc.UiccController;
43
44 import java.util.HashMap;
45 import java.util.concurrent.atomic.AtomicBoolean;
46 import java.util.concurrent.atomic.AtomicInteger;
47 import java.util.concurrent.atomic.AtomicReference;
48
49 public 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(PhoneBase 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*/);
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*/, true /*isText*/);
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, true /*isText*/);
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 }