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