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