GsmSMSDispatcher.java revision 91f8e19cdafe6382ec1469167e208e2e928123cc
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 void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { 188 throw new IllegalStateException("This method must be called only on ImsSMSDispatcher"); 189 } 190 191 /** {@inheritDoc} */ 192 @Override 193 protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody, 194 boolean use7bitOnly) { 195 return SmsMessage.calculateLength(messageBody, use7bitOnly); 196 } 197 198 /** {@inheritDoc} */ 199 @Override 200 protected void sendNewSubmitPdu(String destinationAddress, String scAddress, 201 String message, SmsHeader smsHeader, int encoding, 202 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) { 203 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, 204 message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader), 205 encoding, smsHeader.languageTable, smsHeader.languageShiftTable); 206 if (pdu != null) { 207 HashMap map = getSmsTrackerMap(destinationAddress, scAddress, 208 message, pdu); 209 SmsTracker tracker = getSmsTracker(map, sentIntent, 210 deliveryIntent, getFormat()); 211 sendRawPdu(tracker); 212 } else { 213 Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null"); 214 } 215 } 216 217 /** {@inheritDoc} */ 218 @Override 219 protected void sendSms(SmsTracker tracker) { 220 HashMap<String, Object> map = tracker.mData; 221 222 byte smsc[] = (byte[]) map.get("smsc"); 223 byte pdu[] = (byte[]) map.get("pdu"); 224 225 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 226 227 if (tracker.mRetryCount > 0) { 228 Rlog.d(TAG, "sendSms: " 229 + " mRetryCount=" + tracker.mRetryCount 230 + " mMessageRef=" + tracker.mMessageRef 231 + " SS=" + mPhone.getServiceState().getState()); 232 233 // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type 234 // TP-RD (bit 2) is 1 for retry 235 // and TP-MR is set to previously failed sms TP-MR 236 if (((0x01 & pdu[0]) == 0x01)) { 237 pdu[0] |= 0x04; // TP-RD 238 pdu[1] = (byte) tracker.mMessageRef; // TP-MR 239 } 240 } 241 Rlog.d(TAG, "sendSms: " 242 +" isIms()="+isIms() 243 +" mRetryCount="+tracker.mRetryCount 244 +" mImsRetry="+tracker.mImsRetry 245 +" mMessageRef="+tracker.mMessageRef 246 +" SS=" +mPhone.getServiceState().getState()); 247 248 // sms over gsm is used: 249 // if sms over IMS is not supported AND 250 // this is not a retry case after sms over IMS failed 251 // indicated by mImsRetry > 0 252 if (0 == tracker.mImsRetry && !isIms()) { 253 if (tracker.mRetryCount > 0) { 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 mCi.sendSMS(IccUtils.bytesToHexString(smsc), 263 IccUtils.bytesToHexString(pdu), reply); 264 } else { 265 mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc), 266 IccUtils.bytesToHexString(pdu), tracker.mImsRetry, 267 tracker.mMessageRef, reply); 268 // increment it here, so in case of SMS_FAIL_RETRY over IMS 269 // next retry will be sent using IMS request again. 270 tracker.mImsRetry++; 271 } 272 } 273 274 protected UiccCardApplication getUiccCardApplication() { 275 Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId() 276 + " slotId = " + mPhone.getPhoneId()); 277 return mUiccController.getUiccCardApplication(mPhone.getPhoneId(), 278 UiccController.APP_FAM_3GPP); 279 } 280 281 private void onUpdateIccAvailability() { 282 if (mUiccController == null ) { 283 return; 284 } 285 286 UiccCardApplication newUiccApplication = getUiccCardApplication(); 287 288 UiccCardApplication app = mUiccApplication.get(); 289 if (app != newUiccApplication) { 290 if (app != null) { 291 Rlog.d(TAG, "Removing stale icc objects."); 292 if (mIccRecords.get() != null) { 293 mIccRecords.get().unregisterForNewSms(this); 294 } 295 mIccRecords.set(null); 296 mUiccApplication.set(null); 297 } 298 if (newUiccApplication != null) { 299 Rlog.d(TAG, "New Uicc application found"); 300 mUiccApplication.set(newUiccApplication); 301 mIccRecords.set(newUiccApplication.getIccRecords()); 302 if (mIccRecords.get() != null) { 303 mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null); 304 } 305 } 306 } 307 } 308} 309