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