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;
49bfda9080cab9a819fc48bdb97ef70911016a0f21Wojciech Staszkiewiczimport java.nio.charset.StandardCharsets;
500825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.ArrayList;
51a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwaltimport java.util.concurrent.atomic.AtomicBoolean;
520825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.HashMap;
530825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Iterator;
540825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Map;
550825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.regex.Pattern;
560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/**
580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Implement the per-application based SMS control, which limits the number of
590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * SMS/MMS messages an app can send in the checking period.
600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This code was formerly part of {@link SMSDispatcher}, and has been moved
620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * into a separate class to support instantiation of multiple SMSDispatchers on
630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * dual-mode devices that require support for both 3GPP and 3GPP2 format messages.
640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
650825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class SmsUsageMonitor {
660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String TAG = "SmsUsageMonitor";
67a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private static final boolean DBG = false;
680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final boolean VDBG = false;
690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
70a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private static final String SHORT_CODE_PATH = "/data/misc/sms/codes";
71a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Default checking period for SMS sent without user permission. */
73e7fd4f7253d22528de63bb3f0fa1f9f9ca879a86Jake Hamby    private static final int DEFAULT_SMS_CHECK_PERIOD = 60000;      // 1 minute
740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Default number of SMS sent in checking period without user permission. */
760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final int DEFAULT_SMS_MAX_COUNT = 30;
770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for regular phone numbers. */
790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_NOT_SHORT_CODE = 0;
800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for free (no cost) short codes. */
820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_FREE_SHORT_CODE = 1;
830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for standard rate (non-premium) short codes. */
850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_STANDARD_SHORT_CODE = 2;
860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for possible premium short codes. */
880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for premium short codes. */
910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
93ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    /** @hide */
94ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    public static int mergeShortCodeCategories(int type1, int type2) {
95ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt        if (type1 > type2) return type1;
96ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt        return type2;
97ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    }
98ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt
994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission for a new package (ask user when first premium SMS sent). */
1004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_UNKNOWN = 0;
1014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Default premium SMS permission (ask user for each premium SMS sent). */
1034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_ASK_USER = 1;
1044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission when the owner has denied the app from sending premium SMS. */
1064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_NEVER_ALLOW = 2;
1074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission when the owner has allowed the app to send premium SMS. */
1094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW = 3;
1104658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final int mCheckPeriod;
1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final int mMaxAllowed;
1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final HashMap<String, ArrayList<Long>> mSmsStamp =
1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            new HashMap<String, ArrayList<Long>>();
1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Context for retrieving regexes from XML resource. */
1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final Context mContext;
1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Country code for the cached short code pattern matcher. */
1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private String mCurrentCountry;
1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Cached short code pattern matcher for {@link #mCurrentCountry}. */
1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private ShortCodePatternMatcher mCurrentPatternMatcher;
1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
126a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    /** Notice when the enabled setting changes - can be changed through gservices */
127a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private final AtomicBoolean mCheckEnabled = new AtomicBoolean(true);
128a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
1290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Handler for responding to content observer updates. */
1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final SettingsObserverHandler mSettingsObserverHandler;
1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
132a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    /** File holding the patterns */
133a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private final File mPatternFile = new File(SHORT_CODE_PATH);
134a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
135a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    /** Last modified time for pattern file */
136a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private long mPatternFileLastModified = 0;
1374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Directory for per-app SMS permission XML file. */
13958c8356d4b119d4e9c0b6fe73526f4daca1112ecRobert Greenwalt    private static final String SMS_POLICY_FILE_DIRECTORY = "/data/misc/sms";
1404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Per-app SMS permission XML filename. */
1424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String SMS_POLICY_FILE_NAME = "premium_sms_policy.xml";
1434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML tag for root element. */
1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String TAG_SHORTCODES = "shortcodes";
1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML tag for short code patterns for a specific country. */
1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String TAG_SHORTCODE = "shortcode";
1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the country code. */
1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_COUNTRY = "country";
1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the short code regex pattern. */
1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_PATTERN = "pattern";
1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the premium short code regex pattern. */
1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_PREMIUM = "premium";
1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the free short code regex pattern. */
1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_FREE = "free";
1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the standard rate short code regex pattern. */
1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_STANDARD = "standard";
1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Stored copy of premium SMS package permissions. */
1664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private AtomicFile mPolicyFile;
1674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Loaded copy of premium SMS package permissions. */
1694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private final HashMap<String, Integer> mPremiumSmsPolicy = new HashMap<String, Integer>();
1704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML tag for root element of premium SMS permissions. */
1724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String TAG_SMS_POLICY_BODY = "premium-sms-policy";
1734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML tag for a package. */
1754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String TAG_PACKAGE = "package";
1764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML attribute for the package name. */
1784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String ATTR_PACKAGE_NAME = "name";
1794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML attribute for the package's premium SMS permission (integer type). */
1814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String ATTR_PACKAGE_SMS_POLICY = "sms-policy";
1824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * SMS short code regex pattern matcher for a specific country.
1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final class ShortCodePatternMatcher {
1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mShortCodePattern;
1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mPremiumShortCodePattern;
1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mFreeShortCodePattern;
1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mStandardShortCodePattern;
1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ShortCodePatternMatcher(String shortCodeRegex, String premiumShortCodeRegex,
1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                String freeShortCodeRegex, String standardShortCodeRegex) {
1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mShortCodePattern = (shortCodeRegex != null ? Pattern.compile(shortCodeRegex) : null);
1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mPremiumShortCodePattern = (premiumShortCodeRegex != null ?
1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(premiumShortCodeRegex) : null);
1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mFreeShortCodePattern = (freeShortCodeRegex != null ?
1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(freeShortCodeRegex) : null);
1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mStandardShortCodePattern = (standardShortCodeRegex != null ?
2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(standardShortCodeRegex) : null);
2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int getNumberCategory(String phoneNumber) {
2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mFreeShortCodePattern != null && mFreeShortCodePattern.matcher(phoneNumber)
2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_FREE_SHORT_CODE;
2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mStandardShortCodePattern != null && mStandardShortCodePattern.matcher(phoneNumber)
2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_STANDARD_SHORT_CODE;
2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mPremiumShortCodePattern != null && mPremiumShortCodePattern.matcher(phoneNumber)
2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_PREMIUM_SHORT_CODE;
2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mShortCodePattern != null && mShortCodePattern.matcher(phoneNumber).matches()) {
2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return CATEGORY_NOT_SHORT_CODE;
2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
224a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt     * Observe the secure setting for enable flag
2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static class SettingsObserver extends ContentObserver {
227a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        private final Context mContext;
228a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        private final AtomicBoolean mEnabled;
2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
230a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        SettingsObserver(Handler handler, Context context, AtomicBoolean enabled) {
2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            super(handler);
232a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            mContext = context;
233a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            mEnabled = enabled;
234a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            onChange(false);
2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public void onChange(boolean selfChange) {
239a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            mEnabled.set(Settings.Global.getInt(mContext.getContentResolver(),
240a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                    Settings.Global.SMS_SHORT_CODE_CONFIRMATION, 1) != 0);
2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
244a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private static class SettingsObserverHandler extends Handler {
245a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        SettingsObserverHandler(Context context, AtomicBoolean enabled) {
246a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            ContentResolver resolver = context.getContentResolver();
247a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            ContentObserver globalObserver = new SettingsObserver(this, context, enabled);
2484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            resolver.registerContentObserver(Settings.Global.getUriFor(
2494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    Settings.Global.SMS_SHORT_CODE_CONFIRMATION), false, globalObserver);
2504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Create SMS usage monitor.
2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param context the context to use to load resources and get TelephonyManager service
2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public SmsUsageMonitor(Context context) {
2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mContext = context;
2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ContentResolver resolver = context.getContentResolver();
2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        mMaxAllowed = Settings.Global.getInt(resolver,
2624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                DEFAULT_SMS_MAX_COUNT);
2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        mCheckPeriod = Settings.Global.getInt(resolver,
2664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                DEFAULT_SMS_CHECK_PERIOD);
2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
269a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        mSettingsObserverHandler = new SettingsObserverHandler(mContext, mCheckEnabled);
2704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
2714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        loadPremiumSmsPolicyDb();
2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Return a pattern matcher object for the specified country.
2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param country the country to search for
2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
279a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt    private ShortCodePatternMatcher getPatternMatcherFromFile(String country) {
280a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        FileReader patternReader = null;
281a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt        XmlPullParser parser = null;
2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
283a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            patternReader = new FileReader(mPatternFile);
284a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            parser = Xml.newPullParser();
285a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            parser.setInput(patternReader);
28681ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            return getPatternMatcherFromXmlParser(parser, country);
28781ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        } catch (FileNotFoundException e) {
28899c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville            Rlog.e(TAG, "Short Code Pattern File not found");
28981ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        } catch (XmlPullParserException e) {
29099c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville            Rlog.e(TAG, "XML parser exception reading short code pattern file", e);
29181ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        } finally {
29281ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            mPatternFileLastModified = mPatternFile.lastModified();
29381ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            if (patternReader != null) {
29481ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                try {
29581ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                    patternReader.close();
29681ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                } catch (IOException e) {}
29781ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            }
29881ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        }
29981ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        return null;
30081ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt    }
30181ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt
30281ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt    private ShortCodePatternMatcher getPatternMatcherFromResource(String country) {
30381ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        int id = com.android.internal.R.xml.sms_short_codes;
30481ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        XmlResourceParser parser = null;
30581ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        try {
30681ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            parser = mContext.getResources().getXml(id);
30781ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            return getPatternMatcherFromXmlParser(parser, country);
30881ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        } finally {
30981ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            if (parser != null) parser.close();
31081ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        }
31181ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt    }
31281ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt
31381ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt    private ShortCodePatternMatcher getPatternMatcherFromXmlParser(XmlPullParser parser,
31481ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt            String country) {
31581ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt        try {
316a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            XmlUtils.beginDocument(parser, TAG_SHORTCODES);
317a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
318a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            while (true) {
319a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                XmlUtils.nextElement(parser);
320a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                String element = parser.getName();
32110270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                if (element == null) {
32299c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Parsing pattern data found null");
32310270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                    break;
32410270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                }
325a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt
326a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                if (element.equals(TAG_SHORTCODE)) {
327a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                    String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
32899c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    if (VDBG) Rlog.d(TAG, "Found country " + currentCountry);
329a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                    if (country.equals(currentCountry)) {
330a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
331a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
332a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        String free = parser.getAttributeValue(null, ATTR_FREE);
333a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        String standard = parser.getAttributeValue(null, ATTR_STANDARD);
334a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        return new ShortCodePatternMatcher(pattern, premium, free, standard);
335a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                    }
336a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                } else {
33799c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Error: skipping unknown XML tag " + element);
338a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                }
339a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            }
3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (XmlPullParserException e) {
34199c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville            Rlog.e(TAG, "XML parser exception reading short code patterns", e);
3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (IOException e) {
34399c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville            Rlog.e(TAG, "I/O exception reading short code patterns", e);
3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
34599c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville        if (DBG) Rlog.d(TAG, "Country (" + country + ") not found");
3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return null;    // country not found
3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Clear the SMS application list for disposal. */
3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    void dispose() {
3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mSmsStamp.clear();
3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Check to see if an application is allowed to send new SMS messages, and confirm with
3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * user if the send limit was reached or if a non-system app is potentially sending to a
3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * premium SMS short code or number.
3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param appName the package name of the app requesting to send an SMS
3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param smsWaiting the number of new messages desired to send
3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if application is allowed to send the requested number
3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *  of new sms messages
3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean check(String appName, int smsWaiting) {
3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSmsStamp) {
3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            removeExpiredTimestamps();
3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ArrayList<Long> sentList = mSmsStamp.get(appName);
3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (sentList == null) {
3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                sentList = new ArrayList<Long>();
3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mSmsStamp.put(appName, sentList);
3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return isUnderLimit(sentList, smsWaiting);
3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Check if the destination is a possible premium short code.
3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * NOTE: the caller is expected to strip non-digits from the destination number with
3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method.
3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * This happens in {@link SMSDispatcher#sendRawPdu} so that we use the same phone number
3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * for testing and in the user confirmation dialog if the user needs to confirm the number.
3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * This makes it difficult for malware to fool the user or the short code pattern matcher
3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * by using non-ASCII characters to make the number appear to be different from the real
3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * destination phone number.
3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param destAddress the destination address to test for possible short code
3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return {@link #CATEGORY_NOT_SHORT_CODE}, {@link #CATEGORY_FREE_SHORT_CODE},
3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *  {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}.
3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int checkDestination(String destAddress, String countryIso) {
3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSettingsObserverHandler) {
3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            // always allow emergency numbers
3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
39699c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                if (DBG) Rlog.d(TAG, "isEmergencyNumber");
3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_NOT_SHORT_CODE;
3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
3994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            // always allow if the feature is disabled
400a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt            if (!mCheckEnabled.get()) {
40199c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                if (DBG) Rlog.e(TAG, "check disabled");
4024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return CATEGORY_NOT_SHORT_CODE;
4034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (countryIso != null) {
406a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                if (mCurrentCountry == null || !countryIso.equals(mCurrentCountry) ||
407a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt                        mPatternFile.lastModified() != mPatternFileLastModified) {
40881ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                    if (mPatternFile.exists()) {
40999c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                        if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from file");
41010270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                        mCurrentPatternMatcher = getPatternMatcherFromFile(countryIso);
41181ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                    } else {
41299c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                        if (DBG) Rlog.d(TAG, "Loading SMS Short Code patterns from resource");
41310270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                        mCurrentPatternMatcher = getPatternMatcherFromResource(countryIso);
41481ddb888952763b5eb7a4a38fb293fc86b897968Robert Greenwalt                    }
4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mCurrentCountry = countryIso;
4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
41910270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt            if (mCurrentPatternMatcher != null) {
42010270d2586e2d940dea23b19f6ad733f702609beRobert Greenwalt                return mCurrentPatternMatcher.getNumberCategory(destAddress);
4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // Generic rule: numbers of 5 digits or less are considered potential short codes
42399c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                Rlog.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (destAddress.length() <= 5) {
4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return CATEGORY_NOT_SHORT_CODE;
4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
4344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Load the premium SMS policy from an XML file.
4354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Based on code from NotificationManagerService.
4364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
4374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private void loadPremiumSmsPolicyDb() {
4384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
4394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (mPolicyFile == null) {
4404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                File dir = new File(SMS_POLICY_FILE_DIRECTORY);
4414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPolicyFile = new AtomicFile(new File(dir, SMS_POLICY_FILE_NAME));
4424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPremiumSmsPolicy.clear();
4444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                FileInputStream infile = null;
4464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                try {
4474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    infile = mPolicyFile.openRead();
4484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    final XmlPullParser parser = Xml.newPullParser();
449bfda9080cab9a819fc48bdb97ef70911016a0f21Wojciech Staszkiewicz                    parser.setInput(infile, StandardCharsets.UTF_8.name());
4504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    XmlUtils.beginDocument(parser, TAG_SMS_POLICY_BODY);
4524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    while (true) {
4544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        XmlUtils.nextElement(parser);
4554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        String element = parser.getName();
4574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        if (element == null) break;
4584658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        if (element.equals(TAG_PACKAGE)) {
4604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
4614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            String policy = parser.getAttributeValue(null, ATTR_PACKAGE_SMS_POLICY);
4624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            if (packageName == null) {
46399c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                                Rlog.e(TAG, "Error: missing package name attribute");
4644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } else if (policy == null) {
46599c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                                Rlog.e(TAG, "Error: missing package policy attribute");
4664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } else try {
4674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                                mPremiumSmsPolicy.put(packageName, Integer.parseInt(policy));
4684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } catch (NumberFormatException e) {
46999c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                                Rlog.e(TAG, "Error: non-numeric policy type " + policy);
4704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            }
4714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        } else {
47299c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                            Rlog.e(TAG, "Error: skipping unknown XML tag " + element);
4734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        }
4744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    }
4754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (FileNotFoundException e) {
4764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    // No data yet
4774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (IOException e) {
47899c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Unable to read premium SMS policy database", e);
4794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (NumberFormatException e) {
48099c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Unable to parse premium SMS policy database", e);
4814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (XmlPullParserException e) {
48299c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                    Rlog.e(TAG, "Unable to parse premium SMS policy database", e);
4834658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } finally {
4844658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    if (infile != null) {
4854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        try {
4864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            infile.close();
4874658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        } catch (IOException ignored) {
4884658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        }
4894658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    }
4904658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
4914658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
4924658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
4934658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
4944658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
4954658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
4964658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Persist the premium SMS policy to an XML file.
4974658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Based on code from NotificationManagerService.
4984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
4994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private void writePremiumSmsPolicyDb() {
5004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
5014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            FileOutputStream outfile = null;
5024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            try {
5034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                outfile = mPolicyFile.startWrite();
5044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                XmlSerializer out = new FastXmlSerializer();
506bfda9080cab9a819fc48bdb97ef70911016a0f21Wojciech Staszkiewicz                out.setOutput(outfile, StandardCharsets.UTF_8.name());
5074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.startDocument(null, true);
5094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5104658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.startTag(null, TAG_SMS_POLICY_BODY);
5114658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5124658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                for (Map.Entry<String, Integer> policy : mPremiumSmsPolicy.entrySet()) {
5134658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.startTag(null, TAG_PACKAGE);
5144658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.attribute(null, ATTR_PACKAGE_NAME, policy.getKey());
5154658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.attribute(null, ATTR_PACKAGE_SMS_POLICY, policy.getValue().toString());
5164658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.endTag(null, TAG_PACKAGE);
5174658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
5184658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5194658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.endTag(null, TAG_SMS_POLICY_BODY);
5204658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.endDocument();
5214658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5224658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPolicyFile.finishWrite(outfile);
5234658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            } catch (IOException e) {
52499c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville                Rlog.e(TAG, "Unable to write premium SMS policy database", e);
5254658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                if (outfile != null) {
5264658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    mPolicyFile.failWrite(outfile);
5274658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
5284658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5304658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5314658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5324658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
5334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Returns the premium SMS permission for the specified package. If the package has never
5344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * been seen before, the default {@link #PREMIUM_SMS_PERMISSION_ASK_USER}
5354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * will be returned.
5364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param packageName the name of the package to query permission
5374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @return one of {@link #PREMIUM_SMS_PERMISSION_UNKNOWN},
5384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ASK_USER},
5394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
5404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
5414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @throws SecurityException if the caller is not a system process
5424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
5434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public int getPremiumSmsPermission(String packageName) {
544f323afdb70b83e0c851ced2af7594b20412542c3Shishir Agrawal        checkCallerIsSystemOrPhoneOrSameApp(packageName);
5454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
5464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            Integer policy = mPremiumSmsPolicy.get(packageName);
5474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (policy == null) {
5484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return PREMIUM_SMS_PERMISSION_UNKNOWN;
5494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            } else {
5504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return policy;
5514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
5564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Sets the premium SMS permission for the specified package and save the value asynchronously
5574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * to persistent storage.
5584658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param packageName the name of the package to set permission
5594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param permission one of {@link #PREMIUM_SMS_PERMISSION_ASK_USER},
5604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
5614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
5624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @throws SecurityException if the caller is not a system process
5634658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
5644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public void setPremiumSmsPermission(String packageName, int permission) {
5654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        checkCallerIsSystemOrPhoneApp();
5664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        if (permission < PREMIUM_SMS_PERMISSION_ASK_USER
5674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                || permission > PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW) {
5684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            throw new IllegalArgumentException("invalid SMS permission type " + permission);
5694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
5714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            mPremiumSmsPolicy.put(packageName, permission);
5724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        // write policy file in the background
5744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        new Thread(new Runnable() {
5754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            @Override
5764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            public void run() {
5774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                writePremiumSmsPolicyDb();
5784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }).start();
5804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
582f323afdb70b83e0c851ced2af7594b20412542c3Shishir Agrawal    private static void checkCallerIsSystemOrPhoneOrSameApp(String pkg) {
5834658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int uid = Binder.getCallingUid();
584f323afdb70b83e0c851ced2af7594b20412542c3Shishir Agrawal        int appId = UserHandle.getAppId(uid);
585f323afdb70b83e0c851ced2af7594b20412542c3Shishir Agrawal        if (appId == Process.SYSTEM_UID || appId == Process.PHONE_UID || uid == 0) {
5864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            return;
5874658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5884658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        try {
5894658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
5904658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    pkg, 0, UserHandle.getCallingUserId());
5914658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (!UserHandle.isSameApp(ai.uid, uid)) {
5924658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                throw new SecurityException("Calling uid " + uid + " gave package"
5934658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        + pkg + " which is owned by uid " + ai.uid);
5944658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5954658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        } catch (RemoteException re) {
5964658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            throw new SecurityException("Unknown package " + pkg + "\n" + re);
5974658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static void checkCallerIsSystemOrPhoneApp() {
6014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int uid = Binder.getCallingUid();
6024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int appId = UserHandle.getAppId(uid);
6034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        if (appId == Process.SYSTEM_UID || appId == Process.PHONE_UID || uid == 0) {
6044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            return;
6054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        throw new SecurityException("Disallowed call for uid " + uid);
6074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
6084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Remove keys containing only old timestamps. This can happen if an SMS app is used
6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * to send messages and then uninstalled.
6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private void removeExpiredTimestamps() {
6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long beginCheckPeriod = System.currentTimeMillis() - mCheckPeriod;
6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSmsStamp) {
6170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Iterator<Map.Entry<String, ArrayList<Long>>> iter = mSmsStamp.entrySet().iterator();
6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            while (iter.hasNext()) {
6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                Map.Entry<String, ArrayList<Long>> entry = iter.next();
6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ArrayList<Long> oldList = entry.getValue();
6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (oldList.isEmpty() || oldList.get(oldList.size() - 1) < beginCheckPeriod) {
6220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    iter.remove();
6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
6240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
6250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) {
6290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Long ct = System.currentTimeMillis();
6300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long beginCheckPeriod = ct - mCheckPeriod;
6310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (VDBG) log("SMS send size=" + sent.size() + " time=" + ct);
6330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) {
6350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            sent.remove(0);
6360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((sent.size() + smsWaiting) <= mMaxAllowed) {
6390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i = 0; i < smsWaiting; i++ ) {
6400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                sent.add(ct);
6410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
6420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return true;
6430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
6440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return false;
6450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void log(String msg) {
64899c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Saville        Rlog.d(TAG, msg);
6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville}
651