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