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