d89c36340033f368716d0935cbf93f73dd09a36a
[android/platform/frameworks/opt/telephony.git] / src / java / com / android / internal / telephony / SMSDispatcher.java
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
17 package com.android.internal.telephony;
18 import android.app.Activity;
19 import android.app.AlertDialog;
20 import android.app.PendingIntent;
21 import android.app.PendingIntent.CanceledException;
22 import android.content.BroadcastReceiver;
23 import android.content.ContentResolver;
24 import android.content.ContentValues;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.Intent;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageInfo;
30 import android.content.pm.PackageManager;
31 import android.content.res.Resources;
32 import android.database.ContentObserver;
33 import android.database.sqlite.SqliteWrapper;
34 import android.net.Uri;
35 import android.os.AsyncResult;
36 import android.os.Binder;
37 import android.os.Bundle;
38 import android.os.Handler;
39 import android.os.Message;
40 import android.os.SystemProperties;
41 import android.provider.Settings;
42 import android.provider.Telephony;
43 import android.provider.Telephony.Sms;
44 import android.provider.Telephony.Sms.Intents;
45 import android.telephony.PhoneNumberUtils;
46 import android.telephony.Rlog;
47 import android.telephony.ServiceState;
48 import android.telephony.SmsManager;
49 import android.telephony.TelephonyManager;
50 import android.text.Html;
51 import android.text.Spanned;
52 import android.text.TextUtils;
53 import android.util.EventLog;
54 import android.view.LayoutInflater;
55 import android.view.View;
56 import android.view.ViewGroup;
57 import android.view.WindowManager;
58 import android.widget.Button;
59 import android.widget.CheckBox;
60 import android.widget.CompoundButton;
61 import android.widget.TextView;
62
63 import com.android.internal.R;
64 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
65 import com.android.internal.telephony.uicc.UiccCard;
66 import com.android.internal.telephony.uicc.UiccController;
67
68 import java.util.ArrayList;
69 import java.util.Collections;
70 import java.util.HashMap;
71 import java.util.List;
72 import java.util.Random;
73 import java.util.concurrent.atomic.AtomicBoolean;
74 import java.util.concurrent.atomic.AtomicInteger;
75
76 import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
77 import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
78 import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
79 import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
80 import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
81 import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
82
83 public 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         public boolean mExpectMore;
1038         String mFormat;
1039
1040         public final PendingIntent mSentIntent;
1041         public final PendingIntent mDeliveryIntent;
1042
1043         public final PackageInfo mAppInfo;
1044         public final String mDestAddress;
1045
1046         public final SmsHeader mSmsHeader;
1047
1048         private long mTimestamp = System.currentTimeMillis();
1049         public Uri mMessageUri; // Uri of persisted message if we wrote one
1050
1051         // Reference to states of a multipart message that this part belongs to
1052         private AtomicInteger mUnsentPartCount;
1053         private AtomicBoolean mAnyPartFailed;
1054
1055         private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1056                 PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
1057                 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
1058                 SmsHeader smsHeader, boolean isExpectMore) {
1059             mData = data;
1060             mSentIntent = sentIntent;
1061             mDeliveryIntent = deliveryIntent;
1062             mRetryCount = 0;
1063             mAppInfo = appInfo;
1064             mDestAddress = destAddr;
1065             mFormat = format;
1066             mExpectMore = isExpectMore;
1067             mImsRetry = 0;
1068             mMessageRef = 0;
1069             mUnsentPartCount = unsentPartCount;
1070             mAnyPartFailed = anyPartFailed;
1071             mMessageUri = messageUri;
1072             mSmsHeader = smsHeader;
1073         }
1074
1075         /**
1076          * Returns whether this tracker holds a multi-part SMS.
1077          * @return true if the tracker holds a multi-part SMS; false otherwise
1078          */
1079         boolean isMultipart() {
1080             return mData.containsKey("parts");
1081         }
1082
1083         /**
1084          * Persist this as a sent message
1085          */
1086         void writeSentMessage(Context context) {
1087             String text = (String)mData.get("text");
1088             if (text != null) {
1089                 boolean deliveryReport = (mDeliveryIntent != null);
1090                 // Using invalid threadId 0 here. When the message is inserted into the db, the
1091                 // provider looks up the threadId based on the recipient(s).
1092                 mMessageUri = Sms.addMessageToUri(context.getContentResolver(),
1093                         Telephony.Sms.Sent.CONTENT_URI,
1094                         mDestAddress,
1095                         text /*body*/,
1096                         null /*subject*/,
1097                         mTimestamp /*date*/,
1098                         true /*read*/,
1099                         deliveryReport /*deliveryReport*/,
1100                         0 /*threadId*/);
1101             }
1102         }
1103
1104         /**
1105          * Update the status of this message if we persisted it
1106          */
1107         public void updateSentMessageStatus(Context context, int status) {
1108             if (mMessageUri != null) {
1109                 // If we wrote this message in writeSentMessage, update it now
1110                 ContentValues values = new ContentValues(1);
1111                 values.put(Sms.STATUS, status);
1112                 SqliteWrapper.update(context, context.getContentResolver(),
1113                         mMessageUri, values, null, null);
1114             }
1115         }
1116
1117         /**
1118          * Update the error_code column of a message
1119          *
1120          * @param context The Context
1121          * @param errorCode The error code
1122          */
1123         private void updateMessageErrorCode(Context context, int errorCode) {
1124             if (mMessageUri == null) {
1125                 return;
1126             }
1127             final ContentValues values = new ContentValues(1);
1128             values.put(Sms.ERROR_CODE, errorCode);
1129             final long identity = Binder.clearCallingIdentity();
1130             try {
1131                 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
1132                         null/*where*/, null/*selectionArgs*/) != 1) {
1133                     Rlog.e(TAG, "Failed to update message error code");
1134                 }
1135             } finally {
1136                 Binder.restoreCallingIdentity(identity);
1137             }
1138         }
1139
1140         /**
1141          * Set the final state of a message: FAILED or SENT
1142          *
1143          * @param context The Context
1144          * @param messageType The final message type
1145          */
1146         private void setMessageFinalState(Context context, int messageType) {
1147             if (mMessageUri == null) {
1148                 return;
1149             }
1150             final ContentValues values = new ContentValues(1);
1151             values.put(Sms.TYPE, messageType);
1152             final long identity = Binder.clearCallingIdentity();
1153             try {
1154                 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values,
1155                         null/*where*/, null/*selectionArgs*/) != 1) {
1156                     Rlog.e(TAG, "Failed to move message to " + messageType);
1157                 }
1158             } finally {
1159                 Binder.restoreCallingIdentity(identity);
1160             }
1161         }
1162
1163         /**
1164          * Handle a failure of a single part message or a part of a multipart message
1165          *
1166          * @param context The Context
1167          * @param error The error to send back with
1168          * @param errorCode
1169          */
1170         public void onFailed(Context context, int error, int errorCode) {
1171             if (mAnyPartFailed != null) {
1172                 mAnyPartFailed.set(true);
1173             }
1174             // is single part or last part of multipart message
1175             boolean isSinglePartOrLastPart = true;
1176             if (mUnsentPartCount != null) {
1177                 isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
1178             }
1179             if (errorCode != 0) {
1180                 updateMessageErrorCode(context, errorCode);
1181             }
1182             if (isSinglePartOrLastPart) {
1183                 setMessageFinalState(context, Sms.MESSAGE_TYPE_FAILED);
1184             }
1185             if (mSentIntent != null) {
1186                 try {
1187                     // Extra information to send with the sent intent
1188                     Intent fillIn = new Intent();
1189                     if (mMessageUri != null) {
1190                         // Pass this to SMS apps so that they know where it is stored
1191                         fillIn.putExtra("uri", mMessageUri.toString());
1192                     }
1193                     if (errorCode != 0) {
1194                         fillIn.putExtra("errorCode", errorCode);
1195                     }
1196                     if (mUnsentPartCount != null && isSinglePartOrLastPart) {
1197                         // Is multipart and last part
1198                         fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
1199                     }
1200                     mSentIntent.send(context, error, fillIn);
1201                 } catch (CanceledException ex) {
1202                     Rlog.e(TAG, "Failed to send result");
1203                 }
1204             }
1205         }
1206
1207         /**
1208          * Handle the sent of a single part message or a part of a multipart message
1209          *
1210          * @param context The Context
1211          */
1212         public void onSent(Context context) {
1213             // is single part or last part of multipart message
1214             boolean isSinglePartOrLastPart = true;
1215             if (mUnsentPartCount != null) {
1216                 isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0;
1217             }
1218             if (isSinglePartOrLastPart) {
1219                 boolean success = true;
1220                 if (mAnyPartFailed != null && mAnyPartFailed.get()) {
1221                     success = false;
1222                 }
1223                 if (success) {
1224                     setMessageFinalState(context, Sms.MESSAGE_TYPE_SENT);
1225                 } else {
1226                     setMessageFinalState(context, Sms.MESSAGE_TYPE_FAILED);
1227                 }
1228             }
1229             if (mSentIntent != null) {
1230                 try {
1231                     // Extra information to send with the sent intent
1232                     Intent fillIn = new Intent();
1233                     if (mMessageUri != null) {
1234                         // Pass this to SMS apps so that they know where it is stored
1235                         fillIn.putExtra("uri", mMessageUri.toString());
1236                     }
1237                     if (mUnsentPartCount != null && isSinglePartOrLastPart) {
1238                         // Is multipart and last part
1239                         fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
1240                     }
1241                     mSentIntent.send(context, Activity.RESULT_OK, fillIn);
1242                 } catch (CanceledException ex) {
1243                     Rlog.e(TAG, "Failed to send result");
1244                 }
1245             }
1246         }
1247     }
1248
1249     protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1250             PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
1251             AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
1252             boolean isExpectMore) {
1253         // Get calling app package name via UID from Binder call
1254         PackageManager pm = mContext.getPackageManager();
1255         String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
1256
1257         // Get package info via packagemanager
1258         PackageInfo appInfo = null;
1259         if (packageNames != null && packageNames.length > 0) {
1260             try {
1261                 // XXX this is lossy- apps can share a UID
1262                 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
1263             } catch (PackageManager.NameNotFoundException e) {
1264                 // error will be logged in sendRawPdu
1265             }
1266         }
1267         // Strip non-digits from destination phone number before checking for short codes
1268         // and before displaying the number to the user if confirmation is required.
1269         String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
1270         return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
1271                 unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore);
1272     }
1273
1274     protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
1275             PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore) {
1276         return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
1277                 null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore);
1278     }
1279
1280     protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1281             String text, SmsMessageBase.SubmitPduBase pdu) {
1282         HashMap<String, Object> map = new HashMap<String, Object>();
1283         map.put("destAddr", destAddr);
1284         map.put("scAddr", scAddr);
1285         map.put("text", text);
1286         map.put("smsc", pdu.encodedScAddress);
1287         map.put("pdu", pdu.encodedMessage);
1288         return map;
1289     }
1290
1291     protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
1292             int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
1293         HashMap<String, Object> map = new HashMap<String, Object>();
1294         map.put("destAddr", destAddr);
1295         map.put("scAddr", scAddr);
1296         map.put("destPort", destPort);
1297         map.put("data", data);
1298         map.put("smsc", pdu.encodedScAddress);
1299         map.put("pdu", pdu.encodedMessage);
1300         return map;
1301     }
1302
1303     /**
1304      * Dialog listener for SMS confirmation dialog.
1305      */
1306     private final class ConfirmDialogListener
1307             implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener,
1308             CompoundButton.OnCheckedChangeListener {
1309
1310         private final SmsTracker mTracker;
1311         private Button mPositiveButton;
1312         private Button mNegativeButton;
1313         private boolean mRememberChoice;    // default is unchecked
1314         private final TextView mRememberUndoInstruction;
1315
1316         ConfirmDialogListener(SmsTracker tracker, TextView textView) {
1317             mTracker = tracker;
1318             mRememberUndoInstruction = textView;
1319         }
1320
1321         void setPositiveButton(Button button) {
1322             mPositiveButton = button;
1323         }
1324
1325         void setNegativeButton(Button button) {
1326             mNegativeButton = button;
1327         }
1328
1329         @Override
1330         public void onClick(DialogInterface dialog, int which) {
1331             // Always set the SMS permission so that Settings will show a permission setting
1332             // for the app (it won't be shown until after the app tries to send to a short code).
1333             int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
1334
1335             if (which == DialogInterface.BUTTON_POSITIVE) {
1336                 Rlog.d(TAG, "CONFIRM sending SMS");
1337                 // XXX this is lossy- apps can have more than one signature
1338                 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER,
1339                                     mTracker.mAppInfo.applicationInfo == null ?
1340                                     -1 : mTracker.mAppInfo.applicationInfo.uid);
1341                 sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker));
1342                 if (mRememberChoice) {
1343                     newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW;
1344                 }
1345             } else if (which == DialogInterface.BUTTON_NEGATIVE) {
1346                 Rlog.d(TAG, "DENY sending SMS");
1347                 // XXX this is lossy- apps can have more than one signature
1348                 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
1349                                     mTracker.mAppInfo.applicationInfo == null ?
1350                                     -1 :  mTracker.mAppInfo.applicationInfo.uid);
1351                 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1352                 if (mRememberChoice) {
1353                     newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
1354                 }
1355             }
1356             setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
1357         }
1358
1359         @Override
1360         public void onCancel(DialogInterface dialog) {
1361             Rlog.d(TAG, "dialog dismissed: don't send SMS");
1362             sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
1363         }
1364
1365         @Override
1366         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1367             Rlog.d(TAG, "remember this choice: " + isChecked);
1368             mRememberChoice = isChecked;
1369             if (isChecked) {
1370                 mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow);
1371                 mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow);
1372                 if (mRememberUndoInstruction != null) {
1373                     mRememberUndoInstruction.
1374                             setText(R.string.sms_short_code_remember_undo_instruction);
1375                     mRememberUndoInstruction.setPadding(0,0,0,32);
1376                 }
1377             } else {
1378                 mPositiveButton.setText(R.string.sms_short_code_confirm_allow);
1379                 mNegativeButton.setText(R.string.sms_short_code_confirm_deny);
1380                 if (mRememberUndoInstruction != null) {
1381                     mRememberUndoInstruction.setText("");
1382                     mRememberUndoInstruction.setPadding(0,0,0,0);
1383                 }
1384             }
1385         }
1386     }
1387
1388     public boolean isIms() {
1389         if (mImsSMSDispatcher != null) {
1390             return mImsSMSDispatcher.isIms();
1391         } else {
1392             Rlog.e(TAG, mImsSMSDispatcher + " is null");
1393             return false;
1394         }
1395     }
1396
1397     public String getImsSmsFormat() {
1398         if (mImsSMSDispatcher != null) {
1399             return mImsSMSDispatcher.getImsSmsFormat();
1400         } else {
1401             Rlog.e(TAG, mImsSMSDispatcher + " is null");
1402             return null;
1403         }
1404     }
1405
1406     protected Uri writeOutboxMessage(long subId, String address, String text,
1407             boolean requireDeliveryReport, String creator) {
1408         final ContentValues values = new ContentValues(8);
1409         values.put(Telephony.Sms.SUB_ID, subId);
1410         values.put(Telephony.Sms.ADDRESS, address);
1411         values.put(Telephony.Sms.BODY, text);
1412         values.put(Telephony.Sms.DATE, System.currentTimeMillis()); // milliseconds
1413         values.put(Telephony.Sms.SEEN, 1);
1414         values.put(Telephony.Sms.READ, 1);
1415         if (!TextUtils.isEmpty(creator)) {
1416             values.put(Telephony.Sms.CREATOR, creator);
1417         }
1418         if (requireDeliveryReport) {
1419             values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_PENDING);
1420         }
1421         final long identity = Binder.clearCallingIdentity();
1422         try {
1423             final Uri uri =  mContext.getContentResolver().insert(
1424                     Telephony.Sms.Outbox.CONTENT_URI, values);
1425             return uri;
1426         } catch (Exception e) {
1427             Rlog.e(TAG, "writeOutboxMessage: Failed to persist outbox message", e);
1428             return null;
1429         } finally {
1430             Binder.restoreCallingIdentity(identity);
1431         }
1432     }
1433
1434     protected void moveToOutbox(long subId, Uri messageUri, String creator) {
1435         final ContentValues values = new ContentValues(4);
1436         values.put(Telephony.Sms.SUB_ID, subId);
1437         if (!TextUtils.isEmpty(creator)) {
1438             // Reset creator/sender
1439             values.put(Telephony.Sms.CREATOR, creator);
1440         }
1441         // Reset the timestamp
1442         values.put(Telephony.Sms.DATE, System.currentTimeMillis()); // milliseconds
1443         values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_OUTBOX);
1444         final long identity = Binder.clearCallingIdentity();
1445         try {
1446             if (mContext.getContentResolver().update(messageUri, values,
1447                     null/*where*/, null/*selectionArgs*/) != 1) {
1448                 Rlog.e(TAG, "moveToOutbox: failed to update message " + messageUri);
1449             }
1450         } catch (Exception e) {
1451             Rlog.e(TAG, "moveToOutbox: Failed to update message", e);
1452         } finally {
1453             Binder.restoreCallingIdentity(identity);
1454         }
1455     }
1456
1457     private String getMultipartMessageText(ArrayList<String> parts) {
1458         final StringBuilder sb = new StringBuilder();
1459         for (String part : parts) {
1460             if (part != null) {
1461                 sb.append(part);
1462             }
1463         }
1464         return sb.toString();
1465     }
1466
1467     protected String getCarrierAppPackageName(Intent intent) {
1468         UiccCard card = UiccController.getInstance().getUiccCard();
1469         if (card == null) {
1470             return null;
1471         }
1472
1473         List<String> carrierPackages = card.getCarrierPackageNamesForIntent(
1474             mContext.getPackageManager(), intent);
1475         return (carrierPackages != null && carrierPackages.size() == 1) ?
1476                 carrierPackages.get(0) : null;
1477
1478     }
1479
1480     protected long getSubId() {
1481         return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.mPhoneId);
1482     }
1483 }