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