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