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