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