CdmaSMSDispatcher.java revision 0825495a331bb44df395a0cdb79fab85e68db5d5
10825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/* 20825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Copyright (C) 2008 The Android Open Source Project 30825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 40825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Licensed under the Apache License, Version 2.0 (the "License"); 50825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * you may not use this file except in compliance with the License. 60825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * You may obtain a copy of the License at 70825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 80825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * http://www.apache.org/licenses/LICENSE-2.0 90825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unless required by applicable law or agreed to in writing, software 110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * distributed under the License is distributed on an "AS IS" BASIS, 120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * See the License for the specific language governing permissions and 140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * limitations under the License. 150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 170825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepackage com.android.internal.telephony.cdma; 180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.app.Activity; 210825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.app.PendingIntent; 220825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.app.PendingIntent.CanceledException; 230825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.ContentValues; 240825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.Intent; 250825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.SharedPreferences; 260825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.database.Cursor; 270825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.database.SQLException; 280825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Message; 290825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.SystemProperties; 300825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.preference.PreferenceManager; 310825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.provider.Telephony; 320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.provider.Telephony.Sms.Intents; 330825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.SmsCbMessage; 340825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.SmsManager; 350825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.cdma.CdmaSmsCbProgramData; 360825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.util.Log; 370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 380825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.CommandsInterface; 390825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.GsmAlphabet; 400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SmsConstants; 410825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SMSDispatcher; 420825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SmsHeader; 430825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SmsMessageBase; 440825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SmsStorageMonitor; 450825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.SmsUsageMonitor; 460825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.TelephonyProperties; 470825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.WspTypeDecoder; 480825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.cdma.sms.SmsEnvelope; 490825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.cdma.sms.UserData; 500825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.util.HexDump; 510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 520825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.ByteArrayOutputStream; 530825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Arrays; 540825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.HashMap; 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.List; 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.res.Resources; 580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 600825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillefinal class CdmaSMSDispatcher extends SMSDispatcher { 610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String TAG = "CDMA"; 620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private byte[] mLastDispatchedSmsFingerprint; 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private byte[] mLastAcknowledgedSmsFingerprint; 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final boolean mCheckForDuplicatePortsInOmadmWapPush = Resources.getSystem().getBoolean( 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville com.android.internal.R.bool.config_duplicate_port_omadm_wappush); 680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville CdmaSMSDispatcher(CDMAPhone phone, SmsStorageMonitor storageMonitor, 700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsUsageMonitor usageMonitor) { 710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(phone, storageMonitor, usageMonitor); 720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCm.setOnNewCdmaSms(this, EVENT_NEW_SMS, null); 730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void dispose() { 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCm.unSetOnNewCdmaSms(this); 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected String getFormat() { 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return android.telephony.SmsMessage.FORMAT_3GPP2; 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void handleCdmaStatusReport(SmsMessage sms) { 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0, count = deliveryPendingList.size(); i < count; i++) { 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsTracker tracker = deliveryPendingList.get(i); 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (tracker.mMessageRef == sms.messageRef) { 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Found it. Remove from list and broadcast. 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville deliveryPendingList.remove(i); 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville PendingIntent intent = tracker.mDeliveryIntent; 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Intent fillIn = new Intent(); 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville fillIn.putExtra("pdu", sms.getPdu()); 940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville fillIn.putExtra("format", android.telephony.SmsMessage.FORMAT_3GPP2); 950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville intent.send(mContext, Activity.RESULT_OK, fillIn); 970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CanceledException ex) {} 980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; // Only expect to see one tracker matching this message. 990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 1040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Dispatch service category program data to the CellBroadcastReceiver app, which filters 1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * the broadcast alerts to display. 1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param sms the SMS message containing one or more 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@link android.telephony.cdma.CdmaSmsCbProgramData} objects. 1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void handleServiceCategoryProgramData(SmsMessage sms) { 1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville List<CdmaSmsCbProgramData> programDataList = sms.getSmsCbProgramData(); 1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (programDataList == null) { 1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "handleServiceCategoryProgramData: program data list is null!"); 1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Intent intent = new Intent(Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION); 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville intent.putExtra("program_data_list", (CdmaSmsCbProgramData[]) programDataList.toArray()); 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville dispatch(intent, RECEIVE_SMS_PERMISSION); 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** {@inheritDoc} */ 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public int dispatchMessage(SmsMessageBase smsb) { 1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // If sms is null, means there was a parsing error. 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (smsb == null) { 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "dispatchMessage: message is null"); 1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_GENERIC_ERROR; 1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (inEcm.equals("true")) { 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Activity.RESULT_OK; 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mSmsReceiveDisabled) { 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Device doesn't support receiving SMS, 1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.d(TAG, "Received short message on device which doesn't support " 1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + "receiving SMS. Ignored."); 1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_HANDLED; 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsMessage sms = (SmsMessage) smsb; 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Handle CMAS emergency broadcast messages. 1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType()) { 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.d(TAG, "Broadcast type message"); 1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsCbMessage message = sms.parseBroadcastSms(); 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (message != null) { 1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville dispatchBroadcastMessage(message); 1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_HANDLED; 1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // See if we have a network duplicate SMS. 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLastDispatchedSmsFingerprint = sms.getIncomingSmsFingerprint(); 1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mLastAcknowledgedSmsFingerprint != null && 1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Arrays.equals(mLastDispatchedSmsFingerprint, mLastAcknowledgedSmsFingerprint)) { 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_HANDLED; 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Decode BD stream and set sms variables. 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sms.parseSms(); 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int teleService = sms.getTeleService(); 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean handled = false; 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((SmsEnvelope.TELESERVICE_VMN == teleService) || 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville (SmsEnvelope.TELESERVICE_MWI == teleService)) { 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // handling Voicemail 1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int voicemailCount = sms.getNumOfVoicemails(); 1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.d(TAG, "Voicemail count=" + voicemailCount); 1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Store the voicemail count in preferences. 1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( 1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mContext); 1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SharedPreferences.Editor editor = sp.edit(); 1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount); 1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville editor.apply(); 1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPhone.setVoiceMessageWaiting(1, voicemailCount); 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville handled = true; 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (((SmsEnvelope.TELESERVICE_WMT == teleService) || 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville (SmsEnvelope.TELESERVICE_WEMT == teleService)) && 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sms.isStatusReportMessage()) { 1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville handleCdmaStatusReport(sms); 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville handled = true; 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (SmsEnvelope.TELESERVICE_SCPT == teleService) { 1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville handleServiceCategoryProgramData(sms); 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville handled = true; 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if ((sms.getUserData() == null)) { 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (false) { 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.d(TAG, "Received SMS without user data"); 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville handled = true; 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (handled) { 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_HANDLED; 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (!mStorageMonitor.isStorageAvailable() && 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) { 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // It's a storable message and there's no storage available. Bail. 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // (See C.S0015-B v2.0 for a description of "Immediate Display" 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // messages, which we represent as CLASS_0.) 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_OUT_OF_MEMORY; 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (SmsEnvelope.TELESERVICE_WAP == teleService) { 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return processCdmaWapPdu(sms.getUserData(), sms.messageRef, 2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sms.getOriginatingAddress()); 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Reject (NAK) any messages with teleservice ids that have 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // not yet been handled and also do not correspond to the two 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // kinds that are processed below. 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((SmsEnvelope.TELESERVICE_WMT != teleService) && 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville (SmsEnvelope.TELESERVICE_WEMT != teleService) && 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville (SmsEnvelope.MESSAGE_TYPE_BROADCAST != sms.getMessageType())) { 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_UNSUPPORTED; 2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return dispatchNormalMessage(smsb); 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Processes inbound messages that are in the WAP-WDP PDU format. See 2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * wap-259-wdp-20010614-a section 6.5 for details on the WAP-WDP PDU format. 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * WDP segments are gathered until a datagram completes and gets dispatched. 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param pdu The WAP-WDP PDU segment 2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a result code from {@link Telephony.Sms.Intents}, or 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@link Activity#RESULT_OK} if the message has been broadcast 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * to applications 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) { 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int index = 0; 2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int msgType = (0xFF & pdu[index++]); 2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (msgType != 0) { 2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.w(TAG, "Received a WAP SMS which is not WDP. Discard."); 2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_HANDLED; 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int totalSegments = (0xFF & pdu[index++]); // >= 1 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int segment = (0xFF & pdu[index++]); // >= 0 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (segment >= totalSegments) { 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "WDP bad segment #" + segment + " expecting 0-" + (totalSegments - 1)); 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Intents.RESULT_SMS_HANDLED; 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Only the first segment contains sourcePort and destination Port 2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int sourcePort = 0; 2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int destinationPort = 0; 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (segment == 0) { 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville //process WDP segment 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sourcePort = (0xFF & pdu[index++]) << 8; 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sourcePort |= 0xFF & pdu[index++]; 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville destinationPort = (0xFF & pdu[index++]) << 8; 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville destinationPort |= 0xFF & pdu[index++]; 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Some carriers incorrectly send duplicate port fields in omadm wap pushes. 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // If configured, check for that here 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mCheckForDuplicatePortsInOmadmWapPush) { 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (checkDuplicatePortOmadmWappush(pdu,index)) { 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville index = index + 4; // skip duplicate port fields 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Lookup all other related parts 2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.i(TAG, "Received WAP PDU. Type = " + msgType + ", originator = " + address 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ", src-port = " + sourcePort + ", dst-port = " + destinationPort 2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville + ", ID = " + referenceNumber + ", segment# = " + segment + '/' + totalSegments); 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // pass the user data portion of the PDU to the shared handler in SMSDispatcher 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[] userData = new byte[pdu.length - index]; 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville System.arraycopy(pdu, index, userData, 0, pdu.length - index); 2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return processMessagePart(userData, address, referenceNumber, segment, totalSegments, 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 0L, destinationPort, true); 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** {@inheritDoc} */ 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void sendData(String destAddr, String scAddr, int destPort, 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville scAddr, destAddr, destPort, data, (deliveryIntent != null)); 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendSubmitPdu(pdu, sentIntent, deliveryIntent, destAddr); 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** {@inheritDoc} */ 2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void sendText(String destAddr, String scAddr, String text, 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville PendingIntent sentIntent, PendingIntent deliveryIntent) { 2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville scAddr, destAddr, text, (deliveryIntent != null), null); 2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendSubmitPdu(pdu, sentIntent, deliveryIntent, destAddr); 2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** {@inheritDoc} */ 2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody, 3010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville boolean use7bitOnly) { 3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return SmsMessage.calculateLength(messageBody, use7bitOnly); 3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** {@inheritDoc} */ 3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void sendNewSubmitPdu(String destinationAddress, String scAddress, 3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String message, SmsHeader smsHeader, int encoding, 3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) { 3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville UserData uData = new UserData(); 3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville uData.payloadStr = message; 3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville uData.userDataHeader = smsHeader; 3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (encoding == SmsConstants.ENCODING_7BIT) { 3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { // assume UTF-16 3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville uData.msgEncoding = UserData.ENCODING_UNICODE_16; 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville uData.msgEncodingSet = true; 3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /* By setting the statusReportRequested bit only for the 3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * last message fragment, this will result in only one 3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * callback to the sender when that last fragment delivery 3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * has been acknowledged. */ 3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress, 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville uData, (deliveryIntent != null) && lastPart); 3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendSubmitPdu(submitPdu, sentIntent, deliveryIntent, destinationAddress); 3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void sendSubmitPdu(SmsMessage.SubmitPdu pdu, 3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville PendingIntent sentIntent, PendingIntent deliveryIntent, String destAddr) { 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) { 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sentIntent != null) { 3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sentIntent.send(SmsManager.RESULT_ERROR_NO_SERVICE); 3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (CanceledException ex) {} 3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (false) { 3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.d(TAG, "Block SMS in Emergency Callback mode"); 3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent, destAddr); 3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** {@inheritDoc} */ 3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void sendSms(SmsTracker tracker) { 3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville HashMap<String, Object> map = tracker.mData; 3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // byte smsc[] = (byte[]) map.get("smsc"); // unused for CDMA 3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte pdu[] = (byte[]) map.get("pdu"); 3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCm.sendCdmaSms(pdu, reply); 3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** {@inheritDoc} */ 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) { 3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); 3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (inEcm.equals("true")) { 3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return; 3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int causeCode = resultToCause(result); 3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCm.acknowledgeLastIncomingCdmaSms(success, causeCode, response); 3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (causeCode == 0) { 3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLastAcknowledgedSmsFingerprint = mLastDispatchedSmsFingerprint; 3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mLastDispatchedSmsFingerprint = null; 3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static int resultToCause(int rc) { 3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (rc) { 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case Activity.RESULT_OK: 3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case Intents.RESULT_SMS_HANDLED: 3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Cause code is ignored on success. 3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return 0; 3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case Intents.RESULT_SMS_OUT_OF_MEMORY: 3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CommandsInterface.CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE; 3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case Intents.RESULT_SMS_UNSUPPORTED: 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CommandsInterface.CDMA_SMS_FAIL_CAUSE_INVALID_TELESERVICE_ID; 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case Intents.RESULT_SMS_GENERIC_ERROR: 3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville default: 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CommandsInterface.CDMA_SMS_FAIL_CAUSE_ENCODING_PROBLEM; 3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Optional check to see if the received WapPush is an OMADM notification with erroneous 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * extra port fields. 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * - Some carriers make this mistake. 3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * ex: MSGTYPE-TotalSegments-CurrentSegment 3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * -SourcePortDestPort-SourcePortDestPort-OMADM PDU 3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param origPdu The WAP-WDP PDU segment 3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param index Current Index while parsing the PDU. 3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return True if OrigPdu is OmaDM Push Message which has duplicate ports. 4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * False if OrigPdu is NOT OmaDM Push Message which has duplicate ports. 4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static boolean checkDuplicatePortOmadmWappush(byte[] origPdu, int index) { 4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville index += 4; 4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville byte[] omaPdu = new byte[origPdu.length - index]; 4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville System.arraycopy(origPdu, index, omaPdu, 0, omaPdu.length); 4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville WspTypeDecoder pduDecoder = new WspTypeDecoder(omaPdu); 4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int wspIndex = 2; 4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Process header length field 4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (pduDecoder.decodeUintvarInteger(wspIndex) == false) { 4120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 4130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville wspIndex += pduDecoder.getDecodedDataLength(); // advance to next field 4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Process content type field 4180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (pduDecoder.decodeContentType(wspIndex) == false) { 4190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String mimeType = pduDecoder.getValueString(); 4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mimeType != null && mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_SYNCML_NOTI)) { 4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 429