SmsUsageMonitor.java revision a7700794b7fbe667e87d72d3117ae3a4d2501313
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; 270825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Message; 284658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.os.Process; 294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.os.RemoteException; 304658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.os.UserHandle; 310825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.provider.Settings; 320825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.telephony.PhoneNumberUtils; 334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.util.AtomicFile; 340825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.util.Log; 354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport android.util.Xml; 360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport com.android.internal.util.FastXmlSerializer; 380825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.util.XmlUtils; 390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 400825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport org.xmlpull.v1.XmlPullParser; 410825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport org.xmlpull.v1.XmlPullParserException; 420825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport org.xmlpull.v1.XmlPullParserFactory; 434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport org.xmlpull.v1.XmlSerializer; 440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport java.io.File; 464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport java.io.FileInputStream; 474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport java.io.FileNotFoundException; 484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hambyimport java.io.FileOutputStream; 49a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwaltimport java.io.FileReader; 500825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.IOException; 510825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.StringReader; 520825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.ArrayList; 53a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwaltimport java.util.concurrent.atomic.AtomicBoolean; 540825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.HashMap; 550825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Iterator; 560825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Map; 570825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.regex.Pattern; 580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/** 600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Implement the per-application based SMS control, which limits the number of 610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * SMS/MMS messages an app can send in the checking period. 620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This code was formerly part of {@link SMSDispatcher}, and has been moved 640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * into a separate class to support instantiation of multiple SMSDispatchers on 650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * dual-mode devices that require support for both 3GPP and 3GPP2 format messages. 660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 670825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class SmsUsageMonitor { 680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String TAG = "SmsUsageMonitor"; 69a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private static final boolean DBG = false; 700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final boolean VDBG = false; 710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 72a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private static final String SHORT_CODE_PATH = "/data/misc/sms/codes"; 73a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt 740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Default checking period for SMS sent without user permission. */ 750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int DEFAULT_SMS_CHECK_PERIOD = 1800000; // 30 minutes 760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Default number of SMS sent in checking period without user permission. */ 780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final int DEFAULT_SMS_MAX_COUNT = 30; 790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for regular phone numbers. */ 810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_NOT_SHORT_CODE = 0; 820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for free (no cost) short codes. */ 840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_FREE_SHORT_CODE = 1; 850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for standard rate (non-premium) short codes. */ 870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_STANDARD_SHORT_CODE = 2; 880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for possible premium short codes. */ 900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3; 910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Return value from {@link #checkDestination} for premium short codes. */ 930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville static final int CATEGORY_PREMIUM_SHORT_CODE = 4; 940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 95ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt /** @hide */ 96ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt public static int mergeShortCodeCategories(int type1, int type2) { 97ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt if (type1 > type2) return type1; 98ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt return type2; 99ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt } 100ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt 1014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** Premium SMS permission for a new package (ask user when first premium SMS sent). */ 1024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby public static final int PREMIUM_SMS_PERMISSION_UNKNOWN = 0; 1034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** Default premium SMS permission (ask user for each premium SMS sent). */ 1054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby public static final int PREMIUM_SMS_PERMISSION_ASK_USER = 1; 1064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** Premium SMS permission when the owner has denied the app from sending premium SMS. */ 1084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby public static final int PREMIUM_SMS_PERMISSION_NEVER_ALLOW = 2; 1094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1104658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** Premium SMS permission when the owner has allowed the app to send premium SMS. */ 1114658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby public static final int PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW = 3; 1124658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final int mCheckPeriod; 1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final int mMaxAllowed; 1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final HashMap<String, ArrayList<Long>> mSmsStamp = 1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville new HashMap<String, ArrayList<Long>>(); 1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Context for retrieving regexes from XML resource. */ 1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Context mContext; 1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Country code for the cached short code pattern matcher. */ 1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String mCurrentCountry; 1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Cached short code pattern matcher for {@link #mCurrentCountry}. */ 1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private ShortCodePatternMatcher mCurrentPatternMatcher; 1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 128a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt /** Notice when the enabled setting changes - can be changed through gservices */ 129a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private final AtomicBoolean mCheckEnabled = new AtomicBoolean(true); 130a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt 1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Cached short code regex patterns from secure settings for {@link #mCurrentCountry}. */ 1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private String mSettingsShortCodePatterns; 1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Handler for responding to content observer updates. */ 1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final SettingsObserverHandler mSettingsObserverHandler; 1360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 137a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt /** File holding the patterns */ 138a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private final File mPatternFile = new File(SHORT_CODE_PATH); 139a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt 140a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt /** Last modified time for pattern file */ 141a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private long mPatternFileLastModified = 0; 1424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** Directory for per-app SMS permission XML file. */ 1444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private static final String SMS_POLICY_FILE_DIRECTORY = "/data/radio"; 1454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** Per-app SMS permission XML filename. */ 1474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private static final String SMS_POLICY_FILE_NAME = "premium_sms_policy.xml"; 1484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML tag for root element. */ 1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String TAG_SHORTCODES = "shortcodes"; 1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML tag for short code patterns for a specific country. */ 1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String TAG_SHORTCODE = "shortcode"; 1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the country code. */ 1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_COUNTRY = "country"; 1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the short code regex pattern. */ 1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_PATTERN = "pattern"; 1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the premium short code regex pattern. */ 1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_PREMIUM = "premium"; 1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the free short code regex pattern. */ 1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_FREE = "free"; 1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** XML attribute for the standard rate short code regex pattern. */ 1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final String ATTR_STANDARD = "standard"; 1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** Stored copy of premium SMS package permissions. */ 1714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private AtomicFile mPolicyFile; 1724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** Loaded copy of premium SMS package permissions. */ 1744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private final HashMap<String, Integer> mPremiumSmsPolicy = new HashMap<String, Integer>(); 1754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** XML tag for root element of premium SMS permissions. */ 1774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private static final String TAG_SMS_POLICY_BODY = "premium-sms-policy"; 1784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** XML tag for a package. */ 1804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private static final String TAG_PACKAGE = "package"; 1814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** XML attribute for the package name. */ 1834658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private static final String ATTR_PACKAGE_NAME = "name"; 1844658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** XML attribute for the package's premium SMS permission (integer type). */ 1864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private static final String ATTR_PACKAGE_SMS_POLICY = "sms-policy"; 1874658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * SMS short code regex pattern matcher for a specific country. 1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static final class ShortCodePatternMatcher { 1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Pattern mShortCodePattern; 1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Pattern mPremiumShortCodePattern; 1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Pattern mFreeShortCodePattern; 1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private final Pattern mStandardShortCodePattern; 1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ShortCodePatternMatcher(String shortCodeRegex, String premiumShortCodeRegex, 1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville String freeShortCodeRegex, String standardShortCodeRegex) { 1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mShortCodePattern = (shortCodeRegex != null ? Pattern.compile(shortCodeRegex) : null); 2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mPremiumShortCodePattern = (premiumShortCodeRegex != null ? 2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pattern.compile(premiumShortCodeRegex) : null); 2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mFreeShortCodePattern = (freeShortCodeRegex != null ? 2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pattern.compile(freeShortCodeRegex) : null); 2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mStandardShortCodePattern = (standardShortCodeRegex != null ? 2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Pattern.compile(standardShortCodeRegex) : null); 2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville int getNumberCategory(String phoneNumber) { 2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mFreeShortCodePattern != null && mFreeShortCodePattern.matcher(phoneNumber) 2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .matches()) { 2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_FREE_SHORT_CODE; 2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mStandardShortCodePattern != null && mStandardShortCodePattern.matcher(phoneNumber) 2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .matches()) { 2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_STANDARD_SHORT_CODE; 2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mPremiumShortCodePattern != null && mPremiumShortCodePattern.matcher(phoneNumber) 2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville .matches()) { 2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_PREMIUM_SHORT_CODE; 2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (mShortCodePattern != null && mShortCodePattern.matcher(phoneNumber).matches()) { 2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE; 2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_NOT_SHORT_CODE; 2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 229a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt * Observe the secure setting for enable flag 2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static class SettingsObserver extends ContentObserver { 232a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private final Context mContext; 233a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private final AtomicBoolean mEnabled; 2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 235a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt SettingsObserver(Handler handler, Context context, AtomicBoolean enabled) { 2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville super(handler); 237a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt mContext = context; 238a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt mEnabled = enabled; 239a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt onChange(false); 2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville @Override 2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public void onChange(boolean selfChange) { 244a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt mEnabled.set(Settings.Global.getInt(mContext.getContentResolver(), 245a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt Settings.Global.SMS_SHORT_CODE_CONFIRMATION, 1) != 0); 2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 249a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private static class SettingsObserverHandler extends Handler { 250a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt SettingsObserverHandler(Context context, AtomicBoolean enabled) { 251a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt ContentResolver resolver = context.getContentResolver(); 252a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt ContentObserver globalObserver = new SettingsObserver(this, context, enabled); 2534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby resolver.registerContentObserver(Settings.Global.getUriFor( 2544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Settings.Global.SMS_SHORT_CODE_CONFIRMATION), false, globalObserver); 2554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Create SMS usage monitor. 2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param context the context to use to load resources and get TelephonyManager service 2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public SmsUsageMonitor(Context context) { 2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mContext = context; 2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ContentResolver resolver = context.getContentResolver(); 2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby mMaxAllowed = Settings.Global.getInt(resolver, 2674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT, 2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville DEFAULT_SMS_MAX_COUNT); 2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby mCheckPeriod = Settings.Global.getInt(resolver, 2714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS, 2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville DEFAULT_SMS_CHECK_PERIOD); 2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 274a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt mSettingsObserverHandler = new SettingsObserverHandler(mContext, mCheckEnabled); 2754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 2764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby loadPremiumSmsPolicyDb(); 2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Return a pattern matcher object for the specified country. 2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param country the country to search for 2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found 2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 284a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt private ShortCodePatternMatcher getPatternMatcherFromFile(String country) { 285a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt FileReader patternReader = null; 286a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt XmlPullParser parser = null; 2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville try { 288a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt patternReader = new FileReader(mPatternFile); 289a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt parser = Xml.newPullParser(); 290a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt parser.setInput(patternReader); 291a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt XmlUtils.beginDocument(parser, TAG_SHORTCODES); 292a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt 293a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt while (true) { 294a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt XmlUtils.nextElement(parser); 295a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt 296a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt String element = parser.getName(); 297a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt if (element == null) break; 298a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt 299a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt if (element.equals(TAG_SHORTCODE)) { 300a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY); 301a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt if (country.equals(currentCountry)) { 302a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt String pattern = parser.getAttributeValue(null, ATTR_PATTERN); 303a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt String premium = parser.getAttributeValue(null, ATTR_PREMIUM); 304a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt String free = parser.getAttributeValue(null, ATTR_FREE); 305a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt String standard = parser.getAttributeValue(null, ATTR_STANDARD); 306a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt return new ShortCodePatternMatcher(pattern, premium, free, standard); 307a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt } 308a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt } else { 309a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt Log.e(TAG, "Error: skipping unknown XML tag " + element); 310a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt } 311a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt } 312a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt return null; // country not found 313a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt } catch (FileNotFoundException e) { 314a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt Log.e(TAG, "Short Code Pattern File not found"); 3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (XmlPullParserException e) { 3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "XML parser exception reading short code pattern resource", e); 3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } catch (IOException e) { 3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "I/O exception reading short code pattern resource", e); 3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } finally { 320a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt mPatternFileLastModified = mPatternFile.lastModified(); 321a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt if (patternReader != null) { 322a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt try { 323a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt patternReader.close(); 324a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt } catch (IOException e) {} 3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return null; // country not found 3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** Clear the SMS application list for disposal. */ 3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville void dispose() { 3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSmsStamp.clear(); 3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Check to see if an application is allowed to send new SMS messages, and confirm with 3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * user if the send limit was reached or if a non-system app is potentially sending to a 3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * premium SMS short code or number. 3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param appName the package name of the app requesting to send an SMS 3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param smsWaiting the number of new messages desired to send 3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return true if application is allowed to send the requested number 3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * of new sms messages 3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public boolean check(String appName, int smsWaiting) { 3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mSmsStamp) { 3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville removeExpiredTimestamps(); 3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ArrayList<Long> sentList = mSmsStamp.get(appName); 3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (sentList == null) { 3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sentList = new ArrayList<Long>(); 3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mSmsStamp.put(appName, sentList); 3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return isUnderLimit(sentList, smsWaiting); 3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Check if the destination is a possible premium short code. 3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * NOTE: the caller is expected to strip non-digits from the destination number with 3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method. 3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This happens in {@link SMSDispatcher#sendRawPdu} so that we use the same phone number 3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * for testing and in the user confirmation dialog if the user needs to confirm the number. 3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * This makes it difficult for malware to fool the user or the short code pattern matcher 3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * by using non-ASCII characters to make the number appear to be different from the real 3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * destination phone number. 3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * 3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @param destAddress the destination address to test for possible short code 3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * @return {@link #CATEGORY_NOT_SHORT_CODE}, {@link #CATEGORY_FREE_SHORT_CODE}, 3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}. 3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville public int checkDestination(String destAddress, String countryIso) { 3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mSettingsObserverHandler) { 3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // always allow emergency numbers 3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) { 3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_NOT_SHORT_CODE; 3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby // always allow if the feature is disabled 380a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt if (!mCheckEnabled.get()) { 3814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby return CATEGORY_NOT_SHORT_CODE; 3824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ShortCodePatternMatcher patternMatcher = null; 3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (countryIso != null) { 3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // query secure settings and initialize content observer for updated regex patterns 388a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt if (mCurrentCountry == null || !countryIso.equals(mCurrentCountry) || 389a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt mPatternFile.lastModified() != mPatternFileLastModified) { 390a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt if (DBG) Log.d(TAG, "Loading SMS Short Code patterns from file"); 391a7700794b7fbe667e87d72d3117ae3a4d2501313Robert Greenwalt patternMatcher = getPatternMatcherFromFile(countryIso); 3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCurrentCountry = countryIso; 3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville mCurrentPatternMatcher = patternMatcher; // may be null if not found 3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (patternMatcher != null) { 3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return patternMatcher.getNumberCategory(destAddress); 3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville // Generic rule: numbers of 5 digits or less are considered potential short codes 4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule"); 4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (destAddress.length() <= 5) { 4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE; 4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } else { 4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return CATEGORY_NOT_SHORT_CODE; 4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville /** 4124658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * Load the premium SMS policy from an XML file. 4134658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * Based on code from NotificationManagerService. 4144658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby */ 4154658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private void loadPremiumSmsPolicyDb() { 4164658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby synchronized (mPremiumSmsPolicy) { 4174658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (mPolicyFile == null) { 4184658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby File dir = new File(SMS_POLICY_FILE_DIRECTORY); 4194658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby mPolicyFile = new AtomicFile(new File(dir, SMS_POLICY_FILE_NAME)); 4204658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4214658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby mPremiumSmsPolicy.clear(); 4224658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4234658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby FileInputStream infile = null; 4244658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby try { 4254658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby infile = mPolicyFile.openRead(); 4264658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby final XmlPullParser parser = Xml.newPullParser(); 4274658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby parser.setInput(infile, null); 4284658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby XmlUtils.beginDocument(parser, TAG_SMS_POLICY_BODY); 4304658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4314658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby while (true) { 4324658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby XmlUtils.nextElement(parser); 4334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby String element = parser.getName(); 4354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (element == null) break; 4364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (element.equals(TAG_PACKAGE)) { 4384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); 4394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby String policy = parser.getAttributeValue(null, ATTR_PACKAGE_SMS_POLICY); 4404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (packageName == null) { 4414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Log.e(TAG, "Error: missing package name attribute"); 4424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } else if (policy == null) { 4434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Log.e(TAG, "Error: missing package policy attribute"); 4444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } else try { 4454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby mPremiumSmsPolicy.put(packageName, Integer.parseInt(policy)); 4464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } catch (NumberFormatException e) { 4474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Log.e(TAG, "Error: non-numeric policy type " + policy); 4484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } else { 4504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Log.e(TAG, "Error: skipping unknown XML tag " + element); 4514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } catch (FileNotFoundException e) { 4544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby // No data yet 4554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } catch (IOException e) { 4564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Log.e(TAG, "Unable to read premium SMS policy database", e); 4574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } catch (NumberFormatException e) { 4584658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Log.e(TAG, "Unable to parse premium SMS policy database", e); 4594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } catch (XmlPullParserException e) { 4604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Log.e(TAG, "Unable to parse premium SMS policy database", e); 4614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } finally { 4624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (infile != null) { 4634658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby try { 4644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby infile.close(); 4654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } catch (IOException ignored) { 4664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** 4744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * Persist the premium SMS policy to an XML file. 4754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * Based on code from NotificationManagerService. 4764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby */ 4774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private void writePremiumSmsPolicyDb() { 4784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby synchronized (mPremiumSmsPolicy) { 4794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby FileOutputStream outfile = null; 4804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby try { 4814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby outfile = mPolicyFile.startWrite(); 4824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4834658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby XmlSerializer out = new FastXmlSerializer(); 4844658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.setOutput(outfile, "utf-8"); 4854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.startDocument(null, true); 4874658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4884658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.startTag(null, TAG_SMS_POLICY_BODY); 4894658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4904658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby for (Map.Entry<String, Integer> policy : mPremiumSmsPolicy.entrySet()) { 4914658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.startTag(null, TAG_PACKAGE); 4924658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.attribute(null, ATTR_PACKAGE_NAME, policy.getKey()); 4934658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.attribute(null, ATTR_PACKAGE_SMS_POLICY, policy.getValue().toString()); 4944658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.endTag(null, TAG_PACKAGE); 4954658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 4964658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 4974658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.endTag(null, TAG_SMS_POLICY_BODY); 4984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby out.endDocument(); 4994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 5004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby mPolicyFile.finishWrite(outfile); 5014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } catch (IOException e) { 5024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Log.e(TAG, "Unable to write premium SMS policy database", e); 5034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (outfile != null) { 5044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby mPolicyFile.failWrite(outfile); 5054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 5104658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** 5114658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * Returns the premium SMS permission for the specified package. If the package has never 5124658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * been seen before, the default {@link #PREMIUM_SMS_PERMISSION_ASK_USER} 5134658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * will be returned. 5144658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * @param packageName the name of the package to query permission 5154658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * @return one of {@link #PREMIUM_SMS_PERMISSION_UNKNOWN}, 5164658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * {@link #PREMIUM_SMS_PERMISSION_ASK_USER}, 5174658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * {@link #PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 5184658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * {@link #PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 5194658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * @throws SecurityException if the caller is not a system process 5204658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby */ 5214658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby public int getPremiumSmsPermission(String packageName) { 5224658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby checkCallerIsSystemOrSameApp(packageName); 5234658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby synchronized (mPremiumSmsPolicy) { 5244658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby Integer policy = mPremiumSmsPolicy.get(packageName); 5254658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (policy == null) { 5264658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby return PREMIUM_SMS_PERMISSION_UNKNOWN; 5274658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } else { 5284658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby return policy; 5294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5304658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5314658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5324658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 5334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** 5344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * Sets the premium SMS permission for the specified package and save the value asynchronously 5354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * to persistent storage. 5364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * @param packageName the name of the package to set permission 5374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby * @param permission one of {@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 void setPremiumSmsPermission(String packageName, int permission) { 5434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby checkCallerIsSystemOrPhoneApp(); 5444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (permission < PREMIUM_SMS_PERMISSION_ASK_USER 5454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby || permission > PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW) { 5464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby throw new IllegalArgumentException("invalid SMS permission type " + permission); 5474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby synchronized (mPremiumSmsPolicy) { 5494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby mPremiumSmsPolicy.put(packageName, permission); 5504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby // write policy file in the background 5524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby new Thread(new Runnable() { 5534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby @Override 5544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby public void run() { 5554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby writePremiumSmsPolicyDb(); 5564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby }).start(); 5584658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 5604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private static void checkCallerIsSystemOrSameApp(String pkg) { 5614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby int uid = Binder.getCallingUid(); 5624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { 5634658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby return; 5644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby try { 5664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo( 5674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby pkg, 0, UserHandle.getCallingUserId()); 5684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (!UserHandle.isSameApp(ai.uid, uid)) { 5694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby throw new SecurityException("Calling uid " + uid + " gave package" 5704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby + pkg + " which is owned by uid " + ai.uid); 5714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } catch (RemoteException re) { 5734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby throw new SecurityException("Unknown package " + pkg + "\n" + re); 5744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 5774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby private static void checkCallerIsSystemOrPhoneApp() { 5784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby int uid = Binder.getCallingUid(); 5794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby int appId = UserHandle.getAppId(uid); 5804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby if (appId == Process.SYSTEM_UID || appId == Process.PHONE_UID || uid == 0) { 5814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby return; 5824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5834658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby throw new SecurityException("Disallowed call for uid " + uid); 5844658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby } 5854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby 5864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby /** 5870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Remove keys containing only old timestamps. This can happen if an SMS app is used 5880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * to send messages and then uninstalled. 5890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */ 5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private void removeExpiredTimestamps() { 5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville long beginCheckPeriod = System.currentTimeMillis() - mCheckPeriod; 5920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 5930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville synchronized (mSmsStamp) { 5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Iterator<Map.Entry<String, ArrayList<Long>>> iter = mSmsStamp.entrySet().iterator(); 5950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville while (iter.hasNext()) { 5960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Map.Entry<String, ArrayList<Long>> entry = iter.next(); 5970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville ArrayList<Long> oldList = entry.getValue(); 5980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (oldList.isEmpty() || oldList.get(oldList.size() - 1) < beginCheckPeriod) { 5990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville iter.remove(); 6000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) { 6060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Long ct = System.currentTimeMillis(); 6070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville long beginCheckPeriod = ct - mCheckPeriod; 6080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if (VDBG) log("SMS send size=" + sent.size() + " time=" + ct); 6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) { 6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sent.remove(0); 6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville if ((sent.size() + smsWaiting) <= mMaxAllowed) { 6160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville for (int i = 0; i < smsWaiting; i++ ) { 6170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville sent.add(ct); 6180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return true; 6200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville return false; 6220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville 6240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville private static void log(String msg) { 6250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville Log.d(TAG, msg); 6260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville } 6270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville} 628