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