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