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;
18 
19 import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
20 import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
21 import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
22 import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_NONE;
23 import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_TEMPORARY;
24 
25 import android.app.Activity;
26 import android.app.PendingIntent;
27 import android.app.PendingIntent.CanceledException;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.net.Uri;
33 import android.os.AsyncResult;
34 import android.os.Handler;
35 import android.os.Message;
36 import android.os.UserManager;
37 import android.provider.Telephony.Sms;
38 import android.provider.Telephony.Sms.Intents;
39 import android.telephony.ServiceState;
40 import android.telephony.SmsManager;
41 import android.telephony.SmsMessage;
42 import android.util.Pair;
43 
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
46 import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
47 import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
48 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
49 import com.android.telephony.Rlog;
50 
51 import java.io.FileDescriptor;
52 import java.io.PrintWriter;
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.Map.Entry;
56 
57 /**
58  *
59  */
60 public class SmsDispatchersController extends Handler {
61     private static final String TAG = "SmsDispatchersController";
62     private static final boolean VDBG = false; // STOPSHIP if true
63 
64     /** Radio is ON */
65     private static final int EVENT_RADIO_ON = 11;
66 
67     /** IMS registration/SMS format changed */
68     private static final int EVENT_IMS_STATE_CHANGED = 12;
69 
70     /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
71     private static final int EVENT_IMS_STATE_DONE = 13;
72 
73     /** Service state changed */
74     private static final int EVENT_SERVICE_STATE_CHANGED = 14;
75 
76     /** Purge old message segments */
77     private static final int EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY = 15;
78 
79     /** User unlocked the device */
80     private static final int EVENT_USER_UNLOCKED = 16;
81 
82     /** InboundSmsHandler exited WaitingState */
83     protected static final int EVENT_SMS_HANDLER_EXITING_WAITING_STATE = 17;
84 
85     /** Delete any partial message segments after being IN_SERVICE for 1 day. */
86     private static final long PARTIAL_SEGMENT_WAIT_DURATION = (long) (60 * 60 * 1000) * 24;
87     /** Constant for invalid time */
88     private static final long INVALID_TIME = -1;
89     /** Time at which last IN_SERVICE event was received */
90     private long mLastInServiceTime = INVALID_TIME;
91     /** Current IN_SERVICE duration */
92     private long mCurrentWaitElapsedDuration = 0;
93     /** Time at which the current PARTIAL_SEGMENT_WAIT_DURATION timer was started */
94     private long mCurrentWaitStartTime = INVALID_TIME;
95 
96     private SMSDispatcher mCdmaDispatcher;
97     private SMSDispatcher mGsmDispatcher;
98     private ImsSmsDispatcher mImsSmsDispatcher;
99 
100     private GsmInboundSmsHandler mGsmInboundSmsHandler;
101     private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
102 
103     private Phone mPhone;
104     /** Outgoing message counter. Shared by all dispatchers. */
105     private final SmsUsageMonitor mUsageMonitor;
106     private final CommandsInterface mCi;
107     private final Context mContext;
108 
109     /** true if IMS is registered and sms is supported, false otherwise.*/
110     private boolean mIms = false;
111     private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
112 
SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor, SmsUsageMonitor usageMonitor)113     public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor,
114             SmsUsageMonitor usageMonitor) {
115         Rlog.d(TAG, "SmsDispatchersController created");
116 
117         mContext = phone.getContext();
118         mUsageMonitor = usageMonitor;
119         mCi = phone.mCi;
120         mPhone = phone;
121 
122         // Create dispatchers, inbound SMS handlers and
123         // broadcast undelivered messages in raw table.
124         mImsSmsDispatcher = new ImsSmsDispatcher(phone, this);
125         mCdmaDispatcher = new CdmaSMSDispatcher(phone, this);
126         mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
127                 storageMonitor, phone);
128         mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
129                 storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
130         mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler);
131         SmsBroadcastUndelivered.initialize(phone.getContext(),
132                 mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
133         InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext());
134 
135         mCi.registerForOn(this, EVENT_RADIO_ON, null);
136         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
137 
138         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
139         if (userManager.isUserUnlocked()) {
140             if (VDBG) {
141                 logd("SmsDispatchersController: user unlocked; registering for service"
142                         + "state changed");
143             }
144             mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
145             resetPartialSegmentWaitTimer();
146         } else {
147             if (VDBG) {
148                 logd("SmsDispatchersController: user locked; waiting for USER_UNLOCKED");
149             }
150             IntentFilter userFilter = new IntentFilter();
151             userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
152             mContext.registerReceiver(mBroadcastReceiver, userFilter);
153         }
154     }
155 
156     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
157         @Override
158         public void onReceive(final Context context, Intent intent) {
159             Rlog.d(TAG, "Received broadcast " + intent.getAction());
160             if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
161                 sendMessage(obtainMessage(EVENT_USER_UNLOCKED));
162             }
163         }
164     };
165 
dispose()166     public void dispose() {
167         mCi.unregisterForOn(this);
168         mCi.unregisterForImsNetworkStateChanged(this);
169         mPhone.unregisterForServiceStateChanged(this);
170         mGsmDispatcher.dispose();
171         mCdmaDispatcher.dispose();
172         mGsmInboundSmsHandler.dispose();
173         mCdmaInboundSmsHandler.dispose();
174     }
175 
176     /**
177      * Handles events coming from the phone stack. Overridden from handler.
178      *
179      * @param msg the message to handle
180      */
181     @Override
handleMessage(Message msg)182     public void handleMessage(Message msg) {
183         AsyncResult ar;
184 
185         switch (msg.what) {
186             case EVENT_RADIO_ON:
187             case EVENT_IMS_STATE_CHANGED: // received unsol
188                 mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE));
189                 break;
190 
191             case EVENT_IMS_STATE_DONE:
192                 ar = (AsyncResult) msg.obj;
193 
194                 if (ar.exception == null) {
195                     updateImsInfo(ar);
196                 } else {
197                     Rlog.e(TAG, "IMS State query failed with exp "
198                             + ar.exception);
199                 }
200                 break;
201 
202             case EVENT_SERVICE_STATE_CHANGED:
203             case EVENT_SMS_HANDLER_EXITING_WAITING_STATE:
204                 reevaluateTimerStatus();
205                 break;
206 
207             case EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY:
208                 handlePartialSegmentTimerExpiry((Long) msg.obj);
209                 break;
210 
211             case EVENT_USER_UNLOCKED:
212                 if (VDBG) {
213                     logd("handleMessage: EVENT_USER_UNLOCKED");
214                 }
215                 mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
216                 resetPartialSegmentWaitTimer();
217                 break;
218 
219             default:
220                 if (isCdmaMo()) {
221                     mCdmaDispatcher.handleMessage(msg);
222                 } else {
223                     mGsmDispatcher.handleMessage(msg);
224                 }
225         }
226     }
227 
reevaluateTimerStatus()228     private void reevaluateTimerStatus() {
229         long currentTime = System.currentTimeMillis();
230 
231         // Remove unhandled timer expiry message. A new message will be posted if needed.
232         removeMessages(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY);
233         // Update timer duration elapsed time (add time since last IN_SERVICE to now).
234         // This is needed for IN_SERVICE as well as OUT_OF_SERVICE because same events can be
235         // received back to back
236         if (mLastInServiceTime != INVALID_TIME) {
237             mCurrentWaitElapsedDuration += (currentTime - mLastInServiceTime);
238         }
239 
240         if (VDBG) {
241             logd("reevaluateTimerStatus: currentTime: " + currentTime
242                     + " mCurrentWaitElapsedDuration: " + mCurrentWaitElapsedDuration);
243         }
244 
245         if (mCurrentWaitElapsedDuration > PARTIAL_SEGMENT_WAIT_DURATION) {
246             // handle this event as timer expiry
247             handlePartialSegmentTimerExpiry(mCurrentWaitStartTime);
248         } else {
249             if (isInService()) {
250                 handleInService(currentTime);
251             } else {
252                 handleOutOfService(currentTime);
253             }
254         }
255     }
256 
handleInService(long currentTime)257     private void handleInService(long currentTime) {
258         if (VDBG) {
259             logd("handleInService: timer expiry in "
260                     + (PARTIAL_SEGMENT_WAIT_DURATION - mCurrentWaitElapsedDuration) + "ms");
261         }
262 
263         // initialize mCurrentWaitStartTime if needed
264         if (mCurrentWaitStartTime == INVALID_TIME) mCurrentWaitStartTime = currentTime;
265 
266         // Post a message for timer expiry time. mCurrentWaitElapsedDuration is the duration already
267         // elapsed from the timer.
268         sendMessageDelayed(
269                 obtainMessage(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY, mCurrentWaitStartTime),
270                 PARTIAL_SEGMENT_WAIT_DURATION - mCurrentWaitElapsedDuration);
271 
272         // update mLastInServiceTime as the current time
273         mLastInServiceTime = currentTime;
274     }
275 
handleOutOfService(long currentTime)276     private void handleOutOfService(long currentTime) {
277         if (VDBG) {
278             logd("handleOutOfService: currentTime: " + currentTime
279                     + " mCurrentWaitElapsedDuration: " + mCurrentWaitElapsedDuration);
280         }
281 
282         // mLastInServiceTime is not relevant now since state is OUT_OF_SERVICE; set it to INVALID
283         mLastInServiceTime = INVALID_TIME;
284     }
285 
handlePartialSegmentTimerExpiry(long waitTimerStart)286     private void handlePartialSegmentTimerExpiry(long waitTimerStart) {
287         if (mGsmInboundSmsHandler.getCurrentState().getName().equals("WaitingState")
288                 || mCdmaInboundSmsHandler.getCurrentState().getName().equals("WaitingState")) {
289             logd("handlePartialSegmentTimerExpiry: ignoring timer expiry as InboundSmsHandler is"
290                     + " in WaitingState");
291             return;
292         }
293 
294         if (VDBG) {
295             logd("handlePartialSegmentTimerExpiry: calling scanRawTable()");
296         }
297         // Timer expired. This indicates that device has been in service for
298         // PARTIAL_SEGMENT_WAIT_DURATION since waitTimerStart. Delete orphaned message segments
299         // older than waitTimerStart.
300         SmsBroadcastUndelivered.scanRawTable(mContext, mCdmaInboundSmsHandler,
301                 mGsmInboundSmsHandler, waitTimerStart);
302         if (VDBG) {
303             logd("handlePartialSegmentTimerExpiry: scanRawTable() done");
304         }
305 
306         resetPartialSegmentWaitTimer();
307     }
308 
resetPartialSegmentWaitTimer()309     private void resetPartialSegmentWaitTimer() {
310         long currentTime = System.currentTimeMillis();
311 
312         removeMessages(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY);
313         if (isInService()) {
314             if (VDBG) {
315                 logd("resetPartialSegmentWaitTimer: currentTime: " + currentTime
316                         + " IN_SERVICE");
317             }
318             mCurrentWaitStartTime = currentTime;
319             mLastInServiceTime = currentTime;
320             sendMessageDelayed(
321                     obtainMessage(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY, mCurrentWaitStartTime),
322                     PARTIAL_SEGMENT_WAIT_DURATION);
323         } else {
324             if (VDBG) {
325                 logd("resetPartialSegmentWaitTimer: currentTime: " + currentTime
326                         + " not IN_SERVICE");
327             }
328             mCurrentWaitStartTime = INVALID_TIME;
329             mLastInServiceTime = INVALID_TIME;
330         }
331 
332         mCurrentWaitElapsedDuration = 0;
333     }
334 
isInService()335     private boolean isInService() {
336         ServiceState serviceState = mPhone.getServiceState();
337         return serviceState != null && serviceState.getState() == ServiceState.STATE_IN_SERVICE;
338     }
339 
setImsSmsFormat(int format)340     private void setImsSmsFormat(int format) {
341         switch (format) {
342             case PhoneConstants.PHONE_TYPE_GSM:
343                 mImsSmsFormat = SmsConstants.FORMAT_3GPP;
344                 break;
345             case PhoneConstants.PHONE_TYPE_CDMA:
346                 mImsSmsFormat = SmsConstants.FORMAT_3GPP2;
347                 break;
348             default:
349                 mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
350                 break;
351         }
352     }
353 
updateImsInfo(AsyncResult ar)354     private void updateImsInfo(AsyncResult ar) {
355         int[] responseArray = (int[]) ar.result;
356         setImsSmsFormat(responseArray[1]);
357         mIms = responseArray[0] == 1 && !SmsConstants.FORMAT_UNKNOWN.equals(mImsSmsFormat);
358         Rlog.d(TAG, "IMS registration state: " + mIms + " format: " + mImsSmsFormat);
359     }
360 
361     /**
362      * Inject an SMS PDU into the android platform only if it is class 1.
363      *
364      * @param pdu is the byte array of pdu to be injected into android telephony layer
365      * @param format is the format of SMS pdu (3gpp or 3gpp2)
366      * @param callback if not NULL this callback is triggered when the message is successfully
367      *                 received by the android telephony layer. This callback is triggered at
368      *                 the same time an SMS received from radio is responded back.
369      */
370     @VisibleForTesting
injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback)371     public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) {
372         // TODO We need to decide whether we should allow injecting GSM(3gpp)
373         // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
374         android.telephony.SmsMessage msg =
375                 android.telephony.SmsMessage.createFromPdu(pdu, format);
376         injectSmsPdu(msg, format, callback, false /* ignoreClass */);
377     }
378 
379     /**
380      * Inject an SMS PDU into the android platform.
381      *
382      * @param msg is the {@link SmsMessage} to be injected into android telephony layer
383      * @param format is the format of SMS pdu (3gpp or 3gpp2)
384      * @param callback if not NULL this callback is triggered when the message is successfully
385      *                 received by the android telephony layer. This callback is triggered at
386      *                 the same time an SMS received from radio is responded back.
387      * @param ignoreClass if set to false, this method will inject class 1 sms only.
388      */
389     @VisibleForTesting
injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback, boolean ignoreClass)390     public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback,
391             boolean ignoreClass) {
392         Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
393         try {
394             if (msg == null) {
395                 Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null");
396                 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
397                 return;
398             }
399 
400             if (!ignoreClass
401                     && msg.getMessageClass() != android.telephony.SmsMessage.MessageClass.CLASS_1) {
402                 Rlog.e(TAG, "injectSmsPdu: not class 1");
403                 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
404                 return;
405             }
406 
407             AsyncResult ar = new AsyncResult(callback, msg, null);
408 
409             if (format.equals(SmsConstants.FORMAT_3GPP)) {
410                 Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
411                         + ", format=" + format + "to mGsmInboundSmsHandler");
412                 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
413             } else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
414                 Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
415                         + ", format=" + format + "to mCdmaInboundSmsHandler");
416                 mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
417             } else {
418                 // Invalid pdu format.
419                 Rlog.e(TAG, "Invalid pdu format: " + format);
420                 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
421             }
422         } catch (Exception e) {
423             Rlog.e(TAG, "injectSmsPdu failed: ", e);
424             callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
425         }
426     }
427 
428     /**
429      * Retry the message along to the radio.
430      *
431      * @param tracker holds the SMS message to send
432      */
sendRetrySms(SMSDispatcher.SmsTracker tracker)433     public void sendRetrySms(SMSDispatcher.SmsTracker tracker) {
434         String oldFormat = tracker.mFormat;
435         boolean retryUsingImsService = false;
436 
437         if (!tracker.mUsesImsServiceForIms && mImsSmsDispatcher.isAvailable()) {
438             // If this tracker has not been handled by ImsSmsDispatcher yet and IMS Service is
439             // available now, retry this failed tracker using IMS Service.
440             retryUsingImsService = true;
441         }
442 
443         // If retryUsingImsService is true, newFormat will be IMS SMS format. Otherwise, newFormat
444         // will be based on voice technology.
445         String newFormat =
446                 retryUsingImsService
447                         ? mImsSmsDispatcher.getFormat()
448                         : (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType())
449                                 ? mCdmaDispatcher.getFormat()
450                                 : mGsmDispatcher.getFormat();
451 
452         Rlog.d(TAG, "old format(" + oldFormat + ") ==> new format (" + newFormat + ")");
453         if (!oldFormat.equals(newFormat)) {
454             // format didn't match, need to re-encode.
455             HashMap map = tracker.getData();
456 
457             // to re-encode, fields needed are: scAddr, destAddr and text if originally sent as
458             // sendText or data and destPort if originally sent as sendData.
459             if (!(map.containsKey("scAddr") && map.containsKey("destAddr")
460                     && (map.containsKey("text")
461                     || (map.containsKey("data") && map.containsKey("destPort"))))) {
462                 // should never come here...
463                 Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
464                 tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
465                 return;
466             }
467             String scAddr = (String) map.get("scAddr");
468             String destAddr = (String) map.get("destAddr");
469 
470             SmsMessageBase.SubmitPduBase pdu = null;
471             // figure out from tracker if this was sendText/Data
472             if (map.containsKey("text")) {
473                 Rlog.d(TAG, "sms failed was text");
474                 String text = (String) map.get("text");
475 
476                 if (isCdmaFormat(newFormat)) {
477                     pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
478                             scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
479                 } else {
480                     pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
481                             scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
482                 }
483             } else if (map.containsKey("data")) {
484                 Rlog.d(TAG, "sms failed was data");
485                 byte[] data = (byte[]) map.get("data");
486                 Integer destPort = (Integer) map.get("destPort");
487 
488                 if (isCdmaFormat(newFormat)) {
489                     pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
490                             scAddr, destAddr, destPort.intValue(), data,
491                             (tracker.mDeliveryIntent != null));
492                 } else {
493                     pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
494                             scAddr, destAddr, destPort.intValue(), data,
495                             (tracker.mDeliveryIntent != null));
496                 }
497             }
498 
499             // replace old smsc and pdu with newly encoded ones
500             map.put("smsc", pdu.encodedScAddress);
501             map.put("pdu", pdu.encodedMessage);
502             tracker.mFormat = newFormat;
503         }
504 
505         SMSDispatcher dispatcher =
506                 retryUsingImsService
507                         ? mImsSmsDispatcher
508                         : (isCdmaFormat(newFormat)) ? mCdmaDispatcher : mGsmDispatcher;
509 
510         dispatcher.sendSms(tracker);
511     }
512 
513     /**
514      * SMS over IMS is supported if IMS is registered and SMS is supported on IMS.
515      *
516      * @return true if SMS over IMS is supported via an IMS Service or mIms is true for the older
517      *         implementation. Otherwise, false.
518      */
isIms()519     public boolean isIms() {
520         return mImsSmsDispatcher.isAvailable() ? true : mIms;
521     }
522 
523     /**
524      * Gets SMS format supported on IMS.
525      *
526      * @return the SMS format from an IMS Service if available. Otherwise, mImsSmsFormat for the
527      *         older implementation.
528      */
getImsSmsFormat()529     public String getImsSmsFormat() {
530         return mImsSmsDispatcher.isAvailable() ? mImsSmsDispatcher.getFormat() : mImsSmsFormat;
531     }
532 
533     /**
534      * Determines whether or not to use CDMA format for MO SMS.
535      * If SMS over IMS is supported, then format is based on IMS SMS format,
536      * otherwise format is based on current phone type.
537      *
538      * @return true if Cdma format should be used for MO SMS, false otherwise.
539      */
isCdmaMo()540     protected boolean isCdmaMo() {
541         if (!isIms()) {
542             // IMS is not registered, use Voice technology to determine SMS format.
543             return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType());
544         }
545         // IMS is registered with SMS support
546         return isCdmaFormat(getImsSmsFormat());
547     }
548 
549     /**
550      * Determines whether or not format given is CDMA format.
551      *
552      * @param format
553      * @return true if format given is CDMA format, false otherwise.
554      */
isCdmaFormat(String format)555     public boolean isCdmaFormat(String format) {
556         return (mCdmaDispatcher.getFormat().equals(format));
557     }
558 
559     /**
560      * Send a data based SMS to a specific application port.
561      *
562      * @param callingPackage the package name of the calling app
563      * @param destAddr the address to send the message to
564      * @param scAddr is the service center address or null to use
565      *  the current default SMSC
566      * @param destPort the port to deliver the message to
567      * @param data the body of the message to send
568      * @param sentIntent if not NULL this <code>PendingIntent</code> is
569      *  broadcast when the message is successfully sent, or failed.
570      *  The result code will be <code>Activity.RESULT_OK<code> for success,
571      *  or one of these errors:<br>
572      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
573      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
574      *  <code>RESULT_ERROR_NULL_PDU</code><br>
575      *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
576      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
577      *  the extra "errorCode" containing a radio technology specific value,
578      *  generally only useful for troubleshooting.<br>
579      *  The per-application based SMS control checks sentIntent. If sentIntent
580      *  is NULL the caller will be checked against all unknown applications,
581      *  which cause smaller number of SMS to be sent in checking period.
582      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
583      *  broadcast when the message is delivered to the recipient.  The
584      *  raw pdu of the status report is in the extended data ("pdu").
585      */
sendData(String callingPackage, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm)586     protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
587             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) {
588         if (mImsSmsDispatcher.isAvailable()) {
589             mImsSmsDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
590                     deliveryIntent, isForVvm);
591         } else if (isCdmaMo()) {
592             mCdmaDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
593                     deliveryIntent, isForVvm);
594         } else {
595             mGsmDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
596                     deliveryIntent, isForVvm);
597         }
598     }
599 
600     /**
601      * Send a text based SMS.
602      *  @param destAddr the address to send the message to
603      * @param scAddr is the service center address or null to use
604      *  the current default SMSC
605      * @param text the body of the message to send
606      * @param sentIntent if not NULL this <code>PendingIntent</code> is
607      *  broadcast when the message is successfully sent, or failed.
608      *  The result code will be <code>Activity.RESULT_OK<code> for success,
609      *  or one of these errors:<br>
610      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
611      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
612      *  <code>RESULT_ERROR_NULL_PDU</code><br>
613      *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
614      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
615      *  the extra "errorCode" containing a radio technology specific value,
616      *  generally only useful for troubleshooting.<br>
617      *  The per-application based SMS control checks sentIntent. If sentIntent
618      *  is NULL the caller will be checked against all unknown applications,
619      *  which cause smaller number of SMS to be sent in checking period.
620      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
621      *  broadcast when the message is delivered to the recipient.  The
622      * @param messageUri optional URI of the message if it is already stored in the system
623      * @param callingPkg the calling package name
624      * @param persistMessage whether to save the sent message into SMS DB for a
625      *   non-default SMS app.
626      * @param priority Priority level of the message
627      *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
628      *  ---------------------------------
629      *  PRIORITY      | Level of Priority
630      *  ---------------------------------
631      *      '00'      |     Normal
632      *      '01'      |     Interactive
633      *      '10'      |     Urgent
634      *      '11'      |     Emergency
635      *  ----------------------------------
636      *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
637      * @param expectMore is a boolean to indicate the sending messages through same link or not.
638      * @param validityPeriod Validity Period of the message in mins.
639      *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
640      *  Validity Period(Minimum) -> 5 mins
641      *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
642      *  Any Other values included Negative considered as Invalid Validity Period of the message.
643      */
sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage, int priority, boolean expectMore, int validityPeriod, boolean isForVvm, long messageId)644     public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
645             PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage,
646             int priority, boolean expectMore, int validityPeriod, boolean isForVvm,
647             long messageId) {
648         if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
649             mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
650                     messageUri, callingPkg, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
651                     false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm,
652                     messageId);
653         } else {
654             if (isCdmaMo()) {
655                 mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
656                         messageUri, callingPkg, persistMessage, priority, expectMore,
657                         validityPeriod, isForVvm, messageId);
658             } else {
659                 mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
660                         messageUri, callingPkg, persistMessage, priority, expectMore,
661                         validityPeriod, isForVvm, messageId);
662             }
663         }
664     }
665 
666     /**
667      * Send a multi-part text based SMS.
668      *  @param destAddr the address to send the message to
669      * @param scAddr is the service center address or null to use
670      *   the current default SMSC
671      * @param parts an <code>ArrayList</code> of strings that, in order,
672      *   comprise the original message
673      * @param sentIntents if not null, an <code>ArrayList</code> of
674      *   <code>PendingIntent</code>s (one for each message part) that is
675      *   broadcast when the corresponding message part has been sent.
676      *   The result code will be <code>Activity.RESULT_OK<code> for success,
677      *   or one of these errors:
678      *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
679      *   <code>RESULT_ERROR_RADIO_OFF</code>
680      *   <code>RESULT_ERROR_NULL_PDU</code>
681      *   <code>RESULT_ERROR_NO_SERVICE</code>.
682      *  The per-application based SMS control checks sentIntent. If sentIntent
683      *  is NULL the caller will be checked against all unknown applications,
684      *  which cause smaller number of SMS to be sent in checking period.
685      * @param deliveryIntents if not null, an <code>ArrayList</code> of
686      *   <code>PendingIntent</code>s (one for each message part) that is
687      *   broadcast when the corresponding message part has been delivered
688      *   to the recipient.  The raw pdu of the status report is in the
689      * @param messageUri optional URI of the message if it is already stored in the system
690      * @param callingPkg the calling package name
691      * @param persistMessage whether to save the sent message into SMS DB for a
692      *   non-default SMS app.
693      * @param priority Priority level of the message
694      *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
695      *  ---------------------------------
696      *  PRIORITY      | Level of Priority
697      *  ---------------------------------
698      *      '00'      |     Normal
699      *      '01'      |     Interactive
700      *      '10'      |     Urgent
701      *      '11'      |     Emergency
702      *  ----------------------------------
703      *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
704      * @param expectMore is a boolean to indicate the sending messages through same link or not.
705      * @param validityPeriod Validity Period of the message in mins.
706      *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
707      *  Validity Period(Minimum) -> 5 mins
708      *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
709      *  Any Other values included Negative considered as Invalid Validity Period of the message.
710      * @param messageId An id that uniquely identifies the message requested to be sent.
711      *                 Used for logging and diagnostics purposes. The id may be 0.
712      *
713      */
sendMultipartText(String destAddr, String scAddr, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg, boolean persistMessage, int priority, boolean expectMore, int validityPeriod, long messageId)714     protected void sendMultipartText(String destAddr, String scAddr,
715             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
716             ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
717             boolean persistMessage, int priority, boolean expectMore, int validityPeriod,
718             long messageId) {
719         if (mImsSmsDispatcher.isAvailable()) {
720             mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
721                     deliveryIntents, messageUri, callingPkg, persistMessage,
722                     SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
723                     false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED,
724                     messageId);
725         } else {
726             if (isCdmaMo()) {
727                 mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
728                         deliveryIntents, messageUri, callingPkg, persistMessage, priority,
729                         expectMore, validityPeriod, messageId);
730             } else {
731                 mGsmDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
732                         deliveryIntents, messageUri, callingPkg, persistMessage, priority,
733                         expectMore, validityPeriod, messageId);
734             }
735         }
736     }
737 
738     /**
739      * Returns the premium SMS permission for the specified package. If the package has never
740      * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN}
741      * will be returned.
742      * @param packageName the name of the package to query permission
743      * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
744      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
745      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
746      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
747      */
getPremiumSmsPermission(String packageName)748     public int getPremiumSmsPermission(String packageName) {
749         return mUsageMonitor.getPremiumSmsPermission(packageName);
750     }
751 
752     /**
753      * Sets the premium SMS permission for the specified package and save the value asynchronously
754      * to persistent storage.
755      * @param packageName the name of the package to set permission
756      * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
757      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
758      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
759      */
setPremiumSmsPermission(String packageName, int permission)760     public void setPremiumSmsPermission(String packageName, int permission) {
761         mUsageMonitor.setPremiumSmsPermission(packageName, permission);
762     }
763 
getUsageMonitor()764     public SmsUsageMonitor getUsageMonitor() {
765         return mUsageMonitor;
766     }
767 
768     /**
769      * Handles the sms status report for the sent sms through ImsSmsDispatcher. Carriers can send
770      * the report over CS even if the previously submitted SMS-SUBMIT was sent over IMS. For this
771      * case, finds a corresponding tracker from the tracker map in ImsSmsDispatcher and handles it.
772      *
773      * @param messageRef the TP-MR of the previously submitted SMS-SUBMIT in the report.
774      * @param format the format.
775      * @param pdu the pdu of the report.
776      */
handleSentOverImsStatusReport(int messageRef, String format, byte[] pdu)777     public void handleSentOverImsStatusReport(int messageRef, String format, byte[] pdu) {
778         for (Entry<Integer, SMSDispatcher.SmsTracker> entry :
779                 mImsSmsDispatcher.mTrackers.entrySet()) {
780             int token = entry.getKey();
781             SMSDispatcher.SmsTracker tracker = entry.getValue();
782             if (tracker.mMessageRef == messageRef) {
783                 Pair<Boolean, Boolean> result = handleSmsStatusReport(tracker, format, pdu);
784                 if (result.second) {
785                     mImsSmsDispatcher.mTrackers.remove(token);
786                 }
787                 return;
788             }
789         }
790     }
791 
792     /**
793      * Triggers the correct method for handling the sms status report based on the format.
794      *
795      * @param tracker the sms tracker.
796      * @param format the format.
797      * @param pdu the pdu of the report.
798      * @return a Pair in which the first boolean is whether the report was handled successfully
799      *          or not and the second boolean is whether processing the sms is complete and the
800      *          tracker no longer need to be kept track of, false if we should expect more callbacks
801      *          and the tracker should be kept.
802      */
handleSmsStatusReport(SMSDispatcher.SmsTracker tracker, String format, byte[] pdu)803     public Pair<Boolean, Boolean> handleSmsStatusReport(SMSDispatcher.SmsTracker tracker,
804             String format, byte[] pdu) {
805         if (isCdmaFormat(format)) {
806             return handleCdmaStatusReport(tracker, format, pdu);
807         } else {
808             return handleGsmStatusReport(tracker, format, pdu);
809         }
810     }
811 
handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker, String format, byte[] pdu)812     private Pair<Boolean, Boolean> handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker,
813             String format, byte[] pdu) {
814         com.android.internal.telephony.cdma.SmsMessage sms =
815                 com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
816         boolean complete = false;
817         boolean success = false;
818         if (sms != null) {
819             // The status is composed of an error class (bits 25-24) and a status code (bits 23-16).
820             int errorClass = (sms.getStatus() >> 24) & 0x03;
821             if (errorClass != ERROR_TEMPORARY) {
822                 // Update the message status (COMPLETE or FAILED)
823                 tracker.updateSentMessageStatus(
824                         mContext,
825                         (errorClass == ERROR_NONE) ? Sms.STATUS_COMPLETE : Sms.STATUS_FAILED);
826                 complete = true;
827             }
828             success = triggerDeliveryIntent(tracker, format, pdu);
829         }
830         return new Pair(success, complete);
831     }
832 
handleGsmStatusReport(SMSDispatcher.SmsTracker tracker, String format, byte[] pdu)833     private Pair<Boolean, Boolean> handleGsmStatusReport(SMSDispatcher.SmsTracker tracker,
834             String format, byte[] pdu) {
835         com.android.internal.telephony.gsm.SmsMessage sms =
836                 com.android.internal.telephony.gsm.SmsMessage.newFromCDS(pdu);
837         boolean complete = false;
838         boolean success = false;
839         if (sms != null) {
840             int tpStatus = sms.getStatus();
841             if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
842                 // Update the message status (COMPLETE or FAILED)
843                 tracker.updateSentMessageStatus(mContext, tpStatus);
844                 complete = true;
845             }
846             success = triggerDeliveryIntent(tracker, format, pdu);
847         }
848         return new Pair(success, complete);
849     }
850 
triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format, byte[] pdu)851     private boolean triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format,
852                                           byte[] pdu) {
853         PendingIntent intent = tracker.mDeliveryIntent;
854         Intent fillIn = new Intent();
855         fillIn.putExtra("pdu", pdu);
856         fillIn.putExtra("format", format);
857         try {
858             intent.send(mContext, Activity.RESULT_OK, fillIn);
859             return true;
860         } catch (CanceledException ex) {
861             return false;
862         }
863     }
864 
865 
866     public interface SmsInjectionCallback {
onSmsInjectedResult(int result)867         void onSmsInjectedResult(int result);
868     }
869 
dump(FileDescriptor fd, PrintWriter pw, String[] args)870     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
871         mGsmInboundSmsHandler.dump(fd, pw, args);
872         mCdmaInboundSmsHandler.dump(fd, pw, args);
873     }
874 
logd(String msg)875     private void logd(String msg) {
876         Rlog.d(TAG, msg);
877     }
878 }
879