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