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
194658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.app.AppGlobals;
200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.ContentResolver;
210825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.Context;
224658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.content.pm.ApplicationInfo;
230825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.res.XmlResourceParser;
240825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.database.ContentObserver;
254658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.os.Binder;
260825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Handler;
274658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.os.Process;
284658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.os.RemoteException;
294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.os.UserHandle;
300825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.provider.Settings;
310825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.PhoneNumberUtils;
324658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.util.AtomicFile;
3399c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Savilleimport android.telephony.Rlog;
344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.util.Xml;
350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport com.android.internal.util.FastXmlSerializer;
370825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.util.XmlUtils;
380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
390825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport org.xmlpull.v1.XmlPullParser;
400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport org.xmlpull.v1.XmlPullParserException;
414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport org.xmlpull.v1.XmlSerializer;
420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport java.io.File;
444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport java.io.FileInputStream;
454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport java.io.FileNotFoundException;
464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport java.io.FileOutputStream;
47a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwaltimport java.io.FileReader;
480825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.IOException;
490825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.ArrayList;
50a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwaltimport java.util.concurrent.atomic.AtomicBoolean;
510825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.HashMap;
520825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Iterator;
530825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Map;
540825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.regex.Pattern;
550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/**
570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Implement the per-application based SMS control, which limits the number of
580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * SMS/MMS messages an app can send in the checking period.
590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This code was formerly part of {@link SMSDispatcher}, and has been moved
610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * into a separate class to support instantiation of multiple SMSDispatchers on
620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * dual-mode devices that require support for both 3GPP and 3GPP2 format messages.
630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
640825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class SmsUsageMonitor {
650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String TAG = "SmsUsageMonitor";
66a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private static final boolean DBG = false;
670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final boolean VDBG = false;
680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
69a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private static final String SHORT_CODE_PATH = "/data/misc/sms/codes";
70a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Default checking period for SMS sent without user permission. */
72e7fd4f7253d22528de63bb3f0fa1f9f9ca879a86Jake Hamby    private static final int DEFAULT_SMS_CHECK_PERIOD = 60000;      // 1 minute
730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Default number of SMS sent in checking period without user permission. */
750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final int DEFAULT_SMS_MAX_COUNT = 30;
760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for regular phone numbers. */
780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_NOT_SHORT_CODE = 0;
790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for free (no cost) short codes. */
810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_FREE_SHORT_CODE = 1;
820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for standard rate (non-premium) short codes. */
840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_STANDARD_SHORT_CODE = 2;
850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for possible premium short codes. */
870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for premium short codes. */
900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
92ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    /** @hide */
93ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    public static int mergeShortCodeCategories(int type1, int type2) {
94ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt        if (type1 > type2) return type1;
95ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt        return type2;
96ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    }
97ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt
984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission for a new package (ask user when first premium SMS sent). */
994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_UNKNOWN = 0;
1004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Default premium SMS permission (ask user for each premium SMS sent). */
1024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_ASK_USER = 1;
1034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission when the owner has denied the app from sending premium SMS. */
1054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_NEVER_ALLOW = 2;
1064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission when the owner has allowed the app to send premium SMS. */
1084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW = 3;
1094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final int mCheckPeriod;
1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final int mMaxAllowed;
1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final HashMap<String, ArrayList<Long>> mSmsStamp =
1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            new HashMap<String, ArrayList<Long>>();
1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Context for retrieving regexes from XML resource. */
1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final Context mContext;
1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Country code for the cached short code pattern matcher. */
1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private String mCurrentCountry;
1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Cached short code pattern matcher for {@link #mCurrentCountry}. */
1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private ShortCodePatternMatcher mCurrentPatternMatcher;
1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
125a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    /** Notice when the enabled setting changes - can be changed through gservices */
126a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private final AtomicBoolean mCheckEnabled = new AtomicBoolean(true);
127a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Handler for responding to content observer updates. */
1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final SettingsObserverHandler mSettingsObserverHandler;
1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
131a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    /** File holding the patterns */
132a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private final File mPatternFile = new File(SHORT_CODE_PATH);
133a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
134a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    /** Last modified time for pattern file */
135a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private long mPatternFileLastModified = 0;
1364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Directory for per-app SMS permission XML file. */
13858c8356d4b119d4e9c0b6fe73526f4daca1112ecRobert Greenwalt    private static final String SMS_POLICY_FILE_DIRECTORY = "/data/misc/sms";
1394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Per-app SMS permission XML filename. */
1414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String SMS_POLICY_FILE_NAME = "premium_sms_policy.xml";
1424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML tag for root element. */
1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String TAG_SHORTCODES = "shortcodes";
1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML tag for short code patterns for a specific country. */
1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String TAG_SHORTCODE = "shortcode";
1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the country code. */
1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_COUNTRY = "country";
1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the short code regex pattern. */
1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_PATTERN = "pattern";
1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the premium short code regex pattern. */
1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_PREMIUM = "premium";
1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the free short code regex pattern. */
1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_FREE = "free";
1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the standard rate short code regex pattern. */
1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_STANDARD = "standard";
1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Stored copy of premium SMS package permissions. */
1654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private AtomicFile mPolicyFile;
1664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Loaded copy of premium SMS package permissions. */
1684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private final HashMap<String, Integer> mPremiumSmsPolicy = new HashMap<String, Integer>();
1694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML tag for root element of premium SMS permissions. */
1714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String TAG_SMS_POLICY_BODY = "premium-sms-policy";
1724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML tag for a package. */
1744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String TAG_PACKAGE = "package";
1754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML attribute for the package name. */
1774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String ATTR_PACKAGE_NAME = "name";
1784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML attribute for the package's premium SMS permission (integer type). */
1804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String ATTR_PACKAGE_SMS_POLICY = "sms-policy";
1814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * SMS short code regex pattern matcher for a specific country.
1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final class ShortCodePatternMatcher {
1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mShortCodePattern;
1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mPremiumShortCodePattern;
1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mFreeShortCodePattern;
1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mStandardShortCodePattern;
1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ShortCodePatternMatcher(String shortCodeRegex, String premiumShortCodeRegex,
1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                String freeShortCodeRegex, String standardShortCodeRegex) {
1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mShortCodePattern = (shortCodeRegex != null ? Pattern.compile(shortCodeRegex) : null);
1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mPremiumShortCodePattern = (premiumShortCodeRegex != null ?
1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(premiumShortCodeRegex) : null);
1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mFreeShortCodePattern = (freeShortCodeRegex != null ?
1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(freeShortCodeRegex) : null);
1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mStandardShortCodePattern = (standardShortCodeRegex != null ?
1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(standardShortCodeRegex) : null);
2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int getNumberCategory(String phoneNumber) {
2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mFreeShortCodePattern != null && mFreeShortCodePattern.matcher(phoneNumber)
2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_FREE_SHORT_CODE;
2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mStandardShortCodePattern != null && mStandardShortCodePattern.matcher(phoneNumber)
2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_STANDARD_SHORT_CODE;
2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mPremiumShortCodePattern != null && mPremiumShortCodePattern.matcher(phoneNumber)
2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_PREMIUM_SHORT_CODE;
2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mShortCodePattern != null && mShortCodePattern.matcher(phoneNumber).matches()) {
2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return CATEGORY_NOT_SHORT_CODE;
2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
223a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt     * Observe the secure setting for enable flag
2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static class SettingsObserver extends ContentObserver {
226a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        private final Context mContext;
227a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        private final AtomicBoolean mEnabled;
2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
229a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        SettingsObserver(Handler handler, Context context, AtomicBoolean enabled) {
2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            super(handler);
231a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            mContext = context;
232a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            mEnabled = enabled;
233a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            onChange(false);
2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public void onChange(boolean selfChange) {
238a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            mEnabled.set(Settings.Global.getInt(mContext.getContentResolver(),
239a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                    Settings.Global.SMS_SHORT_CODE_CONFIRMATION, 1) != 0);
2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
243a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private static class SettingsObserverHandler extends Handler {
244a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        SettingsObserverHandler(Context context, AtomicBoolean enabled) {
245a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            ContentResolver resolver = context.getContentResolver();
246a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            ContentObserver globalObserver = new SettingsObserver(this, context, enabled);
2474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            resolver.registerContentObserver(Settings.Global.getUriFor(
2484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    Settings.Global.SMS_SHORT_CODE_CONFIRMATION), false, globalObserver);
2494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
2500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Create SMS usage monitor.
2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param context the context to use to load resources and get TelephonyManager service
2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public SmsUsageMonitor(Context context) {
2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mContext = context;
2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ContentResolver resolver = context.getContentResolver();
2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        mMaxAllowed = Settings.Global.getInt(resolver,
2614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                DEFAULT_SMS_MAX_COUNT);
2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        mCheckPeriod = Settings.Global.getInt(resolver,
2654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                DEFAULT_SMS_CHECK_PERIOD);
2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
268a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        mSettingsObserverHandler = new SettingsObserverHandler(mContext, mCheckEnabled);
2694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
2704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        loadPremiumSmsPolicyDb();
2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Return a pattern matcher object for the specified country.
2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param country the country to search for
2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
278a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private ShortCodePatternMatcher getPatternMatcherFromFile(String country) {
279a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        FileReader patternReader = null;
280a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        XmlPullParser parser = null;
2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
282a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            patternReader = new FileReader(mPatternFile);
283a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            parser = Xml.newPullParser();
284a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            parser.setInput(patternReader);
28581ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            return getPatternMatcherFromXmlParser(parser, country);
28681ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        } catch (FileNotFoundException e) {
28799c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville            Rlog.e(TAG, "Short Code Pattern File not found");
28881ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        } catch (XmlPullParserException e) {
28999c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville            Rlog.e(TAG, "XML parser exception reading short code pattern file", e);
29081ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        } finally {
29181ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            mPatternFileLastModified = mPatternFile.lastModified();
29281ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            if (patternReader != null) {
29381ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                try {
29481ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                    patternReader.close();
29581ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                } catch (IOException e) {}
29681ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            }
29781ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        }
29881ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        return null;
29981ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt    }
30081ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt
30181ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt    private ShortCodePatternMatcher getPatternMatcherFromResource(String country) {
30281ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        int id = com.android.internal.R.xml.sms_short_codes;
30381ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        XmlResourceParser parser = null;
30481ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        try {
30581ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            parser = mContext.getResources().getXml(id);
30681ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            return getPatternMatcherFromXmlParser(parser, country);
30781ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        } finally {
30881ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            if (parser != null) parser.close();
30981ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        }
31081ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt    }
31181ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt
31281ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt    private ShortCodePatternMatcher getPatternMatcherFromXmlParser(XmlPullParser parser,
31381ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            String country) {
31481ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        try {
315a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            XmlUtils.beginDocument(parser, TAG_SHORTCODES);
316a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
317a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            while (true) {
318a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                XmlUtils.nextElement(parser);
319a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                String element = parser.getName();
32010270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                if (element == null) {
32199c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Parsing pattern data found null");
32210270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                    break;
32310270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                }
324a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
325a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                if (element.equals(TAG_SHORTCODE)) {
326a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                    String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
32799c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    if (VDBG) Rlog.d(TAG, "Found country " + currentCountry);
328a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                    if (country.equals(currentCountry)) {
329a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
330a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
331a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        String free = parser.getAttributeValue(null, ATTR_FREE);
332a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        String standard = parser.getAttributeValue(null, ATTR_STANDARD);
333a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        return new ShortCodePatternMatcher(pattern, premium, free, standard);
334a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                    }
335a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                } else {
33699c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Error: skipping unknown XML tag " + element);
337a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                }
338a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            }
3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (XmlPullParserException e) {
34099c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville            Rlog.e(TAG, "XML parser exception reading short code patterns", e);
3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (IOException e) {
34299c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville            Rlog.e(TAG, "I/O exception reading short code patterns", e);
3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
34499c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville        if (DBG) Rlog.d(TAG, "Country (" + country + ") not found");
3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return null;    // country not found
3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Clear the SMS application list for disposal. */
3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    void dispose() {
3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mSmsStamp.clear();
3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Check to see if an application is allowed to send new SMS messages, and confirm with
3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * user if the send limit was reached or if a non-system app is potentially sending to a
3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * premium SMS short code or number.
3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param appName the package name of the app requesting to send an SMS
3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param smsWaiting the number of new messages desired to send
3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if application is allowed to send the requested number
3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *  of new sms messages
3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean check(String appName, int smsWaiting) {
3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSmsStamp) {
3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            removeExpiredTimestamps();
3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ArrayList<Long> sentList = mSmsStamp.get(appName);
3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (sentList == null) {
3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                sentList = new ArrayList<Long>();
3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mSmsStamp.put(appName, sentList);
3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return isUnderLimit(sentList, smsWaiting);
3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Check if the destination is a possible premium short code.
3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * NOTE: the caller is expected to strip non-digits from the destination number with
3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method.
3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * This happens in {@link SMSDispatcher#sendRawPdu} so that we use the same phone number
3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * for testing and in the user confirmation dialog if the user needs to confirm the number.
3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * This makes it difficult for malware to fool the user or the short code pattern matcher
3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * by using non-ASCII characters to make the number appear to be different from the real
3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * destination phone number.
3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param destAddress the destination address to test for possible short code
3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return {@link #CATEGORY_NOT_SHORT_CODE}, {@link #CATEGORY_FREE_SHORT_CODE},
3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *  {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}.
3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int checkDestination(String destAddress, String countryIso) {
3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSettingsObserverHandler) {
3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            // always allow emergency numbers
3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
39599c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                if (DBG) Rlog.d(TAG, "isEmergencyNumber");
3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_NOT_SHORT_CODE;
3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
3984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            // always allow if the feature is disabled
399a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            if (!mCheckEnabled.get()) {
40099c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                if (DBG) Rlog.e(TAG, "check disabled");
4014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return CATEGORY_NOT_SHORT_CODE;
4024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (countryIso != null) {
405a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                if (mCurrentCountry == null || !countryIso.equals(mCurrentCountry) ||
406a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        mPatternFile.lastModified() != mPatternFileLastModified) {
40781ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                    if (mPatternFile.exists()) {
40899c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                        if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from file");
40910270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                        mCurrentPatternMatcher = getPatternMatcherFromFile(countryIso);
41081ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                    } else {
41199c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                        if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from resource");
41210270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                        mCurrentPatternMatcher = getPatternMatcherFromResource(countryIso);
41381ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                    }
4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mCurrentCountry = countryIso;
4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
41810270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt            if (mCurrentPatternMatcher != null) {
41910270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                return mCurrentPatternMatcher.getNumberCategory(destAddress);
4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // Generic rule: numbers of 5 digits or less are considered potential short codes
42299c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                Rlog.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (destAddress.length() <= 5) {
4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return CATEGORY_NOT_SHORT_CODE;
4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
4334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Load the premium SMS policy from an XML file.
4344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Based on code from NotificationManagerService.
4354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
4364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private void loadPremiumSmsPolicyDb() {
4374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
4384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (mPolicyFile == null) {
4394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                File dir = new File(SMS_POLICY_FILE_DIRECTORY);
4404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPolicyFile = new AtomicFile(new File(dir, SMS_POLICY_FILE_NAME));
4414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPremiumSmsPolicy.clear();
4434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                FileInputStream infile = null;
4454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                try {
4464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    infile = mPolicyFile.openRead();
4474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    final XmlPullParser parser = Xml.newPullParser();
4484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    parser.setInput(infile, null);
4494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    XmlUtils.beginDocument(parser, TAG_SMS_POLICY_BODY);
4514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    while (true) {
4534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        XmlUtils.nextElement(parser);
4544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        String element = parser.getName();
4564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        if (element == null) break;
4574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4584658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        if (element.equals(TAG_PACKAGE)) {
4594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
4604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            String policy = parser.getAttributeValue(null, ATTR_PACKAGE_SMS_POLICY);
4614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            if (packageName == null) {
46299c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                                Rlog.e(TAG, "Error: missing package name attribute");
4634658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } else if (policy == null) {
46499c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                                Rlog.e(TAG, "Error: missing package policy attribute");
4654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } else try {
4664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                                mPremiumSmsPolicy.put(packageName, Integer.parseInt(policy));
4674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } catch (NumberFormatException e) {
46899c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                                Rlog.e(TAG, "Error: non-numeric policy type " + policy);
4694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            }
4704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        } else {
47199c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                            Rlog.e(TAG, "Error: skipping unknown XML tag " + element);
4724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        }
4734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    }
4744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (FileNotFoundException e) {
4754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    // No data yet
4764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (IOException e) {
47799c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Unable to read premium SMS policy database", e);
4784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (NumberFormatException e) {
47999c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Unable to parse premium SMS policy database", e);
4804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (XmlPullParserException e) {
48199c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Unable to parse premium SMS policy database", e);
4824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } finally {
4834658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    if (infile != null) {
4844658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        try {
4854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            infile.close();
4864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        } catch (IOException ignored) {
4874658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        }
4884658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    }
4894658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
4904658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
4914658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
4924658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
4934658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4944658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
4954658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Persist the premium SMS policy to an XML file.
4964658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Based on code from NotificationManagerService.
4974658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
4984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private void writePremiumSmsPolicyDb() {
4994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
5004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            FileOutputStream outfile = null;
5014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            try {
5024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                outfile = mPolicyFile.startWrite();
5034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                XmlSerializer out = new FastXmlSerializer();
5054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.setOutput(outfile, "utf-8");
5064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.startDocument(null, true);
5084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.startTag(null, TAG_SMS_POLICY_BODY);
5104658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5114658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                for (Map.Entry<String, Integer> policy : mPremiumSmsPolicy.entrySet()) {
5124658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.startTag(null, TAG_PACKAGE);
5134658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.attribute(null, ATTR_PACKAGE_NAME, policy.getKey());
5144658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.attribute(null, ATTR_PACKAGE_SMS_POLICY, policy.getValue().toString());
5154658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.endTag(null, TAG_PACKAGE);
5164658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
5174658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5184658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.endTag(null, TAG_SMS_POLICY_BODY);
5194658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.endDocument();
5204658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5214658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPolicyFile.finishWrite(outfile);
5224658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            } catch (IOException e) {
52399c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                Rlog.e(TAG, "Unable to write premium SMS policy database", e);
5244658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                if (outfile != null) {
5254658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    mPolicyFile.failWrite(outfile);
5264658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
5274658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5284658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5304658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5314658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
5324658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Returns the premium SMS permission for the specified package. If the package has never
5334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * been seen before, the default {@link #PREMIUM_SMS_PERMISSION_ASK_USER}
5344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * will be returned.
5354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param packageName the name of the package to query permission
5364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @return one of {@link #PREMIUM_SMS_PERMISSION_UNKNOWN},
5374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ASK_USER},
5384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
5394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
5404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @throws SecurityException if the caller is not a system process
5414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
5424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public int getPremiumSmsPermission(String packageName) {
543f323afdb70b83e0c851ced2af7594b20412542c3Shishir Agrawal        checkCallerIsSystemOrPhoneOrSameApp(packageName);
5444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
5454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            Integer policy = mPremiumSmsPolicy.get(packageName);
5464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (policy == null) {
5474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return PREMIUM_SMS_PERMISSION_UNKNOWN;
5484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            } else {
5494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return policy;
5504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
5554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Sets the premium SMS permission for the specified package and save the value asynchronously
5564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * to persistent storage.
5574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param packageName the name of the package to set permission
5584658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param permission one of {@link #PREMIUM_SMS_PERMISSION_ASK_USER},
5594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
5604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
5614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @throws SecurityException if the caller is not a system process
5624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
5634658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public void setPremiumSmsPermission(String packageName, int permission) {
5644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        checkCallerIsSystemOrPhoneApp();
5654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        if (permission < PREMIUM_SMS_PERMISSION_ASK_USER
5664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                || permission > PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW) {
5674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            throw new IllegalArgumentException("invalid SMS permission type " + permission);
5684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
5704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            mPremiumSmsPolicy.put(packageName, permission);
5714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        // write policy file in the background
5734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        new Thread(new Runnable() {
5744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            @Override
5754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            public void run() {
5764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                writePremiumSmsPolicyDb();
5774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }).start();
5794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
581f323afdb70b83e0c851ced2af7594b20412542c3Shishir Agrawal    private static void checkCallerIsSystemOrPhoneOrSameApp(String pkg) {
5824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int uid = Binder.getCallingUid();
583f323afdb70b83e0c851ced2af7594b20412542c3Shishir Agrawal        int appId = UserHandle.getAppId(uid);
584f323afdb70b83e0c851ced2af7594b20412542c3Shishir Agrawal        if (appId == Process.SYSTEM_UID || appId == Process.PHONE_UID || uid == 0) {
5854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            return;
5864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5874658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        try {
5884658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
5894658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    pkg, 0, UserHandle.getCallingUserId());
5904658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (!UserHandle.isSameApp(ai.uid, uid)) {
5914658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                throw new SecurityException("Calling uid " + uid + " gave package"
5924658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        + pkg + " which is owned by uid " + ai.uid);
5934658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5944658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        } catch (RemoteException re) {
5954658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            throw new SecurityException("Unknown package " + pkg + "\n" + re);
5964658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5974658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static void checkCallerIsSystemOrPhoneApp() {
6004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int uid = Binder.getCallingUid();
6014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int appId = UserHandle.getAppId(uid);
6024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        if (appId == Process.SYSTEM_UID || appId == Process.PHONE_UID || uid == 0) {
6034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            return;
6044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        throw new SecurityException("Disallowed call for uid " + uid);
6064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
6074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Remove keys containing only old timestamps. This can happen if an SMS app is used
6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * to send messages and then uninstalled.
6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private void removeExpiredTimestamps() {
6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long beginCheckPeriod = System.currentTimeMillis() - mCheckPeriod;
6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSmsStamp) {
6160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Iterator<Map.Entry<String, ArrayList<Long>>> iter = mSmsStamp.entrySet().iterator();
6170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            while (iter.hasNext()) {
6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                Map.Entry<String, ArrayList<Long>> entry = iter.next();
6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ArrayList<Long> oldList = entry.getValue();
6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (oldList.isEmpty() || oldList.get(oldList.size() - 1) < beginCheckPeriod) {
6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    iter.remove();
6220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
6240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) {
6280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Long ct = System.currentTimeMillis();
6290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long beginCheckPeriod = ct - mCheckPeriod;
6300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (VDBG) log("SMS send size=" + sent.size() + " time=" + ct);
6320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) {
6340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            sent.remove(0);
6350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((sent.size() + smsWaiting) <= mMaxAllowed) {
6380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i = 0; i < smsWaiting; i++ ) {
6390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                sent.add(ct);
6400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
6410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return true;
6420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return false;
6440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void log(String msg) {
64799c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville        Rlog.d(TAG, msg);
6480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville}
650