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_RESPOND_VIA_MESSAGE_PERMISSION =
93            "android.permission.SEND_RESPOND_VIA_MESSAGE";
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     * @param persistMessage whether to save the sent message into SMS DB for a
780     *   non-default SMS app.
781     */
782    protected abstract void sendText(String destAddr, String scAddr, String text,
783            PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
784            String callingPkg, boolean persistMessage);
785
786    /**
787     * Inject an SMS PDU into the android platform.
788     *
789     * @param pdu is the byte array of pdu to be injected into android telephony layer
790     * @param format is the format of SMS pdu (3gpp or 3gpp2)
791     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
792     *  broadcast when the message is successfully received by the
793     *  android telephony layer. This intent is broadcasted at
794     *  the same time an SMS received from radio is responded back.
795     */
796    protected abstract void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent);
797
798    /**
799     * Calculate the number of septets needed to encode the message. This function should only be
800     * called for individual segments of multipart message.
801     *
802     * @param messageBody the message to encode
803     * @param use7bitOnly ignore (but still count) illegal characters if true
804     * @return TextEncodingDetails
805     */
806    protected abstract TextEncodingDetails calculateLength(CharSequence messageBody,
807            boolean use7bitOnly);
808
809    /**
810     * Send a multi-part text based SMS.
811     *  @param destAddr the address to send the message to
812     * @param scAddr is the service center address or null to use
813     *   the current default SMSC
814     * @param parts an <code>ArrayList</code> of strings that, in order,
815     *   comprise the original message
816     * @param sentIntents if not null, an <code>ArrayList</code> of
817     *   <code>PendingIntent</code>s (one for each message part) that is
818     *   broadcast when the corresponding message part has been sent.
819     *   The result code will be <code>Activity.RESULT_OK<code> for success,
820     *   or one of these errors:
821     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
822     *   <code>RESULT_ERROR_RADIO_OFF</code>
823     *   <code>RESULT_ERROR_NULL_PDU</code>
824     *   <code>RESULT_ERROR_NO_SERVICE</code>.
825     *  The per-application based SMS control checks sentIntent. If sentIntent
826     *  is NULL the caller will be checked against all unknown applications,
827     *  which cause smaller number of SMS to be sent in checking period.
828     * @param deliveryIntents if not null, an <code>ArrayList</code> of
829     *   <code>PendingIntent</code>s (one for each message part) that is
830     *   broadcast when the corresponding message part has been delivered
831     *   to the recipient.  The raw pdu of the status report is in the
832     * @param messageUri optional URI of the message if it is already stored in the system
833     * @param callingPkg the calling package name
834     * @param persistMessage whether to save the sent message into SMS DB for a
835     *   non-default SMS app.
836     */
837    protected void sendMultipartText(String destAddr, String scAddr,
838            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
839            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
840            boolean persistMessage) {
841        final String fullMessageText = getMultipartMessageText(parts);
842        int refNumber = getNextConcatenatedRef() & 0x00FF;
843        int msgCount = parts.size();
844        int encoding = SmsConstants.ENCODING_UNKNOWN;
845
846        TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
847        for (int i = 0; i < msgCount; i++) {
848            TextEncodingDetails details = calculateLength(parts.get(i), false);
849            if (encoding != details.codeUnitSize
850                    && (encoding == SmsConstants.ENCODING_UNKNOWN
851                            || encoding == SmsConstants.ENCODING_7BIT)) {
852                encoding = details.codeUnitSize;
853            }
854            encodingForParts[i] = details;
855        }
856
857        SmsTracker[] trackers = new SmsTracker[msgCount];
858
859        // States to track at the message level (for all parts)
860        final AtomicInteger unsentPartCount = new AtomicInteger(msgCount);
861        final AtomicBoolean anyPartFailed = new AtomicBoolean(false);
862
863        for (int i = 0; i < msgCount; i++) {
864            SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
865            concatRef.refNumber = refNumber;
866            concatRef.seqNumber = i + 1;  // 1-based sequence
867            concatRef.msgCount = msgCount;
868            // TODO: We currently set this to true since our messaging app will never
869            // send more than 255 parts (it converts the message to MMS well before that).
870            // However, we should support 3rd party messaging apps that might need 16-bit
871            // references
872            // Note:  It's not sufficient to just flip this bit to true; it will have
873            // ripple effects (several calculations assume 8-bit ref).
874            concatRef.isEightBits = true;
875            SmsHeader smsHeader = new SmsHeader();
876            smsHeader.concatRef = concatRef;
877
878            // Set the national language tables for 3GPP 7-bit encoding, if enabled.
879            if (encoding == SmsConstants.ENCODING_7BIT) {
880                smsHeader.languageTable = encodingForParts[i].languageTable;
881                smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
882            }
883
884            PendingIntent sentIntent = null;
885            if (sentIntents != null && sentIntents.size() > i) {
886                sentIntent = sentIntents.get(i);
887            }
888
889            PendingIntent deliveryIntent = null;
890            if (deliveryIntents != null && deliveryIntents.size() > i) {
891                deliveryIntent = deliveryIntents.get(i);
892            }
893
894            trackers[i] =
895                getNewSubmitPduTracker(destAddr, scAddr, parts.get(i), smsHeader, encoding,
896                        sentIntent, deliveryIntent, (i == (msgCount - 1)),
897                        unsentPartCount, anyPartFailed, messageUri, fullMessageText);
898            trackers[i].mPersistMessage = persistMessage;
899        }
900
901        if (parts == null || trackers == null || trackers.length == 0
902                || trackers[0] == null) {
903            Rlog.e(TAG, "Cannot send multipart text. parts=" + parts + " trackers=" + trackers);
904            return;
905        }
906
907        String carrierPackage = getCarrierAppPackageName();
908        if (carrierPackage != null) {
909            Rlog.d(TAG, "Found carrier package.");
910            MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers);
911            smsSender.sendSmsByCarrierApp(carrierPackage, new MultipartSmsSenderCallback(smsSender));
912        } else {
913            Rlog.v(TAG, "No carrier package.");
914            for (SmsTracker tracker : trackers) {
915                if (tracker != null) {
916                    sendSubmitPdu(tracker);
917                } else {
918                    Rlog.e(TAG, "Null tracker.");
919                }
920            }
921        }
922    }
923
924    /**
925     * Create a new SubmitPdu and return the SMS tracker.
926     */
927    protected abstract SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
928            String message, SmsHeader smsHeader, int encoding,
929            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
930            AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
931            String fullMessageText);
932
933    /**
934     * Send an SMS
935     * @param tracker will contain:
936     * -smsc the SMSC to send the message through, or NULL for the
937     *  default SMSC
938     * -pdu the raw PDU to send
939     * -sentIntent if not NULL this <code>Intent</code> is
940     *  broadcast when the message is successfully sent, or failed.
941     *  The result code will be <code>Activity.RESULT_OK<code> for success,
942     *  or one of these errors:
943     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
944     *  <code>RESULT_ERROR_RADIO_OFF</code>
945     *  <code>RESULT_ERROR_NULL_PDU</code>
946     *  <code>RESULT_ERROR_NO_SERVICE</code>.
947     *  The per-application based SMS control checks sentIntent. If sentIntent
948     *  is NULL the caller will be checked against all unknown applications,
949     *  which cause smaller number of SMS to be sent in checking period.
950     * -deliveryIntent if not NULL this <code>Intent</code> is
951     *  broadcast when the message is delivered to the recipient.  The
952     *  raw pdu of the status report is in the extended data ("pdu").
953     * -param destAddr the destination phone number (for short code confirmation)
954     */
955    protected void sendRawPdu(SmsTracker tracker) {
956        HashMap map = tracker.mData;
957        byte pdu[] = (byte[]) map.get("pdu");
958
959        if (mSmsSendDisabled) {
960            Rlog.e(TAG, "Device does not support sending sms.");
961            tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
962            return;
963        }
964
965        if (pdu == null) {
966            Rlog.e(TAG, "Empty PDU");
967            tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/);
968            return;
969        }
970
971        // Get calling app package name via UID from Binder call
972        PackageManager pm = mContext.getPackageManager();
973        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
974
975        if (packageNames == null || packageNames.length == 0) {
976            // Refuse to send SMS if we can't get the calling package name.
977            Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS");
978            tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
979            return;
980        }
981
982        // Get package info via packagemanager
983        PackageInfo appInfo;
984        try {
985            // XXX this is lossy- apps can share a UID
986            appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
987        } catch (PackageManager.NameNotFoundException e) {
988            Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");
989            tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
990            return;
991        }
992
993        // checkDestination() returns true if the destination is not a premium short code or the
994        // sending app is approved to send to short codes. Otherwise, a message is sent to our
995        // handler with the SmsTracker to request user confirmation before sending.
996        if (checkDestination(tracker)) {
997            // check for excessive outgoing SMS usage by this app
998            if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {
999                sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
1000                return;
1001            }
1002
1003            sendSms(tracker);
1004        }
1005    }
1006
1007    /**
1008     * Check if destination is a potential premium short code and sender is not pre-approved to
1009     * send to short codes.
1010     *
1011     * @param tracker the tracker for the SMS to send
1012     * @return true if the destination is approved; false if user confirmation event was sent
1013     */
1014    boolean checkDestination(SmsTracker tracker) {
1015        if (mContext.checkCallingOrSelfPermission(SEND_RESPOND_VIA_MESSAGE_PERMISSION)
1016                == PackageManager.PERMISSION_GRANTED) {
1017            return true;            // app is pre-approved to send to short codes
1018        } else {
1019            int rule = mPremiumSmsRule.get();
1020            int smsCategory = SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE;
1021            if (rule == PREMIUM_RULE_USE_SIM || rule == PREMIUM_RULE_USE_BOTH) {
1022                String simCountryIso = mTelephonyManager.getSimCountryIso();
1023                if (simCountryIso == null || simCountryIso.length() != 2) {
1024                    Rlog.e(TAG, "Can't get SIM country Iso: trying network country Iso");
1025                    simCountryIso = mTelephonyManager.getNetworkCountryIso();
1026                }
1027
1028                smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso);
1029            }
1030            if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) {
1031                String networkCountryIso = mTelephonyManager.getNetworkCountryIso();
1032                if (networkCountryIso == null || networkCountryIso.length() != 2) {
1033                    Rlog.e(TAG, "Can't get Network country Iso: trying SIM country Iso");
1034                    networkCountryIso = mTelephonyManager.getSimCountryIso();
1035                }
1036
1037                smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory,
1038                        mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso));
1039            }
1040
1041            if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE
1042                    || smsCategory == SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE
1043                    || smsCategory == SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE) {
1044                return true;    // not a premium short code
1045            }
1046
1047            // Wait for user confirmation unless the user has set permission to always allow/deny
1048            int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
1049                    tracker.mAppInfo.packageName);
1050            if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
1051                // First time trying to send to premium SMS.
1052                premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
1053            }
1054
1055            switch (premiumSmsPermission) {
1056                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW:
1057                    Rlog.d(TAG, "User approved this app to send to premium SMS");
1058                    return true;
1059
1060                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
1061                    Rlog.w(TAG, "User denied this app from sending to premium SMS");
1062                    sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker));
1063                    return false;   // reject this message
1064
1065                case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
1066                default:
1067                    int event;
1068                    if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) {
1069                        event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE;
1070                    } else {
1071                        event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE;
1072                    }
1073                    sendMessage(obtainMessage(event, tracker));
1074                    return false;   // wait for user confirmation
1075            }
1076        }
1077    }
1078
1079    /**
1080     * Deny sending an SMS if the outgoing queue limit is reached. Used when the message
1081     * must be confirmed by the user due to excessive usage or potential premium SMS detected.
1082     * @param tracker the SmsTracker for the message to send
1083     * @return true if the message was denied; false to continue with send confirmation
1084     */
1085    private boolean denyIfQueueLimitReached(SmsTracker tracker) {
1086        if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) {
1087            // Deny sending message when the queue limit is reached.
1088            Rlog.e(TAG, "Denied because queue limit reached");
1089            tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
1090            return true;
1091        }
1092        mPendingTrackerCount++;
1093        return false;
1094    }
1095
1096    /**
1097     * Returns the label for the specified app package name.
1098     * @param appPackage the package name of the app requesting to send an SMS
1099     * @return the label for the specified app, or the package name if getApplicationInfo() fails
1100     */
1101    private CharSequence getAppLabel(String appPackage) {
1102        PackageManager pm = mContext.getPackageManager();
1103        try {
1104            ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0);
1105            return appInfo.loadLabel(pm);
1106        } catch (PackageManager.NameNotFoundException e) {
1107            Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage);
1108            return appPackage;  // fall back to package name if we can't get app label
1109        }
1110    }
1111
1112    /**
1113     * Post an alert when SMS needs confirmation due to excessive usage.
1114     * @param tracker an SmsTracker for the current message.
1115     */
1116    protected void handleReachSentLimit(SmsTracker tracker) {
1117        if (denyIfQueueLimitReached(tracker)) {
1118            return;     // queue limit reached; error was returned to caller
1119        }
1120
1121        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
1122        Resources r = Resources.getSystem();
1123        Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
1124
1125        ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null);
1126
1127        AlertDialog d = new AlertDialog.Builder(mContext)
1128                .setTitle(R.string.sms_control_title)
1129                .setIcon(R.drawable.stat_sys_warning)
1130                .setMessage(messageText)
1131                .setPositiveButton(r.getString(R.string.sms_control_yes), listener)
1132                .setNegativeButton(r.getString(R.string.sms_control_no), listener)
1133                .setOnCancelListener(listener)
1134                .create();
1135
1136        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1137        d.show();
1138    }
1139
1140    /**
1141     * Post an alert for user confirmation when sending to a potential short code.
1142     * @param isPremium true if the destination is known to be a premium short code
1143     * @param tracker the SmsTracker for the current message.
1144     */
1145    protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) {
1146        if (denyIfQueueLimitReached(tracker)) {
1147            return;     // queue limit reached; error was returned to caller
1148        }
1149
1150        int detailsId;
1151        if (isPremium) {
1152            detailsId = R.string.sms_premium_short_code_details;
1153        } else {
1154            detailsId = R.string.sms_short_code_details;
1155        }
1156
1157        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
1158        Resources r = Resources.getSystem();
1159        Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
1160                appLabel, tracker.mDestAddress));
1161
1162        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1163                Context.LAYOUT_INFLATER_SERVICE);
1164        View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null);
1165
1166        ConfirmDialogListener listener = new ConfirmDialogListener(tracker,
1167                (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction));
1168
1169
1170        TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message);
1171        messageView.setText(messageText);
1172
1173        ViewGroup detailsLayout = (ViewGroup) layout.findViewById(
1174                R.id.sms_short_code_detail_layout);
1175        TextView detailsView = (TextView) detailsLayout.findViewById(
1176                R.id.sms_short_code_detail_message);
1177        detailsView.setText(detailsId);
1178
1179        CheckBox rememberChoice = (CheckBox) layout.findViewById(
1180                R.id.sms_short_code_remember_choice_checkbox);
1181        rememberChoice.setOnCheckedChangeListener(listener);
1182
1183        AlertDialog d = new AlertDialog.Builder(mContext)
1184                .setView(layout)
1185                .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener)
1186                .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener)
1187                .setOnCancelListener(listener)
1188                .create();
1189
1190        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
1191        d.show();
1192
1193        listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE));
1194        listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE));
1195    }
1196
1197    /**
1198     * Returns the premium SMS permission for the specified package. If the package has never
1199     * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
1200     * will be returned.
1201     * @param packageName the name of the package to query permission
1202     * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
1203     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
1204     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
1205     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
1206     */
1207    public int getPremiumSmsPermission(String packageName) {
1208        return mUsageMonitor.getPremiumSmsPermission(packageName);
1209    }
1210
1211    /**
1212     * Sets the premium SMS permission for the specified package and save the value asynchronously
1213     * to persistent storage.
1214     * @param packageName the name of the package to set permission
1215     * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
1216     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
1217     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
1218     */
1219    public void setPremiumSmsPermission(String packageName, int permission) {
1220        mUsageMonitor.setPremiumSmsPermission(packageName, permission);
1221    }
1222
1223    /**
1224     * Send the message along to the radio.
1225     *
1226     * @param tracker holds the SMS message to send
1227     */
1228    protected abstract void sendSms(SmsTracker tracker);
1229
1230    /**
1231     * Send the SMS via the PSTN network.
1232     *
1233     * @param tracker holds the Sms tracker ready to be sent
1234     */
1235    protected abstract void sendSmsByPstn(SmsTracker tracker);
1236
1237    /**
1238     * Retry the message along to the radio.
1239     *
1240     * @param tracker holds the SMS message to send
1241     */
1242    public void sendRetrySms(SmsTracker tracker) {
1243        // re-routing to ImsSMSDispatcher
1244        if (mImsSMSDispatcher != null) {
1245            mImsSMSDispatcher.sendRetrySms(tracker);
1246        } else {
1247            Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed");
1248        }
1249    }
1250
1251    /**
1252     * Send the multi-part SMS based on multipart Sms tracker
1253     *
1254     * @param tracker holds the multipart Sms tracker ready to be sent
1255     */
1256    private void sendMultipartSms(SmsTracker tracker) {
1257        ArrayList<String> parts;
1258        ArrayList<PendingIntent> sentIntents;
1259        ArrayList<PendingIntent> deliveryIntents;
1260
1261        HashMap<String, Object> map = tracker.mData;
1262
1263        String destinationAddress = (String) map.get("destination");
1264        String scAddress = (String) map.get("scaddress");
1265
1266        parts = (ArrayList<String>) map.get("parts");
1267        sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
1268        deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents");
1269
1270        // check if in service
1271        int ss = mPhone.getServiceState().getState();
1272        // if sms over IMS is not supported on data and voice is not available...
1273        if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
1274            for (int i = 0, count = parts.size(); i < count; i++) {
1275                PendingIntent sentIntent = null;
1276                if (sentIntents != null && sentIntents.size() > i) {
1277                    sentIntent = sentIntents.get(i);
1278                }
1279                handleNotInService(ss, sentIntent);
1280            }
1281            return;
1282        }
1283
1284        sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
1285                null/*messageUri*/, null/*callingPkg*/, tracker.mPersistMessage);
1286    }
1287
1288    /**
1289     * Keeps track of an SMS that has been sent to the RIL, until it has
1290     * successfully been sent, or we're done trying.
1291     */
1292    protected static final class SmsTracker {
1293        // fields need to be public for derived SmsDispatchers
1294        public final HashMap<String, Object> mData;
1295        public int mRetryCount;
1296        public int mImsRetry; // nonzero indicates initial message was sent over Ims
1297        public int mMessageRef;
1298        public boolean mExpectMore;
1299        String mFormat;
1300
1301        public final PendingIntent mSentIntent;
1302        public final PendingIntent mDeliveryIntent;
1303
1304        public final PackageInfo mAppInfo;
1305        public final String mDestAddress;
1306
1307        public final SmsHeader mSmsHeader;
1308
1309        private long mTimestamp = System.currentTimeMillis();
1310        public Uri mMessageUri; // Uri of persisted message if we wrote one
1311
1312        // Reference to states of a multipart message that this part belongs to
1313        private AtomicInteger mUnsentPartCount;
1314        private AtomicBoolean mAnyPartFailed;
1315        // The full message content of a single part message
1316        // or a multipart message that this part belongs to
1317        private String mFullMessageText;
1318
1319        private int mSubId;
1320
1321        // If this is a text message (instead of data message)
1322        private boolean mIsText;
1323
1324        private boolean mPersistMessage;
1325
1326        private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1327                PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
1328                AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
1329                SmsHeader smsHeader, boolean isExpectMore, String fullMessageText, int subId,
1330                boolean isText, boolean persistMessage) {
1331            mData = data;
1332            mSentIntent = sentIntent;
1333            mDeliveryIntent = deliveryIntent;
1334            mRetryCount = 0;
1335            mAppInfo = appInfo;
1336            mDestAddress = destAddr;
1337            mFormat = format;
1338            mExpectMore = isExpectMore;
1339            mImsRetry = 0;
1340            mMessageRef = 0;
1341            mUnsentPartCount = unsentPartCount;
1342            mAnyPartFailed = anyPartFailed;
1343            mMessageUri = messageUri;
1344            mSmsHeader = smsHeader;
1345            mFullMessageText = fullMessageText;
1346            mSubId = subId;
1347            mIsText = isText;
1348            mPersistMessage = persistMessage;
1349        }
1350
1351        /**
1352         * Returns whether this tracker holds a multi-part SMS.
1353         * @return true if the tracker holds a multi-part SMS; false otherwise
1354         */
1355        boolean isMultipart() {
1356            return mData.containsKey("parts");
1357        }
1358
1359        /**
1360         * Update the status of this message if we persisted it
1361         */
1362        public void updateSentMessageStatus(Context context, int status) {
1363            if (mMessageUri != null) {
1364                // If we wrote this message in writeSentMessage, update it now
1365                ContentValues values = new ContentValues(1);
1366                values.put(Sms.STATUS, status);
1367                SqliteWrapper.update(context, context.getContentResolver(),
1368                        mMessageUri, values, null, null);
1369            }
1370        }
1371
1372        /**
1373         * Set the final state of a message: FAILED or SENT
1374         *
1375         * @param context The Context
1376         * @param messageType The final message type
1377         * @param errorCode The error code
1378         */
1379        private void updateMessageState(Context context, int messageType, int errorCode) {
1380            if (mMessageUri == null) {
1381                return;
1382            }
1383            final ContentValues values = new ContentValues(2);
1384            values.put(Sms.TYPE, messageType);
1385            values.put(Sms.ERROR_CODE, errorCode);
1386            final long identity = Binder.clearCallingIdentity();
1387            try {
1388                if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
1389                        null/*where*/, null/*selectionArgs*/) != 1) {
1390                    Rlog.e(TAG, "Failed to move message to " + messageType);
1391                }
1392            } finally {
1393                Binder.restoreCallingIdentity(identity);
1394            }
1395        }
1396
1397        /**
1398         * Persist a sent SMS if required:
1399         * 1. It is a text message
1400         * 2. SmsApplication tells us to persist: sent from apps that are not default-SMS app or
1401         *    bluetooth
1402         *
1403         * @param context
1404         * @param messageType The folder to store (FAILED or SENT)
1405         * @param errorCode The current error code for this SMS or SMS part
1406         * @return The telephony provider URI if stored
1407         */
1408        private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) {
1409            if (!mIsText || !mPersistMessage ||
1410                    !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) {
1411                return null;
1412            }
1413            Rlog.d(TAG, "Persist SMS into "
1414                    + (messageType == Sms.MESSAGE_TYPE_FAILED ? "FAILED" : "SENT"));
1415            final ContentValues values = new ContentValues();
1416            values.put(Sms.SUBSCRIPTION_ID, mSubId);
1417            values.put(Sms.ADDRESS, mDestAddress);
1418            values.put(Sms.BODY, mFullMessageText);
1419            values.put(Sms.DATE, System.currentTimeMillis()); // milliseconds
1420            values.put(Sms.SEEN, 1);
1421            values.put(Sms.READ, 1);
1422            final String creator = mAppInfo != null ? mAppInfo.packageName : null;
1423            if (!TextUtils.isEmpty(creator)) {
1424                values.put(Sms.CREATOR, creator);
1425            }
1426            if (mDeliveryIntent != null) {
1427                values.put(Sms.STATUS, Telephony.Sms.STATUS_PENDING);
1428            }
1429            if (errorCode != 0) {
1430                values.put(Sms.ERROR_CODE, errorCode);
1431            }
1432            final long identity = Binder.clearCallingIdentity();
1433            final ContentResolver resolver = context.getContentResolver();
1434            try {
1435                final Uri uri =  resolver.insert(Telephony.Sms.Sent.CONTENT_URI, values);
1436                if (uri != null && messageType == Sms.MESSAGE_TYPE_FAILED) {
1437                    // Since we can't persist a message directly into FAILED box,
1438                    // we have to update the column after we persist it into SENT box.
1439                    // The gap between the state change is tiny so I would not expect
1440                    // it to cause any serious problem
1441                    // TODO: we should add a "failed" URI for this in SmsProvider?
1442                    final ContentValues updateValues = new ContentValues(1);
1443                    updateValues.put(Sms.TYPE, Sms.MESSAGE_TYPE_FAILED);
1444                    resolver.update(uri, updateValues, null/*where*/, null/*selectionArgs*/);
1445                }
1446                return uri;
1447            } catch (Exception e) {
1448                Rlog.e(TAG, "writeOutboxMessage: Failed to persist outbox message", e);
1449                return null;
1450            } finally {
1451                Binder.restoreCallingIdentity(identity);
1452            }
1453        }
1454
1455        /**
1456         * Persist or update an SMS depending on if we send a new message or a stored message
1457         *
1458         * @param context
1459         * @param messageType The message folder for this SMS, FAILED or SENT
1460         * @param errorCode The current error code for this SMS or SMS part
1461         */
1462        private void persistOrUpdateMessage(Context context, int messageType, int errorCode) {
1463            if (mMessageUri != null) {
1464                updateMessageState(context, messageType, errorCode);
1465            } else {
1466                mMessageUri = persistSentMessageIfRequired(context, messageType, errorCode);
1467            }
1468        }
1469
1470        /**
1471         * Handle a failure of a single part message or a part of a multipart message
1472         *
1473         * @param context The Context
1474         * @param error The error to send back with
1475         * @param errorCode
1476         */
1477        public void onFailed(Context context, int error, int errorCode) {
1478            if (mAnyPartFailed != null) {
1479                mAnyPartFailed.set(true);
1480            }
1481            // is single part or last part of multipart message
1482            boolean isSinglePartOrLastPart = true;
1483            if (mUnsentPartCount != null) {
1484                isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
1485            }
1486            if (isSinglePartOrLastPart) {
1487                persistOrUpdateMessage(context, Sms.MESSAGE_TYPE_FAILED, errorCode);
1488            }
1489            if (mSentIntent != null) {
1490                try {
1491                    // Extra information to send with the sent intent
1492                    Intent fillIn = new Intent();
1493                    if (mMessageUri != null) {
1494                        // Pass this to SMS apps so that they know where it is stored
1495                        fillIn.putExtra("uri", mMessageUri.toString());
1496                    }
1497                    if (errorCode != 0) {
1498                        fillIn.putExtra("errorCode", errorCode);
1499                    }
1500                    if (mUnsentPartCount != null && isSinglePartOrLastPart) {
1501                        // Is multipart and last part
1502                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
1503                    }
1504                    mSentIntent.send(context, error, fillIn);
1505                } catch (CanceledException ex) {
1506                    Rlog.e(TAG, "Failed to send result");
1507                }
1508            }
1509        }
1510
1511        /**
1512         * Handle the sent of a single part message or a part of a multipart message
1513         *
1514         * @param context The Context
1515         */
1516        public void onSent(Context context) {
1517            // is single part or last part of multipart message
1518            boolean isSinglePartOrLastPart = true;
1519            if (mUnsentPartCount != null) {
1520                isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
1521            }
1522            if (isSinglePartOrLastPart) {
1523                int messageType = Sms.MESSAGE_TYPE_SENT;
1524                if (mAnyPartFailed != null && mAnyPartFailed.get()) {
1525                    messageType = Sms.MESSAGE_TYPE_FAILED;
1526                }
1527                persistOrUpdateMessage(context, messageType, 0/*errorCode*/);
1528            }
1529            if (mSentIntent != null) {
1530                try {
1531                    // Extra information to send with the sent intent
1532                    Intent fillIn = new Intent();
1533                    if (mMessageUri != null) {
1534                        // Pass this to SMS apps so that they know where it is stored
1535                        fillIn.putExtra("uri", mMessageUri.toString());
1536                    }
1537                    if (mUnsentPartCount != null && isSinglePartOrLastPart) {
1538                        // Is multipart and last part
1539                        fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
1540                    }
1541                    mSentIntent.send(context, Activity.RESULT_OK, fillIn);
1542                } catch (CanceledException ex) {
1543                    Rlog.e(TAG, "Failed to send result");
1544                }
1545            }
1546        }
1547    }
1548
1549    protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1550            PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
1551            AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
1552            boolean isExpectMore, String fullMessageText, boolean isText, boolean persistMessage) {
1553        // Get calling app package name via UID from Binder call
1554        PackageManager pm = mContext.getPackageManager();
1555        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
1556
1557        // Get package info via packagemanager
1558        PackageInfo appInfo = null;
1559        if (packageNames != null && packageNames.length > 0) {
1560            try {
1561                // XXX this is lossy- apps can share a UID
1562                appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
1563            } catch (PackageManager.NameNotFoundException e) {
1564                // error will be logged in sendRawPdu
1565            }
1566        }
1567        // Strip non-digits from destination phone number before checking for short codes
1568        // and before displaying the number to the user if confirmation is required.
1569        String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
1570        return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
1571                unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore,
1572                fullMessageText, getSubId(), isText, persistMessage);
1573    }
1574
1575    protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1576            PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore,
1577            String fullMessageText, boolean isText, boolean persistMessage) {
1578        return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
1579                null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore,
1580                fullMessageText, isText, persistMessage);
1581    }
1582
1583    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1584            String text, SmsMessageBase.SubmitPduBase pdu) {
1585        HashMap<String, Object> map = new HashMap<String, Object>();
1586        map.put("destAddr", destAddr);
1587        map.put("scAddr", scAddr);
1588        map.put("text", text);
1589        map.put("smsc", pdu.encodedScAddress);
1590        map.put("pdu", pdu.encodedMessage);
1591        return map;
1592    }
1593
1594    protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1595            int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
1596        HashMap<String, Object> map = new HashMap<String, Object>();
1597        map.put("destAddr", destAddr);
1598        map.put("scAddr", scAddr);
1599        map.put("destPort", destPort);
1600        map.put("data", data);
1601        map.put("smsc", pdu.encodedScAddress);
1602        map.put("pdu", pdu.encodedMessage);
1603        return map;
1604    }
1605
1606    /**
1607     * Dialog listener for SMS confirmation dialog.
1608     */
1609    private final class ConfirmDialogListener
1610            implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
1611            CompoundButton.OnCheckedChangeListener {
1612
1613        private final SmsTracker mTracker;
1614        private Button mPositiveButton;
1615        private Button mNegativeButton;
1616        private boolean mRememberChoice;    // default is unchecked
1617        private final TextView mRememberUndoInstruction;
1618
1619        ConfirmDialogListener(SmsTracker tracker, TextView textView) {
1620            mTracker = tracker;
1621            mRememberUndoInstruction = textView;
1622        }
1623
1624        void setPositiveButton(Button button) {
1625            mPositiveButton = button;
1626        }
1627
1628        void setNegativeButton(Button button) {
1629            mNegativeButton = button;
1630        }
1631
1632        @Override
1633        public void onClick(DialogInterface dialog, int which) {
1634            // Always set the SMS permission so that Settings will show a permission setting
1635            // for the app (it won't be shown until after the app tries to send to a short code).
1636            int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
1637
1638            if (which == DialogInterface.BUTTON_POSITIVE) {
1639                Rlog.d(TAG, "CONFIRM sending SMS");
1640                // XXX this is lossy- apps can have more than one signature
1641                EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER,
1642                                    mTracker.mAppInfo.applicationInfo == null ?
1643                                    -1 : mTracker.mAppInfo.applicationInfo.uid);
1644                sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker));
1645                if (mRememberChoice) {
1646                    newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW;
1647                }
1648            } else if (which == DialogInterface.BUTTON_NEGATIVE) {
1649                Rlog.d(TAG, "DENY sending SMS");
1650                // XXX this is lossy- apps can have more than one signature
1651                EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
1652                                    mTracker.mAppInfo.applicationInfo == null ?
1653                                    -1 :  mTracker.mAppInfo.applicationInfo.uid);
1654                sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1655                if (mRememberChoice) {
1656                    newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
1657                }
1658            }
1659            setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
1660        }
1661
1662        @Override
1663        public void onCancel(DialogInterface dialog) {
1664            Rlog.d(TAG, "dialog dismissed: don't send SMS");
1665            sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1666        }
1667
1668        @Override
1669        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1670            Rlog.d(TAG, "remember this choice: " + isChecked);
1671            mRememberChoice = isChecked;
1672            if (isChecked) {
1673                mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow);
1674                mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow);
1675                if (mRememberUndoInstruction != null) {
1676                    mRememberUndoInstruction.
1677                            setText(R.string.sms_short_code_remember_undo_instruction);
1678                    mRememberUndoInstruction.setPadding(0,0,0,32);
1679                }
1680            } else {
1681                mPositiveButton.setText(R.string.sms_short_code_confirm_allow);
1682                mNegativeButton.setText(R.string.sms_short_code_confirm_deny);
1683                if (mRememberUndoInstruction != null) {
1684                    mRememberUndoInstruction.setText("");
1685                    mRememberUndoInstruction.setPadding(0,0,0,0);
1686                }
1687            }
1688        }
1689    }
1690
1691    public boolean isIms() {
1692        if (mImsSMSDispatcher != null) {
1693            return mImsSMSDispatcher.isIms();
1694        } else {
1695            Rlog.e(TAG, mImsSMSDispatcher + " is null");
1696            return false;
1697        }
1698    }
1699
1700    public String getImsSmsFormat() {
1701        if (mImsSMSDispatcher != null) {
1702            return mImsSMSDispatcher.getImsSmsFormat();
1703        } else {
1704            Rlog.e(TAG, mImsSMSDispatcher + " is null");
1705            return null;
1706        }
1707    }
1708
1709    private String getMultipartMessageText(ArrayList<String> parts) {
1710        final StringBuilder sb = new StringBuilder();
1711        for (String part : parts) {
1712            if (part != null) {
1713                sb.append(part);
1714            }
1715        }
1716        return sb.toString();
1717    }
1718
1719    protected String getCarrierAppPackageName() {
1720        UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
1721        if (card == null) {
1722            return null;
1723        }
1724
1725        List<String> carrierPackages = card.getCarrierPackageNamesForIntent(
1726            mContext.getPackageManager(), new Intent(CarrierMessagingService.SERVICE_INTERFACE));
1727        return (carrierPackages != null && carrierPackages.size() == 1) ?
1728                carrierPackages.get(0) : null;
1729    }
1730
1731    protected int getSubId() {
1732        return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.mPhoneId);
1733    }
1734
1735    private void checkCallerIsPhoneOrCarrierApp() {
1736        int uid = Binder.getCallingUid();
1737        int appId = UserHandle.getAppId(uid);
1738        if (appId == Process.PHONE_UID || uid == 0) {
1739            return;
1740        }
1741        try {
1742            PackageManager pm = mContext.getPackageManager();
1743            ApplicationInfo ai = pm.getApplicationInfo(getCarrierAppPackageName(), 0);
1744            if (!UserHandle.isSameApp(ai.uid, Binder.getCallingUid())) {
1745                throw new SecurityException("Caller is not phone or carrier app!");
1746            }
1747        } catch (PackageManager.NameNotFoundException re) {
1748            throw new SecurityException("Caller is not phone or carrier app!");
1749        }
1750    }
1751}
1752