1 /* 2 * Copyright (C) 2008 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.cdma; 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.net.Uri; 24 import android.os.Message; 25 import android.os.SystemProperties; 26 import android.provider.Telephony.Sms; 27 import android.telephony.Rlog; 28 import android.telephony.ServiceState; 29 import android.telephony.SmsManager; 30 import android.telephony.TelephonyManager; 31 32 import com.android.internal.telephony.GsmAlphabet; 33 import com.android.internal.telephony.GsmCdmaPhone; 34 import com.android.internal.telephony.ImsSMSDispatcher; 35 import com.android.internal.telephony.Phone; 36 import com.android.internal.telephony.PhoneConstants; 37 import com.android.internal.telephony.SMSDispatcher; 38 import com.android.internal.telephony.SmsConstants; 39 import com.android.internal.telephony.SmsHeader; 40 import com.android.internal.telephony.SmsUsageMonitor; 41 import com.android.internal.telephony.TelephonyProperties; 42 import com.android.internal.telephony.cdma.sms.UserData; 43 44 import java.util.HashMap; 45 import java.util.concurrent.atomic.AtomicBoolean; 46 import java.util.concurrent.atomic.AtomicInteger; 47 48 public class CdmaSMSDispatcher extends SMSDispatcher { 49 private static final String TAG = "CdmaSMSDispatcher"; 50 private static final boolean VDBG = false; 51 CdmaSMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor, ImsSMSDispatcher imsSMSDispatcher)52 public CdmaSMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor, 53 ImsSMSDispatcher imsSMSDispatcher) { 54 super(phone, usageMonitor, imsSMSDispatcher); 55 Rlog.d(TAG, "CdmaSMSDispatcher created"); 56 } 57 58 @Override getFormat()59 public String getFormat() { 60 return SmsConstants.FORMAT_3GPP2; 61 } 62 63 /** 64 * Send the SMS status report to the dispatcher thread to process. 65 * @param sms the CDMA SMS message containing the status report 66 */ sendStatusReportMessage(SmsMessage sms)67 public void sendStatusReportMessage(SmsMessage sms) { 68 if (VDBG) Rlog.d(TAG, "sending EVENT_HANDLE_STATUS_REPORT message"); 69 sendMessage(obtainMessage(EVENT_HANDLE_STATUS_REPORT, sms)); 70 } 71 72 @Override handleStatusReport(Object o)73 protected void handleStatusReport(Object o) { 74 if (o instanceof SmsMessage) { 75 if (VDBG) Rlog.d(TAG, "calling handleCdmaStatusReport()"); 76 handleCdmaStatusReport((SmsMessage) o); 77 } else { 78 Rlog.e(TAG, "handleStatusReport() called for object type " + o.getClass().getName()); 79 } 80 } 81 82 /** 83 * Called from parent class to handle status report from {@code CdmaInboundSmsHandler}. 84 * @param sms the CDMA SMS message to process 85 */ handleCdmaStatusReport(SmsMessage sms)86 private void handleCdmaStatusReport(SmsMessage sms) { 87 for (int i = 0, count = deliveryPendingList.size(); i < count; i++) { 88 SmsTracker tracker = deliveryPendingList.get(i); 89 if (tracker.mMessageRef == sms.mMessageRef) { 90 // Found it. Remove from list and broadcast. 91 deliveryPendingList.remove(i); 92 // Update the message status (COMPLETE) 93 tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE); 94 95 PendingIntent intent = tracker.mDeliveryIntent; 96 Intent fillIn = new Intent(); 97 fillIn.putExtra("pdu", sms.getPdu()); 98 fillIn.putExtra("format", getFormat()); 99 try { 100 intent.send(mContext, Activity.RESULT_OK, fillIn); 101 } catch (CanceledException ex) {} 102 break; // Only expect to see one tracker matching this message. 103 } 104 } 105 } 106 107 /** {@inheritDoc} */ 108 @Override sendData(String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)109 public void sendData(String destAddr, String scAddr, int destPort, 110 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 111 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 112 scAddr, destAddr, destPort, data, (deliveryIntent != null)); 113 if (pdu != null) { 114 HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu); 115 SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), 116 null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/, 117 false /*isText*/, true /*persistMessage*/); 118 119 String carrierPackage = getCarrierAppPackageName(); 120 if (carrierPackage != null) { 121 Rlog.d(TAG, "Found carrier package."); 122 DataSmsSender smsSender = new DataSmsSender(tracker); 123 smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender)); 124 } else { 125 Rlog.v(TAG, "No carrier package."); 126 sendSubmitPdu(tracker); 127 } 128 } else { 129 Rlog.e(TAG, "CdmaSMSDispatcher.sendData(): getSubmitPdu() returned null"); 130 if (sentIntent != null) { 131 try { 132 sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); 133 } catch (CanceledException ex) { 134 Rlog.e(TAG, "Intent has been canceled!"); 135 } 136 } 137 } 138 } 139 140 /** {@inheritDoc} */ 141 @Override sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage)142 public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, 143 PendingIntent deliveryIntent, Uri messageUri, String callingPkg, 144 boolean persistMessage) { 145 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 146 scAddr, destAddr, text, (deliveryIntent != null), null); 147 if (pdu != null) { 148 HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); 149 SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), 150 messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage); 151 152 String carrierPackage = getCarrierAppPackageName(); 153 if (carrierPackage != null) { 154 Rlog.d(TAG, "Found carrier package."); 155 TextSmsSender smsSender = new TextSmsSender(tracker); 156 smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender)); 157 } else { 158 Rlog.v(TAG, "No carrier package."); 159 sendSubmitPdu(tracker); 160 } 161 } else { 162 Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null"); 163 if (sentIntent != null) { 164 try { 165 sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); 166 } catch (CanceledException ex) { 167 Rlog.e(TAG, "Intent has been canceled!"); 168 } 169 } 170 } 171 } 172 173 /** {@inheritDoc} */ 174 @Override injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent)175 protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { 176 throw new IllegalStateException("This method must be called only on ImsSMSDispatcher"); 177 } 178 179 /** {@inheritDoc} */ 180 @Override calculateLength(CharSequence messageBody, boolean use7bitOnly)181 protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody, 182 boolean use7bitOnly) { 183 return SmsMessage.calculateLength(messageBody, use7bitOnly, false); 184 } 185 186 /** {@inheritDoc} */ 187 @Override getNewSubmitPduTracker(String destinationAddress, String scAddress, String message, SmsHeader smsHeader, int encoding, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, String fullMessageText)188 protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress, 189 String message, SmsHeader smsHeader, int encoding, 190 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, 191 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, 192 String fullMessageText) { 193 UserData uData = new UserData(); 194 uData.payloadStr = message; 195 uData.userDataHeader = smsHeader; 196 if (encoding == SmsConstants.ENCODING_7BIT) { 197 uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; 198 } else { // assume UTF-16 199 uData.msgEncoding = UserData.ENCODING_UNICODE_16; 200 } 201 uData.msgEncodingSet = true; 202 203 /* By setting the statusReportRequested bit only for the 204 * last message fragment, this will result in only one 205 * callback to the sender when that last fragment delivery 206 * has been acknowledged. */ 207 SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress, 208 uData, (deliveryIntent != null) && lastPart); 209 210 HashMap map = getSmsTrackerMap(destinationAddress, scAddress, 211 message, submitPdu); 212 return getSmsTracker(map, sentIntent, deliveryIntent, 213 getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader, 214 false /*isExpextMore*/, fullMessageText, true /*isText*/, 215 true /*persistMessage*/); 216 } 217 218 @Override sendSubmitPdu(SmsTracker tracker)219 protected void sendSubmitPdu(SmsTracker tracker) { 220 if (mPhone.isInEcm()) { 221 if (VDBG) { 222 Rlog.d(TAG, "Block SMS in Emergency Callback mode"); 223 } 224 tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/); 225 return; 226 } 227 sendRawPdu(tracker); 228 } 229 230 /** {@inheritDoc} */ 231 @Override sendSms(SmsTracker tracker)232 public void sendSms(SmsTracker tracker) { 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 sendSmsByPstn(tracker); 241 } 242 243 /** {@inheritDoc} */ 244 @Override sendSmsByPstn(SmsTracker tracker)245 protected void sendSmsByPstn(SmsTracker tracker) { 246 int ss = mPhone.getServiceState().getState(); 247 // if sms over IMS is not supported on data and voice is not available... 248 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 249 tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/); 250 return; 251 } 252 253 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 254 byte[] pdu = (byte[]) tracker.getData().get("pdu"); 255 256 int currentDataNetwork = mPhone.getServiceState().getDataNetworkType(); 257 boolean imsSmsDisabled = (currentDataNetwork == TelephonyManager.NETWORK_TYPE_EHRPD 258 || (ServiceState.isLte(currentDataNetwork) 259 && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed())) 260 && mPhone.getServiceState().getVoiceNetworkType() 261 == TelephonyManager.NETWORK_TYPE_1xRTT 262 && ((GsmCdmaPhone) mPhone).mCT.mState != PhoneConstants.State.IDLE; 263 264 // sms over cdma is used: 265 // if sms over IMS is not supported AND 266 // this is not a retry case after sms over IMS failed 267 // indicated by mImsRetry > 0 268 if (0 == tracker.mImsRetry && !isIms() || imsSmsDisabled) { 269 mCi.sendCdmaSms(pdu, reply); 270 } else { 271 mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply); 272 // increment it here, so in case of SMS_FAIL_RETRY over IMS 273 // next retry will be sent using IMS request again. 274 tracker.mImsRetry++; 275 } 276 } 277 } 278