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