2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.telephony.gsm;
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;
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;
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;
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;
58 /** Status report received */
59 private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
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");
73 public void dispose() {
75 mCi.unSetOnSmsStatus(this);
76 mUiccController.unregisterForIccChanged(this);
80 protected String getFormat() {
81 return SmsConstants.FORMAT_3GPP;
85 * Handles 3GPP format-specific events coming from the phone stack.
86 * Other events are handled by {@link SMSDispatcher#handleMessage}.
88 * @param msg the message to handle
91 public void handleMessage(Message msg) {
93 case EVENT_NEW_SMS_STATUS_REPORT:
94 handleStatusReport((AsyncResult) msg.obj);
97 case EVENT_NEW_ICC_SMS:
98 // pass to InboundSmsHandler to process
99 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj);
102 case EVENT_ICC_CHANGED:
103 onUpdateIccAvailability();
107 super.handleMessage(msg);
112 * Called when a status report is received. This should correspond to
113 * a previously successful SEND.
115 * @param ar AsyncResult passed into the message handler. ar.result should
116 * be a String representing the status report PDU, as ASCII hex.
118 private void handleStatusReport(AsyncResult ar) {
119 String pduString = (String) ar.result;
120 SmsMessage sms = SmsMessage.newFromCDS(pduString);
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);
134 PendingIntent intent = tracker.mDeliveryIntent;
135 Intent fillIn = new Intent();
136 fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
137 fillIn.putExtra("format", getFormat());
139 intent.send(mContext, Activity.RESULT_OK, fillIn);
140 } catch (CanceledException ex) {}
142 // Only expect to see one tracker matching this messageref
147 mCi.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
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));
157 HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
158 SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
159 null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
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));
168 Rlog.v(TAG, "No carrier package.");
172 Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
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));
183 HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
184 SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
185 messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/);
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));
193 Rlog.v(TAG, "No carrier package.");
197 Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
203 protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
204 throw new IllegalStateException("This method must be called only on ImsSMSDispatcher");
209 protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
210 boolean use7bitOnly) {
211 return SmsMessage.calculateLength(messageBody, use7bitOnly);
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);
225 HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
227 return getSmsTracker(map, sentIntent,
228 deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
229 smsHeader, !lastPart, fullMessageText, true /*isText*/);
231 Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
237 protected void sendSubmitPdu(SmsTracker tracker) {
243 protected void sendSms(SmsTracker tracker) {
244 HashMap<String, Object> map = tracker.mData;
246 byte pdu[] = (byte[]) map.get("pdu");
248 if (tracker.mRetryCount > 0) {
249 Rlog.d(TAG, "sendSms: "
250 + " mRetryCount=" + tracker.mRetryCount
251 + " mMessageRef=" + tracker.mMessageRef
252 + " SS=" + mPhone.getServiceState().getState());
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
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());
269 sendSmsByPstn(tracker);
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*/);
282 HashMap<String, Object> map = tracker.mData;
284 byte smsc[] = (byte[]) map.get("smsc");
285 byte[] pdu = (byte[]) map.get("pdu");
286 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
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
302 if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
303 mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
304 IccUtils.bytesToHexString(pdu), reply);
306 mCi.sendSMS(IccUtils.bytesToHexString(smsc),
307 IccUtils.bytesToHexString(pdu), reply);
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.
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);
326 private void onUpdateIccAvailability() {
327 if (mUiccController == null ) {
331 UiccCardApplication newUiccApplication = getUiccCardApplication();
333 UiccCardApplication app = mUiccApplication.get();
334 if (app != newUiccApplication) {
336 Rlog.d(TAG, "Removing stale icc objects.");
337 if (mIccRecords.get() != null) {
338 mIccRecords.get().unregisterForNewSms(this);
340 mIccRecords.set(null);
341 mUiccApplication.set(null);
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);