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