Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2013 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 android.app.Activity;
     20 import android.app.ActivityManagerNative;
     21 import android.app.AppOpsManager;
     22 import android.app.PendingIntent;
     23 import android.app.PendingIntent.CanceledException;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.ContentResolver;
     27 import android.content.ContentUris;
     28 import android.content.ContentValues;
     29 import android.content.Context;
     30 import android.content.Intent;
     31 import android.content.pm.UserInfo;
     32 import android.database.Cursor;
     33 import android.database.SQLException;
     34 import android.net.Uri;
     35 import android.os.AsyncResult;
     36 import android.os.Binder;
     37 import android.os.Build;
     38 import android.os.Bundle;
     39 import android.os.Message;
     40 import android.os.PowerManager;
     41 import android.os.RemoteException;
     42 import android.os.SystemProperties;
     43 import android.os.UserHandle;
     44 import android.os.UserManager;
     45 import android.provider.Telephony;
     46 import android.provider.Telephony.Sms.Intents;
     47 import android.telephony.Rlog;
     48 import android.telephony.SmsManager;
     49 import android.telephony.SmsMessage;
     50 import android.telephony.SubscriptionManager;
     51 import android.telephony.TelephonyManager;
     52 import android.text.TextUtils;
     53 import android.util.Log;
     54 
     55 import com.android.internal.telephony.uicc.UiccCard;
     56 import com.android.internal.telephony.uicc.UiccController;
     57 import com.android.internal.util.HexDump;
     58 import com.android.internal.util.State;
     59 import com.android.internal.util.StateMachine;
     60 
     61 import java.io.ByteArrayOutputStream;
     62 import java.util.Arrays;
     63 import java.util.List;
     64 
     65 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
     66 
     67 /**
     68  * This class broadcasts incoming SMS messages to interested apps after storing them in
     69  * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
     70  * broadcast, its parts are removed from the raw table. If the device crashes after ACKing
     71  * but before the broadcast completes, the pending messages will be rebroadcast on the next boot.
     72  *
     73  * <p>The state machine starts in {@link IdleState} state. When the {@link SMSDispatcher} receives a
     74  * new SMS from the radio, it calls {@link #dispatchNormalMessage},
     75  * which sends a message to the state machine, causing the wakelock to be acquired in
     76  * {@link #haltedProcessMessage}, which transitions to {@link DeliveringState} state, where the message
     77  * is saved to the raw table, then acknowledged via the {@link SMSDispatcher} which called us.
     78  *
     79  * <p>After saving the SMS, if the message is complete (either single-part or the final segment
     80  * of a multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to
     81  * {@link WaitingState} state to wait for the broadcast to complete. When the local
     82  * {@link BroadcastReceiver} is called with the result, it sends {@link #EVENT_BROADCAST_COMPLETE}
     83  * to the state machine, causing us to either broadcast the next pending message (if one has
     84  * arrived while waiting for the broadcast to complete), or to transition back to the halted state
     85  * after all messages are processed. Then the wakelock is released and we wait for the next SMS.
     86  */
     87 public abstract class InboundSmsHandler extends StateMachine {
     88     protected static final boolean DBG = true;
     89     private static final boolean VDBG = false;  // STOPSHIP if true, logs user data
     90 
     91     /** Query projection for checking for duplicate message segments. */
     92     private static final String[] PDU_PROJECTION = {
     93             "pdu"
     94     };
     95 
     96     /** Query projection for combining concatenated message segments. */
     97     private static final String[] PDU_SEQUENCE_PORT_PROJECTION = {
     98             "pdu",
     99             "sequence",
    100             "destination_port"
    101     };
    102 
    103     static final int PDU_COLUMN = 0;
    104     static final int SEQUENCE_COLUMN = 1;
    105     static final int DESTINATION_PORT_COLUMN = 2;
    106     static final int DATE_COLUMN = 3;
    107     static final int REFERENCE_NUMBER_COLUMN = 4;
    108     static final int COUNT_COLUMN = 5;
    109     static final int ADDRESS_COLUMN = 6;
    110     static final int ID_COLUMN = 7;
    111 
    112     static final String SELECT_BY_ID = "_id=?";
    113     static final String SELECT_BY_REFERENCE = "address=? AND reference_number=? AND count=?";
    114 
    115     /** New SMS received as an AsyncResult. */
    116     public static final int EVENT_NEW_SMS = 1;
    117 
    118     /** Message type containing a {@link InboundSmsTracker} ready to broadcast to listeners. */
    119     static final int EVENT_BROADCAST_SMS = 2;
    120 
    121     /** Message from resultReceiver notifying {@link WaitingState} of a completed broadcast. */
    122     static final int EVENT_BROADCAST_COMPLETE = 3;
    123 
    124     /** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */
    125     static final int EVENT_RETURN_TO_IDLE = 4;
    126 
    127     /** Release wakelock after a short timeout when returning to idle state. */
    128     static final int EVENT_RELEASE_WAKELOCK = 5;
    129 
    130     /** Sent by {@link SmsBroadcastUndelivered} after cleaning the raw table. */
    131     static final int EVENT_START_ACCEPTING_SMS = 6;
    132 
    133     /** Update phone object */
    134     static final int EVENT_UPDATE_PHONE_OBJECT = 7;
    135 
    136     /** New SMS received as an AsyncResult. */
    137     public static final int EVENT_INJECT_SMS = 8;
    138 
    139     /** Wakelock release delay when returning to idle state. */
    140     private static final int WAKELOCK_TIMEOUT = 3000;
    141 
    142     /** URI for raw table of SMS provider. */
    143     private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
    144 
    145     protected final Context mContext;
    146     private final ContentResolver mResolver;
    147 
    148     /** Special handler for WAP push messages. */
    149     private final WapPushOverSms mWapPush;
    150 
    151     /** Wake lock to ensure device stays awake while dispatching the SMS intents. */
    152     final PowerManager.WakeLock mWakeLock;
    153 
    154     /** DefaultState throws an exception or logs an error for unhandled message types. */
    155     final DefaultState mDefaultState = new DefaultState();
    156 
    157     /** Startup state. Waiting for {@link SmsBroadcastUndelivered} to complete. */
    158     final StartupState mStartupState = new StartupState();
    159 
    160     /** Idle state. Waiting for messages to process. */
    161     final IdleState mIdleState = new IdleState();
    162 
    163     /** Delivering state. Saves the PDU in the raw table and acknowledges to SMSC. */
    164     final DeliveringState mDeliveringState = new DeliveringState();
    165 
    166     /** Broadcasting state. Waits for current broadcast to complete before delivering next. */
    167     final WaitingState mWaitingState = new WaitingState();
    168 
    169     /** Helper class to check whether storage is available for incoming messages. */
    170     protected SmsStorageMonitor mStorageMonitor;
    171 
    172     private final boolean mSmsReceiveDisabled;
    173 
    174     protected PhoneBase mPhone;
    175 
    176     protected CellBroadcastHandler mCellBroadcastHandler;
    177 
    178     private UserManager mUserManager;
    179 
    180     /**
    181      * Create a new SMS broadcast helper.
    182      * @param name the class name for logging
    183      * @param context the context of the phone app
    184      * @param storageMonitor the SmsStorageMonitor to check for storage availability
    185      */
    186     protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor,
    187             PhoneBase phone, CellBroadcastHandler cellBroadcastHandler) {
    188         super(name);
    189 
    190         mContext = context;
    191         mStorageMonitor = storageMonitor;
    192         mPhone = phone;
    193         mCellBroadcastHandler = cellBroadcastHandler;
    194         mResolver = context.getContentResolver();
    195         mWapPush = new WapPushOverSms(context);
    196 
    197         boolean smsCapable = mContext.getResources().getBoolean(
    198                 com.android.internal.R.bool.config_sms_capable);
    199         mSmsReceiveDisabled = !SystemProperties.getBoolean(
    200                 TelephonyProperties.PROPERTY_SMS_RECEIVE, smsCapable);
    201 
    202         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    203         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
    204         mWakeLock.acquire();    // wake lock released after we enter idle state
    205         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    206 
    207         addState(mDefaultState);
    208         addState(mStartupState, mDefaultState);
    209         addState(mIdleState, mDefaultState);
    210         addState(mDeliveringState, mDefaultState);
    211             addState(mWaitingState, mDeliveringState);
    212 
    213         setInitialState(mStartupState);
    214         if (DBG) log("created InboundSmsHandler");
    215     }
    216 
    217     /**
    218      * Tell the state machine to quit after processing all messages.
    219      */
    220     public void dispose() {
    221         quit();
    222     }
    223 
    224     /**
    225      * Update the phone object when it changes.
    226      */
    227     public void updatePhoneObject(PhoneBase phone) {
    228         sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone);
    229     }
    230 
    231     /**
    232      * Dispose of the WAP push object and release the wakelock.
    233      */
    234     @Override
    235     protected void onQuitting() {
    236         mWapPush.dispose();
    237 
    238         while (mWakeLock.isHeld()) {
    239             mWakeLock.release();
    240         }
    241     }
    242 
    243     // CAF_MSIM Is this used anywhere ? if not remove it
    244     public PhoneBase getPhone() {
    245         return mPhone;
    246     }
    247 
    248     /**
    249      * This parent state throws an exception (for debug builds) or prints an error for unhandled
    250      * message types.
    251      */
    252     class DefaultState extends State {
    253         @Override
    254         public boolean processMessage(Message msg) {
    255             switch (msg.what) {
    256                 case EVENT_UPDATE_PHONE_OBJECT: {
    257                     onUpdatePhoneObject((PhoneBase) msg.obj);
    258                     break;
    259                 }
    260                 default: {
    261                     String errorText = "processMessage: unhandled message type " + msg.what +
    262                         " currState=" + getCurrentState().getName();
    263                     if (Build.IS_DEBUGGABLE) {
    264                         loge("---- Dumping InboundSmsHandler ----");
    265                         loge("Total records=" + getLogRecCount());
    266                         for (int i = Math.max(getLogRecSize() - 20, 0); i < getLogRecSize(); i++) {
    267                             loge("Rec[%d]: %s\n" + i + getLogRec(i).toString());
    268                         }
    269                         loge("---- Dumped InboundSmsHandler ----");
    270 
    271                         throw new RuntimeException(errorText);
    272                     } else {
    273                         loge(errorText);
    274                     }
    275                     break;
    276                 }
    277             }
    278             return HANDLED;
    279         }
    280     }
    281 
    282     /**
    283      * The Startup state waits for {@link SmsBroadcastUndelivered} to process the raw table and
    284      * notify the state machine to broadcast any complete PDUs that might not have been broadcast.
    285      */
    286     class StartupState extends State {
    287         @Override
    288         public boolean processMessage(Message msg) {
    289             log("StartupState.processMessage:" + msg.what);
    290             switch (msg.what) {
    291                 case EVENT_NEW_SMS:
    292                 case EVENT_INJECT_SMS:
    293                 case EVENT_BROADCAST_SMS:
    294                     deferMessage(msg);
    295                     return HANDLED;
    296 
    297                 case EVENT_START_ACCEPTING_SMS:
    298                     transitionTo(mIdleState);
    299                     return HANDLED;
    300 
    301                 case EVENT_BROADCAST_COMPLETE:
    302                 case EVENT_RETURN_TO_IDLE:
    303                 case EVENT_RELEASE_WAKELOCK:
    304                 default:
    305                     // let DefaultState handle these unexpected message types
    306                     return NOT_HANDLED;
    307             }
    308         }
    309     }
    310 
    311     /**
    312      * In the idle state the wakelock is released until a new SM arrives, then we transition
    313      * to Delivering mode to handle it, acquiring the wakelock on exit.
    314      */
    315     class IdleState extends State {
    316         @Override
    317         public void enter() {
    318             if (DBG) log("entering Idle state");
    319             sendMessageDelayed(EVENT_RELEASE_WAKELOCK, WAKELOCK_TIMEOUT);
    320         }
    321 
    322         @Override
    323         public void exit() {
    324             mWakeLock.acquire();
    325             if (DBG) log("acquired wakelock, leaving Idle state");
    326         }
    327 
    328         @Override
    329         public boolean processMessage(Message msg) {
    330             log("IdleState.processMessage:" + msg.what);
    331             if (DBG) log("Idle state processing message type " + msg.what);
    332             switch (msg.what) {
    333                 case EVENT_NEW_SMS:
    334                 case EVENT_INJECT_SMS:
    335                 case EVENT_BROADCAST_SMS:
    336                     deferMessage(msg);
    337                     transitionTo(mDeliveringState);
    338                     return HANDLED;
    339 
    340                 case EVENT_RELEASE_WAKELOCK:
    341                     mWakeLock.release();
    342                     if (DBG) {
    343                         if (mWakeLock.isHeld()) {
    344                             // this is okay as long as we call release() for every acquire()
    345                             log("mWakeLock is still held after release");
    346                         } else {
    347                             log("mWakeLock released");
    348                         }
    349                     }
    350                     return HANDLED;
    351 
    352                 case EVENT_RETURN_TO_IDLE:
    353                     // already in idle state; ignore
    354                     return HANDLED;
    355 
    356                 case EVENT_BROADCAST_COMPLETE:
    357                 case EVENT_START_ACCEPTING_SMS:
    358                 default:
    359                     // let DefaultState handle these unexpected message types
    360                     return NOT_HANDLED;
    361             }
    362         }
    363     }
    364 
    365     /**
    366      * In the delivering state, the inbound SMS is processed and stored in the raw table.
    367      * The message is acknowledged before we exit this state. If there is a message to broadcast,
    368      * transition to {@link WaitingState} state to send the ordered broadcast and wait for the
    369      * results. When all messages have been processed, the halting state will release the wakelock.
    370      */
    371     class DeliveringState extends State {
    372         @Override
    373         public void enter() {
    374             if (DBG) log("entering Delivering state");
    375         }
    376 
    377         @Override
    378         public void exit() {
    379             if (DBG) log("leaving Delivering state");
    380         }
    381 
    382         @Override
    383         public boolean processMessage(Message msg) {
    384             log("DeliveringState.processMessage:" + msg.what);
    385             switch (msg.what) {
    386                 case EVENT_NEW_SMS:
    387                     // handle new SMS from RIL
    388                     handleNewSms((AsyncResult) msg.obj);
    389                     sendMessage(EVENT_RETURN_TO_IDLE);
    390                     return HANDLED;
    391 
    392                 case EVENT_INJECT_SMS:
    393                     // handle new injected SMS
    394                     handleInjectSms((AsyncResult) msg.obj);
    395                     sendMessage(EVENT_RETURN_TO_IDLE);
    396                     return HANDLED;
    397 
    398                 case EVENT_BROADCAST_SMS:
    399                     // if any broadcasts were sent, transition to waiting state
    400                     if (processMessagePart((InboundSmsTracker) msg.obj)) {
    401                         transitionTo(mWaitingState);
    402                     }
    403                     return HANDLED;
    404 
    405                 case EVENT_RETURN_TO_IDLE:
    406                     // return to idle after processing all other messages
    407                     transitionTo(mIdleState);
    408                     return HANDLED;
    409 
    410                 case EVENT_RELEASE_WAKELOCK:
    411                     mWakeLock.release();    // decrement wakelock from previous entry to Idle
    412                     if (!mWakeLock.isHeld()) {
    413                         // wakelock should still be held until 3 seconds after we enter Idle
    414                         loge("mWakeLock released while delivering/broadcasting!");
    415                     }
    416                     return HANDLED;
    417 
    418                 // we shouldn't get this message type in this state, log error and halt.
    419                 case EVENT_BROADCAST_COMPLETE:
    420                 case EVENT_START_ACCEPTING_SMS:
    421                 default:
    422                     // let DefaultState handle these unexpected message types
    423                     return NOT_HANDLED;
    424             }
    425         }
    426     }
    427 
    428     /**
    429      * The waiting state delegates handling of new SMS to parent {@link DeliveringState}, but
    430      * defers handling of the {@link #EVENT_BROADCAST_SMS} phase until after the current
    431      * result receiver sends {@link #EVENT_BROADCAST_COMPLETE}. Before transitioning to
    432      * {@link DeliveringState}, {@link #EVENT_RETURN_TO_IDLE} is sent to transition to
    433      * {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled.
    434      */
    435     class WaitingState extends State {
    436         @Override
    437         public boolean processMessage(Message msg) {
    438             log("WaitingState.processMessage:" + msg.what);
    439             switch (msg.what) {
    440                 case EVENT_BROADCAST_SMS:
    441                     // defer until the current broadcast completes
    442                     deferMessage(msg);
    443                     return HANDLED;
    444 
    445                 case EVENT_BROADCAST_COMPLETE:
    446                     // return to idle after handling all deferred messages
    447                     sendMessage(EVENT_RETURN_TO_IDLE);
    448                     transitionTo(mDeliveringState);
    449                     return HANDLED;
    450 
    451                 case EVENT_RETURN_TO_IDLE:
    452                     // not ready to return to idle; ignore
    453                     return HANDLED;
    454 
    455                 default:
    456                     // parent state handles the other message types
    457                     return NOT_HANDLED;
    458             }
    459         }
    460     }
    461 
    462     void handleNewSms(AsyncResult ar) {
    463         if (ar.exception != null) {
    464             loge("Exception processing incoming SMS: " + ar.exception);
    465             return;
    466         }
    467 
    468         int result;
    469         try {
    470             SmsMessage sms = (SmsMessage) ar.result;
    471             result = dispatchMessage(sms.mWrappedSmsMessage);
    472         } catch (RuntimeException ex) {
    473             loge("Exception dispatching message", ex);
    474             result = Intents.RESULT_SMS_GENERIC_ERROR;
    475         }
    476 
    477         // RESULT_OK means that the SMS will be acknowledged by special handling,
    478         // e.g. for SMS-PP data download. Any other result, we should ack here.
    479         if (result != Activity.RESULT_OK) {
    480             boolean handled = (result == Intents.RESULT_SMS_HANDLED);
    481             notifyAndAcknowledgeLastIncomingSms(handled, result, null);
    482         }
    483     }
    484 
    485     /**
    486      * This method is called when a new SMS PDU is injected into application framework.
    487      * @param ar is the AsyncResult that has the SMS PDU to be injected.
    488      */
    489     void handleInjectSms(AsyncResult ar) {
    490         int result;
    491         PendingIntent receivedIntent = null;
    492         try {
    493             receivedIntent = (PendingIntent) ar.userObj;
    494             SmsMessage sms = (SmsMessage) ar.result;
    495             if (sms == null) {
    496               result = Intents.RESULT_SMS_GENERIC_ERROR;
    497             } else {
    498               result = dispatchMessage(sms.mWrappedSmsMessage);
    499             }
    500         } catch (RuntimeException ex) {
    501             loge("Exception dispatching message", ex);
    502             result = Intents.RESULT_SMS_GENERIC_ERROR;
    503         }
    504 
    505         if (receivedIntent != null) {
    506             try {
    507                 receivedIntent.send(result);
    508             } catch (CanceledException e) { }
    509         }
    510     }
    511 
    512     /**
    513      * Process an SMS message from the RIL, calling subclass methods to handle 3GPP and
    514      * 3GPP2-specific message types.
    515      *
    516      * @param smsb the SmsMessageBase object from the RIL
    517      * @return a result code from {@link android.provider.Telephony.Sms.Intents},
    518      *  or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
    519      */
    520     public int dispatchMessage(SmsMessageBase smsb) {
    521         // If sms is null, there was a parsing error.
    522         if (smsb == null) {
    523             loge("dispatchSmsMessage: message is null");
    524             return Intents.RESULT_SMS_GENERIC_ERROR;
    525         }
    526 
    527         if (mSmsReceiveDisabled) {
    528             // Device doesn't support receiving SMS,
    529             log("Received short message on device which doesn't support "
    530                     + "receiving SMS. Ignored.");
    531             return Intents.RESULT_SMS_HANDLED;
    532         }
    533 
    534         return dispatchMessageRadioSpecific(smsb);
    535     }
    536 
    537     /**
    538      * Process voicemail notification, SMS-PP data download, CDMA CMAS, CDMA WAP push, and other
    539      * 3GPP/3GPP2-specific messages. Regular SMS messages are handled by calling the shared
    540      * {@link #dispatchNormalMessage} from this class.
    541      *
    542      * @param smsb the SmsMessageBase object from the RIL
    543      * @return a result code from {@link android.provider.Telephony.Sms.Intents},
    544      *  or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
    545      */
    546     protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb);
    547 
    548     /**
    549      * Send an acknowledge message to the SMSC.
    550      * @param success indicates that last message was successfully received.
    551      * @param result result code indicating any error
    552      * @param response callback message sent when operation completes.
    553      */
    554     protected abstract void acknowledgeLastIncomingSms(boolean success,
    555             int result, Message response);
    556 
    557     /**
    558      * Called when the phone changes the default method updates mPhone
    559      * mStorageMonitor and mCellBroadcastHandler.updatePhoneObject.
    560      * Override if different or other behavior is desired.
    561      *
    562      * @param phone
    563      */
    564     protected void onUpdatePhoneObject(PhoneBase phone) {
    565         mPhone = phone;
    566         mStorageMonitor = mPhone.mSmsStorageMonitor;
    567         log("onUpdatePhoneObject: phone=" + mPhone.getClass().getSimpleName());
    568     }
    569 
    570     /**
    571      * Notify interested apps if the framework has rejected an incoming SMS,
    572      * and send an acknowledge message to the network.
    573      * @param success indicates that last message was successfully received.
    574      * @param result result code indicating any error
    575      * @param response callback message sent when operation completes.
    576      */
    577     void notifyAndAcknowledgeLastIncomingSms(boolean success,
    578             int result, Message response) {
    579         if (!success) {
    580             // broadcast SMS_REJECTED_ACTION intent
    581             Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
    582             intent.putExtra("result", result);
    583             mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
    584         }
    585         acknowledgeLastIncomingSms(success, result, response);
    586     }
    587 
    588     /**
    589      * Return true if this handler is for 3GPP2 messages; false for 3GPP format.
    590      * @return true for the 3GPP2 handler; false for the 3GPP handler
    591      */
    592     protected abstract boolean is3gpp2();
    593 
    594     /**
    595      * Dispatch a normal incoming SMS. This is called from {@link #dispatchMessageRadioSpecific}
    596      * if no format-specific handling was required. Saves the PDU to the SMS provider raw table,
    597      * creates an {@link InboundSmsTracker}, then sends it to the state machine as an
    598      * {@link #EVENT_BROADCAST_SMS}. Returns {@link Intents#RESULT_SMS_HANDLED} or an error value.
    599      *
    600      * @param sms the message to dispatch
    601      * @return {@link Intents#RESULT_SMS_HANDLED} if the message was accepted, or an error status
    602      */
    603     protected int dispatchNormalMessage(SmsMessageBase sms) {
    604         SmsHeader smsHeader = sms.getUserDataHeader();
    605         InboundSmsTracker tracker;
    606 
    607         if ((smsHeader == null) || (smsHeader.concatRef == null)) {
    608             // Message is not concatenated.
    609             int destPort = -1;
    610             if (smsHeader != null && smsHeader.portAddrs != null) {
    611                 // The message was sent to a port.
    612                 destPort = smsHeader.portAddrs.destPort;
    613                 if (DBG) log("destination port: " + destPort);
    614             }
    615 
    616             tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
    617                     is3gpp2(), false);
    618         } else {
    619             // Create a tracker for this message segment.
    620             SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
    621             SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs;
    622             int destPort = (portAddrs != null ? portAddrs.destPort : -1);
    623 
    624             tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort,
    625                     is3gpp2(), sms.getOriginatingAddress(), concatRef.refNumber,
    626                     concatRef.seqNumber, concatRef.msgCount, false);
    627         }
    628 
    629         if (VDBG) log("created tracker: " + tracker);
    630         return addTrackerToRawTableAndSendMessage(tracker);
    631     }
    632 
    633     /**
    634      * Helper to add the tracker to the raw table and then send a message to broadcast it, if
    635      * successful. Returns the SMS intent status to return to the SMSC.
    636      * @param tracker the tracker to save to the raw table and then deliver
    637      * @return {@link Intents#RESULT_SMS_HANDLED} or {@link Intents#RESULT_SMS_GENERIC_ERROR}
    638      * or {@link Intents#RESULT_SMS_DUPLICATED}
    639      */
    640     protected int addTrackerToRawTableAndSendMessage(InboundSmsTracker tracker) {
    641         switch(addTrackerToRawTable(tracker)) {
    642         case Intents.RESULT_SMS_HANDLED:
    643             sendMessage(EVENT_BROADCAST_SMS, tracker);
    644             return Intents.RESULT_SMS_HANDLED;
    645 
    646         case Intents.RESULT_SMS_DUPLICATED:
    647             return Intents.RESULT_SMS_HANDLED;
    648 
    649         case Intents.RESULT_SMS_GENERIC_ERROR:
    650         default:
    651             return Intents.RESULT_SMS_GENERIC_ERROR;
    652         }
    653     }
    654 
    655     /**
    656      * Process the inbound SMS segment. If the message is complete, send it as an ordered
    657      * broadcast to interested receivers and return true. If the message is a segment of an
    658      * incomplete multi-part SMS, return false.
    659      * @param tracker the tracker containing the message segment to process
    660      * @return true if an ordered broadcast was sent; false if waiting for more message segments
    661      */
    662     boolean processMessagePart(InboundSmsTracker tracker) {
    663         int messageCount = tracker.getMessageCount();
    664         byte[][] pdus;
    665         int destPort = tracker.getDestPort();
    666 
    667         if (messageCount == 1) {
    668             // single-part message
    669             pdus = new byte[][]{tracker.getPdu()};
    670         } else {
    671             // multi-part message
    672             Cursor cursor = null;
    673             try {
    674                 // used by several query selection arguments
    675                 String address = tracker.getAddress();
    676                 String refNumber = Integer.toString(tracker.getReferenceNumber());
    677                 String count = Integer.toString(tracker.getMessageCount());
    678 
    679                 // query for all segments and broadcast message if we have all the parts
    680                 String[] whereArgs = {address, refNumber, count};
    681                 cursor = mResolver.query(sRawUri, PDU_SEQUENCE_PORT_PROJECTION,
    682                         SELECT_BY_REFERENCE, whereArgs, null);
    683 
    684                 int cursorCount = cursor.getCount();
    685                 if (cursorCount < messageCount) {
    686                     // Wait for the other message parts to arrive. It's also possible for the last
    687                     // segment to arrive before processing the EVENT_BROADCAST_SMS for one of the
    688                     // earlier segments. In that case, the broadcast will be sent as soon as all
    689                     // segments are in the table, and any later EVENT_BROADCAST_SMS messages will
    690                     // get a row count of 0 and return.
    691                     return false;
    692                 }
    693 
    694                 // All the parts are in place, deal with them
    695                 pdus = new byte[messageCount][];
    696                 while (cursor.moveToNext()) {
    697                     // subtract offset to convert sequence to 0-based array index
    698                     int index = cursor.getInt(SEQUENCE_COLUMN) - tracker.getIndexOffset();
    699 
    700                     pdus[index] = HexDump.hexStringToByteArray(cursor.getString(PDU_COLUMN));
    701 
    702                     // Read the destination port from the first segment (needed for CDMA WAP PDU).
    703                     // It's not a bad idea to prefer the port from the first segment in other cases.
    704                     if (index == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
    705                         int port = cursor.getInt(DESTINATION_PORT_COLUMN);
    706                         // strip format flags and convert to real port number, or -1
    707                         port = InboundSmsTracker.getRealDestPort(port);
    708                         if (port != -1) {
    709                             destPort = port;
    710                         }
    711                     }
    712                 }
    713             } catch (SQLException e) {
    714                 loge("Can't access multipart SMS database", e);
    715                 return false;
    716             } finally {
    717                 if (cursor != null) {
    718                     cursor.close();
    719                 }
    720             }
    721         }
    722 
    723         BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);
    724 
    725         if (destPort == SmsHeader.PORT_WAP_PUSH) {
    726             // Build up the data stream
    727             ByteArrayOutputStream output = new ByteArrayOutputStream();
    728             for (byte[] pdu : pdus) {
    729                 // 3GPP needs to extract the User Data from the PDU; 3GPP2 has already done this
    730                 if (!tracker.is3gpp2()) {
    731                     SmsMessage msg = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP);
    732                     pdu = msg.getUserData();
    733                 }
    734                 output.write(pdu, 0, pdu.length);
    735             }
    736             int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);
    737             if (DBG) log("dispatchWapPdu() returned " + result);
    738             // result is Activity.RESULT_OK if an ordered broadcast was sent
    739             return (result == Activity.RESULT_OK);
    740         }
    741 
    742         Intent intent = new Intent(Intents.SMS_FILTER_ACTION);
    743         List<String> carrierPackages = null;
    744         UiccCard card = UiccController.getInstance().getUiccCard();
    745         if (card != null) {
    746             carrierPackages = card.getCarrierPackageNamesForIntent(
    747                     mContext.getPackageManager(), intent);
    748         }
    749         if (carrierPackages != null && carrierPackages.size() == 1) {
    750             intent.setPackage(carrierPackages.get(0));
    751             intent.putExtra("destport", destPort);
    752         } else {
    753             setAndDirectIntent(intent, destPort);
    754         }
    755 
    756         intent.putExtra("pdus", pdus);
    757         intent.putExtra("format", tracker.getFormat());
    758         dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
    759                 AppOpsManager.OP_RECEIVE_SMS, resultReceiver, UserHandle.OWNER);
    760         return true;
    761     }
    762 
    763     /**
    764      * Dispatch the intent with the specified permission, appOp, and result receiver, using
    765      * this state machine's handler thread to run the result receiver.
    766      *
    767      * @param intent the intent to broadcast
    768      * @param permission receivers are required to have this permission
    769      * @param appOp app op that is being performed when dispatching to a receiver
    770      * @param user user to deliver the intent to
    771      */
    772     protected void dispatchIntent(Intent intent, String permission, int appOp,
    773             BroadcastReceiver resultReceiver, UserHandle user) {
    774         intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
    775         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
    776         if (user.equals(UserHandle.ALL)) {
    777             // Get a list of currently started users.
    778             int[] users = null;
    779             try {
    780                 users = ActivityManagerNative.getDefault().getRunningUserIds();
    781             } catch (RemoteException re) {
    782             }
    783             if (users == null) {
    784                 users = new int[] {user.getIdentifier()};
    785             }
    786             // Deliver the broadcast only to those running users that are permitted
    787             // by user policy.
    788             for (int i = users.length - 1; i >= 0; i--) {
    789                 UserHandle targetUser = new UserHandle(users[i]);
    790                 if (users[i] != UserHandle.USER_OWNER) {
    791                     // Is the user not allowed to use SMS?
    792                     if (mUserManager.hasUserRestriction(UserManager.DISALLOW_SMS, targetUser)) {
    793                         continue;
    794                     }
    795                     // Skip unknown users and managed profiles as well
    796                     UserInfo info = mUserManager.getUserInfo(users[i]);
    797                     if (info == null || info.isManagedProfile()) {
    798                         continue;
    799                     }
    800                 }
    801                 // Only pass in the resultReceiver when the USER_OWNER is processed.
    802                 mContext.sendOrderedBroadcastAsUser(intent, targetUser, permission, appOp,
    803                         users[i] == UserHandle.USER_OWNER ? resultReceiver : null,
    804                         getHandler(), Activity.RESULT_OK, null, null);
    805             }
    806         } else {
    807             mContext.sendOrderedBroadcastAsUser(intent, user, permission, appOp,
    808                     resultReceiver,
    809                     getHandler(), Activity.RESULT_OK, null, null);
    810         }
    811     }
    812 
    813     /**
    814      * Helper for {@link SmsBroadcastUndelivered} to delete an old message in the raw table.
    815      */
    816     void deleteFromRawTable(String deleteWhere, String[] deleteWhereArgs) {
    817         int rows = mResolver.delete(sRawUri, deleteWhere, deleteWhereArgs);
    818         if (rows == 0) {
    819             loge("No rows were deleted from raw table!");
    820         } else if (DBG) {
    821             log("Deleted " + rows + " rows from raw table.");
    822         }
    823     }
    824 
    825     /**
    826      * Set the appropriate intent action and direct the intent to the default SMS app or the
    827      * appropriate port.
    828      *
    829      * @param intent the intent to set and direct
    830      * @param destPort the destination port
    831      */
    832     void setAndDirectIntent(Intent intent, int destPort) {
    833         if (destPort == -1) {
    834             intent.setAction(Intents.SMS_DELIVER_ACTION);
    835 
    836             // Direct the intent to only the default SMS app. If we can't find a default SMS app
    837             // then sent it to all broadcast receivers.
    838             // We are deliberately delivering to the primary user's default SMS App.
    839             ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true);
    840             if (componentName != null) {
    841                 // Deliver SMS message only to this receiver.
    842                 intent.setComponent(componentName);
    843                 log("Delivering SMS to: " + componentName.getPackageName() +
    844                     " " + componentName.getClassName());
    845             } else {
    846                 intent.setComponent(null);
    847             }
    848         } else {
    849             intent.setAction(Intents.DATA_SMS_RECEIVED_ACTION);
    850             Uri uri = Uri.parse("sms://localhost:" + destPort);
    851             intent.setData(uri);
    852             intent.setComponent(null);
    853         }
    854     }
    855 
    856     /**
    857      * Insert a message PDU into the raw table so we can acknowledge it immediately.
    858      * If the device crashes before the broadcast to listeners completes, it will be delivered
    859      * from the raw table on the next device boot. For single-part messages, the deleteWhere
    860      * and deleteWhereArgs fields of the tracker will be set to delete the correct row after
    861      * the ordered broadcast completes.
    862      *
    863      * @param tracker the tracker to add to the raw table
    864      * @return true on success; false on failure to write to database
    865      */
    866     private int addTrackerToRawTable(InboundSmsTracker tracker) {
    867         if (tracker.getMessageCount() != 1) {
    868             // check for duplicate message segments
    869             Cursor cursor = null;
    870             try {
    871                 // sequence numbers are 1-based except for CDMA WAP, which is 0-based
    872                 int sequence = tracker.getSequenceNumber();
    873 
    874                 // convert to strings for query
    875                 String address = tracker.getAddress();
    876                 String refNumber = Integer.toString(tracker.getReferenceNumber());
    877                 String count = Integer.toString(tracker.getMessageCount());
    878 
    879                 String seqNumber = Integer.toString(sequence);
    880 
    881                 // set the delete selection args for multi-part message
    882                 String[] deleteWhereArgs = {address, refNumber, count};
    883                 tracker.setDeleteWhere(SELECT_BY_REFERENCE, deleteWhereArgs);
    884 
    885                 // Check for duplicate message segments
    886                 cursor = mResolver.query(sRawUri, PDU_PROJECTION,
    887                         "address=? AND reference_number=? AND count=? AND sequence=?",
    888                         new String[] {address, refNumber, count, seqNumber}, null);
    889 
    890                 // moveToNext() returns false if no duplicates were found
    891                 if (cursor.moveToNext()) {
    892                     loge("Discarding duplicate message segment, refNumber=" + refNumber
    893                             + " seqNumber=" + seqNumber);
    894                     String oldPduString = cursor.getString(PDU_COLUMN);
    895                     byte[] pdu = tracker.getPdu();
    896                     byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString);
    897                     if (!Arrays.equals(oldPdu, tracker.getPdu())) {
    898                         loge("Warning: dup message segment PDU of length " + pdu.length
    899                                 + " is different from existing PDU of length " + oldPdu.length);
    900                     }
    901                     return Intents.RESULT_SMS_DUPLICATED;   // reject message
    902                 }
    903                 cursor.close();
    904             } catch (SQLException e) {
    905                 loge("Can't access multipart SMS database", e);
    906                 return Intents.RESULT_SMS_GENERIC_ERROR;    // reject message
    907             } finally {
    908                 if (cursor != null) {
    909                     cursor.close();
    910                 }
    911             }
    912         }
    913 
    914         ContentValues values = tracker.getContentValues();
    915 
    916         if (VDBG) log("adding content values to raw table: " + values.toString());
    917         Uri newUri = mResolver.insert(sRawUri, values);
    918         if (DBG) log("URI of new row -> " + newUri);
    919 
    920         try {
    921             long rowId = ContentUris.parseId(newUri);
    922             if (tracker.getMessageCount() == 1) {
    923                 // set the delete selection args for single-part message
    924                 tracker.setDeleteWhere(SELECT_BY_ID, new String[]{Long.toString(rowId)});
    925             }
    926             return Intents.RESULT_SMS_HANDLED;
    927         } catch (Exception e) {
    928             loge("error parsing URI for new row: " + newUri, e);
    929             return Intents.RESULT_SMS_GENERIC_ERROR;
    930         }
    931     }
    932 
    933     /**
    934      * Returns whether the default message format for the current radio technology is 3GPP2.
    935      * @return true if the radio technology uses 3GPP2 format by default, false for 3GPP format
    936      */
    937     static boolean isCurrentFormat3gpp2() {
    938         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
    939         return (PHONE_TYPE_CDMA == activePhone);
    940     }
    941 
    942     /**
    943      * Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and
    944      * logs the broadcast duration (as an error if the other receivers were especially slow).
    945      */
    946     private final class SmsBroadcastReceiver extends BroadcastReceiver {
    947         private final String mDeleteWhere;
    948         private final String[] mDeleteWhereArgs;
    949         private long mBroadcastTimeNano;
    950 
    951         SmsBroadcastReceiver(InboundSmsTracker tracker) {
    952             mDeleteWhere = tracker.getDeleteWhere();
    953             mDeleteWhereArgs = tracker.getDeleteWhereArgs();
    954             mBroadcastTimeNano = System.nanoTime();
    955         }
    956 
    957         @Override
    958         public void onReceive(Context context, Intent intent) {
    959             String action = intent.getAction();
    960             if (action.equals(Intents.SMS_FILTER_ACTION)) {
    961                 int rc = getResultCode();
    962                 if (rc == Activity.RESULT_OK) {
    963                   // Overwrite pdus data if the SMS filter has set it.
    964                   Bundle resultExtras = getResultExtras(false);
    965                   if (resultExtras != null && resultExtras.containsKey("pdus")) {
    966                       intent.putExtra("pdus", (byte[][]) resultExtras.get("pdus"));
    967                   }
    968                   if (intent.hasExtra("destport")) {
    969                       int destPort = intent.getIntExtra("destport", -1);
    970                       intent.removeExtra("destport");
    971                       setAndDirectIntent(intent, destPort);
    972                       if (SmsManager.getDefault().getAutoPersisting()) {
    973                           final Uri uri = writeInboxMessage(intent);
    974                           if (uri != null) {
    975                               // Pass this to SMS apps so that they know where it is stored
    976                               intent.putExtra("uri", uri.toString());
    977                           }
    978                       }
    979                       dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
    980                                      AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.OWNER);
    981                   } else {
    982                       loge("destport doesn't exist in the extras for SMS filter action.");
    983                   }
    984                 } else {
    985                   // Drop this SMS.
    986                   log("SMS filtered by result code " + rc);
    987                   deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs);
    988                   sendMessage(EVENT_BROADCAST_COMPLETE);
    989                 }
    990             } else if (action.equals(Intents.SMS_DELIVER_ACTION)) {
    991                 // Now dispatch the notification only intent
    992                 intent.setAction(Intents.SMS_RECEIVED_ACTION);
    993                 intent.setComponent(null);
    994                 // All running users will be notified of the received sms.
    995                 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
    996                         AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.ALL);
    997             } else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) {
    998                 // Now dispatch the notification only intent
    999                 intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION);
   1000                 intent.setComponent(null);
   1001                 // Only the primary user will receive notification of incoming mms.
   1002                 // That app will do the actual downloading of the mms.
   1003                 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
   1004                         AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.OWNER);
   1005             } else {
   1006                 // Now that the intents have been deleted we can clean up the PDU data.
   1007                 if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
   1008                         && !Intents.SMS_RECEIVED_ACTION.equals(action)
   1009                         && !Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
   1010                         && !Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) {
   1011                     loge("unexpected BroadcastReceiver action: " + action);
   1012                 }
   1013 
   1014                 int rc = getResultCode();
   1015                 if ((rc != Activity.RESULT_OK) && (rc != Intents.RESULT_SMS_HANDLED)) {
   1016                     loge("a broadcast receiver set the result code to " + rc
   1017                             + ", deleting from raw table anyway!");
   1018                 } else if (DBG) {
   1019                     log("successful broadcast, deleting from raw table.");
   1020                 }
   1021 
   1022                 deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs);
   1023                 sendMessage(EVENT_BROADCAST_COMPLETE);
   1024 
   1025                 int durationMillis = (int) ((System.nanoTime() - mBroadcastTimeNano) / 1000000);
   1026                 if (durationMillis >= 5000) {
   1027                     loge("Slow ordered broadcast completion time: " + durationMillis + " ms");
   1028                 } else if (DBG) {
   1029                     log("ordered broadcast completed in: " + durationMillis + " ms");
   1030                 }
   1031             }
   1032         }
   1033     }
   1034 
   1035     /**
   1036      * Log with debug level.
   1037      * @param s the string to log
   1038      */
   1039     @Override
   1040     protected void log(String s) {
   1041         Rlog.d(getName(), s);
   1042     }
   1043 
   1044     /**
   1045      * Log with error level.
   1046      * @param s the string to log
   1047      */
   1048     @Override
   1049     protected void loge(String s) {
   1050         Rlog.e(getName(), s);
   1051     }
   1052 
   1053     /**
   1054      * Log with error level.
   1055      * @param s the string to log
   1056      * @param e is a Throwable which logs additional information.
   1057      */
   1058     @Override
   1059     protected void loge(String s, Throwable e) {
   1060         Rlog.e(getName(), s, e);
   1061     }
   1062 
   1063     /**
   1064      * Store a received SMS into Telephony provider
   1065      *
   1066      * @param intent The intent containing the received SMS
   1067      * @return The URI of written message
   1068      */
   1069     private Uri writeInboxMessage(Intent intent) {
   1070         final SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent);
   1071         if (messages == null || messages.length < 1) {
   1072             loge("Failed to parse SMS pdu");
   1073             return null;
   1074         }
   1075         // Sometimes, SmsMessage.mWrappedSmsMessage is null causing NPE when we access
   1076         // the methods on it although the SmsMessage itself is not null. So do this check
   1077         // before we do anything on the parsed SmsMessages.
   1078         for (final SmsMessage sms : messages) {
   1079             try {
   1080                 sms.getDisplayMessageBody();
   1081             } catch (NullPointerException e) {
   1082                 loge("NPE inside SmsMessage");
   1083                 return null;
   1084             }
   1085         }
   1086         final ContentValues values = parseSmsMessage(messages);
   1087         final long identity = Binder.clearCallingIdentity();
   1088         try {
   1089             return mContext.getContentResolver().insert(Telephony.Sms.Inbox.CONTENT_URI, values);
   1090         } catch (Exception e) {
   1091             loge("Failed to persist inbox message", e);
   1092         } finally {
   1093             Binder.restoreCallingIdentity(identity);
   1094         }
   1095         return null;
   1096     }
   1097 
   1098     /**
   1099      * Convert SmsMessage[] into SMS database schema columns
   1100      *
   1101      * @param msgs The SmsMessage array of the received SMS
   1102      * @return ContentValues representing the columns of parsed SMS
   1103      */
   1104     private static ContentValues parseSmsMessage(SmsMessage[] msgs) {
   1105         final SmsMessage sms = msgs[0];
   1106         final ContentValues values = new ContentValues();
   1107         values.put(Telephony.Sms.Inbox.ADDRESS, sms.getDisplayOriginatingAddress());
   1108         values.put(Telephony.Sms.Inbox.BODY, buildMessageBodyFromPdus(msgs));
   1109         values.put(Telephony.Sms.Inbox.DATE_SENT, sms.getTimestampMillis());
   1110         values.put(Telephony.Sms.Inbox.DATE, System.currentTimeMillis());
   1111         values.put(Telephony.Sms.Inbox.PROTOCOL, sms.getProtocolIdentifier());
   1112         values.put(Telephony.Sms.Inbox.SEEN, 0);
   1113         values.put(Telephony.Sms.Inbox.READ, 0);
   1114         final String subject = sms.getPseudoSubject();
   1115         if (!TextUtils.isEmpty(subject)) {
   1116             values.put(Telephony.Sms.Inbox.SUBJECT, subject);
   1117         }
   1118         values.put(Telephony.Sms.Inbox.REPLY_PATH_PRESENT, sms.isReplyPathPresent() ? 1 : 0);
   1119         values.put(Telephony.Sms.Inbox.SERVICE_CENTER, sms.getServiceCenterAddress());
   1120         return values;
   1121     }
   1122 
   1123     /**
   1124      * Build up the SMS message body from the SmsMessage array of received SMS
   1125      *
   1126      * @param msgs The SmsMessage array of the received SMS
   1127      * @return The text message body
   1128      */
   1129     private static String buildMessageBodyFromPdus(SmsMessage[] msgs) {
   1130         if (msgs.length == 1) {
   1131             // There is only one part, so grab the body directly.
   1132             return replaceFormFeeds(msgs[0].getDisplayMessageBody());
   1133         } else {
   1134             // Build up the body from the parts.
   1135             StringBuilder body = new StringBuilder();
   1136             for (SmsMessage msg: msgs) {
   1137                 // getDisplayMessageBody() can NPE if mWrappedMessage inside is null.
   1138                 body.append(msg.getDisplayMessageBody());
   1139             }
   1140             return replaceFormFeeds(body.toString());
   1141         }
   1142     }
   1143 
   1144     // Some providers send formfeeds in their messages. Convert those formfeeds to newlines.
   1145     private static String replaceFormFeeds(String s) {
   1146         return s == null ? "" : s.replace('\f', '\n');
   1147     }
   1148 }
   1149