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