[go: nahoru, domu]

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            // Wait for user confirmation unless the user has set permission to always allow/deny
1048            int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
1049                    tracker.mAppInfo.packageName);
1050            if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
1051                // First time trying to send to premium SMS.
1052                premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
1053            }
1054
1055            switch (premiumSmsPermission) {
1056                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW:
1057                    Rlog.d(TAG, "User approved this app to send to premium SMS");
1058                    return true;
1059
1060                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
1061                    Rlog.w(TAG, "User denied this app from sending to premium SMS");
1062                    sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker));
1063                    return false;   // reject this message
1064
1065                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
1066                default:
1067                    int event;
1068                    if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) {
1069                        event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE;
1070                    } else {
1071                        event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE;
1072                    }
1073                    sendMessage(obtainMessage(event, tracker));
1074                    return false;   // wait for user confirmation
1075            }
1076        }
1077    }
1078
1079    /**
1080     * Deny sending an SMS if the outgoing queue limit is reached. Used when the message
1081     * must be confirmed by the user due to excessive usage or potential premium SMS detected.
1082     * @param tracker the SmsTracker for the message to send
1083     * @return true if the message was denied; false to continue with send confirmation
1084     */
1085    private boolean denyIfQueueLimitReached(SmsTracker tracker) {
1086        if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) {
1087            // Deny sending message when the queue limit is reached.
1088            Rlog.e(TAG, "Denied because queue limit reached");
1089            tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
1090            return true;
1091        }
1092        mPendingTrackerCount++;
1093        return false;
1094    }
1095
1096    /**
1097     * Returns the label for the specified app package name.
1098     * @param appPackage the package name of the app requesting to send an SMS
1099     * @return the label for the specified app, or the package name if getApplicationInfo() fails
1100     */
1101    private CharSequence getAppLabel(String appPackage) {
1102        PackageManager pm = mContext.getPackageManager();
1103        try {
1104            ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0);
1105            return appInfo.loadSafeLabel(pm);
1106        } catch (PackageManager.NameNotFoundException e) {
1107            Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage);
1108            return appPackage;  // fall back to package name if we can't get app label
1109        }
1110    }
1111
1112    /**
1113     * Post an alert when SMS needs confirmation due to excessive usage.
1114     * @param tracker an SmsTracker for the current message.
1115     */
1116    protected void handleReachSentLimit(SmsTracker tracker) {
1117        if (denyIfQueueLimitReached(tracker)) {
1118            return;     // queue limit reached; error was returned to caller
1119        }
1120
1121        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
1122        Resources r = Resources.getSystem();
1123        Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
1124
1125        ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null);
1126
1127        AlertDialog d = new AlertDialog.Builder(mContext)
1128                .setTitle(R.string.sms_control_title)
1129                .setIcon(R.drawable.stat_sys_warning)
1130                .setMessage(messageText)
1131                .setPositiveButton(r.getString(R.string.sms_control_yes), listener)
1132                .setNegativeButton(r.getString(R.string.sms_control_no), listener)
1133                .setOnCancelListener(listener)
1134                .create();
1135
1136        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1137        d.show();
1138    }
1139
1140    /**
1141     * Post an alert for user confirmation when sending to a potential short code.
1142     * @param isPremium true if the destination is known to be a premium short code
1143     * @param tracker the SmsTracker for the current message.
1144     */
1145    protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) {
1146        if (denyIfQueueLimitReached(tracker)) {
1147            return;     // queue limit reached; error was returned to caller
1148        }
1149
1150        int detailsId;
1151        if (isPremium) {
1152            detailsId = R.string.sms_premium_short_code_details;
1153        } else {
1154            detailsId = R.string.sms_short_code_details;
1155        }
1156
1157        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
1158        Resources r = Resources.getSystem();
1159        Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
1160                appLabel, tracker.mDestAddress));
1161
1162        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1163                Context.LAYOUT_INFLATER_SERVICE);
1164        View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null);
1165
1166        ConfirmDialogListener listener = new ConfirmDialogListener(tracker,
1167                (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction));
1168
1169
1170        TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message);
1171        messageView.setText(messageText);
1172
1173        ViewGroup detailsLayout = (ViewGroup) layout.findViewById(
1174                R.id.sms_short_code_detail_layout);
1175        TextView detailsView = (TextView) detailsLayout.findViewById(
1176                R.id.sms_short_code_detail_message);
1177        detailsView.setText(detailsId);
1178
1179        CheckBox rememberChoice = (CheckBox) layout.findViewById(
1180                R.id.sms_short_code_remember_choice_checkbox);
1181        rememberChoice.setOnCheckedChangeListener(listener);
1182
1183        AlertDialog d = new AlertDialog.Builder(mContext)
1184                .setView(layout)
1185                .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener)
1186                .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener)
1187                .setOnCancelListener(listener)
1188                .create();
1189
1190        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1191        d.show();
1192
1193        listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE));
1194        listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE));
1195    }
1196
1197    /**
1198     * Returns the premium SMS permission for the specified package. If the package has never
1199     * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
1200     * will be returned.
1201     * @param packageName the name of the package to query permission
1202     * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
1203     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
1204     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
1205     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
1206     */
1207    public int getPremiumSmsPermission(String packageName) {
1208        return mUsageMonitor.getPremiumSmsPermission(packageName);
1209    }
1210
1211    /**
1212     * Sets the premium SMS permission for the specified package and save the value asynchronously
1213     * to persistent storage.
1214     * @param packageName the name of the package to set permission
1215     * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
1216     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
1217     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
1218     */
1219    public void setPremiumSmsPermission(String packageName, int permission) {
1220        mUsageMonitor.setPremiumSmsPermission(packageName, permission);
1221    }
1222
1223    /**
1224     * Send the message along to the radio.
1225     *
1226     * @param tracker holds the SMS message to send
1227     */
1228    protected abstract void sendSms(SmsTracker tracker);
1229
1230    /**
1231     * Send the SMS via the PSTN network.
1232     *
1233     * @param tracker holds the Sms tracker ready to be sent
1234     */
1235    protected abstract void sendSmsByPstn(SmsTracker tracker);
1236
1237    /**
1238     * Retry the message along to the radio.
1239     *
1240     * @param tracker holds the SMS message to send
1241     */
1242    public void sendRetrySms(SmsTracker tracker) {
1243        // re-routing to ImsSMSDispatcher
1244        if (mImsSMSDispatcher != null) {
1245            mImsSMSDispatcher.sendRetrySms(tracker);
1246        } else {
1247            Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed");
1248        }
1249    }
1250
1251    /**
1252     * Send the multi-part SMS based on multipart Sms tracker
1253     *
1254     * @param tracker holds the multipart Sms tracker ready to be sent
1255     */
1256    private void sendMultipartSms(SmsTracker tracker) {
1257        ArrayList<String> parts;
1258        ArrayList<PendingIntent> sentIntents;
1259        ArrayList<PendingIntent> deliveryIntents;
1260
1261        HashMap<String, Object> map = tracker.getData();
1262
1263        String destinationAddress = (String) map.get("destination");
1264        String scAddress = (String) map.get("scaddress");
1265
1266        parts = (ArrayList<String>) map.get("parts");
1267        sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
1268        deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
1269
1270        // check if in service
1271        int ss = mPhone.getServiceState().getState();
1272        // if sms over IMS is not supported on data and voice is not available...
1273        if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
1274            for (int i = 0, count = parts.size(); i < count; i++) {
1275                PendingIntent sentIntent = null;
1276                if (sentIntents != null && sentIntents.size() > i) {
1277                    sentIntent = sentIntents.get(i);
1278                }
1279                handleNotInService(ss, sentIntent);
1280            }
1281            return;
1282        }
1283
1284        sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
1285                null/*messageUri*/, null/*callingPkg*/, tracker.mPersistMessage);
1286    }
1287
1288    /**
1289     * Keeps track of an SMS that has been sent to the RIL, until it has
1290     * successfully been sent, or we're done trying.
1291     */
1292    public static class SmsTracker {
1293        // fields need to be public for derived SmsDispatchers
1294        private final HashMap<String, Object> mData;
1295        public int mRetryCount;
1296        public int mImsRetry; // nonzero indicates initial message was sent over Ims
1297        public int mMessageRef;
1298        public boolean mExpectMore;
1299        String mFormat;
1300
1301        public final PendingIntent mSentIntent;
1302        public final PendingIntent mDeliveryIntent;
1303
1304        public final PackageInfo mAppInfo;
1305        public final String mDestAddress;
1306
1307        public final SmsHeader mSmsHeader;
1308
1309        private long mTimestamp = System.currentTimeMillis();
1310        public Uri mMessageUri; // Uri of persisted message if we wrote one
1311
1312        // Reference to states of a multipart message that this part belongs to
1313        private AtomicInteger mUnsentPartCount;
1314        private AtomicBoolean mAnyPartFailed;
1315        // The full message content of a single part message
1316        // or a multipart message that this part belongs to
1317        private String mFullMessageText;
1318
1319        private int mSubId;
1320
1321        // If this is a text message (instead of data message)
1322        private boolean mIsText;
1323
1324        private boolean mPersistMessage;
1325
1326        private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1327                PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
1328                AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
1329                SmsHeader smsHeader, boolean isExpectMore, String fullMessageText, int subId,
1330                boolean isText, boolean persistMessage) {
1331            mData = data;
1332            mSentIntent = sentIntent;
1333            mDeliveryIntent = deliveryIntent;
1334            mRetryCount = 0;
1335            mAppInfo = appInfo;
1336            mDestAddress = destAddr;
1337            mFormat = format;
1338            mExpectMore = isExpectMore;
1339            mImsRetry = 0;
1340            mMessageRef = 0;
1341            mUnsentPartCount = unsentPartCount;
1342            mAnyPartFailed = anyPartFailed;
1343            mMessageUri = messageUri;
1344            mSmsHeader = smsHeader;
1345            mFullMessageText = fullMessageText;
1346            mSubId = subId;
1347            mIsText = isText;
1348            mPersistMessage = persistMessage;
1349        }
1350
1351        /**
1352         * Returns whether this tracker holds a multi-part SMS.
1353         * @return true if the tracker holds a multi-part SMS; false otherwise
1354         */
1355        boolean isMultipart() {
1356            return mData.containsKey("parts");
1357        }
1358
1359        public HashMap<String, Object> getData() {
1360            return mData;
1361        }
1362
1363        /**
1364         * Update the status of this message if we persisted it
1365         */
1366        public void updateSentMessageStatus(Context context, int status) {
1367            if (mMessageUri != null) {
1368                // If we wrote this message in writeSentMessage, update it now
1369                ContentValues values = new ContentValues(1);
1370                values.put(Sms.STATUS, status);
1371                SqliteWrapper.update(context, context.getContentResolver(),
1372                        mMessageUri, values, null, null);
1373            }
1374        }
1375
1376        /**
1377         * Set the final state of a message: FAILED or SENT
1378         *
1379         * @param context The Context
1380         * @param messageType The final message type
1381         * @param errorCode The error code
1382         */
1383        private void updateMessageState(Context context, int messageType, int errorCode) {
1384            if (mMessageUri == null) {
1385                return;
1386            }
1387            final ContentValues values = new ContentValues(2);
1388            values.put(Sms.TYPE, messageType);
1389            values.put(Sms.ERROR_CODE, errorCode);
1390            final long identity = Binder.clearCallingIdentity();
1391            try {
1392                if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
1393                        null/*where*/, null/*selectionArgs*/) != 1) {
1394                    Rlog.e(TAG, "Failed to move message to " + messageType);
1395                }
1396            } finally {
1397                Binder.restoreCallingIdentity(identity);
1398            }
1399        }
1400
1401        /**
1402         * Persist a sent SMS if required:
1403         * 1. It is a text message
1404         * 2. SmsApplication tells us to persist: sent from apps that are not default-SMS app or
1405         *    bluetooth
1406         *
1407         * @param context
1408         * @param messageType The folder to store (FAILED or SENT)
1409         * @param errorCode The current error code for this SMS or SMS part
1410         * @return The telephony provider URI if stored
1411         */
1412        private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) {
1413            if (!mIsText || !mPersistMessage ||
1414                    !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) {
1415                return null;
1416            }
1417            Rlog.d(TAG, "Persist SMS into "
1418                    + (messageType == Sms.MESSAGE_TYPE_FAILED ? "FAILED" : "SENT"));
1419            final ContentValues values = new ContentValues();
1420            values.put(Sms.SUBSCRIPTION_ID, mSubId);
1421            values.put(Sms.ADDRESS, mDestAddress);
1422            values.put(Sms.BODY, mFullMessageText);
1423            values.put(Sms.DATE, System.currentTimeMillis()); // milliseconds
1424            values.put(Sms.SEEN, 1);
1425            values.put(Sms.READ, 1);
1426            final String creator = mAppInfo != null ? mAppInfo.packageName : null;
1427            if (!TextUtils.isEmpty(creator)) {
1428                values.put(Sms.CREATOR, creator);
1429            }
1430            if (mDeliveryIntent != null) {
1431                values.put(Sms.STATUS, Telephony.Sms.STATUS_PENDING);
1432            }
1433            if (errorCode != 0) {
1434                values.put(Sms.ERROR_CODE, errorCode);
1435            }
1436            final long identity = Binder.clearCallingIdentity();
1437            final ContentResolver resolver = context.getContentResolver();
1438            try {
1439                final Uri uri =  resolver.insert(Telephony.Sms.Sent.CONTENT_URI, values);
1440                if (uri != null && messageType == Sms.MESSAGE_TYPE_FAILED) {
1441                    // Since we can't persist a message directly into FAILED box,
1442                    // we have to update the column after we persist it into SENT box.
1443                    // The gap between the state change is tiny so I would not expect
1444                    // it to cause any serious problem
1445                    // TODO: we should add a "failed" URI for this in SmsProvider?
1446                    final ContentValues updateValues = new ContentValues(1);
1447                    updateValues.put(Sms.TYPE, Sms.MESSAGE_TYPE_FAILED);
1448                    resolver.update(uri, updateValues, null/*where*/, null/*selectionArgs*/);
1449                }
1450                return uri;
1451            } catch (Exception e) {
1452                Rlog.e(TAG, "writeOutboxMessage: Failed to persist outbox message", e);
1453                return null;
1454            } finally {
1455                Binder.restoreCallingIdentity(identity);
1456            }
1457        }
1458
1459        /**
1460         * Persist or update an SMS depending on if we send a new message or a stored message
1461         *
1462         * @param context
1463         * @param messageType The message folder for this SMS, FAILED or SENT
1464         * @param errorCode The current error code for this SMS or SMS part
1465         */
1466        private void persistOrUpdateMessage(Context context, int messageType, int errorCode) {
1467            if (mMessageUri != null) {
1468                updateMessageState(context, messageType, errorCode);
1469            } else {
1470                mMessageUri = persistSentMessageIfRequired(context, messageType, errorCode);
1471            }
1472        }
1473
1474        /**
1475         * Handle a failure of a single part message or a part of a multipart message
1476         *
1477         * @param context The Context
1478         * @param error The error to send back with
1479         * @param errorCode
1480         */
1481        public void onFailed(Context context, int error, int errorCode) {
1482            if (mAnyPartFailed != null) {
1483                mAnyPartFailed.set(true);
1484            }
1485            // is single part or last part of multipart message
1486            boolean isSinglePartOrLastPart = true;
1487            if (mUnsentPartCount != null) {
1488                isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
1489            }
1490            if (isSinglePartOrLastPart) {
1491                persistOrUpdateMessage(context, Sms.MESSAGE_TYPE_FAILED, errorCode);
1492            }
1493            if (mSentIntent != null) {
1494                try {
1495                    // Extra information to send with the sent intent
1496                    Intent fillIn = new Intent();
1497                    if (mMessageUri != null) {
1498                        // Pass this to SMS apps so that they know where it is stored
1499                        fillIn.putExtra("uri", mMessageUri.toString());
1500                    }
1501                    if (errorCode != 0) {
1502                        fillIn.putExtra("errorCode", errorCode);
1503                    }
1504                    if (mUnsentPartCount != null && isSinglePartOrLastPart) {
1505                        // Is multipart and last part
1506                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
1507                    }
1508                    mSentIntent.send(context, error, fillIn);
1509                } catch (CanceledException ex) {
1510                    Rlog.e(TAG, "Failed to send result");
1511                }
1512            }
1513        }
1514
1515        /**
1516         * Handle the sent of a single part message or a part of a multipart message
1517         *
1518         * @param context The Context
1519         */
1520        public void onSent(Context context) {
1521            // is single part or last part of multipart message
1522            boolean isSinglePartOrLastPart = true;
1523            if (mUnsentPartCount != null) {
1524                isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
1525            }
1526            if (isSinglePartOrLastPart) {
1527                int messageType = Sms.MESSAGE_TYPE_SENT;
1528                if (mAnyPartFailed != null && mAnyPartFailed.get()) {
1529                    messageType = Sms.MESSAGE_TYPE_FAILED;
1530                }
1531                persistOrUpdateMessage(context, messageType, 0/*errorCode*/);
1532            }
1533            if (mSentIntent != null) {
1534                try {
1535                    // Extra information to send with the sent intent
1536                    Intent fillIn = new Intent();
1537                    if (mMessageUri != null) {
1538                        // Pass this to SMS apps so that they know where it is stored
1539                        fillIn.putExtra("uri", mMessageUri.toString());
1540                    }
1541                    if (mUnsentPartCount != null && isSinglePartOrLastPart) {
1542                        // Is multipart and last part
1543                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
1544                    }
1545                    mSentIntent.send(context, Activity.RESULT_OK, fillIn);
1546                } catch (CanceledException ex) {
1547                    Rlog.e(TAG, "Failed to send result");
1548                }
1549            }
1550        }
1551    }
1552
1553    protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1554            PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
1555            AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
1556            boolean isExpectMore, String fullMessageText, boolean isText, boolean persistMessage) {
1557        // Get calling app package name via UID from Binder call
1558        PackageManager pm = mContext.getPackageManager();
1559        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
1560
1561        // Get package info via packagemanager
1562        PackageInfo appInfo = null;
1563        if (packageNames != null && packageNames.length > 0) {
1564            try {
1565                // XXX this is lossy- apps can share a UID
1566                appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
1567            } catch (PackageManager.NameNotFoundException e) {
1568                // error will be logged in sendRawPdu
1569            }
1570        }
1571        // Strip non-digits from destination phone number before checking for short codes
1572        // and before displaying the number to the user if confirmation is required.
1573        String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
1574        return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
1575                unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore,
1576                fullMessageText, getSubId(), isText, persistMessage);
1577    }
1578
1579    protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1580            PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore,
1581            String fullMessageText, boolean isText, boolean persistMessage) {
1582        return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
1583                null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore,
1584                fullMessageText, isText, persistMessage);
1585    }
1586
1587    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1588            String text, SmsMessageBase.SubmitPduBase pdu) {
1589        HashMap<String, Object> map = new HashMap<String, Object>();
1590        map.put("destAddr", destAddr);
1591        map.put("scAddr", scAddr);
1592        map.put("text", text);
1593        map.put("smsc", pdu.encodedScAddress);
1594        map.put("pdu", pdu.encodedMessage);
1595        return map;
1596    }
1597
1598    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1599            int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
1600        HashMap<String, Object> map = new HashMap<String, Object>();
1601        map.put("destAddr", destAddr);
1602        map.put("scAddr", scAddr);
1603        map.put("destPort", destPort);
1604        map.put("data", data);
1605        map.put("smsc", pdu.encodedScAddress);
1606        map.put("pdu", pdu.encodedMessage);
1607        return map;
1608    }
1609
1610    /**
1611     * Dialog listener for SMS confirmation dialog.
1612     */
1613    private final class ConfirmDialogListener
1614            implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
1615            CompoundButton.OnCheckedChangeListener {
1616
1617        private final SmsTracker mTracker;
1618        private Button mPositiveButton;
1619        private Button mNegativeButton;
1620        private boolean mRememberChoice;    // default is unchecked
1621        private final TextView mRememberUndoInstruction;
1622
1623        ConfirmDialogListener(SmsTracker tracker, TextView textView) {
1624            mTracker = tracker;
1625            mRememberUndoInstruction = textView;
1626        }
1627
1628        void setPositiveButton(Button button) {
1629            mPositiveButton = button;
1630        }
1631
1632        void setNegativeButton(Button button) {
1633            mNegativeButton = button;
1634        }
1635
1636        @Override
1637        public void onClick(DialogInterface dialog, int which) {
1638            // Always set the SMS permission so that Settings will show a permission setting
1639            // for the app (it won't be shown until after the app tries to send to a short code).
1640            int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
1641
1642            if (which == DialogInterface.BUTTON_POSITIVE) {
1643                Rlog.d(TAG, "CONFIRM sending SMS");
1644                // XXX this is lossy- apps can have more than one signature
1645                EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER,
1646                                    mTracker.mAppInfo.applicationInfo == null ?
1647                                    -1 : mTracker.mAppInfo.applicationInfo.uid);
1648                sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker));
1649                if (mRememberChoice) {
1650                    newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW;
1651                }
1652            } else if (which == DialogInterface.BUTTON_NEGATIVE) {
1653                Rlog.d(TAG, "DENY sending SMS");
1654                // XXX this is lossy- apps can have more than one signature
1655                EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
1656                                    mTracker.mAppInfo.applicationInfo == null ?
1657                                    -1 :  mTracker.mAppInfo.applicationInfo.uid);
1658                sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1659                if (mRememberChoice) {
1660                    newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
1661                }
1662            }
1663            setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
1664        }
1665
1666        @Override
1667        public void onCancel(DialogInterface dialog) {
1668            Rlog.d(TAG, "dialog dismissed: don't send SMS");
1669            sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1670        }
1671
1672        @Override
1673        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1674            Rlog.d(TAG, "remember this choice: " + isChecked);
1675            mRememberChoice = isChecked;
1676            if (isChecked) {
1677                mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow);
1678                mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow);
1679                if (mRememberUndoInstruction != null) {
1680                    mRememberUndoInstruction.
1681                            setText(R.string.sms_short_code_remember_undo_instruction);
1682                    mRememberUndoInstruction.setPadding(0,0,0,32);
1683                }
1684            } else {
1685                mPositiveButton.setText(R.string.sms_short_code_confirm_allow);
1686                mNegativeButton.setText(R.string.sms_short_code_confirm_deny);
1687                if (mRememberUndoInstruction != null) {
1688                    mRememberUndoInstruction.setText("");
1689                    mRememberUndoInstruction.setPadding(0,0,0,0);
1690                }
1691            }
1692        }
1693    }
1694
1695    public boolean isIms() {
1696        if (mImsSMSDispatcher != null) {
1697            return mImsSMSDispatcher.isIms();
1698        } else {
1699            Rlog.e(TAG, mImsSMSDispatcher + " is null");
1700            return false;
1701        }
1702    }
1703
1704    public String getImsSmsFormat() {
1705        if (mImsSMSDispatcher != null) {
1706            return mImsSMSDispatcher.getImsSmsFormat();
1707        } else {
1708            Rlog.e(TAG, mImsSMSDispatcher + " is null");
1709            return null;
1710        }
1711    }
1712
1713    private String getMultipartMessageText(ArrayList<String> parts) {
1714        final StringBuilder sb = new StringBuilder();
1715        for (String part : parts) {
1716            if (part != null) {
1717                sb.append(part);
1718            }
1719        }
1720        return sb.toString();
1721    }
1722
1723    protected String getCarrierAppPackageName() {
1724        UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
1725        if (card == null) {
1726            return null;
1727        }
1728
1729        List<String> carrierPackages = card.getCarrierPackageNamesForIntent(
1730            mContext.getPackageManager(), new Intent(CarrierMessagingService.SERVICE_INTERFACE));
1731        return (carrierPackages != null && carrierPackages.size() == 1) ?
1732                carrierPackages.get(0) : null;
1733    }
1734
1735    protected int getSubId() {
1736        return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.getPhoneId());
1737    }
1738
1739    private void checkCallerIsPhoneOrCarrierApp() {
1740        int uid = Binder.getCallingUid();
1741        int appId = UserHandle.getAppId(uid);
1742        if (appId == Process.PHONE_UID || uid == 0) {
1743            return;
1744        }
1745        try {
1746            PackageManager pm = mContext.getPackageManager();
1747            ApplicationInfo ai = pm.getApplicationInfo(getCarrierAppPackageName(), 0);
1748            if (!UserHandle.isSameApp(ai.uid, Binder.getCallingUid())) {
1749                throw new SecurityException("Caller is not phone or carrier app!");
1750            }
1751        } catch (PackageManager.NameNotFoundException re) {
1752            throw new SecurityException("Caller is not phone or carrier app!");
1753        }
1754    }
1755}
1756