SmsUsageMonitor.java revision ee7b6094d4f3d4c641e54246aec8f61349d6e9d0
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;
490825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.IOException;
500825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.io.StringReader;
510825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.ArrayList;
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";
670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final boolean VDBG = false;
680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Default checking period for SMS sent without user permission. */
700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final int DEFAULT_SMS_CHECK_PERIOD = 1800000;    // 30 minutes
710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Default number of SMS sent in checking period without user permission. */
730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final int DEFAULT_SMS_MAX_COUNT = 30;
740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for regular phone numbers. */
760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_NOT_SHORT_CODE = 0;
770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for free (no cost) short codes. */
790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_FREE_SHORT_CODE = 1;
800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for standard rate (non-premium) short codes. */
820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_STANDARD_SHORT_CODE = 2;
830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for possible premium short codes. */
850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Return value from {@link #checkDestination} for premium short codes. */
880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
90ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    /** @hide */
91ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    public static int mergeShortCodeCategories(int type1, int type2) {
92ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt        if (type1 > type2) return type1;
93ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt        return type2;
94ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt    }
95ee7b6094d4f3d4c641e54246aec8f61349d6e9d0Robert Greenwalt
964658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission for a new package (ask user when first premium SMS sent). */
974658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_UNKNOWN = 0;
984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Default premium SMS permission (ask user for each premium SMS sent). */
1004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_ASK_USER = 1;
1014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission when the owner has denied the app from sending premium SMS. */
1034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_NEVER_ALLOW = 2;
1044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Premium SMS permission when the owner has allowed the app to send premium SMS. */
1064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public static final int PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW = 3;
1074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final int mCheckPeriod;
1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final int mMaxAllowed;
1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final HashMap<String, ArrayList<Long>> mSmsStamp =
1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            new HashMap<String, ArrayList<Long>>();
1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Context for retrieving regexes from XML resource. */
1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final Context mContext;
1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Country code for the cached short code pattern matcher. */
1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private String mCurrentCountry;
1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Cached short code pattern matcher for {@link #mCurrentCountry}. */
1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private ShortCodePatternMatcher mCurrentPatternMatcher;
1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Cached short code regex patterns from secure settings for {@link #mCurrentCountry}. */
1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private String mSettingsShortCodePatterns;
1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Handler for responding to content observer updates. */
1270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private final SettingsObserverHandler mSettingsObserverHandler;
1280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** SMS short code blocking feature enabled (can be disabled in Gservices). */
1304658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private boolean mEnableShortCodeConfirmation = true;
1314658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1324658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Directory for per-app SMS permission XML file. */
1334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String SMS_POLICY_FILE_DIRECTORY = "/data/radio";
1344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Per-app SMS permission XML filename. */
1364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String SMS_POLICY_FILE_NAME = "premium_sms_policy.xml";
1374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML tag for root element. */
1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String TAG_SHORTCODES = "shortcodes";
1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML tag for short code patterns for a specific country. */
1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String TAG_SHORTCODE = "shortcode";
1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the country code. */
1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_COUNTRY = "country";
1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the short code regex pattern. */
1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_PATTERN = "pattern";
1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the premium short code regex pattern. */
1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_PREMIUM = "premium";
1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the free short code regex pattern. */
1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_FREE = "free";
1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** XML attribute for the standard rate short code regex pattern. */
1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final String ATTR_STANDARD = "standard";
1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Stored copy of premium SMS package permissions. */
1604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private AtomicFile mPolicyFile;
1614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** Loaded copy of premium SMS package permissions. */
1634658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private final HashMap<String, Integer> mPremiumSmsPolicy = new HashMap<String, Integer>();
1644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML tag for root element of premium SMS permissions. */
1664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String TAG_SMS_POLICY_BODY = "premium-sms-policy";
1674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML tag for a package. */
1694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String TAG_PACKAGE = "package";
1704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML attribute for the package name. */
1724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String ATTR_PACKAGE_NAME = "name";
1734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /** XML attribute for the package's premium SMS permission (integer type). */
1754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static final String ATTR_PACKAGE_SMS_POLICY = "sms-policy";
1764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * SMS short code regex pattern matcher for a specific country.
1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final class ShortCodePatternMatcher {
1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mShortCodePattern;
1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mPremiumShortCodePattern;
1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mFreeShortCodePattern;
1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Pattern mStandardShortCodePattern;
1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ShortCodePatternMatcher(String shortCodeRegex, String premiumShortCodeRegex,
1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                String freeShortCodeRegex, String standardShortCodeRegex) {
1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mShortCodePattern = (shortCodeRegex != null ? Pattern.compile(shortCodeRegex) : null);
1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mPremiumShortCodePattern = (premiumShortCodeRegex != null ?
1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(premiumShortCodeRegex) : null);
1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mFreeShortCodePattern = (freeShortCodeRegex != null ?
1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(freeShortCodeRegex) : null);
1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mStandardShortCodePattern = (standardShortCodeRegex != null ?
1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    Pattern.compile(standardShortCodeRegex) : null);
1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int getNumberCategory(String phoneNumber) {
1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mFreeShortCodePattern != null && mFreeShortCodePattern.matcher(phoneNumber)
1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_FREE_SHORT_CODE;
2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mStandardShortCodePattern != null && mStandardShortCodePattern.matcher(phoneNumber)
2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_STANDARD_SHORT_CODE;
2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mPremiumShortCodePattern != null && mPremiumShortCodePattern.matcher(phoneNumber)
2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    .matches()) {
2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_PREMIUM_SHORT_CODE;
2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (mShortCodePattern != null && mShortCodePattern.matcher(phoneNumber).matches()) {
2110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return CATEGORY_NOT_SHORT_CODE;
2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Observe the secure setting for updated regex patterns.
2190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static class SettingsObserver extends ContentObserver {
2210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final int mWhat;
2220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private final Handler mHandler;
2230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        SettingsObserver(Handler handler, int what) {
2250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            super(handler);
2260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mHandler = handler;
2270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mWhat = what;
2280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
2310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public void onChange(boolean selfChange) {
2320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mHandler.obtainMessage(mWhat).sendToTarget();
2330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Handler to update regex patterns when secure setting for the current country is updated.
2380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private class SettingsObserverHandler extends Handler {
2400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /** Current content observer, or null. */
2410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        SettingsObserver mSettingsObserver;
2420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /** Current country code to watch for settings updates. */
2440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        private String mCountryIso;
2450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /** Request to start observing a secure setting. */
2470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        static final int OBSERVE_SETTING = 1;
2480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /** Handler event for updated secure settings. */
2504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        static final int GLOBAL_SETTINGS_CHANGED = 2;
2514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
2524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        SettingsObserverHandler() {
2534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            ContentResolver resolver = mContext.getContentResolver();
2544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            ContentObserver globalObserver = new SettingsObserver(this, GLOBAL_SETTINGS_CHANGED);
2554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            resolver.registerContentObserver(Settings.Global.getUriFor(
2564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    Settings.Global.SMS_SHORT_CODE_CONFIRMATION), false, globalObserver);
2574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /** Send a message to this handler requesting to observe the setting for a new country. */
2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        void observeSettingForCountry(String countryIso) {
2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            obtainMessage(OBSERVE_SETTING, countryIso).sendToTarget();
2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public void handleMessage(Message msg) {
2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            switch (msg.what) {
2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                case OBSERVE_SETTING:
2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    if (msg.obj != null && msg.obj instanceof String) {
2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        mCountryIso = (String) msg.obj;
2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        String settingName = getSettingNameForCountry(mCountryIso);
2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        ContentResolver resolver = mContext.getContentResolver();
2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (mSettingsObserver != null) {
2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            if (VDBG) log("Unregistering old content observer");
2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            resolver.unregisterContentObserver(mSettingsObserver);
2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
2770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        mSettingsObserver = new SettingsObserver(this, GLOBAL_SETTINGS_CHANGED);
2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        resolver.registerContentObserver(
2804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                                Settings.Global.getUriFor(settingName), false, mSettingsObserver);
2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (VDBG) log("Registered content observer for " + settingName);
2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                case GLOBAL_SETTINGS_CHANGED:
2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    loadPatternsFromSettings(mCountryIso);
2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    break;
2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Create SMS usage monitor.
2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param context the context to use to load resources and get TelephonyManager service
2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public SmsUsageMonitor(Context context) {
2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mContext = context;
2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ContentResolver resolver = context.getContentResolver();
2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        mMaxAllowed = Settings.Global.getInt(resolver,
3014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                DEFAULT_SMS_MAX_COUNT);
3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        mCheckPeriod = Settings.Global.getInt(resolver,
3054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                Settings.Global.SMS_OUTGOING_CHECK_INTERVAL_MS,
3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                DEFAULT_SMS_CHECK_PERIOD);
3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mSettingsObserverHandler = new SettingsObserverHandler();
3094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
3104658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        loadPremiumSmsPolicyDb();
3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Return a pattern matcher object for the specified country.
3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param country the country to search for
3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private ShortCodePatternMatcher getPatternMatcher(String country) {
3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int id = com.android.internal.R.xml.sms_short_codes;
3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        XmlResourceParser parser = mContext.getResources().getXml(id);
3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return getPatternMatcher(country, parser);
3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (XmlPullParserException e) {
3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(TAG, "XML parser exception reading short code pattern resource", e);
3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (IOException e) {
3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(TAG, "I/O exception reading short code pattern resource", e);
3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } finally {
3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            parser.close();
3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return null;    // country not found
3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Return a pattern matcher object for the specified country from a secure settings string.
3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static ShortCodePatternMatcher getPatternMatcher(String country, String settingsPattern) {
3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // embed pattern tag into an XML document.
3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        String document = "<shortcodes>" + settingsPattern + "</shortcodes>";
3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (VDBG) log("loading updated patterns from: " + document);
3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        try {
3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            XmlPullParser parser = factory.newPullParser();
3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            parser.setInput(new StringReader(document));
3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return getPatternMatcher(country, parser);
3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (XmlPullParserException e) {
3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(TAG, "XML parser exception reading short code pattern from settings", e);
3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } catch (IOException e) {
3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Log.e(TAG, "I/O exception reading short code pattern from settings", e);
3520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return null;    // country not found
3540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Return a pattern matcher object for the specified country and pattern XML parser.
3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param country the country to search for
3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static ShortCodePatternMatcher getPatternMatcher(String country, XmlPullParser parser)
3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            throws XmlPullParserException, IOException
3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    {
3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        XmlUtils.beginDocument(parser, TAG_SHORTCODES);
3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        while (true) {
3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            XmlUtils.nextElement(parser);
3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            String element = parser.getName();
3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (element == null) break;
3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (element.equals(TAG_SHORTCODE)) {
3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (country.equals(currentCountry)) {
3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    String free = parser.getAttributeValue(null, ATTR_FREE);
3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    String standard = parser.getAttributeValue(null, ATTR_STANDARD);
3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return new ShortCodePatternMatcher(pattern, premium, free, standard);
3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                Log.e(TAG, "Error: skipping unknown XML tag " + element);
3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return null;    // country not found
3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /** Clear the SMS application list for disposal. */
3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    void dispose() {
3900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mSmsStamp.clear();
3910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
3920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
3930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
3940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Check to see if an application is allowed to send new SMS messages, and confirm with
3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * user if the send limit was reached or if a non-system app is potentially sending to a
3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * premium SMS short code or number.
3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param appName the package name of the app requesting to send an SMS
3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param smsWaiting the number of new messages desired to send
4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if application is allowed to send the requested number
4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *  of new sms messages
4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean check(String appName, int smsWaiting) {
4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSmsStamp) {
4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            removeExpiredTimestamps();
4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ArrayList<Long> sentList = mSmsStamp.get(appName);
4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (sentList == null) {
4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                sentList = new ArrayList<Long>();
4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mSmsStamp.put(appName, sentList);
4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return isUnderLimit(sentList, smsWaiting);
4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
4180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Check if the destination is a possible premium short code.
4190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * NOTE: the caller is expected to strip non-digits from the destination number with
4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * {@link PhoneNumberUtils#extractNetworkPortion} before calling this method.
4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * This happens in {@link SMSDispatcher#sendRawPdu} so that we use the same phone number
4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * for testing and in the user confirmation dialog if the user needs to confirm the number.
4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * This makes it difficult for malware to fool the user or the short code pattern matcher
4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * by using non-ASCII characters to make the number appear to be different from the real
4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * destination phone number.
4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param destAddress the destination address to test for possible short code
4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return {@link #CATEGORY_NOT_SHORT_CODE}, {@link #CATEGORY_FREE_SHORT_CODE},
4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *  {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}.
4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int checkDestination(String destAddress, String countryIso) {
4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSettingsObserverHandler) {
4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            // always allow emergency numbers
4340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
4350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return CATEGORY_NOT_SHORT_CODE;
4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            // always allow if the feature is disabled
4384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (!mEnableShortCodeConfirmation) {
4394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return CATEGORY_NOT_SHORT_CODE;
4404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            ShortCodePatternMatcher patternMatcher = null;
4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (countryIso != null) {
4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // query secure settings and initialize content observer for updated regex patterns
4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (mCurrentCountry == null || !countryIso.equals(mCurrentCountry)) {
4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    loadPatternsFromSettings(countryIso);
4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mSettingsObserverHandler.observeSettingForCountry(countryIso);
4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (countryIso.equals(mCurrentCountry)) {
4520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    patternMatcher = mCurrentPatternMatcher;
4530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
4540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    patternMatcher = getPatternMatcher(countryIso);
4550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mCurrentCountry = countryIso;
4560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mCurrentPatternMatcher = patternMatcher;    // may be null if not found
4570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (patternMatcher != null) {
4610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return patternMatcher.getNumberCategory(destAddress);
4620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // Generic rule: numbers of 5 digits or less are considered potential short codes
4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (destAddress.length() <= 5) {
4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
4680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return CATEGORY_NOT_SHORT_CODE;
4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static String getSettingNameForCountry(String countryIso) {
4754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        return Settings.Global.SMS_SHORT_CODES_PREFIX + countryIso;
4760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Load regex patterns from secure settings if present.
4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param countryIso the country to search for
4810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    void loadPatternsFromSettings(String countryIso) {
4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSettingsObserverHandler) {
4840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (VDBG) log("loadPatternsFromSettings(" + countryIso + ") called");
4854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            mEnableShortCodeConfirmation = Settings.Global.getInt(mContext.getContentResolver(),
4864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    Settings.Global.SMS_SHORT_CODE_CONFIRMATION, 1) != 0;
4874658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (!mEnableShortCodeConfirmation) {
4884658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                log("Short code blocking disabled: not loading patterns");
4894658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return;
4904658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
4914658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            String settingsPatterns = Settings.Global.getString(
4920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mContext.getContentResolver(), getSettingNameForCountry(countryIso));
4930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (settingsPatterns != null && !settingsPatterns.equals(
4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mSettingsShortCodePatterns)) {
4950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // settings pattern string has changed: update the pattern matcher
4960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mSettingsShortCodePatterns = settingsPatterns;
4970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ShortCodePatternMatcher matcher = getPatternMatcher(countryIso, settingsPatterns);
4980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (matcher != null) {
4990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mCurrentCountry = countryIso;
5000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    mCurrentPatternMatcher = matcher;
5010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
5020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else if (settingsPatterns == null && mSettingsShortCodePatterns != null) {
5030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // pattern string was removed: caller will load default patterns from XML resource
5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mCurrentCountry = null;
5050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mCurrentPatternMatcher = null;
5060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mSettingsShortCodePatterns = null;
5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
5090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
5124658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Load the premium SMS policy from an XML file.
5134658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Based on code from NotificationManagerService.
5144658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
5154658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private void loadPremiumSmsPolicyDb() {
5164658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
5174658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (mPolicyFile == null) {
5184658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                File dir = new File(SMS_POLICY_FILE_DIRECTORY);
5194658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPolicyFile = new AtomicFile(new File(dir, SMS_POLICY_FILE_NAME));
5204658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5214658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPremiumSmsPolicy.clear();
5224658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5234658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                FileInputStream infile = null;
5244658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                try {
5254658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    infile = mPolicyFile.openRead();
5264658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    final XmlPullParser parser = Xml.newPullParser();
5274658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    parser.setInput(infile, null);
5284658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    XmlUtils.beginDocument(parser, TAG_SMS_POLICY_BODY);
5304658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5314658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    while (true) {
5324658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        XmlUtils.nextElement(parser);
5334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        String element = parser.getName();
5354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        if (element == null) break;
5364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        if (element.equals(TAG_PACKAGE)) {
5384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
5394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            String policy = parser.getAttributeValue(null, ATTR_PACKAGE_SMS_POLICY);
5404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            if (packageName == null) {
5414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                                Log.e(TAG, "Error: missing package name attribute");
5424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } else if (policy == null) {
5434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                                Log.e(TAG, "Error: missing package policy attribute");
5444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } else try {
5454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                                mPremiumSmsPolicy.put(packageName, Integer.parseInt(policy));
5464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            } catch (NumberFormatException e) {
5474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                                Log.e(TAG, "Error: non-numeric policy type " + policy);
5484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            }
5494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        } else {
5504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            Log.e(TAG, "Error: skipping unknown XML tag " + element);
5514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        }
5524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    }
5534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (FileNotFoundException e) {
5544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    // No data yet
5554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (IOException e) {
5564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    Log.e(TAG, "Unable to read premium SMS policy database", e);
5574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (NumberFormatException e) {
5584658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    Log.e(TAG, "Unable to parse premium SMS policy database", e);
5594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } catch (XmlPullParserException e) {
5604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    Log.e(TAG, "Unable to parse premium SMS policy database", e);
5614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                } finally {
5624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    if (infile != null) {
5634658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        try {
5644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                            infile.close();
5654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        } catch (IOException ignored) {
5664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        }
5674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    }
5684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
5694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
5704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
5714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
5724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
5744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Persist the premium SMS policy to an XML file.
5754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Based on code from NotificationManagerService.
5764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
5774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private void writePremiumSmsPolicyDb() {
5784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
5794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            FileOutputStream outfile = null;
5804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            try {
5814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                outfile = mPolicyFile.startWrite();
5824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5834658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                XmlSerializer out = new FastXmlSerializer();
5844658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.setOutput(outfile, "utf-8");
5854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.startDocument(null, true);
5874658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5884658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.startTag(null, TAG_SMS_POLICY_BODY);
5894658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5904658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                for (Map.Entry<String, Integer> policy : mPremiumSmsPolicy.entrySet()) {
5914658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.startTag(null, TAG_PACKAGE);
5924658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.attribute(null, ATTR_PACKAGE_NAME, policy.getKey());
5934658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.attribute(null, ATTR_PACKAGE_SMS_POLICY, policy.getValue().toString());
5944658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    out.endTag(null, TAG_PACKAGE);
5954658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
5964658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
5974658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.endTag(null, TAG_SMS_POLICY_BODY);
5984658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                out.endDocument();
5994658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6004658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                mPolicyFile.finishWrite(outfile);
6014658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            } catch (IOException e) {
6024658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                Log.e(TAG, "Unable to write premium SMS policy database", e);
6034658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                if (outfile != null) {
6044658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    mPolicyFile.failWrite(outfile);
6054658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                }
6064658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
6074658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6084658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
6094658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6104658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
6114658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Returns the premium SMS permission for the specified package. If the package has never
6124658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * been seen before, the default {@link #PREMIUM_SMS_PERMISSION_ASK_USER}
6134658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * will be returned.
6144658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param packageName the name of the package to query permission
6154658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @return one of {@link #PREMIUM_SMS_PERMISSION_UNKNOWN},
6164658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ASK_USER},
6174658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
6184658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
6194658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @throws SecurityException if the caller is not a system process
6204658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
6214658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public int getPremiumSmsPermission(String packageName) {
6224658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        checkCallerIsSystemOrSameApp(packageName);
6234658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
6244658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            Integer policy = mPremiumSmsPolicy.get(packageName);
6254658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (policy == null) {
6264658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return PREMIUM_SMS_PERMISSION_UNKNOWN;
6274658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            } else {
6284658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                return policy;
6294658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
6304658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6314658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
6324658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6334658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
6344658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * Sets the premium SMS permission for the specified package and save the value asynchronously
6354658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * to persistent storage.
6364658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param packageName the name of the package to set permission
6374658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @param permission one of {@link #PREMIUM_SMS_PERMISSION_ASK_USER},
6384658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
6394658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     *  {@link #PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
6404658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     * @throws SecurityException if the caller is not a system process
6414658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby     */
6424658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    public void setPremiumSmsPermission(String packageName, int permission) {
6434658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        checkCallerIsSystemOrPhoneApp();
6444658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        if (permission < PREMIUM_SMS_PERMISSION_ASK_USER
6454658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                || permission > PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW) {
6464658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            throw new IllegalArgumentException("invalid SMS permission type " + permission);
6474658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6484658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        synchronized (mPremiumSmsPolicy) {
6494658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            mPremiumSmsPolicy.put(packageName, permission);
6504658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6514658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        // write policy file in the background
6524658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        new Thread(new Runnable() {
6534658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            @Override
6544658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            public void run() {
6554658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                writePremiumSmsPolicyDb();
6564658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
6574658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }).start();
6584658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
6594658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6604658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static void checkCallerIsSystemOrSameApp(String pkg) {
6614658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int uid = Binder.getCallingUid();
6624658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
6634658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            return;
6644658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6654658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        try {
6664658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
6674658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                    pkg, 0, UserHandle.getCallingUserId());
6684658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            if (!UserHandle.isSameApp(ai.uid, uid)) {
6694658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                throw new SecurityException("Calling uid " + uid + " gave package"
6704658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby                        + pkg + " which is owned by uid " + ai.uid);
6714658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            }
6724658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        } catch (RemoteException re) {
6734658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            throw new SecurityException("Unknown package " + pkg + "\n" + re);
6744658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6754658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
6764658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6774658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    private static void checkCallerIsSystemOrPhoneApp() {
6784658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int uid = Binder.getCallingUid();
6794658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        int appId = UserHandle.getAppId(uid);
6804658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        if (appId == Process.SYSTEM_UID || appId == Process.PHONE_UID || uid == 0) {
6814658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby            return;
6824658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        }
6834658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby        throw new SecurityException("Disallowed call for uid " + uid);
6844658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    }
6854658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby
6864658a1a8c23111d5cc89feb040ce547a7b65dfb0Jake Hamby    /**
6870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Remove keys containing only old timestamps. This can happen if an SMS app is used
6880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * to send messages and then uninstalled.
6890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private void removeExpiredTimestamps() {
6910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long beginCheckPeriod = System.currentTimeMillis() - mCheckPeriod;
6920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (mSmsStamp) {
6940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            Iterator<Map.Entry<String, ArrayList<Long>>> iter = mSmsStamp.entrySet().iterator();
6950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            while (iter.hasNext()) {
6960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                Map.Entry<String, ArrayList<Long>> entry = iter.next();
6970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ArrayList<Long> oldList = entry.getValue();
6980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (oldList.isEmpty() || oldList.get(oldList.size() - 1) < beginCheckPeriod) {
6990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    iter.remove();
7000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
7010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
7020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
7040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) {
7060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Long ct = System.currentTimeMillis();
7070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        long beginCheckPeriod = ct - mCheckPeriod;
7080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (VDBG) log("SMS send size=" + sent.size() + " time=" + ct);
7100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) {
7120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            sent.remove(0);
7130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((sent.size() + smsWaiting) <= mMaxAllowed) {
7160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i = 0; i < smsWaiting; i++ ) {
7170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                sent.add(ct);
7180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
7190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return true;
7200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
7210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return false;
7220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
7230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
7240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static void log(String msg) {
7250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Log.d(TAG, msg);
7260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
7270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville}
728