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