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