diff options
author | Robert Greenwalt <robdroid@android.com> | 2010-04-14 22:37:12 -0700 |
---|---|---|
committer | Robert Greenwalt <robdroid@android.com> | 2010-04-18 10:21:20 -0700 |
commit | 7171ea8179e09270e4d6ab825a2320816eee39c5 (patch) | |
tree | ee44e8832f8308434b8a6d01b411d3b6c6a20a9e /services | |
parent | 975d86dfc6407314491a18bc715c8c95a8f8f0b1 (diff) | |
download | frameworks_base-7171ea8179e09270e4d6ab825a2320816eee39c5.zip frameworks_base-7171ea8179e09270e4d6ab825a2320816eee39c5.tar.gz frameworks_base-7171ea8179e09270e4d6ab825a2320816eee39c5.tar.bz2 |
Make ThrottleService more tamper resistant.
Use elapsed time not wall time for alarms so users can't play with the
system time to get around things.
Also using NTP servers to pull in an authoritative time - if we the build
is configured with an NTP server we will not advance to the next cycle
without it, but we also will not trottle - rather not throttle users
on an error.
Note that the poll alarm is just relative to the last poll time and real
time doesn't matter.
Defining the time-fetching API's as returning time in the system wallclock
range (correcting if we are using NTP time internally).
bug:2597530
Change-Id: I1c0ac0923314c2f8a04edd0b36c4845352eae99a
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/ThrottleService.java | 191 |
1 files changed, 138 insertions, 53 deletions
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java index 7bc222e..9838fd2 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -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,19 +617,59 @@ public class ThrottleService extends IThrottleManager.Stub { " bytes read and " + mRecorder.getPeriodTx(0) + " written"); } - Calendar end = calculatePeriodEnd(); - Calendar start = calculatePeriodStart(end); + long now = getBestTime(); - clearThrottleAndNotification(); + if (mNtpActive || (mNtpServer == null)) { + Calendar end = calculatePeriodEnd(now); + Calendar start = calculatePeriodStart(end); - mRecorder.setNextPeriod(start,end); + if (mRecorder.setNextPeriod(start, end)) { + clearThrottleAndNotification(); + } + + mAlarmManager.cancel(mPendingResetIntent); + 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; - mAlarmManager.cancel(mPendingResetIntent); - mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(), - mPendingResetIntent); + 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 // for logging and other purposes // @@ -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,11 +897,10 @@ public class ThrottleService extends IThrottleManager.Stub { builder.append(mPeriodStart.getTimeInMillis()); builder.append(":"); builder.append(mPeriodEnd.getTimeInMillis()); - builder.append(":"); BufferedWriter out = null; try { - out = new BufferedWriter(new FileWriter(getDataFile()),256); + out = new BufferedWriter(new FileWriter(getDataFile()), 256); out.write(builder.toString()); } catch (IOException e) { Slog.e(TAG, "Error writing data file"); @@ -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); |