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