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