SmsUsageMonitor.java revision 0825495a331bb44df395a0cdb79fab85e68db5d5
10825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/* 20825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Copyright (C) 2011 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; 180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 190825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.ContentResolver; 200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.Context; 210825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.res.XmlResourceParser; 220825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.database.ContentObserver; 230825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Handler; 240825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Message; 250825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.provider.Settings; 260825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.PhoneNumberUtils; 270825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.util.Log; 280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 290825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.util.XmlUtils; 300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 310825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport org.xmlpull.v1.XmlPullParser; 320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport org.xmlpull.v1.XmlPullParserException; 330825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport org.xmlpull.v1.XmlPullParserFactory; 340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 350825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.IOException; 360825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.StringReader; 370825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.ArrayList; 380825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.HashMap; 390825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.HashSet; 400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Iterator; 410825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Map; 420825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.regex.Pattern; 430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/** 450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Implement the per-application based SMS control, which limits the number of 460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * SMS/MMS messages an app can send in the checking period. 470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This code was formerly part of {@link SMSDispatcher}, and has been moved 490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * into a separate class to support instantiation of multiple SMSDispatchers on 500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * dual-mode devices that require support for both 3GPP and 3GPP2 format messages. 510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 520825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class SmsUsageMonitor { 530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String TAG = "SmsUsageMonitor"; 540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final boolean DBG = true; 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final boolean VDBG = false; 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Default checking period for SMS sent without user permission. */ 580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int DEFAULT_SMS_CHECK_PERIOD = 1800000; // 30 minutes 590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Default number of SMS sent in checking period without user permission. */ 610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int DEFAULT_SMS_MAX_COUNT = 30; 620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for regular phone numbers. */ 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_NOT_SHORT_CODE = 0; 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for free (no cost) short codes. */ 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_FREE_SHORT_CODE = 1; 680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for standard rate (non-premium) short codes. */ 700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_STANDARD_SHORT_CODE = 2; 710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for possible premium short codes. */ 730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3; 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for premium short codes. */ 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_PREMIUM_SHORT_CODE = 4; 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final int mCheckPeriod; 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final int mMaxAllowed; 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final HashMap<String, ArrayList<Long>> mSmsStamp = 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville new HashMap<String, ArrayList<Long>>(); 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Context for retrieving regexes from XML resource. */ 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Context mContext; 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Country code for the cached short code pattern matcher. */ 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String mCurrentCountry; 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Cached short code pattern matcher for {@link #mCurrentCountry}. */ 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private ShortCodePatternMatcher mCurrentPatternMatcher; 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Cached short code regex patterns from secure settings for {@link #mCurrentCountry}. */ 940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String mSettingsShortCodePatterns; 950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Handler for responding to content observer updates. */ 970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final SettingsObserverHandler mSettingsObserverHandler; 980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML tag for root element. */ 1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String TAG_SHORTCODES = "shortcodes"; 1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML tag for short code patterns for a specific country. */ 1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String TAG_SHORTCODE = "shortcode"; 1040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the country code. */ 1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_COUNTRY = "country"; 1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the short code regex pattern. */ 1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_PATTERN = "pattern"; 1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the premium short code regex pattern. */ 1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_PREMIUM = "premium"; 1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the free short code regex pattern. */ 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_FREE = "free"; 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the standard rate short code regex pattern. */ 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_STANDARD = "standard"; 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * SMS short code regex pattern matcher for a specific country. 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final class ShortCodePatternMatcher { 1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Pattern mShortCodePattern; 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Pattern mPremiumShortCodePattern; 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Pattern mFreeShortCodePattern; 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Pattern mStandardShortCodePattern; 1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ShortCodePatternMatcher(String shortCodeRegex, String premiumShortCodeRegex, 1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String freeShortCodeRegex, String standardShortCodeRegex) { 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mShortCodePattern = (shortCodeRegex != null ? Pattern.compile(shortCodeRegex) : null); 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPremiumShortCodePattern = (premiumShortCodeRegex != null ? 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pattern.compile(premiumShortCodeRegex) : null); 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mFreeShortCodePattern = (freeShortCodeRegex != null ? 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pattern.compile(freeShortCodeRegex) : null); 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mStandardShortCodePattern = (standardShortCodeRegex != null ? 1370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pattern.compile(standardShortCodeRegex) : null); 1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int getNumberCategory(String phoneNumber) { 1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mFreeShortCodePattern != null && mFreeShortCodePattern.matcher(phoneNumber) 1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .matches()) { 1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_FREE_SHORT_CODE; 1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mStandardShortCodePattern != null && mStandardShortCodePattern.matcher(phoneNumber) 1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .matches()) { 1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_STANDARD_SHORT_CODE; 1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mPremiumShortCodePattern != null && mPremiumShortCodePattern.matcher(phoneNumber) 1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .matches()) { 1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_PREMIUM_SHORT_CODE; 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mShortCodePattern != null && mShortCodePattern.matcher(phoneNumber).matches()) { 1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE; 1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_NOT_SHORT_CODE; 1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Observe the secure setting for updated regex patterns. 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static class SettingsObserver extends ContentObserver { 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final int mWhat; 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Handler mHandler; 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SettingsObserver(Handler handler, int what) { 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(handler); 1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mHandler = handler; 1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mWhat = what; 1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onChange(boolean selfChange) { 1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mHandler.obtainMessage(mWhat).sendToTarget(); 1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Handler to update regex patterns when secure setting for the current country is updated. 1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private class SettingsObserverHandler extends Handler { 1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Current content observer, or null. */ 1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville SettingsObserver mSettingsObserver; 1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Current country code to watch for settings updates. */ 1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String mCountryIso; 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Request to start observing a secure setting. */ 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int OBSERVE_SETTING = 1; 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Handler event for updated secure settings. */ 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int SECURE_SETTINGS_CHANGED = 2; 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Send a message to this handler requesting to observe the setting for a new country. */ 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void observeSettingForCountry(String countryIso) { 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville obtainMessage(OBSERVE_SETTING, countryIso).sendToTarget(); 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void handleMessage(Message msg) { 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville switch (msg.what) { 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case OBSERVE_SETTING: 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (msg.obj != null && msg.obj instanceof String) { 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCountryIso = (String) msg.obj; 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String settingName = getSettingNameForCountry(mCountryIso); 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ContentResolver resolver = mContext.getContentResolver(); 2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mSettingsObserver != null) { 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (VDBG) log("Unregistering old content observer"); 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville resolver.unregisterContentObserver(mSettingsObserver); 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSettingsObserver = new SettingsObserver(this, SECURE_SETTINGS_CHANGED); 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville resolver.registerContentObserver( 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Settings.Secure.getUriFor(settingName), false, mSettingsObserver); 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (VDBG) log("Registered content observer for " + settingName); 2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville case SECURE_SETTINGS_CHANGED: 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville loadPatternsFromSettings(mCountryIso); 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville break; 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Create SMS usage monitor. 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param context the context to use to load resources and get TelephonyManager service 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SmsUsageMonitor(Context context) { 2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mContext = context; 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ContentResolver resolver = context.getContentResolver(); 2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mMaxAllowed = Settings.Secure.getInt(resolver, 2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT, 2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville DEFAULT_SMS_MAX_COUNT); 2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCheckPeriod = Settings.Secure.getInt(resolver, 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS, 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville DEFAULT_SMS_CHECK_PERIOD); 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSettingsObserverHandler = new SettingsObserverHandler(); 2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Return a pattern matcher object for the specified country. 2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param country the country to search for 2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found 2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private ShortCodePatternMatcher getPatternMatcher(String country) { 2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int id = com.android.internal.R.xml.sms_short_codes; 2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville XmlResourceParser parser = mContext.getResources().getXml(id); 2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return getPatternMatcher(country, parser); 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (XmlPullParserException e) { 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "XML parser exception reading short code pattern resource", e); 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (IOException e) { 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "I/O exception reading short code pattern resource", e); 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } finally { 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville parser.close(); 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return null; // country not found 2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Return a pattern matcher object for the specified country from a secure settings string. 2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found 2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static ShortCodePatternMatcher getPatternMatcher(String country, String settingsPattern) { 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // embed pattern tag into an XML document. 2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String document = "<shortcodes>" + settingsPattern + "</shortcodes>"; 2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (VDBG) log("loading updated patterns from: " + document); 2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville XmlPullParser parser = factory.newPullParser(); 2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville parser.setInput(new StringReader(document)); 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return getPatternMatcher(country, parser); 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (XmlPullParserException e) { 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "XML parser exception reading short code pattern from settings", e); 2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (IOException e) { 2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "I/O exception reading short code pattern from settings", e); 2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return null; // country not found 2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Return a pattern matcher object for the specified country and pattern XML parser. 2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param country the country to search for 2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found 2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static ShortCodePatternMatcher getPatternMatcher(String country, XmlPullParser parser) 2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville throws XmlPullParserException, IOException 2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville { 2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville XmlUtils.beginDocument(parser, TAG_SHORTCODES); 2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville while (true) { 3010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville XmlUtils.nextElement(parser); 3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String element = parser.getName(); 3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (element == null) break; 3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (element.equals(TAG_SHORTCODE)) { 3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY); 3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (country.equals(currentCountry)) { 3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String pattern = parser.getAttributeValue(null, ATTR_PATTERN); 3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String premium = parser.getAttributeValue(null, ATTR_PREMIUM); 3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String free = parser.getAttributeValue(null, ATTR_FREE); 3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String standard = parser.getAttributeValue(null, ATTR_STANDARD); 3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return new ShortCodePatternMatcher(pattern, premium, free, standard); 3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "Error: skipping unknown XML tag " + element); 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return null; // country not found 3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Clear the SMS application list for disposal. */ 3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void dispose() { 3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSmsStamp.clear(); 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Check to see if an application is allowed to send new SMS messages, and confirm with 3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * user if the send limit was reached or if a non-system app is potentially sending to a 3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * premium SMS short code or number. 3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param appName the package name of the app requesting to send an SMS 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param smsWaiting the number of new messages desired to send 3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return true if application is allowed to send the requested number 3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * of new sms messages 3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean check(String appName, int smsWaiting) { 3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mSmsStamp) { 3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville removeExpiredTimestamps(); 3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ArrayList<Long> sentList = mSmsStamp.get(appName); 3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sentList == null) { 3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sentList = new ArrayList<Long>(); 3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSmsStamp.put(appName, sentList); 3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return isUnderLimit(sentList, smsWaiting); 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Check if the destination is a possible premium short code. 3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * NOTE: the caller is expected to strip non-digits from the destination number with 3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method. 3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This happens in {@link SMSDispatcher#sendRawPdu} so that we use the same phone number 3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * for testing and in the user confirmation dialog if the user needs to confirm the number. 3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This makes it difficult for malware to fool the user or the short code pattern matcher 3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * by using non-ASCII characters to make the number appear to be different from the real 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * destination phone number. 3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param destAddress the destination address to test for possible short code 3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return {@link #CATEGORY_NOT_SHORT_CODE}, {@link #CATEGORY_FREE_SHORT_CODE}, 3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}. 3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public int checkDestination(String destAddress, String countryIso) { 3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mSettingsObserverHandler) { 3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // always allow emergency numbers 3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) { 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_NOT_SHORT_CODE; 3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ShortCodePatternMatcher patternMatcher = null; 3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (countryIso != null) { 3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // query secure settings and initialize content observer for updated regex patterns 3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mCurrentCountry == null || !countryIso.equals(mCurrentCountry)) { 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville loadPatternsFromSettings(countryIso); 3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSettingsObserverHandler.observeSettingForCountry(countryIso); 3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (countryIso.equals(mCurrentCountry)) { 3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville patternMatcher = mCurrentPatternMatcher; 3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville patternMatcher = getPatternMatcher(countryIso); 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCurrentCountry = countryIso; 3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCurrentPatternMatcher = patternMatcher; // may be null if not found 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (patternMatcher != null) { 3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return patternMatcher.getNumberCategory(destAddress); 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Generic rule: numbers of 5 digits or less are considered potential short codes 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule"); 3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (destAddress.length() <= 5) { 3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE; 3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_NOT_SHORT_CODE; 3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static String getSettingNameForCountry(String countryIso) { 4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return Settings.Secure.SMS_SHORT_CODES_PREFIX + countryIso; 4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Load regex patterns from secure settings if present. 4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param countryIso the country to search for 4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 4120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void loadPatternsFromSettings(String countryIso) { 4130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mSettingsObserverHandler) { 4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (VDBG) log("loadPatternsFromSettings(" + countryIso + ") called"); 4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String settingsPatterns = Settings.Secure.getString( 4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mContext.getContentResolver(), getSettingNameForCountry(countryIso)); 4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (settingsPatterns != null && !settingsPatterns.equals( 4180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSettingsShortCodePatterns)) { 4190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // settings pattern string has changed: update the pattern matcher 4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSettingsShortCodePatterns = settingsPatterns; 4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ShortCodePatternMatcher matcher = getPatternMatcher(countryIso, settingsPatterns); 4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (matcher != null) { 4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCurrentCountry = countryIso; 4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCurrentPatternMatcher = matcher; 4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else if (settingsPatterns == null && mSettingsShortCodePatterns != null) { 4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // pattern string was removed: caller will load default patterns from XML resource 4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCurrentCountry = null; 4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCurrentPatternMatcher = null; 4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSettingsShortCodePatterns = null; 4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Remove keys containing only old timestamps. This can happen if an SMS app is used 4370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * to send messages and then uninstalled. 4380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void removeExpiredTimestamps() { 4400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville long beginCheckPeriod = System.currentTimeMillis() - mCheckPeriod; 4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mSmsStamp) { 4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Iterator<Map.Entry<String, ArrayList<Long>>> iter = mSmsStamp.entrySet().iterator(); 4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville while (iter.hasNext()) { 4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Map.Entry<String, ArrayList<Long>> entry = iter.next(); 4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ArrayList<Long> oldList = entry.getValue(); 4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (oldList.isEmpty() || oldList.get(oldList.size() - 1) < beginCheckPeriod) { 4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville iter.remove(); 4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) { 4550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Long ct = System.currentTimeMillis(); 4560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville long beginCheckPeriod = ct - mCheckPeriod; 4570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (VDBG) log("SMS send size=" + sent.size() + " time=" + ct); 4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) { 4610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sent.remove(0); 4620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((sent.size() + smsWaiting) <= mMaxAllowed) { 4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < smsWaiting; i++ ) { 4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sent.add(ct); 4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static void log(String msg) { 4740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.d(TAG, msg); 4750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 477