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