Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.telephony;
     18 
     19 import android.app.Activity;
     20 import android.app.PendingIntent;
     21 import android.app.AlertDialog;
     22 import android.app.PendingIntent.CanceledException;
     23 import android.content.BroadcastReceiver;
     24 import android.content.ContentResolver;
     25 import android.content.ContentValues;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.DialogInterface;
     29 import android.content.IntentFilter;
     30 import android.content.res.Resources;
     31 import android.database.Cursor;
     32 import android.database.SQLException;
     33 import android.net.Uri;
     34 import android.os.AsyncResult;
     35 import android.os.Environment;
     36 import android.os.Handler;
     37 import android.os.Message;
     38 import android.os.PowerManager;
     39 import android.os.StatFs;
     40 import android.provider.Telephony;
     41 import android.provider.Telephony.Sms.Intents;
     42 import android.provider.Settings;
     43 import android.telephony.SmsMessage;
     44 import android.telephony.ServiceState;
     45 import android.util.Config;
     46 import android.util.Log;
     47 import android.view.WindowManager;
     48 
     49 import com.android.internal.util.HexDump;
     50 
     51 import java.io.ByteArrayOutputStream;
     52 import java.util.ArrayList;
     53 import java.util.HashMap;
     54 import java.util.Random;
     55 
     56 import com.android.internal.R;
     57 
     58 import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
     59 import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
     60 import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
     61 import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
     62 import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
     63 import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
     64 
     65 
     66 public abstract class SMSDispatcher extends Handler {
     67     private static final String TAG = "SMS";
     68 
     69     /** Default checking period for SMS sent without user permit */
     70     private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000;
     71 
     72     /** Default number of SMS sent in checking period without user permit */
     73     private static final int DEFAULT_SMS_MAX_COUNT = 100;
     74 
     75     /** Default timeout for SMS sent query */
     76     private static final int DEFAULT_SMS_TIMEOUT = 6000;
     77 
     78     protected static final String[] RAW_PROJECTION = new String[] {
     79         "pdu",
     80         "sequence",
     81         "destination_port",
     82     };
     83 
     84     static final protected int EVENT_NEW_SMS = 1;
     85 
     86     static final protected int EVENT_SEND_SMS_COMPLETE = 2;
     87 
     88     /** Retry sending a previously failed SMS message */
     89     static final protected int EVENT_SEND_RETRY = 3;
     90 
     91     /** Status report received */
     92     static final protected int EVENT_NEW_SMS_STATUS_REPORT = 5;
     93 
     94     /** SIM/RUIM storage is full */
     95     static final protected int EVENT_ICC_FULL = 6;
     96 
     97     /** SMS confirm required */
     98     static final protected int EVENT_POST_ALERT = 7;
     99 
    100     /** Send the user confirmed SMS */
    101     static final protected int EVENT_SEND_CONFIRMED_SMS = 8;
    102 
    103     /** Alert is timeout */
    104     static final protected int EVENT_ALERT_TIMEOUT = 9;
    105 
    106     /** Stop the sending */
    107     static final protected int EVENT_STOP_SENDING = 10;
    108 
    109     /** Memory status reporting is acknowledged by RIL */
    110     static final protected int EVENT_REPORT_MEMORY_STATUS_DONE = 11;
    111 
    112     /** Radio is ON */
    113     static final protected int EVENT_RADIO_ON = 12;
    114 
    115     protected Phone mPhone;
    116     protected Context mContext;
    117     protected ContentResolver mResolver;
    118     protected CommandsInterface mCm;
    119 
    120     protected final WapPushOverSms mWapPush;
    121 
    122     protected final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
    123 
    124     /** Maximum number of times to retry sending a failed SMS. */
    125     private static final int MAX_SEND_RETRIES = 3;
    126     /** Delay before next send attempt on a failed SMS, in milliseconds. */
    127     private static final int SEND_RETRY_DELAY = 2000;
    128     /** single part SMS */
    129     private static final int SINGLE_PART_SMS = 1;
    130     /** Message sending queue limit */
    131     private static final int MO_MSG_QUEUE_LIMIT = 5;
    132 
    133     /**
    134      * Message reference for a CONCATENATED_8_BIT_REFERENCE or
    135      * CONCATENATED_16_BIT_REFERENCE message set.  Should be
    136      * incremented for each set of concatenated messages.
    137      */
    138     private static int sConcatenatedRef;
    139 
    140     private SmsCounter mCounter;
    141 
    142     private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
    143 
    144     /** Wake lock to ensure device stays awake while dispatching the SMS intent. */
    145     private PowerManager.WakeLock mWakeLock;
    146 
    147     /**
    148      * Hold the wake lock for 5 seconds, which should be enough time for
    149      * any receiver(s) to grab its own wake lock.
    150      */
    151     private final int WAKE_LOCK_TIMEOUT = 5000;
    152 
    153     protected boolean mStorageAvailable = true;
    154     protected boolean mReportMemoryStatusPending = false;
    155 
    156     protected static int getNextConcatenatedRef() {
    157         sConcatenatedRef += 1;
    158         return sConcatenatedRef;
    159     }
    160 
    161     /**
    162      *  Implement the per-application based SMS control, which only allows
    163      *  a limit on the number of SMS/MMS messages an app can send in checking
    164      *  period.
    165      */
    166     private class SmsCounter {
    167         private int mCheckPeriod;
    168         private int mMaxAllowed;
    169         private HashMap<String, ArrayList<Long>> mSmsStamp;
    170 
    171         /**
    172          * Create SmsCounter
    173          * @param mMax is the number of SMS allowed without user permit
    174          * @param mPeriod is the checking period
    175          */
    176         SmsCounter(int mMax, int mPeriod) {
    177             mMaxAllowed = mMax;
    178             mCheckPeriod = mPeriod;
    179             mSmsStamp = new HashMap<String, ArrayList<Long>> ();
    180         }
    181 
    182         /**
    183          * Check to see if an application allow to send new SMS messages
    184          *
    185          * @param appName is the application sending sms
    186          * @param smsWaiting is the number of new sms wants to be sent
    187          * @return true if application is allowed to send the requested number
    188          *         of new sms messages
    189          */
    190         boolean check(String appName, int smsWaiting) {
    191             if (!mSmsStamp.containsKey(appName)) {
    192                 mSmsStamp.put(appName, new ArrayList<Long>());
    193             }
    194 
    195             return isUnderLimit(mSmsStamp.get(appName), smsWaiting);
    196         }
    197 
    198         private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) {
    199             Long ct =  System.currentTimeMillis();
    200 
    201             Log.d(TAG, "SMS send size=" + sent.size() + "time=" + ct);
    202 
    203             while (sent.size() > 0 && (ct - sent.get(0)) > mCheckPeriod ) {
    204                     sent.remove(0);
    205             }
    206 
    207 
    208             if ( (sent.size() + smsWaiting) <= mMaxAllowed) {
    209                 for (int i = 0; i < smsWaiting; i++ ) {
    210                     sent.add(ct);
    211                 }
    212                 return true;
    213             }
    214             return false;
    215         }
    216     }
    217 
    218     protected SMSDispatcher(PhoneBase phone) {
    219         mPhone = phone;
    220         mWapPush = new WapPushOverSms(phone, this);
    221         mContext = phone.getContext();
    222         mResolver = mContext.getContentResolver();
    223         mCm = phone.mCM;
    224 
    225         createWakelock();
    226 
    227         int check_period = Settings.Secure.getInt(mResolver,
    228                 Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS,
    229                 DEFAULT_SMS_CHECK_PERIOD);
    230         int max_count = Settings.Secure.getInt(mResolver,
    231                 Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT,
    232                 DEFAULT_SMS_MAX_COUNT);
    233         mCounter = new SmsCounter(max_count, check_period);
    234 
    235         mCm.setOnNewSMS(this, EVENT_NEW_SMS, null);
    236         mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
    237         mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null);
    238         mCm.registerForOn(this, EVENT_RADIO_ON, null);
    239 
    240         // Don't always start message ref at 0.
    241         sConcatenatedRef = new Random().nextInt(256);
    242 
    243         // Register for device storage intents.  Use these to notify the RIL
    244         // that storage for SMS is or is not available.
    245         IntentFilter filter = new IntentFilter();
    246         filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL);
    247         filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
    248         mContext.registerReceiver(mResultReceiver, filter);
    249     }
    250 
    251     public void dispose() {
    252         mCm.unSetOnNewSMS(this);
    253         mCm.unSetOnSmsStatus(this);
    254         mCm.unSetOnIccSmsFull(this);
    255         mCm.unregisterForOn(this);
    256     }
    257 
    258     protected void finalize() {
    259         Log.d(TAG, "SMSDispatcher finalized");
    260     }
    261 
    262 
    263     /* TODO: Need to figure out how to keep track of status report routing in a
    264      *       persistent manner. If the phone process restarts (reboot or crash),
    265      *       we will lose this list and any status reports that come in after
    266      *       will be dropped.
    267      */
    268     /** Sent messages awaiting a delivery status report. */
    269     protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>();
    270 
    271     /**
    272      * Handles events coming from the phone stack. Overridden from handler.
    273      *
    274      * @param msg the message to handle
    275      */
    276     @Override
    277     public void handleMessage(Message msg) {
    278         AsyncResult ar;
    279 
    280         switch (msg.what) {
    281         case EVENT_NEW_SMS:
    282             // A new SMS has been received by the device
    283             if (Config.LOGD) {
    284                 Log.d(TAG, "New SMS Message Received");
    285             }
    286 
    287             SmsMessage sms;
    288 
    289             ar = (AsyncResult) msg.obj;
    290 
    291             if (ar.exception != null) {
    292                 Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception);
    293                 return;
    294             }
    295 
    296             sms = (SmsMessage) ar.result;
    297             try {
    298                 int result = dispatchMessage(sms.mWrappedSmsMessage);
    299                 if (result != Activity.RESULT_OK) {
    300                     // RESULT_OK means that message was broadcast for app(s) to handle.
    301                     // Any other result, we should ack here.
    302                     boolean handled = (result == Intents.RESULT_SMS_HANDLED);
    303                     notifyAndAcknowledgeLastIncomingSms(handled, result, null);
    304                 }
    305             } catch (RuntimeException ex) {
    306                 Log.e(TAG, "Exception dispatching message", ex);
    307                 notifyAndAcknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
    308             }
    309 
    310             break;
    311 
    312         case EVENT_SEND_SMS_COMPLETE:
    313             // An outbound SMS has been successfully transferred, or failed.
    314             handleSendComplete((AsyncResult) msg.obj);
    315             break;
    316 
    317         case EVENT_SEND_RETRY:
    318             sendSms((SmsTracker) msg.obj);
    319             break;
    320 
    321         case EVENT_NEW_SMS_STATUS_REPORT:
    322             handleStatusReport((AsyncResult)msg.obj);
    323             break;
    324 
    325         case EVENT_ICC_FULL:
    326             handleIccFull();
    327             break;
    328 
    329         case EVENT_POST_ALERT:
    330             handleReachSentLimit((SmsTracker)(msg.obj));
    331             break;
    332 
    333         case EVENT_ALERT_TIMEOUT:
    334             ((AlertDialog)(msg.obj)).dismiss();
    335             msg.obj = null;
    336             if (mSTrackers.isEmpty() == false) {
    337                 try {
    338                     SmsTracker sTracker = mSTrackers.remove(0);
    339                     sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
    340                 } catch (CanceledException ex) {
    341                     Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
    342                 }
    343             }
    344             if (Config.LOGD) {
    345                 Log.d(TAG, "EVENT_ALERT_TIMEOUT, message stop sending");
    346             }
    347             break;
    348 
    349         case EVENT_SEND_CONFIRMED_SMS:
    350             if (mSTrackers.isEmpty() == false) {
    351                 SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
    352                 if (isMultipartTracker(sTracker)) {
    353                     sendMultipartSms(sTracker);
    354                 } else {
    355                     sendSms(sTracker);
    356                 }
    357                 removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
    358             }
    359             break;
    360 
    361         case EVENT_STOP_SENDING:
    362             if (mSTrackers.isEmpty() == false) {
    363                 // Remove the latest one.
    364                 try {
    365                     SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1);
    366                     sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
    367                 } catch (CanceledException ex) {
    368                     Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
    369                 }
    370                 removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
    371             }
    372             break;
    373 
    374         case EVENT_REPORT_MEMORY_STATUS_DONE:
    375             ar = (AsyncResult)msg.obj;
    376             if (ar.exception != null) {
    377                 mReportMemoryStatusPending = true;
    378                 Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = "
    379                         + mStorageAvailable);
    380             } else {
    381                 mReportMemoryStatusPending = false;
    382             }
    383             break;
    384 
    385         case EVENT_RADIO_ON:
    386             if (mReportMemoryStatusPending) {
    387                 Log.v(TAG, "Sending pending memory status report : mStorageAvailable = "
    388                         + mStorageAvailable);
    389                 mCm.reportSmsMemoryStatus(mStorageAvailable,
    390                         obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
    391             }
    392             break;
    393         }
    394     }
    395 
    396     private void createWakelock() {
    397         PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
    398         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher");
    399         mWakeLock.setReferenceCounted(true);
    400     }
    401 
    402     /**
    403      * Grabs a wake lock and sends intent as an ordered broadcast.
    404      * The resultReceiver will check for errors and ACK/NACK back
    405      * to the RIL.
    406      *
    407      * @param intent intent to broadcast
    408      * @param permission Receivers are required to have this permission
    409      */
    410     void dispatch(Intent intent, String permission) {
    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, mResultReceiver,
    415                 this, Activity.RESULT_OK, null, null);
    416     }
    417 
    418     /**
    419      * Called when SIM_FULL message is received from the RIL.  Notifies interested
    420      * parties that SIM storage for SMS messages is full.
    421      */
    422     private void handleIccFull(){
    423         // broadcast SIM_FULL intent
    424         Intent intent = new Intent(Intents.SIM_FULL_ACTION);
    425         mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
    426         mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS");
    427     }
    428 
    429     /**
    430      * Called when a status report is received.  This should correspond to
    431      * a previously successful SEND.
    432      *
    433      * @param ar AsyncResult passed into the message handler.  ar.result should
    434      *           be a String representing the status report PDU, as ASCII hex.
    435      */
    436     protected abstract void handleStatusReport(AsyncResult ar);
    437 
    438     /**
    439      * Called when SMS send completes. Broadcasts a sentIntent on success.
    440      * On failure, either sets up retries or broadcasts a sentIntent with
    441      * the failure in the result code.
    442      *
    443      * @param ar AsyncResult passed into the message handler.  ar.result should
    444      *           an SmsResponse instance if send was successful.  ar.userObj
    445      *           should be an SmsTracker instance.
    446      */
    447     protected void handleSendComplete(AsyncResult ar) {
    448         SmsTracker tracker = (SmsTracker) ar.userObj;
    449         PendingIntent sentIntent = tracker.mSentIntent;
    450 
    451         if (ar.exception == null) {
    452             if (Config.LOGD) {
    453                 Log.d(TAG, "SMS send complete. Broadcasting "
    454                         + "intent: " + sentIntent);
    455             }
    456 
    457             if (tracker.mDeliveryIntent != null) {
    458                 // Expecting a status report.  Add it to the list.
    459                 int messageRef = ((SmsResponse)ar.result).messageRef;
    460                 tracker.mMessageRef = messageRef;
    461                 deliveryPendingList.add(tracker);
    462             }
    463 
    464             if (sentIntent != null) {
    465                 try {
    466                     sentIntent.send(Activity.RESULT_OK);
    467                 } catch (CanceledException ex) {}
    468             }
    469         } else {
    470             if (Config.LOGD) {
    471                 Log.d(TAG, "SMS send failed");
    472             }
    473 
    474             int ss = mPhone.getServiceState().getState();
    475 
    476             if (ss != ServiceState.STATE_IN_SERVICE) {
    477                 handleNotInService(ss, tracker);
    478             } else if ((((CommandException)(ar.exception)).getCommandError()
    479                     == CommandException.Error.SMS_FAIL_RETRY) &&
    480                    tracker.mRetryCount < MAX_SEND_RETRIES) {
    481                 // Retry after a delay if needed.
    482                 // TODO: According to TS 23.040, 9.2.3.6, we should resend
    483                 //       with the same TP-MR as the failed message, and
    484                 //       TP-RD set to 1.  However, we don't have a means of
    485                 //       knowing the MR for the failed message (EF_SMSstatus
    486                 //       may or may not have the MR corresponding to this
    487                 //       message, depending on the failure).  Also, in some
    488                 //       implementations this retry is handled by the baseband.
    489                 tracker.mRetryCount++;
    490                 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
    491                 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
    492             } else if (tracker.mSentIntent != null) {
    493                 int error = RESULT_ERROR_GENERIC_FAILURE;
    494 
    495                 if (((CommandException)(ar.exception)).getCommandError()
    496                         == CommandException.Error.FDN_CHECK_FAILURE) {
    497                     error = RESULT_ERROR_FDN_CHECK_FAILURE;
    498                 }
    499                 // Done retrying; return an error to the app.
    500                 try {
    501                     Intent fillIn = new Intent();
    502                     if (ar.result != null) {
    503                         fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
    504                     }
    505                     tracker.mSentIntent.send(mContext, error, fillIn);
    506 
    507                 } catch (CanceledException ex) {}
    508             }
    509         }
    510     }
    511 
    512     /**
    513      * Handles outbound message when the phone is not in service.
    514      *
    515      * @param ss     Current service state.  Valid values are:
    516      *                  OUT_OF_SERVICE
    517      *                  EMERGENCY_ONLY
    518      *                  POWER_OFF
    519      * @param tracker   An SmsTracker for the current message.
    520      */
    521     protected void handleNotInService(int ss, SmsTracker tracker) {
    522         if (tracker.mSentIntent != null) {
    523             try {
    524                 if (ss == ServiceState.STATE_POWER_OFF) {
    525                     tracker.mSentIntent.send(RESULT_ERROR_RADIO_OFF);
    526                 } else {
    527                     tracker.mSentIntent.send(RESULT_ERROR_NO_SERVICE);
    528                 }
    529             } catch (CanceledException ex) {}
    530         }
    531     }
    532 
    533     /**
    534      * Dispatches an incoming SMS messages.
    535      *
    536      * @param sms the incoming message from the phone
    537      * @return a result code from {@link Telephony.Sms.Intents}, or
    538      *         {@link Activity#RESULT_OK} if the message has been broadcast
    539      *         to applications
    540      */
    541     protected abstract int dispatchMessage(SmsMessageBase sms);
    542 
    543 
    544     /**
    545      * If this is the last part send the parts out to the application, otherwise
    546      * the part is stored for later processing.
    547      *
    548      * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
    549      * @return a result code from {@link Telephony.Sms.Intents}, or
    550      *         {@link Activity#RESULT_OK} if the message has been broadcast
    551      *         to applications
    552      */
    553     protected int processMessagePart(SmsMessageBase sms,
    554             SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) {
    555 
    556         // Lookup all other related parts
    557         StringBuilder where = new StringBuilder("reference_number =");
    558         where.append(concatRef.refNumber);
    559         where.append(" AND address = ?");
    560         String[] whereArgs = new String[] {sms.getOriginatingAddress()};
    561 
    562         byte[][] pdus = null;
    563         Cursor cursor = null;
    564         try {
    565             cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null);
    566             int cursorCount = cursor.getCount();
    567             if (cursorCount != concatRef.msgCount - 1) {
    568                 // We don't have all the parts yet, store this one away
    569                 ContentValues values = new ContentValues();
    570                 values.put("date", new Long(sms.getTimestampMillis()));
    571                 values.put("pdu", HexDump.toHexString(sms.getPdu()));
    572                 values.put("address", sms.getOriginatingAddress());
    573                 values.put("reference_number", concatRef.refNumber);
    574                 values.put("count", concatRef.msgCount);
    575                 values.put("sequence", concatRef.seqNumber);
    576                 if (portAddrs != null) {
    577                     values.put("destination_port", portAddrs.destPort);
    578                 }
    579                 mResolver.insert(mRawUri, values);
    580                 return Intents.RESULT_SMS_HANDLED;
    581             }
    582 
    583             // All the parts are in place, deal with them
    584             int pduColumn = cursor.getColumnIndex("pdu");
    585             int sequenceColumn = cursor.getColumnIndex("sequence");
    586 
    587             pdus = new byte[concatRef.msgCount][];
    588             for (int i = 0; i < cursorCount; i++) {
    589                 cursor.moveToNext();
    590                 int cursorSequence = (int)cursor.getLong(sequenceColumn);
    591                 pdus[cursorSequence - 1] = HexDump.hexStringToByteArray(
    592                         cursor.getString(pduColumn));
    593             }
    594             // This one isn't in the DB, so add it
    595             pdus[concatRef.seqNumber - 1] = sms.getPdu();
    596 
    597             // Remove the parts from the database
    598             mResolver.delete(mRawUri, where.toString(), whereArgs);
    599         } catch (SQLException e) {
    600             Log.e(TAG, "Can't access multipart SMS database", e);
    601             // TODO:  Would OUT_OF_MEMORY be more appropriate?
    602             return Intents.RESULT_SMS_GENERIC_ERROR;
    603         } finally {
    604             if (cursor != null) cursor.close();
    605         }
    606 
    607         /**
    608          * TODO(cleanup): The following code has duplicated logic with
    609          * the radio-specific dispatchMessage code, which is fragile,
    610          * in addition to being redundant.  Instead, if this method
    611          * maybe returned the reassembled message (or just contents),
    612          * the following code (which is not really related to
    613          * reconstruction) could be better consolidated.
    614          */
    615 
    616         // Dispatch the PDUs to applications
    617         if (portAddrs != null) {
    618             if (portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) {
    619                 // Build up the data stream
    620                 ByteArrayOutputStream output = new ByteArrayOutputStream();
    621                 for (int i = 0; i < concatRef.msgCount; i++) {
    622                     SmsMessage msg = SmsMessage.createFromPdu(pdus[i]);
    623                     byte[] data = msg.getUserData();
    624                     output.write(data, 0, data.length);
    625                 }
    626                 // Handle the PUSH
    627                 return mWapPush.dispatchWapPdu(output.toByteArray());
    628             } else {
    629                 // The messages were sent to a port, so concoct a URI for it
    630                 dispatchPortAddressedPdus(pdus, portAddrs.destPort);
    631             }
    632         } else {
    633             // The messages were not sent to a port
    634             dispatchPdus(pdus);
    635         }
    636         return Activity.RESULT_OK;
    637     }
    638 
    639     /**
    640      * Dispatches standard PDUs to interested applications
    641      *
    642      * @param pdus The raw PDUs making up the message
    643      */
    644     protected void dispatchPdus(byte[][] pdus) {
    645         Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
    646         intent.putExtra("pdus", pdus);
    647         dispatch(intent, "android.permission.RECEIVE_SMS");
    648     }
    649 
    650     /**
    651      * Dispatches port addressed PDUs to interested applications
    652      *
    653      * @param pdus The raw PDUs making up the message
    654      * @param port The destination port of the messages
    655      */
    656     protected void dispatchPortAddressedPdus(byte[][] pdus, int port) {
    657         Uri uri = Uri.parse("sms://localhost:" + port);
    658         Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri);
    659         intent.putExtra("pdus", pdus);
    660         dispatch(intent, "android.permission.RECEIVE_SMS");
    661     }
    662 
    663     /**
    664      * Send a data based SMS to a specific application port.
    665      *
    666      * @param destAddr the address to send the message to
    667      * @param scAddr is the service center address or null to use
    668      *  the current default SMSC
    669      * @param destPort the port to deliver the message to
    670      * @param data the body of the message to send
    671      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    672      *  broadcast when the message is successfully sent, or failed.
    673      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    674      *  or one of these errors:<br>
    675      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    676      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    677      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    678      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    679      *  the extra "errorCode" containing a radio technology specific value,
    680      *  generally only useful for troubleshooting.<br>
    681      *  The per-application based SMS control checks sentIntent. If sentIntent
    682      *  is NULL the caller will be checked against all unknown applications,
    683      *  which cause smaller number of SMS to be sent in checking period.
    684      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    685      *  broadcast when the message is delivered to the recipient.  The
    686      *  raw pdu of the status report is in the extended data ("pdu").
    687      */
    688     protected abstract void sendData(String destAddr, String scAddr, int destPort,
    689             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
    690 
    691     /**
    692      * Send a text based SMS.
    693      *
    694      * @param destAddr the address to send the message to
    695      * @param scAddr is the service center address or null to use
    696      *  the current default SMSC
    697      * @param text the body of the message to send
    698      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    699      *  broadcast when the message is successfully sent, or failed.
    700      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    701      *  or one of these errors:<br>
    702      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    703      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    704      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    705      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    706      *  the extra "errorCode" containing a radio technology specific value,
    707      *  generally only useful for troubleshooting.<br>
    708      *  The per-application based SMS control checks sentIntent. If sentIntent
    709      *  is NULL the caller will be checked against all unknown applications,
    710      *  which cause smaller number of SMS to be sent in checking period.
    711      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    712      *  broadcast when the message is delivered to the recipient.  The
    713      *  raw pdu of the status report is in the extended data ("pdu").
    714      */
    715     protected abstract void sendText(String destAddr, String scAddr,
    716             String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
    717 
    718     /**
    719      * Send a multi-part text based SMS.
    720      *
    721      * @param destAddr the address to send the message to
    722      * @param scAddr is the service center address or null to use
    723      *   the current default SMSC
    724      * @param parts an <code>ArrayList</code> of strings that, in order,
    725      *   comprise the original message
    726      * @param sentIntents if not null, an <code>ArrayList</code> of
    727      *   <code>PendingIntent</code>s (one for each message part) that is
    728      *   broadcast when the corresponding message part has been sent.
    729      *   The result code will be <code>Activity.RESULT_OK<code> for success,
    730      *   or one of these errors:
    731      *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
    732      *   <code>RESULT_ERROR_RADIO_OFF</code>
    733      *   <code>RESULT_ERROR_NULL_PDU</code>.
    734      *  The per-application based SMS control checks sentIntent. If sentIntent
    735      *  is NULL the caller will be checked against all unknown applications,
    736      *  which cause smaller number of SMS to be sent in checking period.
    737      * @param deliveryIntents if not null, an <code>ArrayList</code> of
    738      *   <code>PendingIntent</code>s (one for each message part) that is
    739      *   broadcast when the corresponding message part has been delivered
    740      *   to the recipient.  The raw pdu of the status report is in the
    741      *   extended data ("pdu").
    742      */
    743     protected abstract void sendMultipartText(String destAddr, String scAddr,
    744             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
    745             ArrayList<PendingIntent> deliveryIntents);
    746 
    747     /**
    748      * Send a SMS
    749      *
    750      * @param smsc the SMSC to send the message through, or NULL for the
    751      *  default SMSC
    752      * @param pdu the raw PDU to send
    753      * @param sentIntent if not NULL this <code>Intent</code> is
    754      *  broadcast when the message is successfully sent, or failed.
    755      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    756      *  or one of these errors:
    757      *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
    758      *  <code>RESULT_ERROR_RADIO_OFF</code>
    759      *  <code>RESULT_ERROR_NULL_PDU</code>.
    760      *  The per-application based SMS control checks sentIntent. If sentIntent
    761      *  is NULL the caller will be checked against all unknown applications,
    762      *  which cause smaller number of SMS to be sent in checking period.
    763      * @param deliveryIntent if not NULL this <code>Intent</code> is
    764      *  broadcast when the message is delivered to the recipient.  The
    765      *  raw pdu of the status report is in the extended data ("pdu").
    766      */
    767     protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
    768             PendingIntent deliveryIntent) {
    769         if (pdu == null) {
    770             if (sentIntent != null) {
    771                 try {
    772                     sentIntent.send(RESULT_ERROR_NULL_PDU);
    773                 } catch (CanceledException ex) {}
    774             }
    775             return;
    776         }
    777 
    778         HashMap<String, Object> map = new HashMap<String, Object>();
    779         map.put("smsc", smsc);
    780         map.put("pdu", pdu);
    781 
    782         SmsTracker tracker = new SmsTracker(map, sentIntent,
    783                 deliveryIntent);
    784         int ss = mPhone.getServiceState().getState();
    785 
    786         if (ss != ServiceState.STATE_IN_SERVICE) {
    787             handleNotInService(ss, tracker);
    788         } else {
    789             String appName = getAppNameByIntent(sentIntent);
    790             if (mCounter.check(appName, SINGLE_PART_SMS)) {
    791                 sendSms(tracker);
    792             } else {
    793                 sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
    794             }
    795         }
    796     }
    797 
    798     /**
    799      * Post an alert while SMS needs user confirm.
    800      *
    801      * An SmsTracker for the current message.
    802      */
    803     protected void handleReachSentLimit(SmsTracker tracker) {
    804         if (mSTrackers.size() >= MO_MSG_QUEUE_LIMIT) {
    805             // Deny the sending when the queue limit is reached.
    806             try {
    807                 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
    808             } catch (CanceledException ex) {
    809                 Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
    810             }
    811             return;
    812         }
    813 
    814         Resources r = Resources.getSystem();
    815 
    816         String appName = getAppNameByIntent(tracker.mSentIntent);
    817 
    818         AlertDialog d = new AlertDialog.Builder(mContext)
    819                 .setTitle(r.getString(R.string.sms_control_title))
    820                 .setMessage(appName + " " + r.getString(R.string.sms_control_message))
    821                 .setPositiveButton(r.getString(R.string.sms_control_yes), mListener)
    822                 .setNegativeButton(r.getString(R.string.sms_control_no), mListener)
    823                 .create();
    824 
    825         d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    826         d.show();
    827 
    828         mSTrackers.add(tracker);
    829         sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d),
    830                 DEFAULT_SMS_TIMEOUT);
    831     }
    832 
    833     protected String getAppNameByIntent(PendingIntent intent) {
    834         Resources r = Resources.getSystem();
    835         return (intent != null) ? intent.getTargetPackage()
    836             : r.getString(R.string.sms_control_default_app_name);
    837     }
    838 
    839     /**
    840      * Send the message along to the radio.
    841      *
    842      * @param tracker holds the SMS message to send
    843      */
    844     protected abstract void sendSms(SmsTracker tracker);
    845 
    846     /**
    847      * Send the multi-part SMS based on multipart Sms tracker
    848      *
    849      * @param tracker holds the multipart Sms tracker ready to be sent
    850      */
    851     protected abstract void sendMultipartSms (SmsTracker tracker);
    852 
    853     /**
    854      * Activate or deactivate cell broadcast SMS.
    855      *
    856      * @param activate
    857      *            0 = activate, 1 = deactivate
    858      * @param response
    859      *            Callback message is empty on completion
    860      */
    861     protected abstract void activateCellBroadcastSms(int activate, Message response);
    862 
    863     /**
    864      * Query the current configuration of cell broadcast SMS.
    865      *
    866      * @param response
    867      *            Callback message contains the configuration from the modem on completion
    868      *            @see #setCellBroadcastConfig
    869      */
    870     protected abstract void getCellBroadcastSmsConfig(Message response);
    871 
    872     /**
    873      * Configure cell broadcast SMS.
    874      *
    875      * @param configValuesArray
    876      *          The first element defines the number of triples that follow.
    877      *          A triple is made up of the service category, the language identifier
    878      *          and a boolean that specifies whether the category is set active.
    879      * @param response
    880      *            Callback message is empty on completion
    881      */
    882     protected abstract void setCellBroadcastConfig(int[] configValuesArray, Message response);
    883 
    884     /**
    885      * Send an acknowledge message.
    886      * @param success indicates that last message was successfully received.
    887      * @param result result code indicating any error
    888      * @param response callback message sent when operation completes.
    889      */
    890     protected abstract void acknowledgeLastIncomingSms(boolean success,
    891             int result, Message response);
    892 
    893     /**
    894      * Notify interested apps if the framework has rejected an incoming SMS,
    895      * and send an acknowledge message to the network.
    896      * @param success indicates that last message was successfully received.
    897      * @param result result code indicating any error
    898      * @param response callback message sent when operation completes.
    899      */
    900     private void notifyAndAcknowledgeLastIncomingSms(boolean success,
    901             int result, Message response) {
    902         if (!success) {
    903             // broadcast SMS_REJECTED_ACTION intent
    904             Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
    905             intent.putExtra("result", result);
    906             mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
    907             mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS");
    908         }
    909         acknowledgeLastIncomingSms(success, result, response);
    910     }
    911 
    912     /**
    913      * Check if a SmsTracker holds multi-part Sms
    914      *
    915      * @param tracker a SmsTracker could hold a multi-part Sms
    916      * @return true for tracker holds Multi-parts Sms
    917      */
    918     private boolean isMultipartTracker (SmsTracker tracker) {
    919         HashMap map = tracker.mData;
    920         return ( map.get("parts") != null);
    921     }
    922 
    923     /**
    924      * Keeps track of an SMS that has been sent to the RIL, until it has
    925      * successfully been sent, or we're done trying.
    926      *
    927      */
    928     static protected class SmsTracker {
    929         // fields need to be public for derived SmsDispatchers
    930         public HashMap mData;
    931         public int mRetryCount;
    932         public int mMessageRef;
    933 
    934         public PendingIntent mSentIntent;
    935         public PendingIntent mDeliveryIntent;
    936 
    937         SmsTracker(HashMap data, PendingIntent sentIntent,
    938                 PendingIntent deliveryIntent) {
    939             mData = data;
    940             mSentIntent = sentIntent;
    941             mDeliveryIntent = deliveryIntent;
    942             mRetryCount = 0;
    943         }
    944     }
    945 
    946     protected SmsTracker SmsTrackerFactory(HashMap data, PendingIntent sentIntent,
    947             PendingIntent deliveryIntent) {
    948         return new SmsTracker(data, sentIntent, deliveryIntent);
    949     }
    950 
    951     private DialogInterface.OnClickListener mListener =
    952         new DialogInterface.OnClickListener() {
    953 
    954             public void onClick(DialogInterface dialog, int which) {
    955                 if (which == DialogInterface.BUTTON_POSITIVE) {
    956                     Log.d(TAG, "click YES to send out sms");
    957                     sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS));
    958                 } else if (which == DialogInterface.BUTTON_NEGATIVE) {
    959                     Log.d(TAG, "click NO to stop sending");
    960                     sendMessage(obtainMessage(EVENT_STOP_SENDING));
    961                 }
    962             }
    963         };
    964 
    965     private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
    966         @Override
    967         public void onReceive(Context context, Intent intent) {
    968             if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) {
    969                 mStorageAvailable = false;
    970                 mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
    971             } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) {
    972                 mStorageAvailable = true;
    973                 mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
    974             } else {
    975                 // Assume the intent is one of the SMS receive intents that
    976                 // was sent as an ordered broadcast.  Check result and ACK.
    977                 int rc = getResultCode();
    978                 boolean success = (rc == Activity.RESULT_OK)
    979                         || (rc == Intents.RESULT_SMS_HANDLED);
    980 
    981                 // For a multi-part message, this only ACKs the last part.
    982                 // Previous parts were ACK'd as they were received.
    983                 acknowledgeLastIncomingSms(success, rc, null);
    984             }
    985         }
    986     };
    987 }
    988