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