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