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.gsm; 18 19 import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE; 20 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.os.AsyncResult; 23 import android.os.Message; 24 import android.telephony.ServiceState; 25 26 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 27 import com.android.internal.telephony.InboundSmsHandler; 28 import com.android.internal.telephony.Phone; 29 import com.android.internal.telephony.SMSDispatcher; 30 import com.android.internal.telephony.SmsConstants; 31 import com.android.internal.telephony.SmsDispatchersController; 32 import com.android.internal.telephony.SmsHeader; 33 import com.android.internal.telephony.SmsMessageBase; 34 import com.android.internal.telephony.uicc.IccRecords; 35 import com.android.internal.telephony.uicc.IccUtils; 36 import com.android.internal.telephony.uicc.UiccCardApplication; 37 import com.android.internal.telephony.uicc.UiccController; 38 import com.android.internal.telephony.util.SMSDispatcherUtil; 39 import com.android.telephony.Rlog; 40 41 import java.util.HashMap; 42 import java.util.concurrent.atomic.AtomicReference; 43 44 public final class GsmSMSDispatcher extends SMSDispatcher { 45 private static final String TAG = "GsmSMSDispatcher"; 46 protected UiccController mUiccController = null; 47 private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>(); 48 private AtomicReference<UiccCardApplication> mUiccApplication = 49 new AtomicReference<UiccCardApplication>(); 50 @UnsupportedAppUsage 51 private GsmInboundSmsHandler mGsmInboundSmsHandler; 52 53 /** Status report received */ 54 private static final int EVENT_NEW_SMS_STATUS_REPORT = 100; 55 GsmSMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController, GsmInboundSmsHandler gsmInboundSmsHandler)56 public GsmSMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController, 57 GsmInboundSmsHandler gsmInboundSmsHandler) { 58 super(phone, smsDispatchersController); 59 mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null); 60 mGsmInboundSmsHandler = gsmInboundSmsHandler; 61 mUiccController = UiccController.getInstance(); 62 mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null); 63 Rlog.d(TAG, "GsmSMSDispatcher created"); 64 } 65 66 @Override dispose()67 public void dispose() { 68 super.dispose(); 69 mCi.unSetOnSmsStatus(this); 70 mUiccController.unregisterForIccChanged(this); 71 } 72 73 @UnsupportedAppUsage 74 @Override getFormat()75 protected String getFormat() { 76 return SmsConstants.FORMAT_3GPP; 77 } 78 79 /** 80 * Handles 3GPP format-specific events coming from the phone stack. 81 * Other events are handled by {@link SMSDispatcher#handleMessage}. 82 * 83 * @param msg the message to handle 84 */ 85 @Override handleMessage(Message msg)86 public void handleMessage(Message msg) { 87 switch (msg.what) { 88 case EVENT_NEW_SMS_STATUS_REPORT: 89 handleStatusReport((AsyncResult) msg.obj); 90 break; 91 92 case EVENT_NEW_ICC_SMS: 93 // pass to InboundSmsHandler to process 94 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj); 95 break; 96 97 case EVENT_ICC_CHANGED: 98 onUpdateIccAvailability(); 99 break; 100 101 default: 102 super.handleMessage(msg); 103 } 104 } 105 106 @Override shouldBlockSmsForEcbm()107 protected boolean shouldBlockSmsForEcbm() { 108 // There is no such thing as ECBM for GSM. This only applies to CDMA. 109 return false; 110 } 111 112 @Override getSubmitPdu(String scAddr, String destAddr, String message, boolean statusReportRequested, SmsHeader smsHeader, int priority, int validityPeriod)113 protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, 114 String message, boolean statusReportRequested, SmsHeader smsHeader, int priority, 115 int validityPeriod) { 116 return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested, 117 validityPeriod); 118 } 119 120 @Override getSubmitPdu(String scAddr, String destAddr, int destPort, byte[] message, boolean statusReportRequested)121 protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr, 122 int destPort, byte[] message, boolean statusReportRequested) { 123 return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, destPort, message, 124 statusReportRequested); 125 } 126 127 @Override calculateLength(CharSequence messageBody, boolean use7bitOnly)128 protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) { 129 return SMSDispatcherUtil.calculateLengthGsm(messageBody, use7bitOnly); 130 } 131 132 /** 133 * Called when a status report is received. This should correspond to a previously successful 134 * SEND. 135 * 136 * @param ar AsyncResult passed into the message handler. ar.result should be a byte array for 137 * the status report PDU. 138 */ handleStatusReport(AsyncResult ar)139 private void handleStatusReport(AsyncResult ar) { 140 byte[] pdu = (byte[]) ar.result; 141 mSmsDispatchersController.handleSmsStatusReport(SmsConstants.FORMAT_3GPP, pdu); 142 mCi.acknowledgeLastIncomingGsmSms(true, 0 /* cause */, null); 143 } 144 145 /** {@inheritDoc} */ 146 @UnsupportedAppUsage 147 @Override sendSms(SmsTracker tracker)148 protected void sendSms(SmsTracker tracker) { 149 int ss = mPhone.getServiceState().getState(); 150 151 Rlog.d(TAG, "sendSms: " 152 + " isIms()=" + isIms() 153 + " mRetryCount=" + tracker.mRetryCount 154 + " mImsRetry=" + tracker.mImsRetry 155 + " mMessageRef=" + tracker.mMessageRef 156 + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms 157 + " SS=" + ss); 158 159 // if sms over IMS is not supported on data and voice is not available... 160 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 161 //In 5G case only Data Rat is reported. 162 if(mPhone.getServiceState().getRilDataRadioTechnology() 163 != ServiceState.RIL_RADIO_TECHNOLOGY_NR) { 164 tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE); 165 return; 166 } 167 } 168 169 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 170 HashMap<String, Object> map = tracker.getData(); 171 byte pdu[] = (byte[]) map.get("pdu"); 172 byte smsc[] = (byte[]) map.get("smsc"); 173 if (tracker.mRetryCount > 0) { 174 // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type 175 // TP-RD (bit 2) is 1 for retry 176 // and TP-MR is set to previously failed sms TP-MR 177 if (((0x01 & pdu[0]) == 0x01)) { 178 pdu[0] |= 0x04; // TP-RD 179 pdu[1] = (byte) tracker.mMessageRef; // TP-MR 180 } 181 } 182 183 // sms over gsm is used: 184 // if sms over IMS is not supported AND 185 // this is not a retry case after sms over IMS failed 186 // indicated by mImsRetry > 0 OR 187 // this tracker uses ImsSmsDispatcher to handle SMS over IMS. This dispatcher has received 188 // this message because the ImsSmsDispatcher has indicated that the message needs to 189 // fall back to sending over CS. 190 if (0 == tracker.mImsRetry && !isIms() || tracker.mUsesImsServiceForIms) { 191 if (tracker.mRetryCount == 0 && tracker.mExpectMore) { 192 mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc), 193 IccUtils.bytesToHexString(pdu), reply); 194 } else { 195 mCi.sendSMS(IccUtils.bytesToHexString(smsc), 196 IccUtils.bytesToHexString(pdu), reply); 197 } 198 } else { 199 mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc), 200 IccUtils.bytesToHexString(pdu), tracker.mImsRetry, 201 tracker.mMessageRef, reply); 202 // increment it here, so in case of SMS_FAIL_RETRY over IMS 203 // next retry will be sent using IMS request again. 204 tracker.mImsRetry++; 205 } 206 } 207 getUiccCardApplication()208 protected UiccCardApplication getUiccCardApplication() { 209 Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId() 210 + " slotId = " + mPhone.getPhoneId()); 211 return mUiccController.getUiccCardApplication(mPhone.getPhoneId(), 212 UiccController.APP_FAM_3GPP); 213 } 214 onUpdateIccAvailability()215 private void onUpdateIccAvailability() { 216 if (mUiccController == null ) { 217 return; 218 } 219 220 UiccCardApplication newUiccApplication = getUiccCardApplication(); 221 222 UiccCardApplication app = mUiccApplication.get(); 223 if (app != newUiccApplication) { 224 if (app != null) { 225 Rlog.d(TAG, "Removing stale icc objects."); 226 if (mIccRecords.get() != null) { 227 mIccRecords.get().unregisterForNewSms(this); 228 } 229 mIccRecords.set(null); 230 mUiccApplication.set(null); 231 } 232 if (newUiccApplication != null) { 233 Rlog.d(TAG, "New Uicc application found"); 234 mUiccApplication.set(newUiccApplication); 235 mIccRecords.set(newUiccApplication.getIccRecords()); 236 if (mIccRecords.get() != null) { 237 mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null); 238 } 239 } 240 } 241 } 242 } 243