Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8987f110 authored by Robert Greenwalt's avatar Robert Greenwalt Committed by Android Git Automerger
Browse files

am c2093636: am 7171ea81: Make ThrottleService more tamper resistant.

Merge commit 'c2093636' into kraken

* commit 'c2093636':
  Make ThrottleService more tamper resistant.
parents c2477a92 c2093636
Loading
Loading
Loading
Loading
+138 −53
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.IThrottleManager;
import android.net.SntpClient;
import android.net.ThrottleManager;
import android.os.Binder;
import android.os.Environment;
@@ -47,6 +48,7 @@ import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Slog;

import com.android.internal.R;
import com.android.internal.telephony.TelephonyProperties;

import java.io.BufferedWriter;
@@ -58,6 +60,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Properties;
import java.util.Random;

// TODO - add comments - reference the ThrottleManager for public API
@@ -73,7 +76,7 @@ public class ThrottleService extends IThrottleManager.Stub {
    private Context mContext;

    private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
    private static final int TESTING_RESET_PERIOD_SEC = 60 * 3;
    private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
    private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;

    private static final int PERIOD_COUNT = 6;
@@ -114,10 +117,16 @@ public class ThrottleService extends IThrottleManager.Stub {
    private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
    private static final int THROTTLE_INDEX_UNTHROTTLED   =  0;

    private static final String PROPERTIES_FILE = "/etc/gps.conf";
    private String mNtpServer;
    private boolean mNtpActive;

    public ThrottleService(Context context) {
        if (DBG) Slog.d(TAG, "Starting ThrottleService");
        mContext = context;

        mNtpActive = false;

        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
        Intent pollIntent = new Intent(ACTION_POLL, null);
        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
@@ -169,21 +178,33 @@ public class ThrottleService extends IThrottleManager.Stub {
    }

    // TODO - fetch for the iface
    // return time in the local, system wall time, correcting for the use of ntp
    public synchronized long getResetTime(String iface) {
        enforceAccessPermission();
        long resetTime = 0;
        if (mRecorder != null) {
            mRecorder.getPeriodEnd();
            long bestEnd = mRecorder.getPeriodEnd();
            long bestNow = getBestTime();
            long localNow = System.currentTimeMillis();

            resetTime = localNow + (bestEnd - bestNow);
        }
        return 0;
        return resetTime;
    }

    // TODO - fetch for the iface
    // return time in the loca, system wall tiem, correcting for the use of ntp
    public synchronized long getPeriodStartTime(String iface) {
        enforceAccessPermission();
        long startTime = 0;
        if (mRecorder != null) {
            mRecorder.getPeriodStart();
            long bestStart = mRecorder.getPeriodStart();
            long bestNow = getBestTime();
            long localNow = System.currentTimeMillis();

            startTime = localNow + (bestStart - bestNow);
        }
        return 0;
        return startTime;
    }
    //TODO - a better name?  getCliffByteCountThreshold?
    // TODO - fetch for the iface
@@ -257,6 +278,23 @@ public class ThrottleService extends IThrottleManager.Stub {

        mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
        mSettingsObserver.observe(mContext);

        FileInputStream stream = null;
        try {
            Properties properties = new Properties();
            File file = new File(PROPERTIES_FILE);
            stream = new FileInputStream(file);
            properties.load(stream);
            mNtpServer = properties.getProperty("NTP_SERVER", null);
        } catch (IOException e) {
            Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (Exception e) {}
            }
        }
    }


@@ -297,13 +335,6 @@ public class ThrottleService extends IThrottleManager.Stub {

            // get policy
            mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();

            // evaluate current conditions
            mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
        }

        private void onSimChange() {
            // TODO
        }

        // check for new policy info (threshold limit/value/etc)
@@ -311,15 +342,15 @@ public class ThrottleService extends IThrottleManager.Stub {
            boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");

            int pollingPeriod = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_datause_polling_period_sec);
                    R.integer.config_datause_polling_period_sec);
            mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
                    Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);

            // TODO - remove testing stuff?
            long defaultThreshold = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_datause_threshold_bytes);
                    R.integer.config_datause_threshold_bytes);
            int defaultValue = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_datause_throttle_kbitsps);
                    R.integer.config_datause_throttle_kbitsps);
            synchronized (ThrottleService.this) {
                mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(),
                        Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
@@ -340,8 +371,7 @@ public class ThrottleService extends IThrottleManager.Stub {
                Settings.Secure.putInt(mContext.getContentResolver(),
                Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
            }
            mIface = mContext.getResources().getString(
                    com.android.internal.R.string.config_datause_iface);
            mIface = mContext.getResources().getString(R.string.config_datause_iface);
            synchronized (ThrottleService.this) {
                if (mIface == null) {
                    mPolicyThreshold = 0;
@@ -349,7 +379,7 @@ public class ThrottleService extends IThrottleManager.Stub {
            }

            int defaultNotificationType = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_datause_notification_type);
                    R.integer.config_datause_notification_type);
            mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
                    Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);

@@ -369,6 +399,9 @@ public class ThrottleService extends IThrottleManager.Stub {
        private void onPollAlarm() {
            long now = SystemClock.elapsedRealtime();
            long next = now + mPolicyPollPeriodSec*1000;

            checkForAuthoritativeTime();

            long incRead = 0;
            long incWrite = 0;
            try {
@@ -407,8 +440,8 @@ public class ThrottleService extends IThrottleManager.Stub {
            Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, mRecorder.getPeriodStart());
            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, mRecorder.getPeriodEnd());
            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, ThrottleService.this.getPeriodStartTime(mIface));
            broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, ThrottleService.this.getResetTime(mIface));
            mContext.sendStickyBroadcast(broadcast);

            mAlarmManager.cancel(mPendingPollIntent);
@@ -417,8 +450,15 @@ public class ThrottleService extends IThrottleManager.Stub {

        private void checkThrottleAndPostNotification(long currentTotal) {
            // is throttling enabled?
            if (mPolicyThreshold == 0)
            if (mPolicyThreshold == 0) {
                return;
            }

            // have we spoken with an ntp server yet?
            // this is controversial, but we'd rather err towards not throttling
            if ((mNtpServer != null) && !mNtpActive) {
                return;
            }

            // check if we need to throttle
            if (currentTotal > mPolicyThreshold) {
@@ -434,12 +474,11 @@ public class ThrottleService extends IThrottleManager.Stub {
                        Slog.e(TAG, "error setting Throttle: " + e);
                    }

                    mNotificationManager.cancel(com.android.internal.R.drawable.
                            stat_sys_throttled);
                    mNotificationManager.cancel(R.drawable.stat_sys_throttled);

                    postNotification(com.android.internal.R.string.throttled_notification_title,
                            com.android.internal.R.string.throttled_notification_message,
                            com.android.internal.R.drawable.stat_sys_throttled,
                    postNotification(R.string.throttled_notification_title,
                            R.string.throttled_notification_message,
                            R.drawable.stat_sys_throttled,
                            Notification.FLAG_ONGOING_EVENT);

                    Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
@@ -469,19 +508,15 @@ public class ThrottleService extends IThrottleManager.Stub {
                    if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) {
                        if (mWarningNotificationSent == false) {
                            mWarningNotificationSent = true;
                            mNotificationManager.cancel(com.android.internal.R.drawable.
                                    stat_sys_throttled);
                            postNotification(com.android.internal.R.string.
                                    throttle_warning_notification_title,
                                    com.android.internal.R.string.
                                    throttle_warning_notification_message,
                                    com.android.internal.R.drawable.stat_sys_throttled,
                            mNotificationManager.cancel(R.drawable.stat_sys_throttled);
                            postNotification(R.string.throttle_warning_notification_title,
                                    R.string.throttle_warning_notification_message,
                                    R.drawable.stat_sys_throttled,
                                    0);
                        }
                    } else {
                        if (mWarningNotificationSent == true) {
                            mNotificationManager.cancel(com.android.internal.R.drawable.
                                    stat_sys_throttled);
                            mNotificationManager.cancel(R.drawable.stat_sys_throttled);
                            mWarningNotificationSent =false;
                        }
                    }
@@ -529,12 +564,13 @@ public class ThrottleService extends IThrottleManager.Stub {
                broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
                mContext.sendStickyBroadcast(broadcast);
            }
            mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttled);
            mNotificationManager.cancel(R.drawable.stat_sys_throttled);
            mWarningNotificationSent = false;
        }

        private Calendar calculatePeriodEnd() {
        private Calendar calculatePeriodEnd(long now) {
            Calendar end = GregorianCalendar.getInstance();
            end.setTimeInMillis(now);
            int day = end.get(Calendar.DAY_OF_MONTH);
            end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
            end.set(Calendar.HOUR_OF_DAY, 0);
@@ -553,6 +589,7 @@ public class ThrottleService extends IThrottleManager.Stub {
            // TODO - remove!
            if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
                end = GregorianCalendar.getInstance();
                end.setTimeInMillis(now);
                end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
            }
            return end;
@@ -580,17 +617,57 @@ public class ThrottleService extends IThrottleManager.Stub {
                        " bytes read and " + mRecorder.getPeriodTx(0) + " written");
            }

            Calendar end = calculatePeriodEnd();
            long now = getBestTime();

            if (mNtpActive || (mNtpServer == null)) {
                Calendar end = calculatePeriodEnd(now);
                Calendar start = calculatePeriodStart(end);

                if (mRecorder.setNextPeriod(start, end)) {
                    clearThrottleAndNotification();

            mRecorder.setNextPeriod(start,end);
                }

                mAlarmManager.cancel(mPendingResetIntent);
            mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(),
                long offset = end.getTimeInMillis() - now;
                // use Elapsed realtime so clock changes don't fool us.
                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime() + offset,
                        mPendingResetIntent);
            } else {
                if (DBG) Slog.d(TAG, "no authoritative time - not resetting period");
            }
        }
    }

    private void checkForAuthoritativeTime() {
        if (mNtpActive || (mNtpServer == null)) return;

        SntpClient client = new SntpClient();
        if (client.requestTime(mNtpServer, 10000)) {
            mNtpActive = true;
            if (DBG) Slog.d(TAG, "found Authoritative time - reseting alarm");
            mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
        }
    }

    private long getBestTime() {
        SntpClient client = new SntpClient();

        long time;
        if ((mNtpServer != null) && client.requestTime(mNtpServer, 10000)) {
            time = client.getNtpTime() ;
            if (!mNtpActive) {
                mNtpActive = true;
                if (DBG) Slog.d(TAG, "found Authoritative time - reseting alarm");
                mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
            }
            if (DBG) Slog.d(TAG, "using Authoritative time: " + time);
        } else {
            time = System.currentTimeMillis();
            if (DBG) Slog.d(TAG, "using User time: " + time);
            mNtpActive = false;
        }
        return time;
    }

    // records bytecount data for a given time and accumulates it into larger time windows
@@ -633,16 +710,16 @@ public class ThrottleService extends IThrottleManager.Stub {
            }
        }

        void setNextPeriod(Calendar start, Calendar end) {
        boolean setNextPeriod(Calendar start, Calendar end) {
            // TODO - how would we deal with a dual-IMSI device?
            checkForSubscriberId();
            boolean startNewPeriod = true;
            if (DBG) {
                Slog.d(TAG, "setting next period to " + start.getTimeInMillis() +
                        " --until-- " + end.getTimeInMillis());
            }
            // if we roll back in time to a previous period, toss out the current data
            // if we roll forward to the next period, advance to the next

            // if we rolled back in time, toss out
            // if we rolled foward, advance to the next
            if (end.before(mPeriodStart)) {
                if (DBG) {
                    Slog.d(TAG, " old start was " + mPeriodStart.getTimeInMillis() + ", wiping");
@@ -662,11 +739,13 @@ public class ThrottleService extends IThrottleManager.Stub {
                    mPeriodTxData[mCurrentPeriod] = 0;
                }
            } else {
                startNewPeriod = false;
                if (DBG) Slog.d(TAG, " we fit - ammending to last period");
            }
            setPeriodStart(start);
            setPeriodEnd(end);
            record();
            return startNewPeriod;
        }

        public long getPeriodEnd() {
@@ -714,6 +793,7 @@ public class ThrottleService extends IThrottleManager.Stub {
        // otherwise time moved forward.
        void addData(long bytesRead, long bytesWritten) {
            checkForSubscriberId();

            synchronized (mParent) {
                mPeriodRxData[mCurrentPeriod] += bytesRead;
                mPeriodTxData[mCurrentPeriod] += bytesWritten;
@@ -817,7 +897,6 @@ public class ThrottleService extends IThrottleManager.Stub {
            builder.append(mPeriodStart.getTimeInMillis());
            builder.append(":");
            builder.append(mPeriodEnd.getTimeInMillis());
            builder.append(":");

            BufferedWriter out = null;
            try {
@@ -854,7 +933,10 @@ public class ThrottleService extends IThrottleManager.Stub {
                }
            }
            String data = new String(buffer);
            if (data == null || data.length() == 0) return;
            if (data == null || data.length() == 0) {
                if (DBG) Slog.d(TAG, "data file empty");
                return;
            }
            synchronized (mParent) {
                String[] parsed = data.split(":");
                int parsedUsed = 0;
@@ -869,7 +951,10 @@ public class ThrottleService extends IThrottleManager.Stub {
                }

                mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
                if (parsed.length != 4 + (2 * mPeriodCount)) return;
                if (parsed.length != 5 + (2 * mPeriodCount)) {
                    Slog.e(TAG, "reading data file with bad length ("+parsed.length+" != "+(4 + (2*mPeriodCount))+") - ignoring");
                    return;
                }

                mPeriodRxData = new long[mPeriodCount];
                for(int i = 0; i < mPeriodCount; i++) {
@@ -922,7 +1007,7 @@ public class ThrottleService extends IThrottleManager.Stub {
                mPolicyThrottleValue + "kbps");
        pw.println("Current period is " +
                (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
                "and ends in " + (mRecorder.getPeriodEnd() - System.currentTimeMillis()) / 1000 +
                "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
                " seconds.");
        pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
        pw.println("Current Throttle Index is " + mThrottleIndex);