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; 18 19 import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; 20 21 import java.util.ArrayList; 22 import java.util.HashMap; 23 24 import android.app.PendingIntent; 25 import android.app.PendingIntent.CanceledException; 26 import android.os.AsyncResult; 27 import android.os.Message; 28 import android.provider.Telephony.Sms.Intents; 29 import android.telephony.Rlog; 30 31 import com.android.internal.telephony.cdma.CdmaSMSDispatcher; 32 import com.android.internal.telephony.gsm.GsmSMSDispatcher; 33 import com.android.internal.telephony.InboundSmsHandler; 34 import com.android.internal.telephony.gsm.GsmInboundSmsHandler; 35 import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; 36 import com.android.internal.telephony.SmsBroadcastUndelivered; 37 38 public final class ImsSMSDispatcher extends SMSDispatcher { 39 private static final String TAG = "RIL_ImsSms"; 40 41 private SMSDispatcher mCdmaDispatcher; 42 private SMSDispatcher mGsmDispatcher; 43 44 private GsmInboundSmsHandler mGsmInboundSmsHandler; 45 private CdmaInboundSmsHandler mCdmaInboundSmsHandler; 46 47 48 /** true if IMS is registered and sms is supported, false otherwise.*/ 49 private boolean mIms = false; 50 private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN; 51 52 public ImsSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor, 53 SmsUsageMonitor usageMonitor) { 54 super(phone, usageMonitor, null); 55 Rlog.d(TAG, "ImsSMSDispatcher created"); 56 57 // Create dispatchers, inbound SMS handlers and 58 // broadcast undelivered messages in raw table. 59 mCdmaDispatcher = new CdmaSMSDispatcher(phone, usageMonitor, this); 60 mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), 61 storageMonitor, phone); 62 mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), 63 storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher); 64 mGsmDispatcher = new GsmSMSDispatcher(phone, usageMonitor, this, mGsmInboundSmsHandler); 65 Thread broadcastThread = new Thread(new SmsBroadcastUndelivered(phone.getContext(), 66 mGsmInboundSmsHandler, mCdmaInboundSmsHandler)); 67 broadcastThread.start(); 68 69 mCi.registerForOn(this, EVENT_RADIO_ON, null); 70 mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null); 71 } 72 73 /* Updates the phone object when there is a change */ 74 @Override 75 protected void updatePhoneObject(PhoneBase phone) { 76 Rlog.d(TAG, "In IMS updatePhoneObject "); 77 super.updatePhoneObject(phone); 78 mCdmaDispatcher.updatePhoneObject(phone); 79 mGsmDispatcher.updatePhoneObject(phone); 80 mGsmInboundSmsHandler.updatePhoneObject(phone); 81 mCdmaInboundSmsHandler.updatePhoneObject(phone); 82 } 83 84 public void dispose() { 85 mCi.unregisterForOn(this); 86 mCi.unregisterForImsNetworkStateChanged(this); 87 mGsmDispatcher.dispose(); 88 mCdmaDispatcher.dispose(); 89 mGsmInboundSmsHandler.dispose(); 90 mCdmaInboundSmsHandler.dispose(); 91 } 92 93 /** 94 * Handles events coming from the phone stack. Overridden from handler. 95 * 96 * @param msg the message to handle 97 */ 98 @Override 99 public void handleMessage(Message msg) { 100 AsyncResult ar; 101 102 switch (msg.what) { 103 case EVENT_RADIO_ON: 104 case EVENT_IMS_STATE_CHANGED: // received unsol 105 mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE)); 106 break; 107 108 case EVENT_IMS_STATE_DONE: 109 ar = (AsyncResult) msg.obj; 110 111 if (ar.exception == null) { 112 updateImsInfo(ar); 113 } else { 114 Rlog.e(TAG, "IMS State query failed with exp " 115 + ar.exception); 116 } 117 break; 118 119 default: 120 super.handleMessage(msg); 121 } 122 } 123 124 private void setImsSmsFormat(int format) { 125 // valid format? 126 switch (format) { 127 case PhoneConstants.PHONE_TYPE_GSM: 128 mImsSmsFormat = "3gpp"; 129 break; 130 case PhoneConstants.PHONE_TYPE_CDMA: 131 mImsSmsFormat = "3gpp2"; 132 break; 133 default: 134 mImsSmsFormat = "unknown"; 135 break; 136 } 137 } 138 139 private void updateImsInfo(AsyncResult ar) { 140 int[] responseArray = (int[])ar.result; 141 142 mIms = false; 143 if (responseArray[0] == 1) { // IMS is registered 144 Rlog.d(TAG, "IMS is registered!"); 145 mIms = true; 146 } else { 147 Rlog.d(TAG, "IMS is NOT registered!"); 148 } 149 150 setImsSmsFormat(responseArray[1]); 151 152 if (("unknown".equals(mImsSmsFormat))) { 153 Rlog.e(TAG, "IMS format was unknown!"); 154 // failed to retrieve valid IMS SMS format info, set IMS to unregistered 155 mIms = false; 156 } 157 } 158 159 @Override 160 protected void sendData(String destAddr, String scAddr, int destPort, 161 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 162 if (isCdmaMo()) { 163 mCdmaDispatcher.sendData(destAddr, scAddr, destPort, 164 data, sentIntent, deliveryIntent); 165 } else { 166 mGsmDispatcher.sendData(destAddr, scAddr, destPort, 167 data, sentIntent, deliveryIntent); 168 } 169 } 170 171 @Override 172 protected void sendMultipartText(String destAddr, String scAddr, 173 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 174 ArrayList<PendingIntent> deliveryIntents) { 175 if (isCdmaMo()) { 176 mCdmaDispatcher.sendMultipartText(destAddr, scAddr, 177 parts, sentIntents, deliveryIntents); 178 } else { 179 mGsmDispatcher.sendMultipartText(destAddr, scAddr, 180 parts, sentIntents, deliveryIntents); 181 } 182 } 183 184 @Override 185 protected void sendSms(SmsTracker tracker) { 186 // sendSms is a helper function to other send functions, sendText/Data... 187 // it is not part of ISms.stub 188 Rlog.e(TAG, "sendSms should never be called from here!"); 189 } 190 191 @Override 192 protected void sendText(String destAddr, String scAddr, String text, 193 PendingIntent sentIntent, PendingIntent deliveryIntent) { 194 Rlog.d(TAG, "sendText"); 195 if (isCdmaMo()) { 196 mCdmaDispatcher.sendText(destAddr, scAddr, 197 text, sentIntent, deliveryIntent); 198 } else { 199 mGsmDispatcher.sendText(destAddr, scAddr, 200 text, sentIntent, deliveryIntent); 201 } 202 } 203 204 @Override 205 public void sendRetrySms(SmsTracker tracker) { 206 String oldFormat = tracker.mFormat; 207 208 // newFormat will be based on voice technology 209 String newFormat = 210 (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) ? 211 mCdmaDispatcher.getFormat() : 212 mGsmDispatcher.getFormat(); 213 214 // was previously sent sms format match with voice tech? 215 if (oldFormat.equals(newFormat)) { 216 if (isCdmaFormat(newFormat)) { 217 Rlog.d(TAG, "old format matched new format (cdma)"); 218 mCdmaDispatcher.sendSms(tracker); 219 return; 220 } else { 221 Rlog.d(TAG, "old format matched new format (gsm)"); 222 mGsmDispatcher.sendSms(tracker); 223 return; 224 } 225 } 226 227 // format didn't match, need to re-encode. 228 HashMap map = tracker.mData; 229 230 // to re-encode, fields needed are: scAddr, destAddr, and 231 // text if originally sent as sendText or 232 // data and destPort if originally sent as sendData. 233 if (!( map.containsKey("scAddr") && map.containsKey("destAddr") && 234 ( map.containsKey("text") || 235 (map.containsKey("data") && map.containsKey("destPort"))))) { 236 // should never come here... 237 Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!"); 238 if (tracker.mSentIntent != null) { 239 int error = RESULT_ERROR_GENERIC_FAILURE; 240 // Done retrying; return an error to the app. 241 try { 242 tracker.mSentIntent.send(mContext, error, null); 243 } catch (CanceledException ex) {} 244 } 245 return; 246 } 247 String scAddr = (String)map.get("scAddr"); 248 String destAddr = (String)map.get("destAddr"); 249 250 SmsMessageBase.SubmitPduBase pdu = null; 251 // figure out from tracker if this was sendText/Data 252 if (map.containsKey("text")) { 253 Rlog.d(TAG, "sms failed was text"); 254 String text = (String)map.get("text"); 255 256 if (isCdmaFormat(newFormat)) { 257 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); 258 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 259 scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); 260 } else { 261 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); 262 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( 263 scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); 264 } 265 } else if (map.containsKey("data")) { 266 Rlog.d(TAG, "sms failed was data"); 267 byte[] data = (byte[])map.get("data"); 268 Integer destPort = (Integer)map.get("destPort"); 269 270 if (isCdmaFormat(newFormat)) { 271 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); 272 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 273 scAddr, destAddr, destPort.intValue(), data, 274 (tracker.mDeliveryIntent != null)); 275 } else { 276 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); 277 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( 278 scAddr, destAddr, destPort.intValue(), data, 279 (tracker.mDeliveryIntent != null)); 280 } 281 } 282 283 // replace old smsc and pdu with newly encoded ones 284 map.put("smsc", pdu.encodedScAddress); 285 map.put("pdu", pdu.encodedMessage); 286 287 SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ? 288 mCdmaDispatcher : mGsmDispatcher; 289 290 tracker.mFormat = dispatcher.getFormat(); 291 dispatcher.sendSms(tracker); 292 } 293 294 @Override 295 protected String getFormat() { 296 // this function should be defined in Gsm/CdmaDispatcher. 297 Rlog.e(TAG, "getFormat should never be called from here!"); 298 return "unknown"; 299 } 300 301 @Override 302 protected GsmAlphabet.TextEncodingDetails calculateLength( 303 CharSequence messageBody, boolean use7bitOnly) { 304 Rlog.e(TAG, "Error! Not implemented for IMS."); 305 return null; 306 } 307 308 @Override 309 protected void sendNewSubmitPdu(String destinationAddress, String scAddress, String message, 310 SmsHeader smsHeader, int format, PendingIntent sentIntent, 311 PendingIntent deliveryIntent, boolean lastPart) { 312 Rlog.e(TAG, "Error! Not implemented for IMS."); 313 } 314 315 @Override 316 public boolean isIms() { 317 return mIms; 318 } 319 320 @Override 321 public String getImsSmsFormat() { 322 return mImsSmsFormat; 323 } 324 325 /** 326 * Determines whether or not to use CDMA format for MO SMS. 327 * If SMS over IMS is supported, then format is based on IMS SMS format, 328 * otherwise format is based on current phone type. 329 * 330 * @return true if Cdma format should be used for MO SMS, false otherwise. 331 */ 332 private boolean isCdmaMo() { 333 if (!isIms()) { 334 // IMS is not registered, use Voice technology to determine SMS format. 335 return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()); 336 } 337 // IMS is registered with SMS support 338 return isCdmaFormat(mImsSmsFormat); 339 } 340 341 /** 342 * Determines whether or not format given is CDMA format. 343 * 344 * @param format 345 * @return true if format given is CDMA format, false otherwise. 346 */ 347 private boolean isCdmaFormat(String format) { 348 return (mCdmaDispatcher.getFormat().equals(format)); 349 } 350 } 351