Home | History | Annotate | Download | only in telephony
      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 import android.app.Activity;
     19 import android.app.AlertDialog;
     20 import android.app.PendingIntent;
     21 import android.app.PendingIntent.CanceledException;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ContentResolver;
     24 import android.content.ContentValues;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.content.pm.ApplicationInfo;
     29 import android.content.pm.PackageInfo;
     30 import android.content.pm.PackageManager;
     31 import android.content.res.Resources;
     32 import android.database.ContentObserver;
     33 import android.database.sqlite.SqliteWrapper;
     34 import android.net.Uri;
     35 import android.os.AsyncResult;
     36 import android.os.Binder;
     37 import android.os.Bundle;
     38 import android.os.Handler;
     39 import android.os.Message;
     40 import android.os.SystemProperties;
     41 import android.provider.Settings;
     42 import android.provider.Telephony;
     43 import android.provider.Telephony.Sms;
     44 import android.provider.Telephony.Sms.Intents;
     45 import android.telephony.PhoneNumberUtils;
     46 import android.telephony.Rlog;
     47 import android.telephony.ServiceState;
     48 import android.telephony.SmsManager;
     49 import android.telephony.TelephonyManager;
     50 import android.text.Html;
     51 import android.text.Spanned;
     52 import android.text.TextUtils;
     53 import android.util.EventLog;
     54 import android.view.LayoutInflater;
     55 import android.view.View;
     56 import android.view.ViewGroup;
     57 import android.view.WindowManager;
     58 import android.widget.Button;
     59 import android.widget.CheckBox;
     60 import android.widget.CompoundButton;
     61 import android.widget.TextView;
     62 
     63 import com.android.internal.R;
     64 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
     65 import com.android.internal.telephony.uicc.UiccCard;
     66 import com.android.internal.telephony.uicc.UiccController;
     67 
     68 import java.util.ArrayList;
     69 import java.util.Collections;
     70 import java.util.HashMap;
     71 import java.util.List;
     72 import java.util.Random;
     73 import java.util.concurrent.atomic.AtomicBoolean;
     74 import java.util.concurrent.atomic.AtomicInteger;
     75 
     76 import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
     77 import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
     78 import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
     79 import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
     80 import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
     81 import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
     82 
     83 public abstract class SMSDispatcher extends Handler {
     84     static final String TAG = "SMSDispatcher";    // accessed from inner class
     85     static final boolean DBG = false;
     86     private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
     87 
     88     /** Permission required to send SMS to short codes without user confirmation. */
     89     private static final String SEND_SMS_NO_CONFIRMATION_PERMISSION =
     90             "android.permission.SEND_SMS_NO_CONFIRMATION";
     91 
     92     private static final int PREMIUM_RULE_USE_SIM = 1;
     93     private static final int PREMIUM_RULE_USE_NETWORK = 2;
     94     private static final int PREMIUM_RULE_USE_BOTH = 3;
     95     private final AtomicInteger mPremiumSmsRule = new AtomicInteger(PREMIUM_RULE_USE_SIM);
     96     private final SettingsObserver mSettingsObserver;
     97 
     98     /** SMS send complete. */
     99     protected static final int EVENT_SEND_SMS_COMPLETE = 2;
    100 
    101     /** Retry sending a previously failed SMS message */
    102     private static final int EVENT_SEND_RETRY = 3;
    103 
    104     /** Confirmation required for sending a large number of messages. */
    105     private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4;
    106 
    107     /** Send the user confirmed SMS */
    108     static final int EVENT_SEND_CONFIRMED_SMS = 5;  // accessed from inner class
    109 
    110     /** Don't send SMS (user did not confirm). */
    111     static final int EVENT_STOP_SENDING = 7;        // accessed from inner class
    112 
    113     /** Confirmation required for third-party apps sending to an SMS short code. */
    114     private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8;
    115 
    116     /** Confirmation required for third-party apps sending to an SMS short code. */
    117     private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9;
    118 
    119     /** Handle status report from {@code CdmaInboundSmsHandler}. */
    120     protected static final int EVENT_HANDLE_STATUS_REPORT = 10;
    121 
    122     /** Radio is ON */
    123     protected static final int EVENT_RADIO_ON = 11;
    124 
    125     /** IMS registration/SMS format changed */
    126     protected static final int EVENT_IMS_STATE_CHANGED = 12;
    127 
    128     /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
    129     protected static final int EVENT_IMS_STATE_DONE = 13;
    130 
    131     // other
    132     protected static final int EVENT_NEW_ICC_SMS = 14;
    133     protected static final int EVENT_ICC_CHANGED = 15;
    134 
    135     protected PhoneBase mPhone;
    136     protected final Context mContext;
    137     protected final ContentResolver mResolver;
    138     protected final CommandsInterface mCi;
    139     protected final TelephonyManager mTelephonyManager;
    140 
    141     /** Maximum number of times to retry sending a failed SMS. */
    142     private static final int MAX_SEND_RETRIES = 3;
    143     /** Delay before next send attempt on a failed SMS, in milliseconds. */
    144     private static final int SEND_RETRY_DELAY = 2000;
    145     /** single part SMS */
    146     private static final int SINGLE_PART_SMS = 1;
    147     /** Message sending queue limit */
    148     private static final int MO_MSG_QUEUE_LIMIT = 5;
    149 
    150     /**
    151      * Message reference for a CONCATENATED_8_BIT_REFERENCE or
    152      * CONCATENATED_16_BIT_REFERENCE message set.  Should be
    153      * incremented for each set of concatenated messages.
    154      * Static field shared by all dispatcher objects.
    155      */
    156     private static int sConcatenatedRef = new Random().nextInt(256);
    157 
    158     /** Outgoing message counter. Shared by all dispatchers. */
    159     private SmsUsageMonitor mUsageMonitor;
    160 
    161     private ImsSMSDispatcher mImsSMSDispatcher;
    162 
    163     /** Number of outgoing SmsTrackers waiting for user confirmation. */
    164     private int mPendingTrackerCount;
    165 
    166     /* Flags indicating whether the current device allows sms service */
    167     protected boolean mSmsCapable = true;
    168     protected boolean mSmsSendDisabled;
    169 
    170     protected static int getNextConcatenatedRef() {
    171         sConcatenatedRef += 1;
    172         return sConcatenatedRef;
    173     }
    174 
    175     /**
    176      * Create a new SMS dispatcher.
    177      * @param phone the Phone to use
    178      * @param usageMonitor the SmsUsageMonitor to use
    179      */
    180     protected SMSDispatcher(PhoneBase phone, SmsUsageMonitor usageMonitor,
    181             ImsSMSDispatcher imsSMSDispatcher) {
    182         mPhone = phone;
    183         mImsSMSDispatcher = imsSMSDispatcher;
    184         mContext = phone.getContext();
    185         mResolver = mContext.getContentResolver();
    186         mCi = phone.mCi;
    187         mUsageMonitor = usageMonitor;
    188         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    189         mSettingsObserver = new SettingsObserver(this, mPremiumSmsRule, mContext);
    190         mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
    191                 Settings.Global.SMS_SHORT_CODE_RULE), false, mSettingsObserver);
    192 
    193         mSmsCapable = mContext.getResources().getBoolean(
    194                 com.android.internal.R.bool.config_sms_capable);
    195         mSmsSendDisabled = !SystemProperties.getBoolean(
    196                                 TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable);
    197         Rlog.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat()
    198                 + " mSmsSendDisabled=" + mSmsSendDisabled);
    199     }
    200 
    201     /**
    202      * Observe the secure setting for updated premium sms determination rules
    203      */
    204     private static class SettingsObserver extends ContentObserver {
    205         private final AtomicInteger mPremiumSmsRule;
    206         private final Context mContext;
    207         SettingsObserver(Handler handler, AtomicInteger premiumSmsRule, Context context) {
    208             super(handler);
    209             mPremiumSmsRule = premiumSmsRule;
    210             mContext = context;
    211             onChange(false); // load initial value;
    212         }
    213 
    214         @Override
    215         public void onChange(boolean selfChange) {
    216             mPremiumSmsRule.set(Settings.Global.getInt(mContext.getContentResolver(),
    217                     Settings.Global.SMS_SHORT_CODE_RULE, PREMIUM_RULE_USE_SIM));
    218         }
    219     }
    220 
    221     protected void updatePhoneObject(PhoneBase phone) {
    222         mPhone = phone;
    223         mUsageMonitor = phone.mSmsUsageMonitor;
    224         Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() );
    225     }
    226 
    227     /** Unregister for incoming SMS events. */
    228     public void dispose() {
    229         mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
    230     }
    231 
    232     /**
    233      * The format of the message PDU in the associated broadcast intent.
    234      * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
    235      * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
    236      *
    237      * Note: All applications which handle incoming SMS messages by processing the
    238      * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent
    239      * into the new methods in {@link android.telephony.SmsMessage} which take an
    240      * extra format parameter. This is required in order to correctly decode the PDU on
    241      * devices which require support for both 3GPP and 3GPP2 formats at the same time,
    242      * such as CDMA/LTE devices and GSM/CDMA world phones.
    243      *
    244      * @return the format of the message PDU
    245      */
    246     protected abstract String getFormat();
    247 
    248     /**
    249      * Pass the Message object to subclass to handle. Currently used to pass CDMA status reports
    250      * from {@link com.android.internal.telephony.cdma.CdmaInboundSmsHandler}.
    251      * @param o the SmsMessage containing the status report
    252      */
    253     protected void handleStatusReport(Object o) {
    254         Rlog.d(TAG, "handleStatusReport() called with no subclass.");
    255     }
    256 
    257     /* TODO: Need to figure out how to keep track of status report routing in a
    258      *       persistent manner. If the phone process restarts (reboot or crash),
    259      *       we will lose this list and any status reports that come in after
    260      *       will be dropped.
    261      */
    262     /** Sent messages awaiting a delivery status report. */
    263     protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>();
    264 
    265     /** Outgoing messages being handled by the carrier app. */
    266     protected final List<SmsTracker> sendPendingList =
    267         Collections.synchronizedList(new ArrayList<SmsTracker>());
    268 
    269     /**
    270      * Handles events coming from the phone stack. Overridden from handler.
    271      *
    272      * @param msg the message to handle
    273      */
    274     @Override
    275     public void handleMessage(Message msg) {
    276         switch (msg.what) {
    277         case EVENT_SEND_SMS_COMPLETE:
    278             // An outbound SMS has been successfully transferred, or failed.
    279             handleSendComplete((AsyncResult) msg.obj);
    280             break;
    281 
    282         case EVENT_SEND_RETRY:
    283             Rlog.d(TAG, "SMS retry..");
    284             sendRetrySms((SmsTracker) msg.obj);
    285             break;
    286 
    287         case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
    288             handleReachSentLimit((SmsTracker)(msg.obj));
    289             break;
    290 
    291         case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
    292             handleConfirmShortCode(false, (SmsTracker)(msg.obj));
    293             break;
    294 
    295         case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
    296             handleConfirmShortCode(true, (SmsTracker)(msg.obj));
    297             break;
    298 
    299         case EVENT_SEND_CONFIRMED_SMS:
    300         {
    301             SmsTracker tracker = (SmsTracker) msg.obj;
    302             if (tracker.isMultipart()) {
    303                 sendMultipartSms(tracker);
    304             } else {
    305                 if (mPendingTrackerCount > 1) {
    306                     tracker.mExpectMore = true;
    307                 } else {
    308                     tracker.mExpectMore = false;
    309                 }
    310                 sendSms(tracker);
    311             }
    312             mPendingTrackerCount--;
    313             break;
    314         }
    315 
    316         case EVENT_STOP_SENDING:
    317         {
    318             SmsTracker tracker = (SmsTracker) msg.obj;
    319             tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
    320             mPendingTrackerCount--;
    321             break;
    322         }
    323 
    324         case EVENT_HANDLE_STATUS_REPORT:
    325             handleStatusReport(msg.obj);
    326             break;
    327 
    328         default:
    329             Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what);
    330         }
    331     }
    332 
    333     /**
    334      * Called when SMS send completes. Broadcasts a sentIntent on success.
    335      * On failure, either sets up retries or broadcasts a sentIntent with
    336      * the failure in the result code.
    337      *
    338      * @param ar AsyncResult passed into the message handler.  ar.result should
    339      *           an SmsResponse instance if send was successful.  ar.userObj
    340      *           should be an SmsTracker instance.
    341      */
    342     protected void handleSendComplete(AsyncResult ar) {
    343         SmsTracker tracker = (SmsTracker) ar.userObj;
    344         PendingIntent sentIntent = tracker.mSentIntent;
    345 
    346         if (ar.result != null) {
    347             tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef;
    348         } else {
    349             Rlog.d(TAG, "SmsResponse was null");
    350         }
    351 
    352         if (ar.exception == null) {
    353             if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent);
    354 
    355             if (tracker.mDeliveryIntent != null) {
    356                 // Expecting a status report.  Add it to the list.
    357                 deliveryPendingList.add(tracker);
    358             }
    359             tracker.onSent(mContext);
    360         } else {
    361             if (DBG) Rlog.d(TAG, "SMS send failed");
    362 
    363             int ss = mPhone.getServiceState().getState();
    364 
    365             if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
    366                 // This is retry after failure over IMS but voice is not available.
    367                 // Set retry to max allowed, so no retry is sent and
    368                 //   cause RESULT_ERROR_GENERIC_FAILURE to be returned to app.
    369                 tracker.mRetryCount = MAX_SEND_RETRIES;
    370 
    371                 Rlog.d(TAG, "handleSendComplete: Skipping retry: "
    372                 +" isIms()="+isIms()
    373                 +" mRetryCount="+tracker.mRetryCount
    374                 +" mImsRetry="+tracker.mImsRetry
    375                 +" mMessageRef="+tracker.mMessageRef
    376                 +" SS= "+mPhone.getServiceState().getState());
    377             }
    378 
    379             // if sms over IMS is not supported on data and voice is not available...
    380             if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
    381                 tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
    382             } else if ((((CommandException)(ar.exception)).getCommandError()
    383                     == CommandException.Error.SMS_FAIL_RETRY) &&
    384                    tracker.mRetryCount < MAX_SEND_RETRIES) {
    385                 // Retry after a delay if needed.
    386                 // TODO: According to TS 23.040, 9.2.3.6, we should resend
    387                 //       with the same TP-MR as the failed message, and
    388                 //       TP-RD set to 1.  However, we don't have a means of
    389                 //       knowing the MR for the failed message (EF_SMSstatus
    390                 //       may or may not have the MR corresponding to this
    391                 //       message, depending on the failure).  Also, in some
    392                 //       implementations this retry is handled by the baseband.
    393                 tracker.mRetryCount++;
    394                 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
    395                 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
    396             } else {
    397                 int errorCode = 0;
    398                 if (ar.result != null) {
    399                     errorCode = ((SmsResponse)ar.result).mErrorCode;
    400                 }
    401                 int error = RESULT_ERROR_GENERIC_FAILURE;
    402                 if (((CommandException)(ar.exception)).getCommandError()
    403                         == CommandException.Error.FDN_CHECK_FAILURE) {
    404                     error = RESULT_ERROR_FDN_CHECK_FAILURE;
    405                 }
    406                 tracker.onFailed(mContext, error, errorCode);
    407             }
    408         }
    409     }
    410 
    411     /**
    412      * Handles outbound message when the phone is not in service.
    413      *
    414      * @param ss     Current service state.  Valid values are:
    415      *                  OUT_OF_SERVICE
    416      *                  EMERGENCY_ONLY
    417      *                  POWER_OFF
    418      * @param sentIntent the PendingIntent to send the error to
    419      */
    420     protected static void handleNotInService(int ss, PendingIntent sentIntent) {
    421         if (sentIntent != null) {
    422             try {
    423                 if (ss == ServiceState.STATE_POWER_OFF) {
    424                     sentIntent.send(RESULT_ERROR_RADIO_OFF);
    425                 } else {
    426                     sentIntent.send(RESULT_ERROR_NO_SERVICE);
    427                 }
    428             } catch (CanceledException ex) {}
    429         }
    430     }
    431 
    432     /**
    433      * @param ss service state
    434      * @return The result error based on input service state for not in service error
    435      */
    436     protected static int getNotInServiceError(int ss) {
    437         if (ss == ServiceState.STATE_POWER_OFF) {
    438             return RESULT_ERROR_RADIO_OFF;
    439         }
    440         return RESULT_ERROR_NO_SERVICE;
    441     }
    442 
    443     /**
    444      * Send a data based SMS to a specific application port.
    445      *
    446      * @param destAddr the address to send the message to
    447      * @param scAddr is the service center address or null to use
    448      *  the current default SMSC
    449      * @param destPort the port to deliver the message to
    450      * @param data the body of the message to send
    451      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    452      *  broadcast when the message is successfully sent, or failed.
    453      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    454      *  or one of these errors:<br>
    455      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    456      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    457      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    458      *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
    459      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    460      *  the extra "errorCode" containing a radio technology specific value,
    461      *  generally only useful for troubleshooting.<br>
    462      *  The per-application based SMS control checks sentIntent. If sentIntent
    463      *  is NULL the caller will be checked against all unknown applications,
    464      *  which cause smaller number of SMS to be sent in checking period.
    465      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    466      *  broadcast when the message is delivered to the recipient.  The
    467      *  raw pdu of the status report is in the extended data ("pdu").
    468      */
    469     protected abstract void sendData(String destAddr, String scAddr, int destPort,
    470             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
    471 
    472     /**
    473      * Send a text based SMS.
    474      *  @param destAddr the address to send the message to
    475      * @param scAddr is the service center address or null to use
    476      *  the current default SMSC
    477      * @param text the body of the message to send
    478      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    479      *  broadcast when the message is successfully sent, or failed.
    480      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    481      *  or one of these errors:<br>
    482      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    483      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    484      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    485      *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
    486      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    487      *  the extra "errorCode" containing a radio technology specific value,
    488      *  generally only useful for troubleshooting.<br>
    489      *  The per-application based SMS control checks sentIntent. If sentIntent
    490      *  is NULL the caller will be checked against all unknown applications,
    491      *  which cause smaller number of SMS to be sent in checking period.
    492      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    493      *  broadcast when the message is delivered to the recipient.  The
    494      * @param messageUri optional URI of the message if it is already stored in the system
    495      * @param callingPkg the calling package name
    496      */
    497     protected abstract void sendText(String destAddr, String scAddr, String text,
    498             PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
    499             String callingPkg);
    500 
    501     /**
    502      * Inject an SMS PDU into the android platform.
    503      *
    504      * @param pdu is the byte array of pdu to be injected into android telephony layer
    505      * @param format is the format of SMS pdu (3gpp or 3gpp2)
    506      * @param receivedIntent if not NULL this <code>PendingIntent</code> is
    507      *  broadcast when the message is successfully received by the
    508      *  android telephony layer. This intent is broadcasted at
    509      *  the same time an SMS received from radio is responded back.
    510      */
    511     protected abstract void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent);
    512 
    513     /**
    514      * Calculate the number of septets needed to encode the message.
    515      *
    516      * @param messageBody the message to encode
    517      * @param use7bitOnly ignore (but still count) illegal characters if true
    518      * @return TextEncodingDetails
    519      */
    520     protected abstract TextEncodingDetails calculateLength(CharSequence messageBody,
    521             boolean use7bitOnly);
    522 
    523     /**
    524      * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
    525      * This outbound message was handled by the carrier app. If the carrier app fails to send
    526      * this message, it would be resent by PSTN.
    527      *
    528      * @param messageRef the reference number of the SMS message.
    529      * @param success True if and only if the message was sent successfully. If its value is
    530      *  false, this message should be resent via PSTN.
    531      */
    532     protected abstract void updateSmsSendStatus(int messageRef, boolean success);
    533 
    534     /**
    535      * Handler for a {@link GsmSMSDispatcher} or {@link CdmaSMSDispatcher} broadcast.
    536      * If SMS sending is successfuly, sends EVENT_SEND_SMS_COMPLETE message. Otherwise,
    537      * send the message via the GSM/CDMA network.
    538      */
    539     protected final class SMSDispatcherReceiver extends BroadcastReceiver {
    540 
    541         private final SmsTracker mTracker;
    542 
    543         public SMSDispatcherReceiver(SmsTracker tracker) {
    544             mTracker = tracker;
    545         }
    546 
    547         @Override
    548         public void onReceive(Context context, Intent intent) {
    549             String action = intent.getAction();
    550             if (action.equals(Intents.SMS_SEND_ACTION)) {
    551                 int rc = getResultCode();
    552                 if (rc == Activity.RESULT_OK) {
    553                     Rlog.d(TAG, "Sending SMS by IP pending.");
    554                     Bundle resultExtras = getResultExtras(false);
    555                     if (resultExtras != null && resultExtras.containsKey("messageref")) {
    556                         mTracker.mMessageRef = resultExtras.getInt("messageref");
    557                         Rlog.d(TAG, "messageref = " + mTracker.mMessageRef);
    558                     } else {
    559                         Rlog.e(TAG, "Can't find messageref in result extras.");
    560                     }
    561                     sendPendingList.add(mTracker);
    562                 } else {
    563                     Rlog.d(TAG, "Sending SMS by IP failed.");
    564                     sendSmsByPstn(mTracker);
    565                 }
    566             } else {
    567                 Rlog.e(TAG, "unexpected BroadcastReceiver action: " + action);
    568             }
    569         }
    570     }
    571 
    572     /**
    573      * Send a multi-part text based SMS.
    574      *  @param destAddr the address to send the message to
    575      * @param scAddr is the service center address or null to use
    576      *   the current default SMSC
    577      * @param parts an <code>ArrayList</code> of strings that, in order,
    578      *   comprise the original message
    579      * @param sentIntents if not null, an <code>ArrayList</code> of
    580      *   <code>PendingIntent</code>s (one for each message part) that is
    581      *   broadcast when the corresponding message part has been sent.
    582      *   The result code will be <code>Activity.RESULT_OK<code> for success,
    583      *   or one of these errors:
    584      *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
    585      *   <code>RESULT_ERROR_RADIO_OFF</code>
    586      *   <code>RESULT_ERROR_NULL_PDU</code>
    587      *   <code>RESULT_ERROR_NO_SERVICE</code>.
    588      *  The per-application based SMS control checks sentIntent. If sentIntent
    589      *  is NULL the caller will be checked against all unknown applications,
    590      *  which cause smaller number of SMS to be sent in checking period.
    591      * @param deliveryIntents if not null, an <code>ArrayList</code> of
    592      *   <code>PendingIntent</code>s (one for each message part) that is
    593      *   broadcast when the corresponding message part has been delivered
    594      *   to the recipient.  The raw pdu of the status report is in the
    595      * @param messageUri optional URI of the message if it is already stored in the system
    596      * @param callingPkg the calling package name
    597      */
    598     protected void sendMultipartText(String destAddr, String scAddr,
    599             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
    600             ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg) {
    601         if (messageUri == null) {
    602             if (SmsApplication.shouldWriteMessageForPackage(callingPkg, mContext)) {
    603                 messageUri = writeOutboxMessage(
    604                         getSubId(),
    605                         destAddr,
    606                         getMultipartMessageText(parts),
    607                         deliveryIntents != null && deliveryIntents.size() > 0,
    608                         callingPkg);
    609             }
    610         } else {
    611             moveToOutbox(getSubId(), messageUri, callingPkg);
    612         }
    613         int refNumber = getNextConcatenatedRef() & 0x00FF;
    614         int msgCount = parts.size();
    615         int encoding = SmsConstants.ENCODING_UNKNOWN;
    616 
    617         TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
    618         for (int i = 0; i < msgCount; i++) {
    619             TextEncodingDetails details = calculateLength(parts.get(i), false);
    620             if (encoding != details.codeUnitSize
    621                     && (encoding == SmsConstants.ENCODING_UNKNOWN
    622                             || encoding == SmsConstants.ENCODING_7BIT)) {
    623                 encoding = details.codeUnitSize;
    624             }
    625             encodingForParts[i] = details;
    626         }
    627 
    628         // States to track at the message level (for all parts)
    629         final AtomicInteger unsentPartCount = new AtomicInteger(msgCount);
    630         final AtomicBoolean anyPartFailed = new AtomicBoolean(false);
    631 
    632         for (int i = 0; i < msgCount; i++) {
    633             SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
    634             concatRef.refNumber = refNumber;
    635             concatRef.seqNumber = i + 1;  // 1-based sequence
    636             concatRef.msgCount = msgCount;
    637             // TODO: We currently set this to true since our messaging app will never
    638             // send more than 255 parts (it converts the message to MMS well before that).
    639             // However, we should support 3rd party messaging apps that might need 16-bit
    640             // references
    641             // Note:  It's not sufficient to just flip this bit to true; it will have
    642             // ripple effects (several calculations assume 8-bit ref).
    643             concatRef.isEightBits = true;
    644             SmsHeader smsHeader = new SmsHeader();
    645             smsHeader.concatRef = concatRef;
    646 
    647             // Set the national language tables for 3GPP 7-bit encoding, if enabled.
    648             if (encoding == SmsConstants.ENCODING_7BIT) {
    649                 smsHeader.languageTable = encodingForParts[i].languageTable;
    650                 smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
    651             }
    652 
    653             PendingIntent sentIntent = null;
    654             if (sentIntents != null && sentIntents.size() > i) {
    655                 sentIntent = sentIntents.get(i);
    656             }
    657 
    658             PendingIntent deliveryIntent = null;
    659             if (deliveryIntents != null && deliveryIntents.size() > i) {
    660                 deliveryIntent = deliveryIntents.get(i);
    661             }
    662 
    663             sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding,
    664                     sentIntent, deliveryIntent, (i == (msgCount - 1)),
    665                     unsentPartCount, anyPartFailed, messageUri);
    666         }
    667     }
    668 
    669     /**
    670      * Create a new SubmitPdu and send it.
    671      */
    672     protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress,
    673             String message, SmsHeader smsHeader, int encoding,
    674             PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
    675             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri);
    676 
    677     /**
    678      * Send a SMS
    679      * @param tracker will contain:
    680      * -smsc the SMSC to send the message through, or NULL for the
    681      *  default SMSC
    682      * -pdu the raw PDU to send
    683      * -sentIntent if not NULL this <code>Intent</code> is
    684      *  broadcast when the message is successfully sent, or failed.
    685      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    686      *  or one of these errors:
    687      *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
    688      *  <code>RESULT_ERROR_RADIO_OFF</code>
    689      *  <code>RESULT_ERROR_NULL_PDU</code>
    690      *  <code>RESULT_ERROR_NO_SERVICE</code>.
    691      *  The per-application based SMS control checks sentIntent. If sentIntent
    692      *  is NULL the caller will be checked against all unknown applications,
    693      *  which cause smaller number of SMS to be sent in checking period.
    694      * -deliveryIntent if not NULL this <code>Intent</code> is
    695      *  broadcast when the message is delivered to the recipient.  The
    696      *  raw pdu of the status report is in the extended data ("pdu").
    697      * -param destAddr the destination phone number (for short code confirmation)
    698      */
    699     protected void sendRawPdu(SmsTracker tracker) {
    700         HashMap map = tracker.mData;
    701         byte pdu[] = (byte[]) map.get("pdu");
    702 
    703         if (mSmsSendDisabled) {
    704             Rlog.e(TAG, "Device does not support sending sms.");
    705             tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
    706             return;
    707         }
    708 
    709         if (pdu == null) {
    710             Rlog.e(TAG, "Empty PDU");
    711             tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/);
    712             return;
    713         }
    714 
    715         // Get calling app package name via UID from Binder call
    716         PackageManager pm = mContext.getPackageManager();
    717         String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
    718 
    719         if (packageNames == null || packageNames.length == 0) {
    720             // Refuse to send SMS if we can't get the calling package name.
    721             Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS");
    722             tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
    723             return;
    724         }
    725 
    726         // Get package info via packagemanager
    727         PackageInfo appInfo;
    728         try {
    729             // XXX this is lossy- apps can share a UID
    730             appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
    731         } catch (PackageManager.NameNotFoundException e) {
    732             Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");
    733             tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
    734             return;
    735         }
    736 
    737         // checkDestination() returns true if the destination is not a premium short code or the
    738         // sending app is approved to send to short codes. Otherwise, a message is sent to our
    739         // handler with the SmsTracker to request user confirmation before sending.
    740         if (checkDestination(tracker)) {
    741             // check for excessive outgoing SMS usage by this app
    742             if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {
    743                 sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
    744                 return;
    745             }
    746 
    747             sendSms(tracker);
    748         }
    749     }
    750 
    751     /**
    752      * Check if destination is a potential premium short code and sender is not pre-approved to
    753      * send to short codes.
    754      *
    755      * @param tracker the tracker for the SMS to send
    756      * @return true if the destination is approved; false if user confirmation event was sent
    757      */
    758     boolean checkDestination(SmsTracker tracker) {
    759         if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION)
    760                 == PackageManager.PERMISSION_GRANTED) {
    761             return true;            // app is pre-approved to send to short codes
    762         } else {
    763             int rule = mPremiumSmsRule.get();
    764             int smsCategory = SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE;
    765             if (rule == PREMIUM_RULE_USE_SIM || rule == PREMIUM_RULE_USE_BOTH) {
    766                 String simCountryIso = mTelephonyManager.getSimCountryIso();
    767                 if (simCountryIso == null || simCountryIso.length() != 2) {
    768                     Rlog.e(TAG, "Can't get SIM country Iso: trying network country Iso");
    769                     simCountryIso = mTelephonyManager.getNetworkCountryIso();
    770                 }
    771 
    772                 smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso);
    773             }
    774             if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) {
    775                 String networkCountryIso = mTelephonyManager.getNetworkCountryIso();
    776                 if (networkCountryIso == null || networkCountryIso.length() != 2) {
    777                     Rlog.e(TAG, "Can't get Network country Iso: trying SIM country Iso");
    778                     networkCountryIso = mTelephonyManager.getSimCountryIso();
    779                 }
    780 
    781                 smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory,
    782                         mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso));
    783             }
    784 
    785             if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE
    786                     || smsCategory == SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE
    787                     || smsCategory == SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE) {
    788                 return true;    // not a premium short code
    789             }
    790 
    791             // Wait for user confirmation unless the user has set permission to always allow/deny
    792             int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
    793                     tracker.mAppInfo.packageName);
    794             if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
    795                 // First time trying to send to premium SMS.
    796                 premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
    797             }
    798 
    799             switch (premiumSmsPermission) {
    800                 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW:
    801                     Rlog.d(TAG, "User approved this app to send to premium SMS");
    802                     return true;
    803 
    804                 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
    805                     Rlog.w(TAG, "User denied this app from sending to premium SMS");
    806                     sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker));
    807                     return false;   // reject this message
    808 
    809                 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
    810                 default:
    811                     int event;
    812                     if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) {
    813                         event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE;
    814                     } else {
    815                         event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE;
    816                     }
    817                     sendMessage(obtainMessage(event, tracker));
    818                     return false;   // wait for user confirmation
    819             }
    820         }
    821     }
    822 
    823     /**
    824      * Deny sending an SMS if the outgoing queue limit is reached. Used when the message
    825      * must be confirmed by the user due to excessive usage or potential premium SMS detected.
    826      * @param tracker the SmsTracker for the message to send
    827      * @return true if the message was denied; false to continue with send confirmation
    828      */
    829     private boolean denyIfQueueLimitReached(SmsTracker tracker) {
    830         if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) {
    831             // Deny sending message when the queue limit is reached.
    832             Rlog.e(TAG, "Denied because queue limit reached");
    833             tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
    834             return true;
    835         }
    836         mPendingTrackerCount++;
    837         return false;
    838     }
    839 
    840     /**
    841      * Returns the label for the specified app package name.
    842      * @param appPackage the package name of the app requesting to send an SMS
    843      * @return the label for the specified app, or the package name if getApplicationInfo() fails
    844      */
    845     private CharSequence getAppLabel(String appPackage) {
    846         PackageManager pm = mContext.getPackageManager();
    847         try {
    848             ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0);
    849             return appInfo.loadLabel(pm);
    850         } catch (PackageManager.NameNotFoundException e) {
    851             Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage);
    852             return appPackage;  // fall back to package name if we can't get app label
    853         }
    854     }
    855 
    856     /**
    857      * Post an alert when SMS needs confirmation due to excessive usage.
    858      * @param tracker an SmsTracker for the current message.
    859      */
    860     protected void handleReachSentLimit(SmsTracker tracker) {
    861         if (denyIfQueueLimitReached(tracker)) {
    862             return;     // queue limit reached; error was returned to caller
    863         }
    864 
    865         CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
    866         Resources r = Resources.getSystem();
    867         Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
    868 
    869         ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null);
    870 
    871         AlertDialog d = new AlertDialog.Builder(mContext)
    872                 .setTitle(R.string.sms_control_title)
    873                 .setIcon(R.drawable.stat_sys_warning)
    874                 .setMessage(messageText)
    875                 .setPositiveButton(r.getString(R.string.sms_control_yes), listener)
    876                 .setNegativeButton(r.getString(R.string.sms_control_no), listener)
    877                 .setOnCancelListener(listener)
    878                 .create();
    879 
    880         d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    881         d.show();
    882     }
    883 
    884     /**
    885      * Post an alert for user confirmation when sending to a potential short code.
    886      * @param isPremium true if the destination is known to be a premium short code
    887      * @param tracker the SmsTracker for the current message.
    888      */
    889     protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) {
    890         if (denyIfQueueLimitReached(tracker)) {
    891             return;     // queue limit reached; error was returned to caller
    892         }
    893 
    894         int detailsId;
    895         if (isPremium) {
    896             detailsId = R.string.sms_premium_short_code_details;
    897         } else {
    898             detailsId = R.string.sms_short_code_details;
    899         }
    900 
    901         CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
    902         Resources r = Resources.getSystem();
    903         Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
    904                 appLabel, tracker.mDestAddress));
    905 
    906         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
    907                 Context.LAYOUT_INFLATER_SERVICE);
    908         View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null);
    909 
    910         ConfirmDialogListener listener = new ConfirmDialogListener(tracker,
    911                 (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction));
    912 
    913 
    914         TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message);
    915         messageView.setText(messageText);
    916 
    917         ViewGroup detailsLayout = (ViewGroup) layout.findViewById(
    918                 R.id.sms_short_code_detail_layout);
    919         TextView detailsView = (TextView) detailsLayout.findViewById(
    920                 R.id.sms_short_code_detail_message);
    921         detailsView.setText(detailsId);
    922 
    923         CheckBox rememberChoice = (CheckBox) layout.findViewById(
    924                 R.id.sms_short_code_remember_choice_checkbox);
    925         rememberChoice.setOnCheckedChangeListener(listener);
    926 
    927         AlertDialog d = new AlertDialog.Builder(mContext)
    928                 .setView(layout)
    929                 .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener)
    930                 .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener)
    931                 .setOnCancelListener(listener)
    932                 .create();
    933 
    934         d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    935         d.show();
    936 
    937         listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE));
    938         listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE));
    939     }
    940 
    941     /**
    942      * Returns the premium SMS permission for the specified package. If the package has never
    943      * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
    944      * will be returned.
    945      * @param packageName the name of the package to query permission
    946      * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
    947      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
    948      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
    949      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
    950      */
    951     public int getPremiumSmsPermission(String packageName) {
    952         return mUsageMonitor.getPremiumSmsPermission(packageName);
    953     }
    954 
    955     /**
    956      * Sets the premium SMS permission for the specified package and save the value asynchronously
    957      * to persistent storage.
    958      * @param packageName the name of the package to set permission
    959      * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
    960      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
    961      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
    962      */
    963     public void setPremiumSmsPermission(String packageName, int permission) {
    964         mUsageMonitor.setPremiumSmsPermission(packageName, permission);
    965     }
    966 
    967     /**
    968      * Send the message along to the radio.
    969      *
    970      * @param tracker holds the SMS message to send
    971      */
    972     protected abstract void sendSms(SmsTracker tracker);
    973 
    974     /**
    975      * Send the SMS via the PSTN network.
    976      *
    977      * @param tracker holds the Sms tracker ready to be sent
    978      */
    979     protected abstract void sendSmsByPstn(SmsTracker tracker);
    980 
    981     /**
    982      * Retry the message along to the radio.
    983      *
    984      * @param tracker holds the SMS message to send
    985      */
    986     public void sendRetrySms(SmsTracker tracker) {
    987         // re-routing to ImsSMSDispatcher
    988         if (mImsSMSDispatcher != null) {
    989             mImsSMSDispatcher.sendRetrySms(tracker);
    990         } else {
    991             Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed");
    992         }
    993     }
    994 
    995     /**
    996      * Send the multi-part SMS based on multipart Sms tracker
    997      *
    998      * @param tracker holds the multipart Sms tracker ready to be sent
    999      */
   1000     private void sendMultipartSms(SmsTracker tracker) {
   1001         ArrayList<String> parts;
   1002         ArrayList<PendingIntent> sentIntents;
   1003         ArrayList<PendingIntent> deliveryIntents;
   1004 
   1005         HashMap<String, Object> map = tracker.mData;
   1006 
   1007         String destinationAddress = (String) map.get("destination");
   1008         String scAddress = (String) map.get("scaddress");
   1009 
   1010         parts = (ArrayList<String>) map.get("parts");
   1011         sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
   1012         deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
   1013 
   1014         // check if in service
   1015         int ss = mPhone.getServiceState().getState();
   1016         // if sms over IMS is not supported on data and voice is not available...
   1017         if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
   1018             for (int i = 0, count = parts.size(); i < count; i++) {
   1019                 PendingIntent sentIntent = null;
   1020                 if (sentIntents != null && sentIntents.size() > i) {
   1021                     sentIntent = sentIntents.get(i);
   1022                 }
   1023                 handleNotInService(ss, sentIntent);
   1024             }
   1025             return;
   1026         }
   1027 
   1028         sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
   1029                 null/*messageUri*/, null/*callingPkg*/);
   1030     }
   1031 
   1032     /**
   1033      * Keeps track of an SMS that has been sent to the RIL, until it has
   1034      * successfully been sent, or we're done trying.
   1035      */
   1036     protected static final class SmsTracker {
   1037         // fields need to be public for derived SmsDispatchers
   1038         public final HashMap<String, Object> mData;
   1039         public int mRetryCount;
   1040         public int mImsRetry; // nonzero indicates initial message was sent over Ims
   1041         public int mMessageRef;
   1042         public boolean mExpectMore;
   1043         String mFormat;
   1044 
   1045         public final PendingIntent mSentIntent;
   1046         public final PendingIntent mDeliveryIntent;
   1047 
   1048         public final PackageInfo mAppInfo;
   1049         public final String mDestAddress;
   1050 
   1051         public final SmsHeader mSmsHeader;
   1052 
   1053         private long mTimestamp = System.currentTimeMillis();
   1054         public Uri mMessageUri; // Uri of persisted message if we wrote one
   1055 
   1056         // Reference to states of a multipart message that this part belongs to
   1057         private AtomicInteger mUnsentPartCount;
   1058         private AtomicBoolean mAnyPartFailed;
   1059 
   1060         private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
   1061                 PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
   1062                 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
   1063                 SmsHeader smsHeader, boolean isExpectMore) {
   1064             mData = data;
   1065             mSentIntent = sentIntent;
   1066             mDeliveryIntent = deliveryIntent;
   1067             mRetryCount = 0;
   1068             mAppInfo = appInfo;
   1069             mDestAddress = destAddr;
   1070             mFormat = format;
   1071             mExpectMore = isExpectMore;
   1072             mImsRetry = 0;
   1073             mMessageRef = 0;
   1074             mUnsentPartCount = unsentPartCount;
   1075             mAnyPartFailed = anyPartFailed;
   1076             mMessageUri = messageUri;
   1077             mSmsHeader = smsHeader;
   1078         }
   1079 
   1080         /**
   1081          * Returns whether this tracker holds a multi-part SMS.
   1082          * @return true if the tracker holds a multi-part SMS; false otherwise
   1083          */
   1084         boolean isMultipart() {
   1085             return mData.containsKey("parts");
   1086         }
   1087 
   1088         /**
   1089          * Persist this as a sent message
   1090          */
   1091         void writeSentMessage(Context context) {
   1092             String text = (String)mData.get("text");
   1093             if (text != null) {
   1094                 boolean deliveryReport = (mDeliveryIntent != null);
   1095                 // Using invalid threadId 0 here. When the message is inserted into the db, the
   1096                 // provider looks up the threadId based on the recipient(s).
   1097                 mMessageUri = Sms.addMessageToUri(context.getContentResolver(),
   1098                         Telephony.Sms.Sent.CONTENT_URI,
   1099                         mDestAddress,
   1100                         text /*body*/,
   1101                         null /*subject*/,
   1102                         mTimestamp /*date*/,
   1103                         true /*read*/,
   1104                         deliveryReport /*deliveryReport*/,
   1105                         0 /*threadId*/);
   1106             }
   1107         }
   1108 
   1109         /**
   1110          * Update the status of this message if we persisted it
   1111          */
   1112         public void updateSentMessageStatus(Context context, int status) {
   1113             if (mMessageUri != null) {
   1114                 // If we wrote this message in writeSentMessage, update it now
   1115                 ContentValues values = new ContentValues(1);
   1116                 values.put(Sms.STATUS, status);
   1117                 SqliteWrapper.update(context, context.getContentResolver(),
   1118                         mMessageUri, values, null, null);
   1119             }
   1120         }
   1121 
   1122         /**
   1123          * Update the error_code column of a message
   1124          *
   1125          * @param context The Context
   1126          * @param errorCode The error code
   1127          */
   1128         private void updateMessageErrorCode(Context context, int errorCode) {
   1129             if (mMessageUri == null) {
   1130                 return;
   1131             }
   1132             final ContentValues values = new ContentValues(1);
   1133             values.put(Sms.ERROR_CODE, errorCode);
   1134             final long identity = Binder.clearCallingIdentity();
   1135             try {
   1136                 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
   1137                         null/*where*/, null/*selectionArgs*/) != 1) {
   1138                     Rlog.e(TAG, "Failed to update message error code");
   1139                 }
   1140             } finally {
   1141                 Binder.restoreCallingIdentity(identity);
   1142             }
   1143         }
   1144 
   1145         /**
   1146          * Set the final state of a message: FAILED or SENT
   1147          *
   1148          * @param context The Context
   1149          * @param messageType The final message type
   1150          */
   1151         private void setMessageFinalState(Context context, int messageType) {
   1152             if (mMessageUri == null) {
   1153                 return;
   1154             }
   1155             final ContentValues values = new ContentValues(1);
   1156             values.put(Sms.TYPE, messageType);
   1157             final long identity = Binder.clearCallingIdentity();
   1158             try {
   1159                 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
   1160                         null/*where*/, null/*selectionArgs*/) != 1) {
   1161                     Rlog.e(TAG, "Failed to move message to " + messageType);
   1162                 }
   1163             } finally {
   1164                 Binder.restoreCallingIdentity(identity);
   1165             }
   1166         }
   1167 
   1168         /**
   1169          * Handle a failure of a single part message or a part of a multipart message
   1170          *
   1171          * @param context The Context
   1172          * @param error The error to send back with
   1173          * @param errorCode
   1174          */
   1175         public void onFailed(Context context, int error, int errorCode) {
   1176             if (mAnyPartFailed != null) {
   1177                 mAnyPartFailed.set(true);
   1178             }
   1179             // is single part or last part of multipart message
   1180             boolean isSinglePartOrLastPart = true;
   1181             if (mUnsentPartCount != null) {
   1182                 isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
   1183             }
   1184             if (errorCode != 0) {
   1185                 updateMessageErrorCode(context, errorCode);
   1186             }
   1187             if (isSinglePartOrLastPart) {
   1188                 setMessageFinalState(context, Sms.MESSAGE_TYPE_FAILED);
   1189             }
   1190             if (mSentIntent != null) {
   1191                 try {
   1192                     // Extra information to send with the sent intent
   1193                     Intent fillIn = new Intent();
   1194                     if (mMessageUri != null) {
   1195                         // Pass this to SMS apps so that they know where it is stored
   1196                         fillIn.putExtra("uri", mMessageUri.toString());
   1197                     }
   1198                     if (errorCode != 0) {
   1199                         fillIn.putExtra("errorCode", errorCode);
   1200                     }
   1201                     if (mUnsentPartCount != null && isSinglePartOrLastPart) {
   1202                         // Is multipart and last part
   1203                         fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
   1204                     }
   1205                     mSentIntent.send(context, error, fillIn);
   1206                 } catch (CanceledException ex) {
   1207                     Rlog.e(TAG, "Failed to send result");
   1208                 }
   1209             }
   1210         }
   1211 
   1212         /**
   1213          * Handle the sent of a single part message or a part of a multipart message
   1214          *
   1215          * @param context The Context
   1216          */
   1217         public void onSent(Context context) {
   1218             // is single part or last part of multipart message
   1219             boolean isSinglePartOrLastPart = true;
   1220             if (mUnsentPartCount != null) {
   1221                 isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
   1222             }
   1223             if (isSinglePartOrLastPart) {
   1224                 boolean success = true;
   1225                 if (mAnyPartFailed != null && mAnyPartFailed.get()) {
   1226                     success = false;
   1227                 }
   1228                 if (success) {
   1229                     setMessageFinalState(context, Sms.MESSAGE_TYPE_SENT);
   1230                 } else {
   1231                     setMessageFinalState(context, Sms.MESSAGE_TYPE_FAILED);
   1232                 }
   1233             }
   1234             if (mSentIntent != null) {
   1235                 try {
   1236                     // Extra information to send with the sent intent
   1237                     Intent fillIn = new Intent();
   1238                     if (mMessageUri != null) {
   1239                         // Pass this to SMS apps so that they know where it is stored
   1240                         fillIn.putExtra("uri", mMessageUri.toString());
   1241                     }
   1242                     if (mUnsentPartCount != null && isSinglePartOrLastPart) {
   1243                         // Is multipart and last part
   1244                         fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
   1245                     }
   1246                     mSentIntent.send(context, Activity.RESULT_OK, fillIn);
   1247                 } catch (CanceledException ex) {
   1248                     Rlog.e(TAG, "Failed to send result");
   1249                 }
   1250             }
   1251         }
   1252     }
   1253 
   1254     protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
   1255             PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
   1256             AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
   1257             boolean isExpectMore) {
   1258         // Get calling app package name via UID from Binder call
   1259         PackageManager pm = mContext.getPackageManager();
   1260         String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
   1261 
   1262         // Get package info via packagemanager
   1263         PackageInfo appInfo = null;
   1264         if (packageNames != null && packageNames.length > 0) {
   1265             try {
   1266                 // XXX this is lossy- apps can share a UID
   1267                 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
   1268             } catch (PackageManager.NameNotFoundException e) {
   1269                 // error will be logged in sendRawPdu
   1270             }
   1271         }
   1272         // Strip non-digits from destination phone number before checking for short codes
   1273         // and before displaying the number to the user if confirmation is required.
   1274         String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
   1275         return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
   1276                 unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore);
   1277     }
   1278 
   1279     protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
   1280             PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore) {
   1281         return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
   1282                 null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore);
   1283     }
   1284 
   1285     protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
   1286             String text, SmsMessageBase.SubmitPduBase pdu) {
   1287         HashMap<String, Object> map = new HashMap<String, Object>();
   1288         map.put("destAddr", destAddr);
   1289         map.put("scAddr", scAddr);
   1290         map.put("text", text);
   1291         map.put("smsc", pdu.encodedScAddress);
   1292         map.put("pdu", pdu.encodedMessage);
   1293         return map;
   1294     }
   1295 
   1296     protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
   1297             int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
   1298         HashMap<String, Object> map = new HashMap<String, Object>();
   1299         map.put("destAddr", destAddr);
   1300         map.put("scAddr", scAddr);
   1301         map.put("destPort", destPort);
   1302         map.put("data", data);
   1303         map.put("smsc", pdu.encodedScAddress);
   1304         map.put("pdu", pdu.encodedMessage);
   1305         return map;
   1306     }
   1307 
   1308     /**
   1309      * Dialog listener for SMS confirmation dialog.
   1310      */
   1311     private final class ConfirmDialogListener
   1312             implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
   1313             CompoundButton.OnCheckedChangeListener {
   1314 
   1315         private final SmsTracker mTracker;
   1316         private Button mPositiveButton;
   1317         private Button mNegativeButton;
   1318         private boolean mRememberChoice;    // default is unchecked
   1319         private final TextView mRememberUndoInstruction;
   1320 
   1321         ConfirmDialogListener(SmsTracker tracker, TextView textView) {
   1322             mTracker = tracker;
   1323             mRememberUndoInstruction = textView;
   1324         }
   1325 
   1326         void setPositiveButton(Button button) {
   1327             mPositiveButton = button;
   1328         }
   1329 
   1330         void setNegativeButton(Button button) {
   1331             mNegativeButton = button;
   1332         }
   1333 
   1334         @Override
   1335         public void onClick(DialogInterface dialog, int which) {
   1336             // Always set the SMS permission so that Settings will show a permission setting
   1337             // for the app (it won't be shown until after the app tries to send to a short code).
   1338             int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
   1339 
   1340             if (which == DialogInterface.BUTTON_POSITIVE) {
   1341                 Rlog.d(TAG, "CONFIRM sending SMS");
   1342                 // XXX this is lossy- apps can have more than one signature
   1343                 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER,
   1344                                     mTracker.mAppInfo.applicationInfo == null ?
   1345                                     -1 : mTracker.mAppInfo.applicationInfo.uid);
   1346                 sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker));
   1347                 if (mRememberChoice) {
   1348                     newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW;
   1349                 }
   1350             } else if (which == DialogInterface.BUTTON_NEGATIVE) {
   1351                 Rlog.d(TAG, "DENY sending SMS");
   1352                 // XXX this is lossy- apps can have more than one signature
   1353                 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
   1354                                     mTracker.mAppInfo.applicationInfo == null ?
   1355                                     -1 :  mTracker.mAppInfo.applicationInfo.uid);
   1356                 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
   1357                 if (mRememberChoice) {
   1358                     newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
   1359                 }
   1360             }
   1361             setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
   1362         }
   1363 
   1364         @Override
   1365         public void onCancel(DialogInterface dialog) {
   1366             Rlog.d(TAG, "dialog dismissed: don't send SMS");
   1367             sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
   1368         }
   1369 
   1370         @Override
   1371         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
   1372             Rlog.d(TAG, "remember this choice: " + isChecked);
   1373             mRememberChoice = isChecked;
   1374             if (isChecked) {
   1375                 mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow);
   1376                 mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow);
   1377                 if (mRememberUndoInstruction != null) {
   1378                     mRememberUndoInstruction.
   1379                             setText(R.string.sms_short_code_remember_undo_instruction);
   1380                     mRememberUndoInstruction.setPadding(0,0,0,32);
   1381                 }
   1382             } else {
   1383                 mPositiveButton.setText(R.string.sms_short_code_confirm_allow);
   1384                 mNegativeButton.setText(R.string.sms_short_code_confirm_deny);
   1385                 if (mRememberUndoInstruction != null) {
   1386                     mRememberUndoInstruction.setText("");
   1387                     mRememberUndoInstruction.setPadding(0,0,0,0);
   1388                 }
   1389             }
   1390         }
   1391     }
   1392 
   1393     public boolean isIms() {
   1394         if (mImsSMSDispatcher != null) {
   1395             return mImsSMSDispatcher.isIms();
   1396         } else {
   1397             Rlog.e(TAG, mImsSMSDispatcher + " is null");
   1398             return false;
   1399         }
   1400     }
   1401 
   1402     public String getImsSmsFormat() {
   1403         if (mImsSMSDispatcher != null) {
   1404             return mImsSMSDispatcher.getImsSmsFormat();
   1405         } else {
   1406             Rlog.e(TAG, mImsSMSDispatcher + " is null");
   1407             return null;
   1408         }
   1409     }
   1410 
   1411     protected Uri writeOutboxMessage(long subId, String address, String text,
   1412             boolean requireDeliveryReport, String creator) {
   1413         final ContentValues values = new ContentValues(8);
   1414         values.put(Telephony.Sms.SUB_ID, subId);
   1415         values.put(Telephony.Sms.ADDRESS, address);
   1416         values.put(Telephony.Sms.BODY, text);
   1417         values.put(Telephony.Sms.DATE, System.currentTimeMillis()); // milliseconds
   1418         values.put(Telephony.Sms.SEEN, 1);
   1419         values.put(Telephony.Sms.READ, 1);
   1420         if (!TextUtils.isEmpty(creator)) {
   1421             values.put(Telephony.Sms.CREATOR, creator);
   1422         }
   1423         if (requireDeliveryReport) {
   1424             values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_PENDING);
   1425         }
   1426         final long identity = Binder.clearCallingIdentity();
   1427         try {
   1428             final Uri uri =  mContext.getContentResolver().insert(
   1429                     Telephony.Sms.Outbox.CONTENT_URI, values);
   1430             return uri;
   1431         } catch (Exception e) {
   1432             Rlog.e(TAG, "writeOutboxMessage: Failed to persist outbox message", e);
   1433             return null;
   1434         } finally {
   1435             Binder.restoreCallingIdentity(identity);
   1436         }
   1437     }
   1438 
   1439     protected void moveToOutbox(long subId, Uri messageUri, String creator) {
   1440         final ContentValues values = new ContentValues(4);
   1441         values.put(Telephony.Sms.SUB_ID, subId);
   1442         if (!TextUtils.isEmpty(creator)) {
   1443             // Reset creator/sender
   1444             values.put(Telephony.Sms.CREATOR, creator);
   1445         }
   1446         // Reset the timestamp
   1447         values.put(Telephony.Sms.DATE, System.currentTimeMillis()); // milliseconds
   1448         values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_OUTBOX);
   1449         final long identity = Binder.clearCallingIdentity();
   1450         try {
   1451             if (mContext.getContentResolver().update(messageUri, values,
   1452                     null/*where*/, null/*selectionArgs*/) != 1) {
   1453                 Rlog.e(TAG, "moveToOutbox: failed to update message " + messageUri);
   1454             }
   1455         } catch (Exception e) {
   1456             Rlog.e(TAG, "moveToOutbox: Failed to update message", e);
   1457         } finally {
   1458             Binder.restoreCallingIdentity(identity);
   1459         }
   1460     }
   1461 
   1462     private String getMultipartMessageText(ArrayList<String> parts) {
   1463         final StringBuilder sb = new StringBuilder();
   1464         for (String part : parts) {
   1465             if (part != null) {
   1466                 sb.append(part);
   1467             }
   1468         }
   1469         return sb.toString();
   1470     }
   1471 
   1472     protected String getCarrierAppPackageName(Intent intent) {
   1473         UiccCard card = UiccController.getInstance().getUiccCard();
   1474         if (card == null) {
   1475             return null;
   1476         }
   1477 
   1478         List<String> carrierPackages = card.getCarrierPackageNamesForIntent(
   1479             mContext.getPackageManager(), intent);
   1480         return (carrierPackages != null && carrierPackages.size() == 1) ?
   1481                 carrierPackages.get(0) : null;
   1482 
   1483     }
   1484 
   1485     protected long getSubId() {
   1486         return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.mPhoneId);
   1487     }
   1488 }
   1489