SMSDispatcher.java revision 96f2245d927c81fcd348bace3bc417bd0f91fb33
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 static android.Manifest.permission.SEND_SMS_NO_CONFIRMATION;
20import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
21import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
22import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
23import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
24import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
25import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
26
27import android.annotation.Nullable;
28import android.app.Activity;
29import android.app.AlertDialog;
30import android.app.PendingIntent;
31import android.app.PendingIntent.CanceledException;
32import android.content.ContentResolver;
33import android.content.ContentValues;
34import android.content.Context;
35import android.content.DialogInterface;
36import android.content.Intent;
37import android.content.pm.ApplicationInfo;
38import android.content.pm.PackageInfo;
39import android.content.pm.PackageManager;
40import android.content.res.Resources;
41import android.database.ContentObserver;
42import android.database.sqlite.SqliteWrapper;
43import android.net.Uri;
44import android.os.AsyncResult;
45import android.os.Binder;
46import android.os.Handler;
47import android.os.Message;
48import android.os.Process;
49import android.os.RemoteException;
50import android.os.UserHandle;
51import android.provider.Settings;
52import android.provider.Telephony;
53import android.provider.Telephony.Sms;
54import android.service.carrier.CarrierMessagingService;
55import android.service.carrier.ICarrierMessagingCallback;
56import android.service.carrier.ICarrierMessagingService;
57import android.telephony.CarrierMessagingServiceManager;
58import android.telephony.PhoneNumberUtils;
59import android.telephony.Rlog;
60import android.telephony.ServiceState;
61import android.telephony.TelephonyManager;
62import android.text.Html;
63import android.text.Spanned;
64import android.text.TextUtils;
65import android.util.EventLog;
66import android.view.LayoutInflater;
67import android.view.View;
68import android.view.ViewGroup;
69import android.view.WindowManager;
70import android.widget.Button;
71import android.widget.CheckBox;
72import android.widget.CompoundButton;
73import android.widget.TextView;
74
75import com.android.internal.R;
76import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
77import com.android.internal.telephony.uicc.UiccCard;
78import com.android.internal.telephony.uicc.UiccController;
79
80import java.util.ArrayList;
81import java.util.HashMap;
82import java.util.List;
83import java.util.Random;
84import java.util.concurrent.atomic.AtomicBoolean;
85import java.util.concurrent.atomic.AtomicInteger;
86
87public abstract class SMSDispatcher extends Handler {
88    static final String TAG = "SMSDispatcher";    // accessed from inner class
89    static final boolean DBG = false;
90    private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
91
92    private static final int PREMIUM_RULE_USE_SIM = 1;
93    private static final int PREMIUM_RULE_USE_NETWORK = 2;
94    private static final int PREMIUM_RULE_USE_BOTH = 3;
95    private final AtomicInteger mPremiumSmsRule = new AtomicInteger(PREMIUM_RULE_USE_SIM);
96    private final SettingsObserver mSettingsObserver;
97
98    /** SMS send complete. */
99    protected static final int EVENT_SEND_SMS_COMPLETE = 2;
100
101    /** Retry sending a previously failed SMS message */
102    private static final int EVENT_SEND_RETRY = 3;
103
104    /** Confirmation required for sending a large number of messages. */
105    private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4;
106
107    /** Send the user confirmed SMS */
108    static final int EVENT_SEND_CONFIRMED_SMS = 5;  // accessed from inner class
109
110    /** Don't send SMS (user did not confirm). */
111    static final int EVENT_STOP_SENDING = 7;        // accessed from inner class
112
113    /** Confirmation required for third-party apps sending to an SMS short code. */
114    private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8;
115
116    /** Confirmation required for third-party apps sending to an SMS short code. */
117    private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9;
118
119    /** Handle status report from {@code CdmaInboundSmsHandler}. */
120    protected static final int EVENT_HANDLE_STATUS_REPORT = 10;
121
122    /** Radio is ON */
123    protected static final int EVENT_RADIO_ON = 11;
124
125    /** IMS registration/SMS format changed */
126    protected static final int EVENT_IMS_STATE_CHANGED = 12;
127
128    /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
129    protected static final int EVENT_IMS_STATE_DONE = 13;
130
131    // other
132    protected static final int EVENT_NEW_ICC_SMS = 14;
133    protected static final int EVENT_ICC_CHANGED = 15;
134
135    protected Phone mPhone;
136    protected final Context mContext;
137    protected final ContentResolver mResolver;
138    protected final CommandsInterface mCi;
139    protected final TelephonyManager mTelephonyManager;
140
141    /** Maximum number of times to retry sending a failed SMS. */
142    private static final int MAX_SEND_RETRIES = 3;
143    /** Delay before next send attempt on a failed SMS, in milliseconds. */
144    private static final int SEND_RETRY_DELAY = 2000;
145    /** single part SMS */
146    private static final int SINGLE_PART_SMS = 1;
147    /** Message sending queue limit */
148    private static final int MO_MSG_QUEUE_LIMIT = 5;
149
150    /**
151     * Message reference for a CONCATENATED_8_BIT_REFERENCE or
152     * CONCATENATED_16_BIT_REFERENCE message set.  Should be
153     * incremented for each set of concatenated messages.
154     * Static field shared by all dispatcher objects.
155     */
156    private static int sConcatenatedRef = new Random().nextInt(256);
157
158    /** Outgoing message counter. Shared by all dispatchers. */
159    private SmsUsageMonitor mUsageMonitor;
160
161    private ImsSMSDispatcher mImsSMSDispatcher;
162
163    /** Number of outgoing SmsTrackers waiting for user confirmation. */
164    private int mPendingTrackerCount;
165
166    /* Flags indicating whether the current device allows sms service */
167    protected boolean mSmsCapable = true;
168    protected boolean mSmsSendDisabled;
169
170    protected static int getNextConcatenatedRef() {
171        sConcatenatedRef += 1;
172        return sConcatenatedRef;
173    }
174
175    /**
176     * Create a new SMS dispatcher.
177     * @param phone the Phone to use
178     * @param usageMonitor the SmsUsageMonitor to use
179     */
180    protected SMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor,
181            ImsSMSDispatcher imsSMSDispatcher) {
182        mPhone = phone;
183        mImsSMSDispatcher = imsSMSDispatcher;
184        mContext = phone.getContext();
185        mResolver = mContext.getContentResolver();
186        mCi = phone.mCi;
187        mUsageMonitor = usageMonitor;
188        mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
189        mSettingsObserver = new SettingsObserver(this, mPremiumSmsRule, mContext);
190        mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
191                Settings.Global.SMS_SHORT_CODE_RULE), false, mSettingsObserver);
192
193        mSmsCapable = mContext.getResources().getBoolean(
194                com.android.internal.R.bool.config_sms_capable);
195        mSmsSendDisabled = !mTelephonyManager.getSmsSendCapableForPhone(
196                mPhone.getPhoneId(), mSmsCapable);
197        Rlog.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat()
198                + " mSmsSendDisabled=" + mSmsSendDisabled);
199    }
200
201    /**
202     * Observe the secure setting for updated premium sms determination rules
203     */
204    private static class SettingsObserver extends ContentObserver {
205        private final AtomicInteger mPremiumSmsRule;
206        private final Context mContext;
207        SettingsObserver(Handler handler, AtomicInteger premiumSmsRule, Context context) {
208            super(handler);
209            mPremiumSmsRule = premiumSmsRule;
210            mContext = context;
211            onChange(false); // load initial value;
212        }
213
214        @Override
215        public void onChange(boolean selfChange) {
216            mPremiumSmsRule.set(Settings.Global.getInt(mContext.getContentResolver(),
217                    Settings.Global.SMS_SHORT_CODE_RULE, PREMIUM_RULE_USE_SIM));
218        }
219    }
220
221    protected void updatePhoneObject(Phone phone) {
222        mPhone = phone;
223        mUsageMonitor = phone.mSmsUsageMonitor;
224        Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() );
225    }
226
227    /** Unregister for incoming SMS events. */
228    public void dispose() {
229        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
230    }
231
232    /**
233     * The format of the message PDU in the associated broadcast intent.
234     * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
235     * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
236     *
237     * Note: All applications which handle incoming SMS messages by processing the
238     * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent
239     * into the new methods in {@link android.telephony.SmsMessage} which take an
240     * extra format parameter. This is required in order to correctly decode the PDU on
241     * devices which require support for both 3GPP and 3GPP2 formats at the same time,
242     * such as CDMA/LTE devices and GSM/CDMA world phones.
243     *
244     * @return the format of the message PDU
245     */
246    protected abstract String getFormat();
247
248    /**
249     * Pass the Message object to subclass to handle. Currently used to pass CDMA status reports
250     * from {@link com.android.internal.telephony.cdma.CdmaInboundSmsHandler}.
251     * @param o the SmsMessage containing the status report
252     */
253    protected void handleStatusReport(Object o) {
254        Rlog.d(TAG, "handleStatusReport() called with no subclass.");
255    }
256
257    /* TODO: Need to figure out how to keep track of status report routing in a
258     *       persistent manner. If the phone process restarts (reboot or crash),
259     *       we will lose this list and any status reports that come in after
260     *       will be dropped.
261     */
262    /** Sent messages awaiting a delivery status report. */
263    protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>();
264
265    /**
266     * Handles events coming from the phone stack. Overridden from handler.
267     *
268     * @param msg the message to handle
269     */
270    @Override
271    public void handleMessage(Message msg) {
272        switch (msg.what) {
273        case EVENT_SEND_SMS_COMPLETE:
274            // An outbound SMS has been successfully transferred, or failed.
275            handleSendComplete((AsyncResult) msg.obj);
276            break;
277
278        case EVENT_SEND_RETRY:
279            Rlog.d(TAG, "SMS retry..");
280            sendRetrySms((SmsTracker) msg.obj);
281            break;
282
283        case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
284            handleReachSentLimit((SmsTracker)(msg.obj));
285            break;
286
287        case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE:
288            handleConfirmShortCode(false, (SmsTracker)(msg.obj));
289            break;
290
291        case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE:
292            handleConfirmShortCode(true, (SmsTracker)(msg.obj));
293            break;
294
295        case EVENT_SEND_CONFIRMED_SMS:
296        {
297            SmsTracker tracker = (SmsTracker) msg.obj;
298            if (tracker.isMultipart()) {
299                sendMultipartSms(tracker);
300            } else {
301                if (mPendingTrackerCount > 1) {
302                    tracker.mExpectMore = true;
303                } else {
304                    tracker.mExpectMore = false;
305                }
306                sendSms(tracker);
307            }
308            mPendingTrackerCount--;
309            break;
310        }
311
312        case EVENT_STOP_SENDING:
313        {
314            SmsTracker tracker = (SmsTracker) msg.obj;
315            tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
316            mPendingTrackerCount--;
317            break;
318        }
319
320        case EVENT_HANDLE_STATUS_REPORT:
321            handleStatusReport(msg.obj);
322            break;
323
324        default:
325            Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what);
326        }
327    }
328
329    /**
330     * Use the carrier messaging service to send a data or text SMS.
331     */
332    protected abstract class SmsSender extends CarrierMessagingServiceManager {
333        protected final SmsTracker mTracker;
334        // Initialized in sendSmsByCarrierApp
335        protected volatile SmsSenderCallback mSenderCallback;
336
337        protected SmsSender(SmsTracker tracker) {
338            mTracker = tracker;
339        }
340
341        public void sendSmsByCarrierApp(String carrierPackageName,
342                                        SmsSenderCallback senderCallback) {
343            mSenderCallback = senderCallback;
344            if (!bindToCarrierMessagingService(mContext, carrierPackageName)) {
345                Rlog.e(TAG, "bindService() for carrier messaging service failed");
346                mSenderCallback.onSendSmsComplete(
347                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
348                        0 /* messageRef */);
349            } else {
350                Rlog.d(TAG, "bindService() for carrier messaging service succeeded");
351            }
352        }
353    }
354
355    private static int getSendSmsFlag(@Nullable PendingIntent deliveryIntent) {
356        if (deliveryIntent == null) {
357            return 0;
358        }
359        return CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS;
360    }
361
362    /**
363     * Use the carrier messaging service to send a text SMS.
364     */
365    protected final class TextSmsSender extends SmsSender {
366        public TextSmsSender(SmsTracker tracker) {
367            super(tracker);
368        }
369
370        @Override
371        protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
372            HashMap<String, Object> map = mTracker.getData();
373            String text = (String) map.get("text");
374
375            if (text != null) {
376                try {
377                    carrierMessagingService.sendTextSms(text, getSubId(),
378                            mTracker.mDestAddress, getSendSmsFlag(mTracker.mDeliveryIntent),
379                            mSenderCallback);
380                } catch (RemoteException e) {
381                    Rlog.e(TAG, "Exception sending the SMS: " + e);
382                    mSenderCallback.onSendSmsComplete(
383                            CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
384                            0 /* messageRef */);
385                }
386            } else {
387                mSenderCallback.onSendSmsComplete(
388                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
389                        0 /* messageRef */);
390            }
391        }
392    }
393
394    /**
395     * Use the carrier messaging service to send a data SMS.
396     */
397    protected final class DataSmsSender extends SmsSender {
398        public DataSmsSender(SmsTracker tracker) {
399            super(tracker);
400        }
401
402        @Override
403        protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
404            HashMap<String, Object> map = mTracker.getData();
405            byte[] data = (byte[]) map.get("data");
406            int destPort = (int) map.get("destPort");
407
408            if (data != null) {
409                try {
410                    carrierMessagingService.sendDataSms(data, getSubId(),
411                            mTracker.mDestAddress, destPort,
412                            getSendSmsFlag(mTracker.mDeliveryIntent), mSenderCallback);
413                } catch (RemoteException e) {
414                    Rlog.e(TAG, "Exception sending the SMS: " + e);
415                    mSenderCallback.onSendSmsComplete(
416                            CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
417                            0 /* messageRef */);
418                }
419            } else {
420                mSenderCallback.onSendSmsComplete(
421                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
422                        0 /* messageRef */);
423            }
424        }
425    }
426
427    /**
428     * Callback for TextSmsSender and DataSmsSender from the carrier messaging service.
429     * Once the result is ready, the carrier messaging service connection is disposed.
430     */
431    protected final class SmsSenderCallback extends ICarrierMessagingCallback.Stub {
432        private final SmsSender mSmsSender;
433
434        public SmsSenderCallback(SmsSender smsSender) {
435            mSmsSender = smsSender;
436        }
437
438        /**
439         * This method should be called only once.
440         */
441        @Override
442        public void onSendSmsComplete(int result, int messageRef) {
443            checkCallerIsPhoneOrCarrierApp();
444            final long identity = Binder.clearCallingIdentity();
445            try {
446                mSmsSender.disposeConnection(mContext);
447                processSendSmsResponse(mSmsSender.mTracker, result, messageRef);
448            } finally {
449                Binder.restoreCallingIdentity(identity);
450            }
451        }
452
453        @Override
454        public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
455            Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with result: " + result);
456        }
457
458        @Override
459        public void onFilterComplete(int result) {
460            Rlog.e(TAG, "Unexpected onFilterComplete call with result: " + result);
461        }
462
463        @Override
464        public void onSendMmsComplete(int result, byte[] sendConfPdu) {
465            Rlog.e(TAG, "Unexpected onSendMmsComplete call with result: " + result);
466        }
467
468        @Override
469        public void onDownloadMmsComplete(int result) {
470            Rlog.e(TAG, "Unexpected onDownloadMmsComplete call with result: " + result);
471        }
472    }
473
474    private void processSendSmsResponse(SmsTracker tracker, int result, int messageRef) {
475        if (tracker == null) {
476            Rlog.e(TAG, "processSendSmsResponse: null tracker");
477            return;
478        }
479
480        SmsResponse smsResponse = new SmsResponse(
481                messageRef, null /* ackPdu */, -1 /* unknown error code */);
482
483        switch (result) {
484        case CarrierMessagingService.SEND_STATUS_OK:
485            Rlog.d(TAG, "Sending SMS by IP succeeded.");
486            sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE,
487                                      new AsyncResult(tracker,
488                                                      smsResponse,
489                                                      null /* exception*/ )));
490            break;
491        case CarrierMessagingService.SEND_STATUS_ERROR:
492            Rlog.d(TAG, "Sending SMS by IP failed.");
493            sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE,
494                    new AsyncResult(tracker, smsResponse,
495                            new CommandException(CommandException.Error.GENERIC_FAILURE))));
496            break;
497        case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK:
498            Rlog.d(TAG, "Sending SMS by IP failed. Retry on carrier network.");
499            sendSubmitPdu(tracker);
500            break;
501        default:
502            Rlog.d(TAG, "Unknown result " + result + " Retry on carrier network.");
503            sendSubmitPdu(tracker);
504        }
505    }
506
507    /**
508     * Use the carrier messaging service to send a multipart text SMS.
509     */
510    private final class MultipartSmsSender extends CarrierMessagingServiceManager {
511        private final List<String> mParts;
512        public final SmsTracker[] mTrackers;
513        // Initialized in sendSmsByCarrierApp
514        private volatile MultipartSmsSenderCallback mSenderCallback;
515
516        MultipartSmsSender(ArrayList<String> parts, SmsTracker[] trackers) {
517            mParts = parts;
518            mTrackers = trackers;
519        }
520
521        void sendSmsByCarrierApp(String carrierPackageName,
522                                 MultipartSmsSenderCallback senderCallback) {
523            mSenderCallback = senderCallback;
524            if (!bindToCarrierMessagingService(mContext, carrierPackageName)) {
525                Rlog.e(TAG, "bindService() for carrier messaging service failed");
526                mSenderCallback.onSendMultipartSmsComplete(
527                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
528                        null /* smsResponse */);
529            } else {
530                Rlog.d(TAG, "bindService() for carrier messaging service succeeded");
531            }
532        }
533
534        @Override
535        protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
536            try {
537                carrierMessagingService.sendMultipartTextSms(
538                        mParts, getSubId(), mTrackers[0].mDestAddress,
539                        getSendSmsFlag(mTrackers[0].mDeliveryIntent), mSenderCallback);
540            } catch (RemoteException e) {
541                Rlog.e(TAG, "Exception sending the SMS: " + e);
542                mSenderCallback.onSendMultipartSmsComplete(
543                        CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
544                        null /* smsResponse */);
545            }
546        }
547    }
548
549    /**
550     * Callback for MultipartSmsSender from the carrier messaging service.
551     * Once the result is ready, the carrier messaging service connection is disposed.
552     */
553    private final class MultipartSmsSenderCallback extends ICarrierMessagingCallback.Stub {
554        private final MultipartSmsSender mSmsSender;
555
556        MultipartSmsSenderCallback(MultipartSmsSender smsSender) {
557            mSmsSender = smsSender;
558        }
559
560        @Override
561        public void onSendSmsComplete(int result, int messageRef) {
562            Rlog.e(TAG, "Unexpected onSendSmsComplete call with result: " + result);
563        }
564
565        /**
566         * This method should be called only once.
567         */
568        @Override
569        public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
570            mSmsSender.disposeConnection(mContext);
571
572            if (mSmsSender.mTrackers == null) {
573                Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with null trackers.");
574                return;
575            }
576
577            checkCallerIsPhoneOrCarrierApp();
578            final long identity = Binder.clearCallingIdentity();
579            try {
580                for (int i = 0; i < mSmsSender.mTrackers.length; i++) {
581                    int messageRef = 0;
582                    if (messageRefs != null && messageRefs.length > i) {
583                        messageRef = messageRefs[i];
584                    }
585                    processSendSmsResponse(mSmsSender.mTrackers[i], result, messageRef);
586                }
587            } finally {
588                Binder.restoreCallingIdentity(identity);
589            }
590        }
591
592        @Override
593        public void onFilterComplete(int result) {
594            Rlog.e(TAG, "Unexpected onFilterComplete call with result: " + result);
595        }
596
597        @Override
598        public void onSendMmsComplete(int result, byte[] sendConfPdu) {
599            Rlog.e(TAG, "Unexpected onSendMmsComplete call with result: " + result);
600        }
601
602        @Override
603        public void onDownloadMmsComplete(int result) {
604            Rlog.e(TAG, "Unexpected onDownloadMmsComplete call with result: " + result);
605        }
606    }
607
608    /**
609     * Send an SMS PDU. Usually just calls {@link sendRawPdu}.
610     */
611    protected abstract void sendSubmitPdu(SmsTracker tracker);
612
613    /**
614     * Called when SMS send completes. Broadcasts a sentIntent on success.
615     * On failure, either sets up retries or broadcasts a sentIntent with
616     * the failure in the result code.
617     *
618     * @param ar AsyncResult passed into the message handler.  ar.result should
619     *           an SmsResponse instance if send was successful.  ar.userObj
620     *           should be an SmsTracker instance.
621     */
622    protected void handleSendComplete(AsyncResult ar) {
623        SmsTracker tracker = (SmsTracker) ar.userObj;
624        PendingIntent sentIntent = tracker.mSentIntent;
625
626        if (ar.result != null) {
627            tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef;
628        } else {
629            Rlog.d(TAG, "SmsResponse was null");
630        }
631
632        if (ar.exception == null) {
633            if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent);
634
635            if (tracker.mDeliveryIntent != null) {
636                // Expecting a status report.  Add it to the list.
637                deliveryPendingList.add(tracker);
638            }
639            tracker.onSent(mContext);
640        } else {
641            if (DBG) Rlog.d(TAG, "SMS send failed");
642
643            int ss = mPhone.getServiceState().getState();
644
645            if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
646                // This is retry after failure over IMS but voice is not available.
647                // Set retry to max allowed, so no retry is sent and
648                //   cause RESULT_ERROR_GENERIC_FAILURE to be returned to app.
649                tracker.mRetryCount = MAX_SEND_RETRIES;
650
651                Rlog.d(TAG, "handleSendComplete: Skipping retry: "
652                +" isIms()="+isIms()
653                +" mRetryCount="+tracker.mRetryCount
654                +" mImsRetry="+tracker.mImsRetry
655                +" mMessageRef="+tracker.mMessageRef
656                +" SS= "+mPhone.getServiceState().getState());
657            }
658
659            // if sms over IMS is not supported on data and voice is not available...
660            if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
661                tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
662            } else if ((((CommandException)(ar.exception)).getCommandError()
663                    == CommandException.Error.SMS_FAIL_RETRY) &&
664                   tracker.mRetryCount < MAX_SEND_RETRIES) {
665                // Retry after a delay if needed.
666                // TODO: According to TS 23.040, 9.2.3.6, we should resend
667                //       with the same TP-MR as the failed message, and
668                //       TP-RD set to 1.  However, we don't have a means of
669                //       knowing the MR for the failed message (EF_SMSstatus
670                //       may or may not have the MR corresponding to this
671                //       message, depending on the failure).  Also, in some
672                //       implementations this retry is handled by the baseband.
673                tracker.mRetryCount++;
674                Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
675                sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
676            } else {
677                int errorCode = 0;
678                if (ar.result != null) {
679                    errorCode = ((SmsResponse)ar.result).mErrorCode;
680                }
681                int error = RESULT_ERROR_GENERIC_FAILURE;
682                if (((CommandException)(ar.exception)).getCommandError()
683                        == CommandException.Error.FDN_CHECK_FAILURE) {
684                    error = RESULT_ERROR_FDN_CHECK_FAILURE;
685                }
686                tracker.onFailed(mContext, error, errorCode);
687            }
688        }
689    }
690
691    /**
692     * Handles outbound message when the phone is not in service.
693     *
694     * @param ss     Current service state.  Valid values are:
695     *                  OUT_OF_SERVICE
696     *                  EMERGENCY_ONLY
697     *                  POWER_OFF
698     * @param sentIntent the PendingIntent to send the error to
699     */
700    protected static void handleNotInService(int ss, PendingIntent sentIntent) {
701        if (sentIntent != null) {
702            try {
703                if (ss == ServiceState.STATE_POWER_OFF) {
704                    sentIntent.send(RESULT_ERROR_RADIO_OFF);
705                } else {
706                    sentIntent.send(RESULT_ERROR_NO_SERVICE);
707                }
708            } catch (CanceledException ex) {}
709        }
710    }
711
712    /**
713     * @param ss service state
714     * @return The result error based on input service state for not in service error
715     */
716    protected static int getNotInServiceError(int ss) {
717        if (ss == ServiceState.STATE_POWER_OFF) {
718            return RESULT_ERROR_RADIO_OFF;
719        }
720        return RESULT_ERROR_NO_SERVICE;
721    }
722
723    /**
724     * Send a data based SMS to a specific application port.
725     *
726     * @param destAddr the address to send the message to
727     * @param scAddr is the service center address or null to use
728     *  the current default SMSC
729     * @param destPort the port to deliver the message to
730     * @param data the body of the message to send
731     * @param sentIntent if not NULL this <code>PendingIntent</code> is
732     *  broadcast when the message is successfully sent, or failed.
733     *  The result code will be <code>Activity.RESULT_OK<code> for success,
734     *  or one of these errors:<br>
735     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
736     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
737     *  <code>RESULT_ERROR_NULL_PDU</code><br>
738     *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
739     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
740     *  the extra "errorCode" containing a radio technology specific value,
741     *  generally only useful for troubleshooting.<br>
742     *  The per-application based SMS control checks sentIntent. If sentIntent
743     *  is NULL the caller will be checked against all unknown applications,
744     *  which cause smaller number of SMS to be sent in checking period.
745     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
746     *  broadcast when the message is delivered to the recipient.  The
747     *  raw pdu of the status report is in the extended data ("pdu").
748     */
749    protected abstract void sendData(String destAddr, String scAddr, int destPort,
750            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
751
752    /**
753     * Send a text based SMS.
754     *  @param destAddr the address to send the message to
755     * @param scAddr is the service center address or null to use
756     *  the current default SMSC
757     * @param text the body of the message to send
758     * @param sentIntent if not NULL this <code>PendingIntent</code> is
759     *  broadcast when the message is successfully sent, or failed.
760     *  The result code will be <code>Activity.RESULT_OK<code> for success,
761     *  or one of these errors:<br>
762     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
763     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
764     *  <code>RESULT_ERROR_NULL_PDU</code><br>
765     *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
766     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
767     *  the extra "errorCode" containing a radio technology specific value,
768     *  generally only useful for troubleshooting.<br>
769     *  The per-application based SMS control checks sentIntent. If sentIntent
770     *  is NULL the caller will be checked against all unknown applications,
771     *  which cause smaller number of SMS to be sent in checking period.
772     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
773     *  broadcast when the message is delivered to the recipient.  The
774     * @param messageUri optional URI of the message if it is already stored in the system
775     * @param callingPkg the calling package name
776     * @param persistMessage whether to save the sent message into SMS DB for a
777     *   non-default SMS app.
778     */
779    protected abstract void sendText(String destAddr, String scAddr, String text,
780            PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
781            String callingPkg, boolean persistMessage);
782
783    /**
784     * Inject an SMS PDU into the android platform.
785     *
786     * @param pdu is the byte array of pdu to be injected into android telephony layer
787     * @param format is the format of SMS pdu (3gpp or 3gpp2)
788     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
789     *  broadcast when the message is successfully received by the
790     *  android telephony layer. This intent is broadcasted at
791     *  the same time an SMS received from radio is responded back.
792     */
793    protected abstract void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent);
794
795    /**
796     * Calculate the number of septets needed to encode the message. This function should only be
797     * called for individual segments of multipart message.
798     *
799     * @param messageBody the message to encode
800     * @param use7bitOnly ignore (but still count) illegal characters if true
801     * @return TextEncodingDetails
802     */
803    protected abstract TextEncodingDetails calculateLength(CharSequence messageBody,
804            boolean use7bitOnly);
805
806    /**
807     * Send a multi-part text based SMS.
808     *  @param destAddr the address to send the message to
809     * @param scAddr is the service center address or null to use
810     *   the current default SMSC
811     * @param parts an <code>ArrayList</code> of strings that, in order,
812     *   comprise the original message
813     * @param sentIntents if not null, an <code>ArrayList</code> of
814     *   <code>PendingIntent</code>s (one for each message part) that is
815     *   broadcast when the corresponding message part has been sent.
816     *   The result code will be <code>Activity.RESULT_OK<code> for success,
817     *   or one of these errors:
818     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
819     *   <code>RESULT_ERROR_RADIO_OFF</code>
820     *   <code>RESULT_ERROR_NULL_PDU</code>
821     *   <code>RESULT_ERROR_NO_SERVICE</code>.
822     *  The per-application based SMS control checks sentIntent. If sentIntent
823     *  is NULL the caller will be checked against all unknown applications,
824     *  which cause smaller number of SMS to be sent in checking period.
825     * @param deliveryIntents if not null, an <code>ArrayList</code> of
826     *   <code>PendingIntent</code>s (one for each message part) that is
827     *   broadcast when the corresponding message part has been delivered
828     *   to the recipient.  The raw pdu of the status report is in the
829     * @param messageUri optional URI of the message if it is already stored in the system
830     * @param callingPkg the calling package name
831     * @param persistMessage whether to save the sent message into SMS DB for a
832     *   non-default SMS app.
833     */
834    protected void sendMultipartText(String destAddr, String scAddr,
835            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
836            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
837            boolean persistMessage) {
838        final String fullMessageText = getMultipartMessageText(parts);
839        int refNumber = getNextConcatenatedRef() & 0x00FF;
840        int msgCount = parts.size();
841        int encoding = SmsConstants.ENCODING_UNKNOWN;
842
843        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
844        for (int i = 0; i < msgCount; i++) {
845            TextEncodingDetails details = calculateLength(parts.get(i), false);
846            if (encoding != details.codeUnitSize
847                    && (encoding == SmsConstants.ENCODING_UNKNOWN
848                            || encoding == SmsConstants.ENCODING_7BIT)) {
849                encoding = details.codeUnitSize;
850            }
851            encodingForParts[i] = details;
852        }
853
854        SmsTracker[] trackers = new SmsTracker[msgCount];
855
856        // States to track at the message level (for all parts)
857        final AtomicInteger unsentPartCount = new AtomicInteger(msgCount);
858        final AtomicBoolean anyPartFailed = new AtomicBoolean(false);
859
860        for (int i = 0; i < msgCount; i++) {
861            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
862            concatRef.refNumber = refNumber;
863            concatRef.seqNumber = i + 1;  // 1-based sequence
864            concatRef.msgCount = msgCount;
865            // TODO: We currently set this to true since our messaging app will never
866            // send more than 255 parts (it converts the message to MMS well before that).
867            // However, we should support 3rd party messaging apps that might need 16-bit
868            // references
869            // Note:  It's not sufficient to just flip this bit to true; it will have
870            // ripple effects (several calculations assume 8-bit ref).
871            concatRef.isEightBits = true;
872            SmsHeader smsHeader = new SmsHeader();
873            smsHeader.concatRef = concatRef;
874
875            // Set the national language tables for 3GPP 7-bit encoding, if enabled.
876            if (encoding == SmsConstants.ENCODING_7BIT) {
877                smsHeader.languageTable = encodingForParts[i].languageTable;
878                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
879            }
880
881            PendingIntent sentIntent = null;
882            if (sentIntents != null && sentIntents.size() > i) {
883                sentIntent = sentIntents.get(i);
884            }
885
886            PendingIntent deliveryIntent = null;
887            if (deliveryIntents != null && deliveryIntents.size() > i) {
888                deliveryIntent = deliveryIntents.get(i);
889            }
890
891            trackers[i] =
892                getNewSubmitPduTracker(destAddr, scAddr, parts.get(i), smsHeader, encoding,
893                        sentIntent, deliveryIntent, (i == (msgCount - 1)),
894                        unsentPartCount, anyPartFailed, messageUri, fullMessageText);
895            trackers[i].mPersistMessage = persistMessage;
896        }
897
898        if (parts == null || trackers == null || trackers.length == 0
899                || trackers[0] == null) {
900            Rlog.e(TAG, "Cannot send multipart text. parts=" + parts + " trackers=" + trackers);
901            return;
902        }
903
904        String carrierPackage = getCarrierAppPackageName();
905        if (carrierPackage != null) {
906            Rlog.d(TAG, "Found carrier package.");
907            MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers);
908            smsSender.sendSmsByCarrierApp(carrierPackage, new MultipartSmsSenderCallback(smsSender));
909        } else {
910            Rlog.v(TAG, "No carrier package.");
911            for (SmsTracker tracker : trackers) {
912                if (tracker != null) {
913                    sendSubmitPdu(tracker);
914                } else {
915                    Rlog.e(TAG, "Null tracker.");
916                }
917            }
918        }
919    }
920
921    /**
922     * Create a new SubmitPdu and return the SMS tracker.
923     */
924    protected abstract SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
925            String message, SmsHeader smsHeader, int encoding,
926            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
927            AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
928            String fullMessageText);
929
930    /**
931     * Send an SMS
932     * @param tracker will contain:
933     * -smsc the SMSC to send the message through, or NULL for the
934     *  default SMSC
935     * -pdu the raw PDU to send
936     * -sentIntent if not NULL this <code>Intent</code> is
937     *  broadcast when the message is successfully sent, or failed.
938     *  The result code will be <code>Activity.RESULT_OK<code> for success,
939     *  or one of these errors:
940     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
941     *  <code>RESULT_ERROR_RADIO_OFF</code>
942     *  <code>RESULT_ERROR_NULL_PDU</code>
943     *  <code>RESULT_ERROR_NO_SERVICE</code>.
944     *  The per-application based SMS control checks sentIntent. If sentIntent
945     *  is NULL the caller will be checked against all unknown applications,
946     *  which cause smaller number of SMS to be sent in checking period.
947     * -deliveryIntent if not NULL this <code>Intent</code> is
948     *  broadcast when the message is delivered to the recipient.  The
949     *  raw pdu of the status report is in the extended data ("pdu").
950     * -param destAddr the destination phone number (for short code confirmation)
951     */
952    protected void sendRawPdu(SmsTracker tracker) {
953        HashMap map = tracker.getData();
954        byte pdu[] = (byte[]) map.get("pdu");
955
956        if (mSmsSendDisabled) {
957            Rlog.e(TAG, "Device does not support sending sms.");
958            tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
959            return;
960        }
961
962        if (pdu == null) {
963            Rlog.e(TAG, "Empty PDU");
964            tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/);
965            return;
966        }
967
968        // Get calling app package name via UID from Binder call
969        PackageManager pm = mContext.getPackageManager();
970        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
971
972        if (packageNames == null || packageNames.length == 0) {
973            // Refuse to send SMS if we can't get the calling package name.
974            Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS");
975            tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
976            return;
977        }
978
979        // Get package info via packagemanager
980        PackageInfo appInfo;
981        try {
982            // XXX this is lossy- apps can share a UID
983            appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
984        } catch (PackageManager.NameNotFoundException e) {
985            Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");
986            tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
987            return;
988        }
989
990        // checkDestination() returns true if the destination is not a premium short code or the
991        // sending app is approved to send to short codes. Otherwise, a message is sent to our
992        // handler with the SmsTracker to request user confirmation before sending.
993        if (checkDestination(tracker)) {
994            // check for excessive outgoing SMS usage by this app
995            if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {
996                sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
997                return;
998            }
999
1000            sendSms(tracker);
1001        }
1002
1003        if (PhoneNumberUtils.isLocalEmergencyNumber(mContext, tracker.mDestAddress)) {
1004            new AsyncEmergencyContactNotifier(mContext).execute();
1005        }
1006    }
1007
1008    /**
1009     * Check if destination is a potential premium short code and sender is not pre-approved to
1010     * send to short codes.
1011     *
1012     * @param tracker the tracker for the SMS to send
1013     * @return true if the destination is approved; false if user confirmation event was sent
1014     */
1015    boolean checkDestination(SmsTracker tracker) {
1016        if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION)
1017                == PackageManager.PERMISSION_GRANTED) {
1018            return true;            // app is pre-approved to send to short codes
1019        } else {
1020            int rule = mPremiumSmsRule.get();
1021            int smsCategory = SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE;
1022            if (rule == PREMIUM_RULE_USE_SIM || rule == PREMIUM_RULE_USE_BOTH) {
1023                String simCountryIso = mTelephonyManager.getSimCountryIso();
1024                if (simCountryIso == null || simCountryIso.length() != 2) {
1025                    Rlog.e(TAG, "Can't get SIM country Iso: trying network country Iso");
1026                    simCountryIso = mTelephonyManager.getNetworkCountryIso();
1027                }
1028
1029                smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso);
1030            }
1031            if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) {
1032                String networkCountryIso = mTelephonyManager.getNetworkCountryIso();
1033                if (networkCountryIso == null || networkCountryIso.length() != 2) {
1034                    Rlog.e(TAG, "Can't get Network country Iso: trying SIM country Iso");
1035                    networkCountryIso = mTelephonyManager.getSimCountryIso();
1036                }
1037
1038                smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory,
1039                        mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso));
1040            }
1041
1042            if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE
1043                    || smsCategory == SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE
1044                    || smsCategory == SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE) {
1045                return true;    // not a premium short code
1046            }
1047
1048            // Do not allow any premium sms during SuW
1049            if (Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
1050                Rlog.e(TAG, "Can't send premium sms during Setup Wizard");
1051                return false;
1052            }
1053
1054            // Wait for user confirmation unless the user has set permission to always allow/deny
1055            int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
1056                    tracker.mAppInfo.packageName);
1057            if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
1058                // First time trying to send to premium SMS.
1059                premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
1060            }
1061
1062            switch (premiumSmsPermission) {
1063                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW:
1064                    Rlog.d(TAG, "User approved this app to send to premium SMS");
1065                    return true;
1066
1067                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
1068                    Rlog.w(TAG, "User denied this app from sending to premium SMS");
1069                    sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker));
1070                    return false;   // reject this message
1071
1072                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
1073                default:
1074                    int event;
1075                    if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) {
1076                        event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE;
1077                    } else {
1078                        event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE;
1079                    }
1080                    sendMessage(obtainMessage(event, tracker));
1081                    return false;   // wait for user confirmation
1082            }
1083        }
1084    }
1085
1086    /**
1087     * Deny sending an SMS if the outgoing queue limit is reached. Used when the message
1088     * must be confirmed by the user due to excessive usage or potential premium SMS detected.
1089     * @param tracker the SmsTracker for the message to send
1090     * @return true if the message was denied; false to continue with send confirmation
1091     */
1092    private boolean denyIfQueueLimitReached(SmsTracker tracker) {
1093        if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) {
1094            // Deny sending message when the queue limit is reached.
1095            Rlog.e(TAG, "Denied because queue limit reached");
1096            tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
1097            return true;
1098        }
1099        mPendingTrackerCount++;
1100        return false;
1101    }
1102
1103    /**
1104     * Returns the label for the specified app package name.
1105     * @param appPackage the package name of the app requesting to send an SMS
1106     * @return the label for the specified app, or the package name if getApplicationInfo() fails
1107     */
1108    private CharSequence getAppLabel(String appPackage) {
1109        PackageManager pm = mContext.getPackageManager();
1110        try {
1111            ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0);
1112            return appInfo.loadSafeLabel(pm);
1113        } catch (PackageManager.NameNotFoundException e) {
1114            Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage);
1115            return appPackage;  // fall back to package name if we can't get app label
1116        }
1117    }
1118
1119    /**
1120     * Post an alert when SMS needs confirmation due to excessive usage.
1121     * @param tracker an SmsTracker for the current message.
1122     */
1123    protected void handleReachSentLimit(SmsTracker tracker) {
1124        if (denyIfQueueLimitReached(tracker)) {
1125            return;     // queue limit reached; error was returned to caller
1126        }
1127
1128        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
1129        Resources r = Resources.getSystem();
1130        Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
1131
1132        ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null);
1133
1134        AlertDialog d = new AlertDialog.Builder(mContext)
1135                .setTitle(R.string.sms_control_title)
1136                .setIcon(R.drawable.stat_sys_warning)
1137                .setMessage(messageText)
1138                .setPositiveButton(r.getString(R.string.sms_control_yes), listener)
1139                .setNegativeButton(r.getString(R.string.sms_control_no), listener)
1140                .setOnCancelListener(listener)
1141                .create();
1142
1143        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1144        d.show();
1145    }
1146
1147    /**
1148     * Post an alert for user confirmation when sending to a potential short code.
1149     * @param isPremium true if the destination is known to be a premium short code
1150     * @param tracker the SmsTracker for the current message.
1151     */
1152    protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) {
1153        if (denyIfQueueLimitReached(tracker)) {
1154            return;     // queue limit reached; error was returned to caller
1155        }
1156
1157        int detailsId;
1158        if (isPremium) {
1159            detailsId = R.string.sms_premium_short_code_details;
1160        } else {
1161            detailsId = R.string.sms_short_code_details;
1162        }
1163
1164        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
1165        Resources r = Resources.getSystem();
1166        Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
1167                appLabel, tracker.mDestAddress));
1168
1169        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1170                Context.LAYOUT_INFLATER_SERVICE);
1171        View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null);
1172
1173        ConfirmDialogListener listener = new ConfirmDialogListener(tracker,
1174                (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction));
1175
1176
1177        TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message);
1178        messageView.setText(messageText);
1179
1180        ViewGroup detailsLayout = (ViewGroup) layout.findViewById(
1181                R.id.sms_short_code_detail_layout);
1182        TextView detailsView = (TextView) detailsLayout.findViewById(
1183                R.id.sms_short_code_detail_message);
1184        detailsView.setText(detailsId);
1185
1186        CheckBox rememberChoice = (CheckBox) layout.findViewById(
1187                R.id.sms_short_code_remember_choice_checkbox);
1188        rememberChoice.setOnCheckedChangeListener(listener);
1189
1190        AlertDialog d = new AlertDialog.Builder(mContext)
1191                .setView(layout)
1192                .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener)
1193                .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener)
1194                .setOnCancelListener(listener)
1195                .create();
1196
1197        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1198        d.show();
1199
1200        listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE));
1201        listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE));
1202    }
1203
1204    /**
1205     * Returns the premium SMS permission for the specified package. If the package has never
1206     * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
1207     * will be returned.
1208     * @param packageName the name of the package to query permission
1209     * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
1210     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
1211     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
1212     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
1213     */
1214    public int getPremiumSmsPermission(String packageName) {
1215        return mUsageMonitor.getPremiumSmsPermission(packageName);
1216    }
1217
1218    /**
1219     * Sets the premium SMS permission for the specified package and save the value asynchronously
1220     * to persistent storage.
1221     * @param packageName the name of the package to set permission
1222     * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
1223     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
1224     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
1225     */
1226    public void setPremiumSmsPermission(String packageName, int permission) {
1227        mUsageMonitor.setPremiumSmsPermission(packageName, permission);
1228    }
1229
1230    /**
1231     * Send the message along to the radio.
1232     *
1233     * @param tracker holds the SMS message to send
1234     */
1235    protected abstract void sendSms(SmsTracker tracker);
1236
1237    /**
1238     * Send the SMS via the PSTN network.
1239     *
1240     * @param tracker holds the Sms tracker ready to be sent
1241     */
1242    protected abstract void sendSmsByPstn(SmsTracker tracker);
1243
1244    /**
1245     * Retry the message along to the radio.
1246     *
1247     * @param tracker holds the SMS message to send
1248     */
1249    public void sendRetrySms(SmsTracker tracker) {
1250        // re-routing to ImsSMSDispatcher
1251        if (mImsSMSDispatcher != null) {
1252            mImsSMSDispatcher.sendRetrySms(tracker);
1253        } else {
1254            Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed");
1255        }
1256    }
1257
1258    /**
1259     * Send the multi-part SMS based on multipart Sms tracker
1260     *
1261     * @param tracker holds the multipart Sms tracker ready to be sent
1262     */
1263    private void sendMultipartSms(SmsTracker tracker) {
1264        ArrayList<String> parts;
1265        ArrayList<PendingIntent> sentIntents;
1266        ArrayList<PendingIntent> deliveryIntents;
1267
1268        HashMap<String, Object> map = tracker.getData();
1269
1270        String destinationAddress = (String) map.get("destination");
1271        String scAddress = (String) map.get("scaddress");
1272
1273        parts = (ArrayList<String>) map.get("parts");
1274        sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
1275        deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
1276
1277        // check if in service
1278        int ss = mPhone.getServiceState().getState();
1279        // if sms over IMS is not supported on data and voice is not available...
1280        if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
1281            for (int i = 0, count = parts.size(); i < count; i++) {
1282                PendingIntent sentIntent = null;
1283                if (sentIntents != null && sentIntents.size() > i) {
1284                    sentIntent = sentIntents.get(i);
1285                }
1286                handleNotInService(ss, sentIntent);
1287            }
1288            return;
1289        }
1290
1291        sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
1292                null/*messageUri*/, null/*callingPkg*/, tracker.mPersistMessage);
1293    }
1294
1295    /**
1296     * Keeps track of an SMS that has been sent to the RIL, until it has
1297     * successfully been sent, or we're done trying.
1298     */
1299    public static class SmsTracker {
1300        // fields need to be public for derived SmsDispatchers
1301        private final HashMap<String, Object> mData;
1302        public int mRetryCount;
1303        public int mImsRetry; // nonzero indicates initial message was sent over Ims
1304        public int mMessageRef;
1305        public boolean mExpectMore;
1306        String mFormat;
1307
1308        public final PendingIntent mSentIntent;
1309        public final PendingIntent mDeliveryIntent;
1310
1311        public final PackageInfo mAppInfo;
1312        public final String mDestAddress;
1313
1314        public final SmsHeader mSmsHeader;
1315
1316        private long mTimestamp = System.currentTimeMillis();
1317        public Uri mMessageUri; // Uri of persisted message if we wrote one
1318
1319        // Reference to states of a multipart message that this part belongs to
1320        private AtomicInteger mUnsentPartCount;
1321        private AtomicBoolean mAnyPartFailed;
1322        // The full message content of a single part message
1323        // or a multipart message that this part belongs to
1324        private String mFullMessageText;
1325
1326        private int mSubId;
1327
1328        // If this is a text message (instead of data message)
1329        private boolean mIsText;
1330
1331        private boolean mPersistMessage;
1332
1333        private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1334                PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
1335                AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
1336                SmsHeader smsHeader, boolean isExpectMore, String fullMessageText, int subId,
1337                boolean isText, boolean persistMessage) {
1338            mData = data;
1339            mSentIntent = sentIntent;
1340            mDeliveryIntent = deliveryIntent;
1341            mRetryCount = 0;
1342            mAppInfo = appInfo;
1343            mDestAddress = destAddr;
1344            mFormat = format;
1345            mExpectMore = isExpectMore;
1346            mImsRetry = 0;
1347            mMessageRef = 0;
1348            mUnsentPartCount = unsentPartCount;
1349            mAnyPartFailed = anyPartFailed;
1350            mMessageUri = messageUri;
1351            mSmsHeader = smsHeader;
1352            mFullMessageText = fullMessageText;
1353            mSubId = subId;
1354            mIsText = isText;
1355            mPersistMessage = persistMessage;
1356        }
1357
1358        /**
1359         * Returns whether this tracker holds a multi-part SMS.
1360         * @return true if the tracker holds a multi-part SMS; false otherwise
1361         */
1362        boolean isMultipart() {
1363            return mData.containsKey("parts");
1364        }
1365
1366        public HashMap<String, Object> getData() {
1367            return mData;
1368        }
1369
1370        /**
1371         * Update the status of this message if we persisted it
1372         */
1373        public void updateSentMessageStatus(Context context, int status) {
1374            if (mMessageUri != null) {
1375                // If we wrote this message in writeSentMessage, update it now
1376                ContentValues values = new ContentValues(1);
1377                values.put(Sms.STATUS, status);
1378                SqliteWrapper.update(context, context.getContentResolver(),
1379                        mMessageUri, values, null, null);
1380            }
1381        }
1382
1383        /**
1384         * Set the final state of a message: FAILED or SENT
1385         *
1386         * @param context The Context
1387         * @param messageType The final message type
1388         * @param errorCode The error code
1389         */
1390        private void updateMessageState(Context context, int messageType, int errorCode) {
1391            if (mMessageUri == null) {
1392                return;
1393            }
1394            final ContentValues values = new ContentValues(2);
1395            values.put(Sms.TYPE, messageType);
1396            values.put(Sms.ERROR_CODE, errorCode);
1397            final long identity = Binder.clearCallingIdentity();
1398            try {
1399                if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
1400                        null/*where*/, null/*selectionArgs*/) != 1) {
1401                    Rlog.e(TAG, "Failed to move message to " + messageType);
1402                }
1403            } finally {
1404                Binder.restoreCallingIdentity(identity);
1405            }
1406        }
1407
1408        /**
1409         * Persist a sent SMS if required:
1410         * 1. It is a text message
1411         * 2. SmsApplication tells us to persist: sent from apps that are not default-SMS app or
1412         *    bluetooth
1413         *
1414         * @param context
1415         * @param messageType The folder to store (FAILED or SENT)
1416         * @param errorCode The current error code for this SMS or SMS part
1417         * @return The telephony provider URI if stored
1418         */
1419        private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) {
1420            if (!mIsText || !mPersistMessage ||
1421                    !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) {
1422                return null;
1423            }
1424            Rlog.d(TAG, "Persist SMS into "
1425                    + (messageType == Sms.MESSAGE_TYPE_FAILED ? "FAILED" : "SENT"));
1426            final ContentValues values = new ContentValues();
1427            values.put(Sms.SUBSCRIPTION_ID, mSubId);
1428            values.put(Sms.ADDRESS, mDestAddress);
1429            values.put(Sms.BODY, mFullMessageText);
1430            values.put(Sms.DATE, System.currentTimeMillis()); // milliseconds
1431            values.put(Sms.SEEN, 1);
1432            values.put(Sms.READ, 1);
1433            final String creator = mAppInfo != null ? mAppInfo.packageName : null;
1434            if (!TextUtils.isEmpty(creator)) {
1435                values.put(Sms.CREATOR, creator);
1436            }
1437            if (mDeliveryIntent != null) {
1438                values.put(Sms.STATUS, Telephony.Sms.STATUS_PENDING);
1439            }
1440            if (errorCode != 0) {
1441                values.put(Sms.ERROR_CODE, errorCode);
1442            }
1443            final long identity = Binder.clearCallingIdentity();
1444            final ContentResolver resolver = context.getContentResolver();
1445            try {
1446                final Uri uri =  resolver.insert(Telephony.Sms.Sent.CONTENT_URI, values);
1447                if (uri != null && messageType == Sms.MESSAGE_TYPE_FAILED) {
1448                    // Since we can't persist a message directly into FAILED box,
1449                    // we have to update the column after we persist it into SENT box.
1450                    // The gap between the state change is tiny so I would not expect
1451                    // it to cause any serious problem
1452                    // TODO: we should add a "failed" URI for this in SmsProvider?
1453                    final ContentValues updateValues = new ContentValues(1);
1454                    updateValues.put(Sms.TYPE, Sms.MESSAGE_TYPE_FAILED);
1455                    resolver.update(uri, updateValues, null/*where*/, null/*selectionArgs*/);
1456                }
1457                return uri;
1458            } catch (Exception e) {
1459                Rlog.e(TAG, "writeOutboxMessage: Failed to persist outbox message", e);
1460                return null;
1461            } finally {
1462                Binder.restoreCallingIdentity(identity);
1463            }
1464        }
1465
1466        /**
1467         * Persist or update an SMS depending on if we send a new message or a stored message
1468         *
1469         * @param context
1470         * @param messageType The message folder for this SMS, FAILED or SENT
1471         * @param errorCode The current error code for this SMS or SMS part
1472         */
1473        private void persistOrUpdateMessage(Context context, int messageType, int errorCode) {
1474            if (mMessageUri != null) {
1475                updateMessageState(context, messageType, errorCode);
1476            } else {
1477                mMessageUri = persistSentMessageIfRequired(context, messageType, errorCode);
1478            }
1479        }
1480
1481        /**
1482         * Handle a failure of a single part message or a part of a multipart message
1483         *
1484         * @param context The Context
1485         * @param error The error to send back with
1486         * @param errorCode
1487         */
1488        public void onFailed(Context context, int error, int errorCode) {
1489            if (mAnyPartFailed != null) {
1490                mAnyPartFailed.set(true);
1491            }
1492            // is single part or last part of multipart message
1493            boolean isSinglePartOrLastPart = true;
1494            if (mUnsentPartCount != null) {
1495                isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
1496            }
1497            if (isSinglePartOrLastPart) {
1498                persistOrUpdateMessage(context, Sms.MESSAGE_TYPE_FAILED, errorCode);
1499            }
1500            if (mSentIntent != null) {
1501                try {
1502                    // Extra information to send with the sent intent
1503                    Intent fillIn = new Intent();
1504                    if (mMessageUri != null) {
1505                        // Pass this to SMS apps so that they know where it is stored
1506                        fillIn.putExtra("uri", mMessageUri.toString());
1507                    }
1508                    if (errorCode != 0) {
1509                        fillIn.putExtra("errorCode", errorCode);
1510                    }
1511                    if (mUnsentPartCount != null && isSinglePartOrLastPart) {
1512                        // Is multipart and last part
1513                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
1514                    }
1515                    mSentIntent.send(context, error, fillIn);
1516                } catch (CanceledException ex) {
1517                    Rlog.e(TAG, "Failed to send result");
1518                }
1519            }
1520        }
1521
1522        /**
1523         * Handle the sent of a single part message or a part of a multipart message
1524         *
1525         * @param context The Context
1526         */
1527        public void onSent(Context context) {
1528            // is single part or last part of multipart message
1529            boolean isSinglePartOrLastPart = true;
1530            if (mUnsentPartCount != null) {
1531                isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
1532            }
1533            if (isSinglePartOrLastPart) {
1534                int messageType = Sms.MESSAGE_TYPE_SENT;
1535                if (mAnyPartFailed != null && mAnyPartFailed.get()) {
1536                    messageType = Sms.MESSAGE_TYPE_FAILED;
1537                }
1538                persistOrUpdateMessage(context, messageType, 0/*errorCode*/);
1539            }
1540            if (mSentIntent != null) {
1541                try {
1542                    // Extra information to send with the sent intent
1543                    Intent fillIn = new Intent();
1544                    if (mMessageUri != null) {
1545                        // Pass this to SMS apps so that they know where it is stored
1546                        fillIn.putExtra("uri", mMessageUri.toString());
1547                    }
1548                    if (mUnsentPartCount != null && isSinglePartOrLastPart) {
1549                        // Is multipart and last part
1550                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
1551                    }
1552                    mSentIntent.send(context, Activity.RESULT_OK, fillIn);
1553                } catch (CanceledException ex) {
1554                    Rlog.e(TAG, "Failed to send result");
1555                }
1556            }
1557        }
1558    }
1559
1560    protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1561            PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
1562            AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
1563            boolean isExpectMore, String fullMessageText, boolean isText, boolean persistMessage) {
1564        // Get calling app package name via UID from Binder call
1565        PackageManager pm = mContext.getPackageManager();
1566        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
1567
1568        // Get package info via packagemanager
1569        PackageInfo appInfo = null;
1570        if (packageNames != null && packageNames.length > 0) {
1571            try {
1572                // XXX this is lossy- apps can share a UID
1573                appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
1574            } catch (PackageManager.NameNotFoundException e) {
1575                // error will be logged in sendRawPdu
1576            }
1577        }
1578        // Strip non-digits from destination phone number before checking for short codes
1579        // and before displaying the number to the user if confirmation is required.
1580        String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
1581        return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
1582                unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore,
1583                fullMessageText, getSubId(), isText, persistMessage);
1584    }
1585
1586    protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1587            PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore,
1588            String fullMessageText, boolean isText, boolean persistMessage) {
1589        return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
1590                null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore,
1591                fullMessageText, isText, persistMessage);
1592    }
1593
1594    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1595            String text, SmsMessageBase.SubmitPduBase pdu) {
1596        HashMap<String, Object> map = new HashMap<String, Object>();
1597        map.put("destAddr", destAddr);
1598        map.put("scAddr", scAddr);
1599        map.put("text", text);
1600        map.put("smsc", pdu.encodedScAddress);
1601        map.put("pdu", pdu.encodedMessage);
1602        return map;
1603    }
1604
1605    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1606            int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
1607        HashMap<String, Object> map = new HashMap<String, Object>();
1608        map.put("destAddr", destAddr);
1609        map.put("scAddr", scAddr);
1610        map.put("destPort", destPort);
1611        map.put("data", data);
1612        map.put("smsc", pdu.encodedScAddress);
1613        map.put("pdu", pdu.encodedMessage);
1614        return map;
1615    }
1616
1617    /**
1618     * Dialog listener for SMS confirmation dialog.
1619     */
1620    private final class ConfirmDialogListener
1621            implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
1622            CompoundButton.OnCheckedChangeListener {
1623
1624        private final SmsTracker mTracker;
1625        private Button mPositiveButton;
1626        private Button mNegativeButton;
1627        private boolean mRememberChoice;    // default is unchecked
1628        private final TextView mRememberUndoInstruction;
1629
1630        ConfirmDialogListener(SmsTracker tracker, TextView textView) {
1631            mTracker = tracker;
1632            mRememberUndoInstruction = textView;
1633        }
1634
1635        void setPositiveButton(Button button) {
1636            mPositiveButton = button;
1637        }
1638
1639        void setNegativeButton(Button button) {
1640            mNegativeButton = button;
1641        }
1642
1643        @Override
1644        public void onClick(DialogInterface dialog, int which) {
1645            // Always set the SMS permission so that Settings will show a permission setting
1646            // for the app (it won't be shown until after the app tries to send to a short code).
1647            int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
1648
1649            if (which == DialogInterface.BUTTON_POSITIVE) {
1650                Rlog.d(TAG, "CONFIRM sending SMS");
1651                // XXX this is lossy- apps can have more than one signature
1652                EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER,
1653                                    mTracker.mAppInfo.applicationInfo == null ?
1654                                    -1 : mTracker.mAppInfo.applicationInfo.uid);
1655                sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker));
1656                if (mRememberChoice) {
1657                    newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW;
1658                }
1659            } else if (which == DialogInterface.BUTTON_NEGATIVE) {
1660                Rlog.d(TAG, "DENY sending SMS");
1661                // XXX this is lossy- apps can have more than one signature
1662                EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
1663                                    mTracker.mAppInfo.applicationInfo == null ?
1664                                    -1 :  mTracker.mAppInfo.applicationInfo.uid);
1665                sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1666                if (mRememberChoice) {
1667                    newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
1668                }
1669            }
1670            setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
1671        }
1672
1673        @Override
1674        public void onCancel(DialogInterface dialog) {
1675            Rlog.d(TAG, "dialog dismissed: don't send SMS");
1676            sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1677        }
1678
1679        @Override
1680        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1681            Rlog.d(TAG, "remember this choice: " + isChecked);
1682            mRememberChoice = isChecked;
1683            if (isChecked) {
1684                mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow);
1685                mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow);
1686                if (mRememberUndoInstruction != null) {
1687                    mRememberUndoInstruction.
1688                            setText(R.string.sms_short_code_remember_undo_instruction);
1689                    mRememberUndoInstruction.setPadding(0,0,0,32);
1690                }
1691            } else {
1692                mPositiveButton.setText(R.string.sms_short_code_confirm_allow);
1693                mNegativeButton.setText(R.string.sms_short_code_confirm_deny);
1694                if (mRememberUndoInstruction != null) {
1695                    mRememberUndoInstruction.setText("");
1696                    mRememberUndoInstruction.setPadding(0,0,0,0);
1697                }
1698            }
1699        }
1700    }
1701
1702    public boolean isIms() {
1703        if (mImsSMSDispatcher != null) {
1704            return mImsSMSDispatcher.isIms();
1705        } else {
1706            Rlog.e(TAG, mImsSMSDispatcher + " is null");
1707            return false;
1708        }
1709    }
1710
1711    public String getImsSmsFormat() {
1712        if (mImsSMSDispatcher != null) {
1713            return mImsSMSDispatcher.getImsSmsFormat();
1714        } else {
1715            Rlog.e(TAG, mImsSMSDispatcher + " is null");
1716            return null;
1717        }
1718    }
1719
1720    private String getMultipartMessageText(ArrayList<String> parts) {
1721        final StringBuilder sb = new StringBuilder();
1722        for (String part : parts) {
1723            if (part != null) {
1724                sb.append(part);
1725            }
1726        }
1727        return sb.toString();
1728    }
1729
1730    protected String getCarrierAppPackageName() {
1731        UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
1732        if (card == null) {
1733            return null;
1734        }
1735
1736        List<String> carrierPackages = card.getCarrierPackageNamesForIntent(
1737            mContext.getPackageManager(), new Intent(CarrierMessagingService.SERVICE_INTERFACE));
1738        // TODO: Add Check for IMS app if there are no carrier packages.
1739        return (carrierPackages != null && carrierPackages.size() == 1) ?
1740                carrierPackages.get(0) : null;
1741    }
1742
1743    protected int getSubId() {
1744        return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.getPhoneId());
1745    }
1746
1747    private void checkCallerIsPhoneOrCarrierApp() {
1748        int uid = Binder.getCallingUid();
1749        int appId = UserHandle.getAppId(uid);
1750        if (appId == Process.PHONE_UID || uid == 0) {
1751            return;
1752        }
1753        try {
1754            PackageManager pm = mContext.getPackageManager();
1755            ApplicationInfo ai = pm.getApplicationInfo(getCarrierAppPackageName(), 0);
1756            if (!UserHandle.isSameApp(ai.uid, Binder.getCallingUid())) {
1757                throw new SecurityException("Caller is not phone or carrier app!");
1758            }
1759        } catch (PackageManager.NameNotFoundException re) {
1760            throw new SecurityException("Caller is not phone or carrier app!");
1761        }
1762    }
1763}
1764