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