SMSDispatcher.java revision 8d8e8f45200d17b78d196d8cba389fd9c547ac11
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.ContentObserver;
34import android.database.sqlite.SqliteWrapper;
35import android.net.Uri;
36import android.os.AsyncResult;
37import android.os.Binder;
38import android.os.Bundle;
39import android.os.Handler;
40import android.os.Message;
41import android.os.SystemProperties;
42import android.provider.Settings;
43import android.provider.Telephony;
44import android.provider.Telephony.Sms;
45import android.provider.Telephony.Sms.Intents;
46import android.telephony.PhoneNumberUtils;
47import android.telephony.Rlog;
48import android.telephony.ServiceState;
49import android.telephony.TelephonyManager;
50import android.text.Html;
51import android.text.Spanned;
52import android.util.EventLog;
53import android.view.LayoutInflater;
54import android.view.View;
55import android.view.ViewGroup;
56import android.view.WindowManager;
57import android.widget.Button;
58import android.widget.CheckBox;
59import android.widget.CompoundButton;
60import android.widget.TextView;
61
62import com.android.internal.R;
63import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
64import com.android.internal.telephony.ImsSMSDispatcher;
65
66import java.util.ArrayList;
67import java.util.Collections;
68import java.util.HashMap;
69import java.util.List;
70import java.util.Random;
71import java.util.concurrent.atomic.AtomicInteger;
72
73import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
74import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
75import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
76import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
77import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
78import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
79
80public abstract class SMSDispatcher extends Handler {
81    static final String TAG = "SMSDispatcher";    // accessed from inner class
82    static final boolean DBG = false;
83    private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
84
85    /** Permission required to send SMS to short codes without user confirmation. */
86    private static final String SEND_SMS_NO_CONFIRMATION_PERMISSION =
87            "android.permission.SEND_SMS_NO_CONFIRMATION";
88
89    private static final int PREMIUM_RULE_USE_SIM = 1;
90    private static final int PREMIUM_RULE_USE_NETWORK = 2;
91    private static final int PREMIUM_RULE_USE_BOTH = 3;
92    private final AtomicInteger mPremiumSmsRule = new AtomicInteger(PREMIUM_RULE_USE_SIM);
93    private final SettingsObserver mSettingsObserver;
94
95    /** SMS send complete. */
96    protected static final int EVENT_SEND_SMS_COMPLETE = 2;
97
98    /** Retry sending a previously failed SMS message */
99    private static final int EVENT_SEND_RETRY = 3;
100
101    /** Confirmation required for sending a large number of messages. */
102    private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4;
103
104    /** Send the user confirmed SMS */
105    static final int EVENT_SEND_CONFIRMED_SMS = 5;  // accessed from inner class
106
107    /** Don't send SMS (user did not confirm). */
108    static final int EVENT_STOP_SENDING = 7;        // accessed from inner class
109
110    /** Confirmation required for third-party apps sending to an SMS short code. */
111    private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8;
112
113    /** Confirmation required for third-party apps sending to an SMS short code. */
114    private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9;
115
116    /** Handle status report from {@code CdmaInboundSmsHandler}. */
117    protected static final int EVENT_HANDLE_STATUS_REPORT = 10;
118
119    /** Radio is ON */
120    protected static final int EVENT_RADIO_ON = 11;
121
122    /** IMS registration/SMS format changed */
123    protected static final int EVENT_IMS_STATE_CHANGED = 12;
124
125    /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
126    protected static final int EVENT_IMS_STATE_DONE = 13;
127
128    // other
129    protected static final int EVENT_NEW_ICC_SMS = 14;
130    protected static final int EVENT_ICC_CHANGED = 15;
131
132    protected PhoneBase mPhone;
133    protected final Context mContext;
134    protected final ContentResolver mResolver;
135    protected final CommandsInterface mCi;
136    protected final TelephonyManager mTelephonyManager;
137
138    /** Maximum number of times to retry sending a failed SMS. */
139    private static final int MAX_SEND_RETRIES = 3;
140    /** Delay before next send attempt on a failed SMS, in milliseconds. */
141    private static final int SEND_RETRY_DELAY = 2000;
142    /** single part SMS */
143    private static final int SINGLE_PART_SMS = 1;
144    /** Message sending queue limit */
145    private static final int MO_MSG_QUEUE_LIMIT = 5;
146
147    /**
148     * Message reference for a CONCATENATED_8_BIT_REFERENCE or
149     * CONCATENATED_16_BIT_REFERENCE message set.  Should be
150     * incremented for each set of concatenated messages.
151     * Static field shared by all dispatcher objects.
152     */
153    private static int sConcatenatedRef = new Random().nextInt(256);
154
155    /** Outgoing message counter. Shared by all dispatchers. */
156    private SmsUsageMonitor mUsageMonitor;
157
158    private ImsSMSDispatcher mImsSMSDispatcher;
159
160    /** Number of outgoing SmsTrackers waiting for user confirmation. */
161    private int mPendingTrackerCount;
162
163    /* Flags indicating whether the current device allows sms service */
164    protected boolean mSmsCapable = true;
165    protected boolean mSmsSendDisabled;
166
167    protected int mRemainingMessages = -1;
168
169    protected static int getNextConcatenatedRef() {
170        sConcatenatedRef += 1;
171        return sConcatenatedRef;
172    }
173
174    /**
175     * Create a new SMS dispatcher.
176     * @param phone the Phone to use
177     * @param usageMonitor the SmsUsageMonitor to use
178     */
179    protected SMSDispatcher(PhoneBase phone, SmsUsageMonitor usageMonitor,
180            ImsSMSDispatcher imsSMSDispatcher) {
181        mPhone = phone;
182        mImsSMSDispatcher = imsSMSDispatcher;
183        mContext = phone.getContext();
184        mResolver = mContext.getContentResolver();
185        mCi = phone.mCi;
186        mUsageMonitor = usageMonitor;
187        mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
188        mSettingsObserver = new SettingsObserver(this, mPremiumSmsRule, mContext);
189        mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
190                Settings.Global.SMS_SHORT_CODE_RULE), false, mSettingsObserver);
191
192        mSmsCapable = mContext.getResources().getBoolean(
193                com.android.internal.R.bool.config_sms_capable);
194        mSmsSendDisabled = !SystemProperties.getBoolean(
195                                TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable);
196        Rlog.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat()
197                + " mSmsSendDisabled=" + mSmsSendDisabled);
198    }
199
200    /**
201     * Observe the secure setting for updated premium sms determination rules
202     */
203    private static class SettingsObserver extends ContentObserver {
204        private final AtomicInteger mPremiumSmsRule;
205        private final Context mContext;
206        SettingsObserver(Handler handler, AtomicInteger premiumSmsRule, Context context) {
207            super(handler);
208            mPremiumSmsRule = premiumSmsRule;
209            mContext = context;
210            onChange(false); // load initial value;
211        }
212
213        @Override
214        public void onChange(boolean selfChange) {
215            mPremiumSmsRule.set(Settings.Global.getInt(mContext.getContentResolver(),
216                    Settings.Global.SMS_SHORT_CODE_RULE, PREMIUM_RULE_USE_SIM));
217        }
218    }
219
220    protected void updatePhoneObject(PhoneBase phone) {
221        mPhone = phone;
222        mUsageMonitor = phone.mSmsUsageMonitor;
223        Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() );
224    }
225
226    /** Unregister for incoming SMS events. */
227    public void dispose() {
228        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
229    }
230
231    /**
232     * The format of the message PDU in the associated broadcast intent.
233     * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
234     * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
235     *
236     * Note: All applications which handle incoming SMS messages by processing the
237     * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent
238     * into the new methods in {@link android.telephony.SmsMessage} which take an
239     * extra format parameter. This is required in order to correctly decode the PDU on
240     * devices which require support for both 3GPP and 3GPP2 formats at the same time,
241     * such as CDMA/LTE devices and GSM/CDMA world phones.
242     *
243     * @return the format of the message PDU
244     */
245    protected abstract String getFormat();
246
247    /**
248     * Pass the Message object to subclass to handle. Currently used to pass CDMA status reports
249     * from {@link com.android.internal.telephony.cdma.CdmaInboundSmsHandler}.
250     * @param o the SmsMessage containing the status report
251     */
252    protected void handleStatusReport(Object o) {
253        Rlog.d(TAG, "handleStatusReport() called with no subclass.");
254    }
255
256    /* TODO: Need to figure out how to keep track of status report routing in a
257     *       persistent manner. If the phone process restarts (reboot or crash),
258     *       we will lose this list and any status reports that come in after
259     *       will be dropped.
260     */
261    /** Sent messages awaiting a delivery status report. */
262    protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>();
263
264    /** Outgoing messages being handled by the carrier app. */
265    protected final List<SmsTracker> sendPendingList =
266        Collections.synchronizedList(new ArrayList<SmsTracker>());
267
268    /**
269     * Handles events coming from the phone stack. Overridden from handler.
270     *
271     * @param msg the message to handle
272     */
273    @Override
274    public void handleMessage(Message msg) {
275        switch (msg.what) {
276        case EVENT_SEND_SMS_COMPLETE:
277            // An outbound SMS has been successfully transferred, or failed.
278            handleSendComplete((AsyncResult) msg.obj);
279            break;
280
281        case EVENT_SEND_RETRY:
282            Rlog.d(TAG, "SMS retry..");
283            sendRetrySms((SmsTracker) msg.obj);
284            break;
285
286        case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
287            handleReachSentLimit((SmsTracker)(msg.obj));
288            break;
289
290        case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
291            handleConfirmShortCode(false, (SmsTracker)(msg.obj));
292            break;
293
294        case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
295            handleConfirmShortCode(true, (SmsTracker)(msg.obj));
296            break;
297
298        case EVENT_SEND_CONFIRMED_SMS:
299        {
300            SmsTracker tracker = (SmsTracker) msg.obj;
301            if (tracker.isMultipart()) {
302                sendMultipartSms(tracker);
303            } else {
304                sendSms(tracker);
305            }
306            mPendingTrackerCount--;
307            break;
308        }
309
310        case EVENT_STOP_SENDING:
311        {
312            SmsTracker tracker = (SmsTracker) msg.obj;
313            if (tracker.mSentIntent != null) {
314                try {
315                    tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
316                } catch (CanceledException ex) {
317                    Rlog.e(TAG, "failed to send RESULT_ERROR_LIMIT_EXCEEDED");
318                }
319            }
320            mPendingTrackerCount--;
321            break;
322        }
323
324        case EVENT_HANDLE_STATUS_REPORT:
325            handleStatusReport(msg.obj);
326            break;
327
328        default:
329            Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what);
330        }
331    }
332
333    /**
334     * Called when SMS send completes. Broadcasts a sentIntent on success.
335     * On failure, either sets up retries or broadcasts a sentIntent with
336     * the failure in the result code.
337     *
338     * @param ar AsyncResult passed into the message handler.  ar.result should
339     *           an SmsResponse instance if send was successful.  ar.userObj
340     *           should be an SmsTracker instance.
341     */
342    protected void handleSendComplete(AsyncResult ar) {
343        SmsTracker tracker = (SmsTracker) ar.userObj;
344        PendingIntent sentIntent = tracker.mSentIntent;
345
346        if (ar.result != null) {
347            tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef;
348        } else {
349            Rlog.d(TAG, "SmsResponse was null");
350        }
351
352        if (ar.exception == null) {
353            if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent);
354
355            if (SmsApplication.shouldWriteMessageForPackage(
356                    tracker.mAppInfo.applicationInfo.packageName, mContext)) {
357                // Persist it into the SMS database as a sent message
358                // so the user can see it in their default app.
359                tracker.writeSentMessage(mContext);
360            }
361
362            if (tracker.mDeliveryIntent != null) {
363                // Expecting a status report.  Add it to the list.
364                deliveryPendingList.add(tracker);
365            }
366
367            if (sentIntent != null) {
368                try {
369                    if (mRemainingMessages > -1) {
370                        mRemainingMessages--;
371                    }
372
373                    if (mRemainingMessages == 0) {
374                        Intent sendNext = new Intent();
375                        sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);
376                        sentIntent.send(mContext, Activity.RESULT_OK, sendNext);
377                    } else {
378                        sentIntent.send(Activity.RESULT_OK);
379                    }
380                } catch (CanceledException ex) {}
381            }
382        } else {
383            if (DBG) Rlog.d(TAG, "SMS send failed");
384
385            int ss = mPhone.getServiceState().getState();
386
387            if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
388                // This is retry after failure over IMS but voice is not available.
389                // Set retry to max allowed, so no retry is sent and
390                //   cause RESULT_ERROR_GENERIC_FAILURE to be returned to app.
391                tracker.mRetryCount = MAX_SEND_RETRIES;
392
393                Rlog.d(TAG, "handleSendComplete: Skipping retry: "
394                +" isIms()="+isIms()
395                +" mRetryCount="+tracker.mRetryCount
396                +" mImsRetry="+tracker.mImsRetry
397                +" mMessageRef="+tracker.mMessageRef
398                +" SS= "+mPhone.getServiceState().getState());
399            }
400
401            // if sms over IMS is not supported on data and voice is not available...
402            if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
403                handleNotInService(ss, tracker.mSentIntent);
404            } else if ((((CommandException)(ar.exception)).getCommandError()
405                    == CommandException.Error.SMS_FAIL_RETRY) &&
406                   tracker.mRetryCount < MAX_SEND_RETRIES) {
407                // Retry after a delay if needed.
408                // TODO: According to TS 23.040, 9.2.3.6, we should resend
409                //       with the same TP-MR as the failed message, and
410                //       TP-RD set to 1.  However, we don't have a means of
411                //       knowing the MR for the failed message (EF_SMSstatus
412                //       may or may not have the MR corresponding to this
413                //       message, depending on the failure).  Also, in some
414                //       implementations this retry is handled by the baseband.
415                tracker.mRetryCount++;
416                Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
417                sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
418            } else if (tracker.mSentIntent != null) {
419                int error = RESULT_ERROR_GENERIC_FAILURE;
420
421                if (((CommandException)(ar.exception)).getCommandError()
422                        == CommandException.Error.FDN_CHECK_FAILURE) {
423                    error = RESULT_ERROR_FDN_CHECK_FAILURE;
424                }
425                // Done retrying; return an error to the app.
426                try {
427                    Intent fillIn = new Intent();
428                    if (ar.result != null) {
429                        fillIn.putExtra("errorCode", ((SmsResponse)ar.result).mErrorCode);
430                    }
431                    if (mRemainingMessages > -1) {
432                        mRemainingMessages--;
433                    }
434
435                    if (mRemainingMessages == 0) {
436                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
437                    }
438
439                    tracker.mSentIntent.send(mContext, error, fillIn);
440                } catch (CanceledException ex) {}
441            }
442        }
443    }
444
445    /**
446     * Handles outbound message when the phone is not in service.
447     *
448     * @param ss     Current service state.  Valid values are:
449     *                  OUT_OF_SERVICE
450     *                  EMERGENCY_ONLY
451     *                  POWER_OFF
452     * @param sentIntent the PendingIntent to send the error to
453     */
454    protected static void handleNotInService(int ss, PendingIntent sentIntent) {
455        if (sentIntent != null) {
456            try {
457                if (ss == ServiceState.STATE_POWER_OFF) {
458                    sentIntent.send(RESULT_ERROR_RADIO_OFF);
459                } else {
460                    sentIntent.send(RESULT_ERROR_NO_SERVICE);
461                }
462            } catch (CanceledException ex) {}
463        }
464    }
465
466    /**
467     * Send a data based SMS to a specific application port.
468     *
469     * @param destAddr the address to send the message to
470     * @param scAddr is the service center address or null to use
471     *  the current default SMSC
472     * @param destPort the port to deliver the message to
473     * @param data the body of the message to send
474     * @param sentIntent if not NULL this <code>PendingIntent</code> is
475     *  broadcast when the message is successfully sent, or failed.
476     *  The result code will be <code>Activity.RESULT_OK<code> for success,
477     *  or one of these errors:<br>
478     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
479     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
480     *  <code>RESULT_ERROR_NULL_PDU</code><br>
481     *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
482     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
483     *  the extra "errorCode" containing a radio technology specific value,
484     *  generally only useful for troubleshooting.<br>
485     *  The per-application based SMS control checks sentIntent. If sentIntent
486     *  is NULL the caller will be checked against all unknown applications,
487     *  which cause smaller number of SMS to be sent in checking period.
488     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
489     *  broadcast when the message is delivered to the recipient.  The
490     *  raw pdu of the status report is in the extended data ("pdu").
491     */
492    protected abstract void sendData(String destAddr, String scAddr, int destPort,
493            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
494
495    /**
496     * Send a text based SMS.
497     *
498     * @param destAddr the address to send the message to
499     * @param scAddr is the service center address or null to use
500     *  the current default SMSC
501     * @param text the body of the message to send
502     * @param sentIntent if not NULL this <code>PendingIntent</code> is
503     *  broadcast when the message is successfully sent, or failed.
504     *  The result code will be <code>Activity.RESULT_OK<code> for success,
505     *  or one of these errors:<br>
506     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
507     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
508     *  <code>RESULT_ERROR_NULL_PDU</code><br>
509     *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
510     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
511     *  the extra "errorCode" containing a radio technology specific value,
512     *  generally only useful for troubleshooting.<br>
513     *  The per-application based SMS control checks sentIntent. If sentIntent
514     *  is NULL the caller will be checked against all unknown applications,
515     *  which cause smaller number of SMS to be sent in checking period.
516     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
517     *  broadcast when the message is delivered to the recipient.  The
518     *  raw pdu of the status report is in the extended data ("pdu").
519     */
520    protected abstract void sendText(String destAddr, String scAddr,
521            String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
522
523    /**
524     * Calculate the number of septets needed to encode the message.
525     *
526     * @param messageBody the message to encode
527     * @param use7bitOnly ignore (but still count) illegal characters if true
528     * @return TextEncodingDetails
529     */
530    protected abstract TextEncodingDetails calculateLength(CharSequence messageBody,
531            boolean use7bitOnly);
532
533    /**
534     * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
535     * This outbound message was handled by the carrier app. If the carrier app fails to send
536     * this message, it would be resent by PSTN.
537     *
538     * @param messageRef the reference number of the SMS message.
539     * @param success True if and only if the message was sent successfully. If its value is
540     *  false, this message should be resent via PSTN.
541     */
542    protected abstract void updateSmsSendStatus(int messageRef, boolean success);
543
544    /**
545     * Handler for a {@link GsmSMSDispatcher} or {@link CdmaSMSDispatcher} broadcast.
546     * If SMS sending is successfuly, sends EVENT_SEND_SMS_COMPLETE message. Otherwise,
547     * send the message via the GSM/CDMA network.
548     */
549    protected final class SMSDispatcherReceiver extends BroadcastReceiver {
550
551        private final SmsTracker mTracker;
552
553        public SMSDispatcherReceiver(SmsTracker tracker) {
554            mTracker = tracker;
555        }
556
557        @Override
558        public void onReceive(Context context, Intent intent) {
559            String action = intent.getAction();
560            if (action.equals(Intents.SMS_SEND_ACTION)) {
561                int rc = getResultCode();
562                if (rc == Activity.RESULT_OK) {
563                    Rlog.d(TAG, "Sending SMS by IP pending.");
564                    Bundle resultExtras = getResultExtras(false);
565                    if (resultExtras != null && resultExtras.containsKey("messageref")) {
566                        mTracker.mMessageRef = resultExtras.getInt("messageref");
567                        Rlog.d(TAG, "messageref = " + mTracker.mMessageRef);
568                    } else {
569                        Rlog.e(TAG, "Can't find messageref in result extras.");
570                    }
571                    sendPendingList.add(mTracker);
572                } else {
573                    Rlog.d(TAG, "Sending SMS by IP failed.");
574                    sendSmsByPstn(mTracker);
575                }
576            } else {
577                Rlog.e(TAG, "unexpected BroadcastReceiver action: " + action);
578            }
579        }
580    }
581
582    /**
583     * Send a multi-part text based SMS.
584     *
585     * @param destAddr the address to send the message to
586     * @param scAddr is the service center address or null to use
587     *   the current default SMSC
588     * @param parts an <code>ArrayList</code> of strings that, in order,
589     *   comprise the original message
590     * @param sentIntents if not null, an <code>ArrayList</code> of
591     *   <code>PendingIntent</code>s (one for each message part) that is
592     *   broadcast when the corresponding message part has been sent.
593     *   The result code will be <code>Activity.RESULT_OK<code> for success,
594     *   or one of these errors:
595     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
596     *   <code>RESULT_ERROR_RADIO_OFF</code>
597     *   <code>RESULT_ERROR_NULL_PDU</code>
598     *   <code>RESULT_ERROR_NO_SERVICE</code>.
599     *  The per-application based SMS control checks sentIntent. If sentIntent
600     *  is NULL the caller will be checked against all unknown applications,
601     *  which cause smaller number of SMS to be sent in checking period.
602     * @param deliveryIntents if not null, an <code>ArrayList</code> of
603     *   <code>PendingIntent</code>s (one for each message part) that is
604     *   broadcast when the corresponding message part has been delivered
605     *   to the recipient.  The raw pdu of the status report is in the
606     *   extended data ("pdu").
607     */
608    protected void sendMultipartText(String destAddr, String scAddr,
609            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
610            ArrayList<PendingIntent> deliveryIntents) {
611
612        int refNumber = getNextConcatenatedRef() & 0x00FF;
613        int msgCount = parts.size();
614        int encoding = SmsConstants.ENCODING_UNKNOWN;
615
616        mRemainingMessages = msgCount;
617
618        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
619        for (int i = 0; i < msgCount; i++) {
620            TextEncodingDetails details = calculateLength(parts.get(i), false);
621            if (encoding != details.codeUnitSize
622                    && (encoding == SmsConstants.ENCODING_UNKNOWN
623                            || encoding == SmsConstants.ENCODING_7BIT)) {
624                encoding = details.codeUnitSize;
625            }
626            encodingForParts[i] = details;
627        }
628
629        for (int i = 0; i < msgCount; i++) {
630            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
631            concatRef.refNumber = refNumber;
632            concatRef.seqNumber = i + 1;  // 1-based sequence
633            concatRef.msgCount = msgCount;
634            // TODO: We currently set this to true since our messaging app will never
635            // send more than 255 parts (it converts the message to MMS well before that).
636            // However, we should support 3rd party messaging apps that might need 16-bit
637            // references
638            // Note:  It's not sufficient to just flip this bit to true; it will have
639            // ripple effects (several calculations assume 8-bit ref).
640            concatRef.isEightBits = true;
641            SmsHeader smsHeader = new SmsHeader();
642            smsHeader.concatRef = concatRef;
643
644            // Set the national language tables for 3GPP 7-bit encoding, if enabled.
645            if (encoding == SmsConstants.ENCODING_7BIT) {
646                smsHeader.languageTable = encodingForParts[i].languageTable;
647                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
648            }
649
650            PendingIntent sentIntent = null;
651            if (sentIntents != null && sentIntents.size() > i) {
652                sentIntent = sentIntents.get(i);
653            }
654
655            PendingIntent deliveryIntent = null;
656            if (deliveryIntents != null && deliveryIntents.size() > i) {
657                deliveryIntent = deliveryIntents.get(i);
658            }
659
660            sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding,
661                    sentIntent, deliveryIntent, (i == (msgCount - 1)));
662        }
663
664    }
665
666    /**
667     * Create a new SubmitPdu and send it.
668     */
669    protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress,
670            String message, SmsHeader smsHeader, int encoding,
671            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart);
672
673    /**
674     * Send a SMS
675     * @param tracker will contain:
676     * -smsc the SMSC to send the message through, or NULL for the
677     *  default SMSC
678     * -pdu the raw PDU to send
679     * -sentIntent if not NULL this <code>Intent</code> is
680     *  broadcast when the message is successfully sent, or failed.
681     *  The result code will be <code>Activity.RESULT_OK<code> for success,
682     *  or one of these errors:
683     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
684     *  <code>RESULT_ERROR_RADIO_OFF</code>
685     *  <code>RESULT_ERROR_NULL_PDU</code>
686     *  <code>RESULT_ERROR_NO_SERVICE</code>.
687     *  The per-application based SMS control checks sentIntent. If sentIntent
688     *  is NULL the caller will be checked against all unknown applications,
689     *  which cause smaller number of SMS to be sent in checking period.
690     * -deliveryIntent if not NULL this <code>Intent</code> is
691     *  broadcast when the message is delivered to the recipient.  The
692     *  raw pdu of the status report is in the extended data ("pdu").
693     * -param destAddr the destination phone number (for short code confirmation)
694     */
695    protected void sendRawPdu(SmsTracker tracker) {
696        HashMap map = tracker.mData;
697        byte pdu[] = (byte[]) map.get("pdu");
698
699        PendingIntent sentIntent = tracker.mSentIntent;
700        if (mSmsSendDisabled) {
701            if (sentIntent != null) {
702                try {
703                    sentIntent.send(RESULT_ERROR_NO_SERVICE);
704                } catch (CanceledException ex) {}
705            }
706            Rlog.d(TAG, "Device does not support sending sms.");
707            return;
708        }
709
710        if (pdu == null) {
711            if (sentIntent != null) {
712                try {
713                    sentIntent.send(RESULT_ERROR_NULL_PDU);
714                } catch (CanceledException ex) {}
715            }
716            return;
717        }
718
719        // Get calling app package name via UID from Binder call
720        PackageManager pm = mContext.getPackageManager();
721        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
722
723        if (packageNames == null || packageNames.length == 0) {
724            // Refuse to send SMS if we can't get the calling package name.
725            Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS");
726            if (sentIntent != null) {
727                try {
728                    sentIntent.send(RESULT_ERROR_GENERIC_FAILURE);
729                } catch (CanceledException ex) {
730                    Rlog.e(TAG, "failed to send error result");
731                }
732            }
733            return;
734        }
735
736        // Get package info via packagemanager
737        PackageInfo appInfo;
738        try {
739            // XXX this is lossy- apps can share a UID
740            appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
741        } catch (PackageManager.NameNotFoundException e) {
742            Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");
743            if (sentIntent != null) {
744                try {
745                    sentIntent.send(RESULT_ERROR_GENERIC_FAILURE);
746                } catch (CanceledException ex) {
747                    Rlog.e(TAG, "failed to send error result");
748                }
749            }
750            return;
751        }
752
753        // checkDestination() returns true if the destination is not a premium short code or the
754        // sending app is approved to send to short codes. Otherwise, a message is sent to our
755        // handler with the SmsTracker to request user confirmation before sending.
756        if (checkDestination(tracker)) {
757            // check for excessive outgoing SMS usage by this app
758            if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {
759                sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
760                return;
761            }
762
763            int ss = mPhone.getServiceState().getState();
764
765            // if sms over IMS is not supported on data and voice is not available...
766            if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
767                handleNotInService(ss, tracker.mSentIntent);
768            } else {
769                sendSms(tracker);
770            }
771        }
772    }
773
774    /**
775     * Check if destination is a potential premium short code and sender is not pre-approved to
776     * send to short codes.
777     *
778     * @param tracker the tracker for the SMS to send
779     * @return true if the destination is approved; false if user confirmation event was sent
780     */
781    boolean checkDestination(SmsTracker tracker) {
782        if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION)
783                == PackageManager.PERMISSION_GRANTED) {
784            return true;            // app is pre-approved to send to short codes
785        } else {
786            int rule = mPremiumSmsRule.get();
787            int smsCategory = SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE;
788            if (rule == PREMIUM_RULE_USE_SIM || rule == PREMIUM_RULE_USE_BOTH) {
789                String simCountryIso = mTelephonyManager.getSimCountryIso();
790                if (simCountryIso == null || simCountryIso.length() != 2) {
791                    Rlog.e(TAG, "Can't get SIM country Iso: trying network country Iso");
792                    simCountryIso = mTelephonyManager.getNetworkCountryIso();
793                }
794
795                smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso);
796            }
797            if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) {
798                String networkCountryIso = mTelephonyManager.getNetworkCountryIso();
799                if (networkCountryIso == null || networkCountryIso.length() != 2) {
800                    Rlog.e(TAG, "Can't get Network country Iso: trying SIM country Iso");
801                    networkCountryIso = mTelephonyManager.getSimCountryIso();
802                }
803
804                smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory,
805                        mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso));
806            }
807
808            if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE
809                    || smsCategory == SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE
810                    || smsCategory == SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE) {
811                return true;    // not a premium short code
812            }
813
814            // Wait for user confirmation unless the user has set permission to always allow/deny
815            int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
816                    tracker.mAppInfo.packageName);
817            if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
818                // First time trying to send to premium SMS.
819                premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
820            }
821
822            switch (premiumSmsPermission) {
823                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW:
824                    Rlog.d(TAG, "User approved this app to send to premium SMS");
825                    return true;
826
827                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
828                    Rlog.w(TAG, "User denied this app from sending to premium SMS");
829                    sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker));
830                    return false;   // reject this message
831
832                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
833                default:
834                    int event;
835                    if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) {
836                        event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE;
837                    } else {
838                        event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE;
839                    }
840                    sendMessage(obtainMessage(event, tracker));
841                    return false;   // wait for user confirmation
842            }
843        }
844    }
845
846    /**
847     * Deny sending an SMS if the outgoing queue limit is reached. Used when the message
848     * must be confirmed by the user due to excessive usage or potential premium SMS detected.
849     * @param tracker the SmsTracker for the message to send
850     * @return true if the message was denied; false to continue with send confirmation
851     */
852    private boolean denyIfQueueLimitReached(SmsTracker tracker) {
853        if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) {
854            // Deny sending message when the queue limit is reached.
855            try {
856                if (tracker.mSentIntent != null) {
857                    tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
858                }
859            } catch (CanceledException ex) {
860                Rlog.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
861            }
862            return true;
863        }
864        mPendingTrackerCount++;
865        return false;
866    }
867
868    /**
869     * Returns the label for the specified app package name.
870     * @param appPackage the package name of the app requesting to send an SMS
871     * @return the label for the specified app, or the package name if getApplicationInfo() fails
872     */
873    private CharSequence getAppLabel(String appPackage) {
874        PackageManager pm = mContext.getPackageManager();
875        try {
876            ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0);
877            return appInfo.loadLabel(pm);
878        } catch (PackageManager.NameNotFoundException e) {
879            Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage);
880            return appPackage;  // fall back to package name if we can't get app label
881        }
882    }
883
884    /**
885     * Post an alert when SMS needs confirmation due to excessive usage.
886     * @param tracker an SmsTracker for the current message.
887     */
888    protected void handleReachSentLimit(SmsTracker tracker) {
889        if (denyIfQueueLimitReached(tracker)) {
890            return;     // queue limit reached; error was returned to caller
891        }
892
893        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
894        Resources r = Resources.getSystem();
895        Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
896
897        ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null);
898
899        AlertDialog d = new AlertDialog.Builder(mContext)
900                .setTitle(R.string.sms_control_title)
901                .setIcon(R.drawable.stat_sys_warning)
902                .setMessage(messageText)
903                .setPositiveButton(r.getString(R.string.sms_control_yes), listener)
904                .setNegativeButton(r.getString(R.string.sms_control_no), listener)
905                .setOnCancelListener(listener)
906                .create();
907
908        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
909        d.show();
910    }
911
912    /**
913     * Post an alert for user confirmation when sending to a potential short code.
914     * @param isPremium true if the destination is known to be a premium short code
915     * @param tracker the SmsTracker for the current message.
916     */
917    protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) {
918        if (denyIfQueueLimitReached(tracker)) {
919            return;     // queue limit reached; error was returned to caller
920        }
921
922        int detailsId;
923        if (isPremium) {
924            detailsId = R.string.sms_premium_short_code_details;
925        } else {
926            detailsId = R.string.sms_short_code_details;
927        }
928
929        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
930        Resources r = Resources.getSystem();
931        Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
932                appLabel, tracker.mDestAddress));
933
934        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
935                Context.LAYOUT_INFLATER_SERVICE);
936        View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null);
937
938        ConfirmDialogListener listener = new ConfirmDialogListener(tracker,
939                (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction));
940
941
942        TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message);
943        messageView.setText(messageText);
944
945        ViewGroup detailsLayout = (ViewGroup) layout.findViewById(
946                R.id.sms_short_code_detail_layout);
947        TextView detailsView = (TextView) detailsLayout.findViewById(
948                R.id.sms_short_code_detail_message);
949        detailsView.setText(detailsId);
950
951        CheckBox rememberChoice = (CheckBox) layout.findViewById(
952                R.id.sms_short_code_remember_choice_checkbox);
953        rememberChoice.setOnCheckedChangeListener(listener);
954
955        AlertDialog d = new AlertDialog.Builder(mContext)
956                .setView(layout)
957                .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener)
958                .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener)
959                .setOnCancelListener(listener)
960                .create();
961
962        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
963        d.show();
964
965        listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE));
966        listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE));
967    }
968
969    /**
970     * Returns the premium SMS permission for the specified package. If the package has never
971     * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
972     * will be returned.
973     * @param packageName the name of the package to query permission
974     * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
975     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
976     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
977     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
978     */
979    public int getPremiumSmsPermission(String packageName) {
980        return mUsageMonitor.getPremiumSmsPermission(packageName);
981    }
982
983    /**
984     * Sets the premium SMS permission for the specified package and save the value asynchronously
985     * to persistent storage.
986     * @param packageName the name of the package to set permission
987     * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
988     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
989     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
990     */
991    public void setPremiumSmsPermission(String packageName, int permission) {
992        mUsageMonitor.setPremiumSmsPermission(packageName, permission);
993    }
994
995    /**
996     * Send the message along to the radio.
997     *
998     * @param tracker holds the SMS message to send
999     */
1000    protected abstract void sendSms(SmsTracker tracker);
1001
1002    /**
1003     * Send the SMS via the PSTN network.
1004     *
1005     * @param tracker holds the Sms tracker ready to be sent
1006     */
1007    protected abstract void sendSmsByPstn(SmsTracker tracker);
1008
1009    /**
1010     * Retry the message along to the radio.
1011     *
1012     * @param tracker holds the SMS message to send
1013     */
1014    public void sendRetrySms(SmsTracker tracker) {
1015        // re-routing to ImsSMSDispatcher
1016        if (mImsSMSDispatcher != null) {
1017            mImsSMSDispatcher.sendRetrySms(tracker);
1018        } else {
1019            Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed");
1020        }
1021    }
1022
1023    /**
1024     * Send the multi-part SMS based on multipart Sms tracker
1025     *
1026     * @param tracker holds the multipart Sms tracker ready to be sent
1027     */
1028    private void sendMultipartSms(SmsTracker tracker) {
1029        ArrayList<String> parts;
1030        ArrayList<PendingIntent> sentIntents;
1031        ArrayList<PendingIntent> deliveryIntents;
1032
1033        HashMap<String, Object> map = tracker.mData;
1034
1035        String destinationAddress = (String) map.get("destination");
1036        String scAddress = (String) map.get("scaddress");
1037
1038        parts = (ArrayList<String>) map.get("parts");
1039        sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
1040        deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
1041
1042        // check if in service
1043        int ss = mPhone.getServiceState().getState();
1044        // if sms over IMS is not supported on data and voice is not available...
1045        if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
1046            for (int i = 0, count = parts.size(); i < count; i++) {
1047                PendingIntent sentIntent = null;
1048                if (sentIntents != null && sentIntents.size() > i) {
1049                    sentIntent = sentIntents.get(i);
1050                }
1051                handleNotInService(ss, sentIntent);
1052            }
1053            return;
1054        }
1055
1056        sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents);
1057    }
1058
1059    /**
1060     * Keeps track of an SMS that has been sent to the RIL, until it has
1061     * successfully been sent, or we're done trying.
1062     */
1063    protected static final class SmsTracker {
1064        // fields need to be public for derived SmsDispatchers
1065        public final HashMap<String, Object> mData;
1066        public int mRetryCount;
1067        public int mImsRetry; // nonzero indicates initial message was sent over Ims
1068        public int mMessageRef;
1069        String mFormat;
1070
1071        public final PendingIntent mSentIntent;
1072        public final PendingIntent mDeliveryIntent;
1073
1074        public final PackageInfo mAppInfo;
1075        public final String mDestAddress;
1076
1077        private long mTimestamp = System.currentTimeMillis();
1078        private Uri mSentMessageUri; // Uri of persisted message if we wrote one
1079
1080        private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1081                PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format) {
1082            mData = data;
1083            mSentIntent = sentIntent;
1084            mDeliveryIntent = deliveryIntent;
1085            mRetryCount = 0;
1086            mAppInfo = appInfo;
1087            mDestAddress = destAddr;
1088            mFormat = format;
1089            mImsRetry = 0;
1090            mMessageRef = 0;
1091        }
1092
1093        /**
1094         * Returns whether this tracker holds a multi-part SMS.
1095         * @return true if the tracker holds a multi-part SMS; false otherwise
1096         */
1097        boolean isMultipart() {
1098            return mData.containsKey("parts");
1099        }
1100
1101        /**
1102         * Persist this as a sent message
1103         */
1104        void writeSentMessage(Context context) {
1105            String text = (String)mData.get("text");
1106            if (text != null) {
1107                boolean deliveryReport = (mDeliveryIntent != null);
1108                // Using invalid threadId 0 here. When the message is inserted into the db, the
1109                // provider looks up the threadId based on the recipient(s).
1110                mSentMessageUri = Sms.addMessageToUri(context.getContentResolver(),
1111                        Telephony.Sms.Sent.CONTENT_URI,
1112                        mDestAddress,
1113                        text /*body*/,
1114                        null /*subject*/,
1115                        mTimestamp /*date*/,
1116                        true /*read*/,
1117                        deliveryReport /*deliveryReport*/,
1118                        0 /*threadId*/);
1119            }
1120        }
1121
1122        /**
1123         * Update the status of this message if we persisted it
1124         */
1125        public void updateSentMessageStatus(Context context, int status) {
1126            if (mSentMessageUri != null) {
1127                // If we wrote this message in writeSentMessage, update it now
1128                ContentValues values = new ContentValues(1);
1129                values.put(Sms.STATUS, status);
1130                SqliteWrapper.update(context, context.getContentResolver(),
1131                        mSentMessageUri, values, null, null);
1132            }
1133        }
1134    }
1135
1136    protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1137            PendingIntent deliveryIntent, String format) {
1138        // Get calling app package name via UID from Binder call
1139        PackageManager pm = mContext.getPackageManager();
1140        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
1141
1142        // Get package info via packagemanager
1143        PackageInfo appInfo = null;
1144        if (packageNames != null && packageNames.length > 0) {
1145            try {
1146                // XXX this is lossy- apps can share a UID
1147                appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
1148            } catch (PackageManager.NameNotFoundException e) {
1149                // error will be logged in sendRawPdu
1150            }
1151        }
1152        // Strip non-digits from destination phone number before checking for short codes
1153        // and before displaying the number to the user if confirmation is required.
1154        String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
1155        return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format);
1156    }
1157
1158    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1159            String text, SmsMessageBase.SubmitPduBase pdu) {
1160        HashMap<String, Object> map = new HashMap<String, Object>();
1161        map.put("destAddr", destAddr);
1162        map.put("scAddr", scAddr);
1163        map.put("text", text);
1164        map.put("smsc", pdu.encodedScAddress);
1165        map.put("pdu", pdu.encodedMessage);
1166        return map;
1167    }
1168
1169    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1170            int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
1171        HashMap<String, Object> map = new HashMap<String, Object>();
1172        map.put("destAddr", destAddr);
1173        map.put("scAddr", scAddr);
1174        map.put("destPort", destPort);
1175        map.put("data", data);
1176        map.put("smsc", pdu.encodedScAddress);
1177        map.put("pdu", pdu.encodedMessage);
1178        return map;
1179    }
1180
1181    /**
1182     * Dialog listener for SMS confirmation dialog.
1183     */
1184    private final class ConfirmDialogListener
1185            implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
1186            CompoundButton.OnCheckedChangeListener {
1187
1188        private final SmsTracker mTracker;
1189        private Button mPositiveButton;
1190        private Button mNegativeButton;
1191        private boolean mRememberChoice;    // default is unchecked
1192        private final TextView mRememberUndoInstruction;
1193
1194        ConfirmDialogListener(SmsTracker tracker, TextView textView) {
1195            mTracker = tracker;
1196            mRememberUndoInstruction = textView;
1197        }
1198
1199        void setPositiveButton(Button button) {
1200            mPositiveButton = button;
1201        }
1202
1203        void setNegativeButton(Button button) {
1204            mNegativeButton = button;
1205        }
1206
1207        @Override
1208        public void onClick(DialogInterface dialog, int which) {
1209            // Always set the SMS permission so that Settings will show a permission setting
1210            // for the app (it won't be shown until after the app tries to send to a short code).
1211            int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
1212
1213            if (which == DialogInterface.BUTTON_POSITIVE) {
1214                Rlog.d(TAG, "CONFIRM sending SMS");
1215                // XXX this is lossy- apps can have more than one signature
1216                EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER,
1217                                    mTracker.mAppInfo.applicationInfo == null ?
1218                                    -1 : mTracker.mAppInfo.applicationInfo.uid);
1219                sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker));
1220                if (mRememberChoice) {
1221                    newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW;
1222                }
1223            } else if (which == DialogInterface.BUTTON_NEGATIVE) {
1224                Rlog.d(TAG, "DENY sending SMS");
1225                // XXX this is lossy- apps can have more than one signature
1226                EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
1227                                    mTracker.mAppInfo.applicationInfo == null ?
1228                                    -1 :  mTracker.mAppInfo.applicationInfo.uid);
1229                sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1230                if (mRememberChoice) {
1231                    newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
1232                }
1233            }
1234            setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
1235        }
1236
1237        @Override
1238        public void onCancel(DialogInterface dialog) {
1239            Rlog.d(TAG, "dialog dismissed: don't send SMS");
1240            sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1241        }
1242
1243        @Override
1244        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1245            Rlog.d(TAG, "remember this choice: " + isChecked);
1246            mRememberChoice = isChecked;
1247            if (isChecked) {
1248                mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow);
1249                mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow);
1250                if (mRememberUndoInstruction != null) {
1251                    mRememberUndoInstruction.
1252                            setText(R.string.sms_short_code_remember_undo_instruction);
1253                    mRememberUndoInstruction.setPadding(0,0,0,32);
1254                }
1255            } else {
1256                mPositiveButton.setText(R.string.sms_short_code_confirm_allow);
1257                mNegativeButton.setText(R.string.sms_short_code_confirm_deny);
1258                if (mRememberUndoInstruction != null) {
1259                    mRememberUndoInstruction.setText("");
1260                    mRememberUndoInstruction.setPadding(0,0,0,0);
1261                }
1262            }
1263        }
1264    }
1265
1266    public boolean isIms() {
1267        if (mImsSMSDispatcher != null) {
1268            return mImsSMSDispatcher.isIms();
1269        } else {
1270            Rlog.e(TAG, mImsSMSDispatcher + " is null");
1271            return false;
1272        }
1273    }
1274
1275    public String getImsSmsFormat() {
1276        if (mImsSMSDispatcher != null) {
1277            return mImsSMSDispatcher.getImsSmsFormat();
1278        } else {
1279            Rlog.e(TAG, mImsSMSDispatcher + " is null");
1280            return null;
1281        }
1282    }
1283}
1284