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