ThrottleService.java revision bdfce2ec05a3e9ca6acd6711de6133e06f2446e6
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server; 18 19import android.app.AlarmManager; 20import android.app.Notification; 21import android.app.NotificationManager; 22import android.app.PendingIntent; 23import android.content.BroadcastReceiver; 24import android.content.ContentResolver; 25import android.content.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.pm.PackageManager; 29import android.content.res.Resources; 30import android.database.ContentObserver; 31import android.net.INetworkManagementEventObserver; 32import android.net.IThrottleManager; 33import android.net.NetworkStats; 34import android.net.ThrottleManager; 35import android.os.Binder; 36import android.os.Environment; 37import android.os.Handler; 38import android.os.HandlerThread; 39import android.os.IBinder; 40import android.os.INetworkManagementService; 41import android.os.Looper; 42import android.os.Message; 43import android.os.RemoteException; 44import android.os.ServiceManager; 45import android.os.SystemClock; 46import android.os.SystemProperties; 47import android.os.UserHandle; 48import android.provider.Settings; 49import android.telephony.TelephonyManager; 50import android.text.TextUtils; 51import android.util.NtpTrustedTime; 52import android.util.Slog; 53import android.util.TrustedTime; 54 55import com.android.internal.R; 56import com.android.internal.telephony.TelephonyProperties; 57 58import java.io.BufferedWriter; 59import java.io.File; 60import java.io.FileDescriptor; 61import java.io.FileInputStream; 62import java.io.FileWriter; 63import java.io.IOException; 64import java.io.PrintWriter; 65import java.util.Calendar; 66import java.util.GregorianCalendar; 67import java.util.Random; 68import java.util.concurrent.atomic.AtomicInteger; 69import java.util.concurrent.atomic.AtomicLong; 70 71// TODO - add comments - reference the ThrottleManager for public API 72public class ThrottleService extends IThrottleManager.Stub { 73 74 private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing"; 75 76 private static final String TAG = "ThrottleService"; 77 private static final boolean DBG = true; 78 private static final boolean VDBG = false; 79 private Handler mHandler; 80 private HandlerThread mThread; 81 82 private Context mContext; 83 84 private static final int INITIAL_POLL_DELAY_SEC = 90; 85 private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1; 86 private static final int TESTING_RESET_PERIOD_SEC = 60 * 10; 87 private static final long TESTING_THRESHOLD = 1 * 1024 * 1024; 88 89 private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000; 90 91 private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE; 92 93 private int mPolicyPollPeriodSec; 94 private AtomicLong mPolicyThreshold; 95 private AtomicInteger mPolicyThrottleValue; 96 private int mPolicyResetDay; // 1-28 97 private int mPolicyNotificationsAllowedMask; 98 99 private long mLastRead; // read byte count from last poll 100 private long mLastWrite; // write byte count from last poll 101 102 private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL"; 103 private static int POLL_REQUEST = 0; 104 private PendingIntent mPendingPollIntent; 105 private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET"; 106 private static int RESET_REQUEST = 1; 107 private PendingIntent mPendingResetIntent; 108 109 private INetworkManagementService mNMService; 110 private AlarmManager mAlarmManager; 111 private NotificationManager mNotificationManager; 112 113 private DataRecorder mRecorder; 114 115 private String mIface; 116 117 private static final int NOTIFICATION_WARNING = 2; 118 119 private Notification mThrottlingNotification; 120 private boolean mWarningNotificationSent = false; 121 122 private InterfaceObserver mInterfaceObserver; 123 private SettingsObserver mSettingsObserver; 124 125 private AtomicInteger mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc 126 private static final int THROTTLE_INDEX_UNINITIALIZED = -1; 127 private static final int THROTTLE_INDEX_UNTHROTTLED = 0; 128 129 private Intent mPollStickyBroadcast; 130 131 private TrustedTime mTime; 132 133 private static INetworkManagementService getNetworkManagementService() { 134 final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 135 return INetworkManagementService.Stub.asInterface(b); 136 } 137 138 public ThrottleService(Context context) { 139 this(context, getNetworkManagementService(), NtpTrustedTime.getInstance(context), 140 context.getResources().getString(R.string.config_datause_iface)); 141 } 142 143 public ThrottleService(Context context, INetworkManagementService nmService, TrustedTime time, 144 String iface) { 145 if (VDBG) Slog.v(TAG, "Starting ThrottleService"); 146 mContext = context; 147 148 mPolicyThreshold = new AtomicLong(); 149 mPolicyThrottleValue = new AtomicInteger(); 150 mThrottleIndex = new AtomicInteger(); 151 152 mIface = iface; 153 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 154 Intent pollIntent = new Intent(ACTION_POLL, null); 155 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); 156 Intent resetIntent = new Intent(ACTION_RESET, null); 157 mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0); 158 159 mNMService = nmService; 160 mTime = time; 161 162 mNotificationManager = (NotificationManager)mContext.getSystemService( 163 Context.NOTIFICATION_SERVICE); 164 } 165 166 private static class InterfaceObserver extends INetworkManagementEventObserver.Stub { 167 private int mMsg; 168 private Handler mHandler; 169 private String mIface; 170 171 InterfaceObserver(Handler handler, int msg, String iface) { 172 super(); 173 mHandler = handler; 174 mMsg = msg; 175 mIface = iface; 176 } 177 178 public void interfaceStatusChanged(String iface, boolean up) { 179 if (up) { 180 if (TextUtils.equals(iface, mIface)) { 181 mHandler.obtainMessage(mMsg).sendToTarget(); 182 } 183 } 184 } 185 186 public void interfaceLinkStateChanged(String iface, boolean up) { 187 } 188 189 public void interfaceAdded(String iface) { 190 // TODO - an interface added in the UP state should also trigger a StatusChanged 191 // notification.. 192 if (TextUtils.equals(iface, mIface)) { 193 mHandler.obtainMessage(mMsg).sendToTarget(); 194 } 195 } 196 197 public void interfaceRemoved(String iface) {} 198 public void limitReached(String limitName, String iface) {} 199 public void interfaceClassDataActivityChanged(String label, boolean active) {} 200 } 201 202 203 private static class SettingsObserver extends ContentObserver { 204 private int mMsg; 205 private Handler mHandler; 206 SettingsObserver(Handler handler, int msg) { 207 super(handler); 208 mHandler = handler; 209 mMsg = msg; 210 } 211 212 void register(Context context) { 213 ContentResolver resolver = context.getContentResolver(); 214 resolver.registerContentObserver(Settings.Global.getUriFor( 215 Settings.Global.THROTTLE_POLLING_SEC), false, this); 216 resolver.registerContentObserver(Settings.Global.getUriFor( 217 Settings.Global.THROTTLE_THRESHOLD_BYTES), false, this); 218 resolver.registerContentObserver(Settings.Global.getUriFor( 219 Settings.Global.THROTTLE_VALUE_KBITSPS), false, this); 220 resolver.registerContentObserver(Settings.Global.getUriFor( 221 Settings.Global.THROTTLE_RESET_DAY), false, this); 222 resolver.registerContentObserver(Settings.Global.getUriFor( 223 Settings.Global.THROTTLE_NOTIFICATION_TYPE), false, this); 224 resolver.registerContentObserver(Settings.Global.getUriFor( 225 Settings.Global.THROTTLE_HELP_URI), false, this); 226 resolver.registerContentObserver(Settings.Global.getUriFor( 227 Settings.Global.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this); 228 } 229 230 void unregister(Context context) { 231 final ContentResolver resolver = context.getContentResolver(); 232 resolver.unregisterContentObserver(this); 233 } 234 235 @Override 236 public void onChange(boolean selfChange) { 237 mHandler.obtainMessage(mMsg).sendToTarget(); 238 } 239 } 240 241 private void enforceAccessPermission() { 242 mContext.enforceCallingOrSelfPermission( 243 android.Manifest.permission.ACCESS_NETWORK_STATE, 244 "ThrottleService"); 245 } 246 247 private long ntpToWallTime(long ntpTime) { 248 // get time quickly without worrying about trusted state 249 long bestNow = mTime.hasCache() ? mTime.currentTimeMillis() 250 : System.currentTimeMillis(); 251 long localNow = System.currentTimeMillis(); 252 return localNow + (ntpTime - bestNow); 253 } 254 255 // TODO - fetch for the iface 256 // return time in the local, system wall time, correcting for the use of ntp 257 258 public long getResetTime(String iface) { 259 enforceAccessPermission(); 260 long resetTime = 0; 261 if (mRecorder != null) { 262 resetTime = mRecorder.getPeriodEnd(); 263 } 264 resetTime = ntpToWallTime(resetTime); 265 return resetTime; 266 } 267 268 // TODO - fetch for the iface 269 // return time in the local, system wall time, correcting for the use of ntp 270 public long getPeriodStartTime(String iface) { 271 long startTime = 0; 272 enforceAccessPermission(); 273 if (mRecorder != null) { 274 startTime = mRecorder.getPeriodStart(); 275 } 276 startTime = ntpToWallTime(startTime); 277 return startTime; 278 } 279 //TODO - a better name? getCliffByteCountThreshold? 280 // TODO - fetch for the iface 281 public long getCliffThreshold(String iface, int cliff) { 282 enforceAccessPermission(); 283 if (cliff == 1) { 284 return mPolicyThreshold.get(); 285 } 286 return 0; 287 } 288 // TODO - a better name? getThrottleRate? 289 // TODO - fetch for the iface 290 public int getCliffLevel(String iface, int cliff) { 291 enforceAccessPermission(); 292 if (cliff == 1) { 293 return mPolicyThrottleValue.get(); 294 } 295 return 0; 296 } 297 298 public String getHelpUri() { 299 enforceAccessPermission(); 300 return Settings.Global.getString(mContext.getContentResolver(), 301 Settings.Global.THROTTLE_HELP_URI); 302 } 303 304 // TODO - fetch for the iface 305 public long getByteCount(String iface, int dir, int period, int ago) { 306 enforceAccessPermission(); 307 if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) { 308 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago); 309 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago); 310 } 311 return 0; 312 } 313 314 // TODO - a better name - getCurrentThrottleRate? 315 // TODO - fetch for the iface 316 public int getThrottle(String iface) { 317 enforceAccessPermission(); 318 if (mThrottleIndex.get() == 1) { 319 return mPolicyThrottleValue.get(); 320 } 321 return 0; 322 } 323 324 void systemReady() { 325 if (VDBG) Slog.v(TAG, "systemReady"); 326 mContext.registerReceiver( 327 new BroadcastReceiver() { 328 @Override 329 public void onReceive(Context context, Intent intent) { 330 dispatchPoll(); 331 } 332 }, new IntentFilter(ACTION_POLL)); 333 334 mContext.registerReceiver( 335 new BroadcastReceiver() { 336 @Override 337 public void onReceive(Context context, Intent intent) { 338 dispatchReset(); 339 } 340 }, new IntentFilter(ACTION_RESET)); 341 342 // use a new thread as we don't want to stall the system for file writes 343 mThread = new HandlerThread(TAG); 344 mThread.start(); 345 mHandler = new MyHandler(mThread.getLooper()); 346 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget(); 347 348 mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface); 349 try { 350 mNMService.registerObserver(mInterfaceObserver); 351 } catch (RemoteException e) { 352 Slog.e(TAG, "Could not register InterfaceObserver " + e); 353 } 354 355 mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED); 356 mSettingsObserver.register(mContext); 357 } 358 359 void shutdown() { 360 // TODO: eventually connect with ShutdownThread to persist stats during 361 // graceful shutdown. 362 363 if (mThread != null) { 364 mThread.quit(); 365 } 366 367 if (mSettingsObserver != null) { 368 mSettingsObserver.unregister(mContext); 369 } 370 371 if (mPollStickyBroadcast != null) { 372 mContext.removeStickyBroadcastAsUser(mPollStickyBroadcast, UserHandle.ALL); 373 } 374 } 375 376 void dispatchPoll() { 377 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget(); 378 } 379 380 void dispatchReset() { 381 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget(); 382 } 383 384 private static final int EVENT_REBOOT_RECOVERY = 0; 385 private static final int EVENT_POLICY_CHANGED = 1; 386 private static final int EVENT_POLL_ALARM = 2; 387 private static final int EVENT_RESET_ALARM = 3; 388 private static final int EVENT_IFACE_UP = 4; 389 private class MyHandler extends Handler { 390 public MyHandler(Looper l) { 391 super(l); 392 } 393 394 @Override 395 public void handleMessage(Message msg) { 396 switch (msg.what) { 397 case EVENT_REBOOT_RECOVERY: 398 onRebootRecovery(); 399 break; 400 case EVENT_POLICY_CHANGED: 401 onPolicyChanged(); 402 break; 403 case EVENT_POLL_ALARM: 404 onPollAlarm(); 405 break; 406 case EVENT_RESET_ALARM: 407 onResetAlarm(); 408 break; 409 case EVENT_IFACE_UP: 410 onIfaceUp(); 411 } 412 } 413 414 private void onRebootRecovery() { 415 if (VDBG) Slog.v(TAG, "onRebootRecovery"); 416 // check for sim change TODO 417 // reregister for notification of policy change 418 419 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED); 420 421 mRecorder = new DataRecorder(mContext, ThrottleService.this); 422 423 // get policy 424 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget(); 425 426 // if we poll now we won't have network connectivity or even imsi access 427 // queue up a poll to happen in a little while - after ntp and imsi are avail 428 // TODO - make this callback based (ie, listen for notificaitons) 429 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM), 430 INITIAL_POLL_DELAY_SEC * 1000); 431 } 432 433 // check for new policy info (threshold limit/value/etc) 434 private void onPolicyChanged() { 435 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true"); 436 437 int pollingPeriod = mContext.getResources().getInteger( 438 R.integer.config_datause_polling_period_sec); 439 mPolicyPollPeriodSec = Settings.Global.getInt(mContext.getContentResolver(), 440 Settings.Global.THROTTLE_POLLING_SEC, pollingPeriod); 441 442 // TODO - remove testing stuff? 443 long defaultThreshold = mContext.getResources().getInteger( 444 R.integer.config_datause_threshold_bytes); 445 int defaultValue = mContext.getResources().getInteger( 446 R.integer.config_datause_throttle_kbitsps); 447 long threshold = Settings.Global.getLong(mContext.getContentResolver(), 448 Settings.Global.THROTTLE_THRESHOLD_BYTES, defaultThreshold); 449 int value = Settings.Global.getInt(mContext.getContentResolver(), 450 Settings.Global.THROTTLE_VALUE_KBITSPS, defaultValue); 451 452 mPolicyThreshold.set(threshold); 453 mPolicyThrottleValue.set(value); 454 if (testing) { 455 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC; 456 mPolicyThreshold.set(TESTING_THRESHOLD); 457 } 458 459 mPolicyResetDay = Settings.Global.getInt(mContext.getContentResolver(), 460 Settings.Global.THROTTLE_RESET_DAY, -1); 461 if (mPolicyResetDay == -1 || 462 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) { 463 Random g = new Random(); 464 mPolicyResetDay = 1 + g.nextInt(28); // 1-28 465 Settings.Global.putInt(mContext.getContentResolver(), 466 Settings.Global.THROTTLE_RESET_DAY, mPolicyResetDay); 467 } 468 if (mIface == null) { 469 mPolicyThreshold.set(0); 470 } 471 472 int defaultNotificationType = mContext.getResources().getInteger( 473 R.integer.config_datause_notification_type); 474 mPolicyNotificationsAllowedMask = Settings.Global.getInt(mContext.getContentResolver(), 475 Settings.Global.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType); 476 477 final int maxNtpCacheAgeSec = Settings.Global.getInt(mContext.getContentResolver(), 478 Settings.Global.THROTTLE_MAX_NTP_CACHE_AGE_SEC, 479 (int) (MAX_NTP_CACHE_AGE / 1000)); 480 mMaxNtpCacheAge = maxNtpCacheAgeSec * 1000; 481 482 if (VDBG || (mPolicyThreshold.get() != 0)) { 483 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + 484 mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() + 485 ", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay + 486 ", noteType=" + mPolicyNotificationsAllowedMask + ", mMaxNtpCacheAge=" + 487 mMaxNtpCacheAge); 488 } 489 490 // force updates 491 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED); 492 493 onResetAlarm(); 494 495 onPollAlarm(); 496 497 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION); 498 mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL); 499 } 500 501 private void onPollAlarm() { 502 long now = SystemClock.elapsedRealtime(); 503 long next = now + mPolicyPollPeriodSec * 1000; 504 505 // when trusted cache outdated, try refreshing 506 if (mTime.getCacheAge() > mMaxNtpCacheAge) { 507 if (mTime.forceRefresh()) { 508 if (VDBG) Slog.d(TAG, "updated trusted time, reseting alarm"); 509 dispatchReset(); 510 } 511 } 512 513 long incRead = 0; 514 long incWrite = 0; 515 try { 516 final NetworkStats stats = mNMService.getNetworkStatsSummaryDev(); 517 final int index = stats.findIndex(mIface, NetworkStats.UID_ALL, 518 NetworkStats.SET_DEFAULT, NetworkStats.TAG_NONE); 519 520 if (index != -1) { 521 final NetworkStats.Entry entry = stats.getValues(index, null); 522 incRead = entry.rxBytes - mLastRead; 523 incWrite = entry.txBytes - mLastWrite; 524 } else { 525 // missing iface, assume stats are 0 526 Slog.w(TAG, "unable to find stats for iface " + mIface); 527 } 528 529 // handle iface resets - on some device the 3g iface comes and goes and gets 530 // totals reset to 0. Deal with it 531 if ((incRead < 0) || (incWrite < 0)) { 532 incRead += mLastRead; 533 incWrite += mLastWrite; 534 mLastRead = 0; 535 mLastWrite = 0; 536 } 537 } catch (IllegalStateException e) { 538 Slog.e(TAG, "problem during onPollAlarm: " + e); 539 } catch (RemoteException e) { 540 Slog.e(TAG, "problem during onPollAlarm: " + e); 541 } 542 543 // don't count this data if we're roaming. 544 boolean roaming = "true".equals( 545 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING)); 546 if (!roaming) { 547 mRecorder.addData(incRead, incWrite); 548 } 549 550 long periodRx = mRecorder.getPeriodRx(0); 551 long periodTx = mRecorder.getPeriodTx(0); 552 long total = periodRx + periodTx; 553 if (VDBG || (mPolicyThreshold.get() != 0)) { 554 Slog.d(TAG, "onPollAlarm - roaming =" + roaming + 555 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total); 556 } 557 mLastRead += incRead; 558 mLastWrite += incWrite; 559 560 checkThrottleAndPostNotification(total); 561 562 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION); 563 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx); 564 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx); 565 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface)); 566 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface)); 567 mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL); 568 mPollStickyBroadcast = broadcast; 569 570 mAlarmManager.cancel(mPendingPollIntent); 571 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent); 572 } 573 574 private void onIfaceUp() { 575 // if we were throttled before, be sure and set it again - the iface went down 576 // (and may have disappeared all together) and these settings were lost 577 if (mThrottleIndex.get() == 1) { 578 try { 579 mNMService.setInterfaceThrottle(mIface, -1, -1); 580 mNMService.setInterfaceThrottle(mIface, 581 mPolicyThrottleValue.get(), mPolicyThrottleValue.get()); 582 } catch (Exception e) { 583 Slog.e(TAG, "error setting Throttle: " + e); 584 } 585 } 586 } 587 588 private void checkThrottleAndPostNotification(long currentTotal) { 589 // is throttling enabled? 590 long threshold = mPolicyThreshold.get(); 591 if (threshold == 0) { 592 clearThrottleAndNotification(); 593 return; 594 } 595 596 // have we spoken with an ntp server yet? 597 // this is controversial, but we'd rather err towards not throttling 598 if (!mTime.hasCache()) { 599 Slog.w(TAG, "missing trusted time, skipping throttle check"); 600 return; 601 } 602 603 // check if we need to throttle 604 if (currentTotal > threshold) { 605 if (mThrottleIndex.get() != 1) { 606 mThrottleIndex.set(1); 607 if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!"); 608 try { 609 mNMService.setInterfaceThrottle(mIface, 610 mPolicyThrottleValue.get(), mPolicyThrottleValue.get()); 611 } catch (Exception e) { 612 Slog.e(TAG, "error setting Throttle: " + e); 613 } 614 615 mNotificationManager.cancel(R.drawable.stat_sys_throttled); 616 617 postNotification(R.string.throttled_notification_title, 618 R.string.throttled_notification_message, 619 R.drawable.stat_sys_throttled, 620 Notification.FLAG_ONGOING_EVENT); 621 622 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); 623 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, 624 mPolicyThrottleValue.get()); 625 mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL); 626 627 } // else already up! 628 } else { 629 clearThrottleAndNotification(); 630 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) { 631 // check if we should warn about throttle 632 // pretend we only have 1/2 the time remaining that we actually do 633 // if our burn rate in the period so far would have us exceed the limit 634 // in that 1/2 window, warn the user. 635 // this gets more generous in the early to middle period and converges back 636 // to the limit as we move toward the period end. 637 638 // adding another factor - it must be greater than the total cap/4 639 // else we may get false alarms very early in the period.. in the first 640 // tenth of a percent of the period if we used more than a tenth of a percent 641 // of the cap we'd get a warning and that's not desired. 642 long start = mRecorder.getPeriodStart(); 643 long end = mRecorder.getPeriodEnd(); 644 long periodLength = end - start; 645 long now = System.currentTimeMillis(); 646 long timeUsed = now - start; 647 long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength); 648 if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) { 649 if (mWarningNotificationSent == false) { 650 mWarningNotificationSent = true; 651 mNotificationManager.cancel(R.drawable.stat_sys_throttled); 652 postNotification(R.string.throttle_warning_notification_title, 653 R.string.throttle_warning_notification_message, 654 R.drawable.stat_sys_throttled, 655 0); 656 } 657 } else { 658 if (mWarningNotificationSent == true) { 659 mNotificationManager.cancel(R.drawable.stat_sys_throttled); 660 mWarningNotificationSent =false; 661 } 662 } 663 } 664 } 665 } 666 667 private void postNotification(int titleInt, int messageInt, int icon, int flags) { 668 Intent intent = new Intent(); 669 // TODO - fix up intent 670 intent.setClassName("com.android.phone", "com.android.phone.DataUsage"); 671 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 672 673 PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, 674 null, UserHandle.CURRENT); 675 676 Resources r = Resources.getSystem(); 677 CharSequence title = r.getText(titleInt); 678 CharSequence message = r.getText(messageInt); 679 if (mThrottlingNotification == null) { 680 mThrottlingNotification = new Notification(); 681 mThrottlingNotification.when = 0; 682 // TODO - fixup icon 683 mThrottlingNotification.icon = icon; 684 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND; 685 } 686 mThrottlingNotification.flags = flags; 687 mThrottlingNotification.tickerText = title; 688 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi); 689 690 mNotificationManager.notifyAsUser(null, mThrottlingNotification.icon, 691 mThrottlingNotification, UserHandle.ALL); 692 } 693 694 695 private void clearThrottleAndNotification() { 696 if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) { 697 mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED); 698 try { 699 mNMService.setInterfaceThrottle(mIface, -1, -1); 700 } catch (Exception e) { 701 Slog.e(TAG, "error clearing Throttle: " + e); 702 } 703 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); 704 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1); 705 mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL); 706 mNotificationManager.cancelAsUser(null, R.drawable.stat_sys_throttled, 707 UserHandle.ALL); 708 mWarningNotificationSent = false; 709 } 710 } 711 712 private Calendar calculatePeriodEnd(long now) { 713 Calendar end = GregorianCalendar.getInstance(); 714 end.setTimeInMillis(now); 715 int day = end.get(Calendar.DAY_OF_MONTH); 716 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay); 717 end.set(Calendar.HOUR_OF_DAY, 0); 718 end.set(Calendar.MINUTE, 0); 719 end.set(Calendar.SECOND, 0); 720 end.set(Calendar.MILLISECOND, 0); 721 if (day >= mPolicyResetDay) { 722 int month = end.get(Calendar.MONTH); 723 if (month == Calendar.DECEMBER) { 724 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1); 725 month = Calendar.JANUARY - 1; 726 } 727 end.set(Calendar.MONTH, month + 1); 728 } 729 730 // TODO - remove! 731 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { 732 end = GregorianCalendar.getInstance(); 733 end.setTimeInMillis(now); 734 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC); 735 } 736 return end; 737 } 738 private Calendar calculatePeriodStart(Calendar end) { 739 Calendar start = (Calendar)end.clone(); 740 int month = end.get(Calendar.MONTH); 741 if (end.get(Calendar.MONTH) == Calendar.JANUARY) { 742 month = Calendar.DECEMBER + 1; 743 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1); 744 } 745 start.set(Calendar.MONTH, month - 1); 746 747 // TODO - remove!! 748 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { 749 start = (Calendar)end.clone(); 750 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC); 751 } 752 return start; 753 } 754 755 private void onResetAlarm() { 756 if (VDBG || (mPolicyThreshold.get() != 0)) { 757 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) + 758 " bytes read and " + mRecorder.getPeriodTx(0) + " written"); 759 } 760 761 // when trusted cache outdated, try refreshing 762 if (mTime.getCacheAge() > mMaxNtpCacheAge) { 763 mTime.forceRefresh(); 764 } 765 766 // as long as we have a trusted time cache, we always reset alarms, 767 // even if the refresh above failed. 768 if (mTime.hasCache()) { 769 final long now = mTime.currentTimeMillis(); 770 Calendar end = calculatePeriodEnd(now); 771 Calendar start = calculatePeriodStart(end); 772 773 if (mRecorder.setNextPeriod(start, end)) { 774 onPollAlarm(); 775 } 776 777 mAlarmManager.cancel(mPendingResetIntent); 778 long offset = end.getTimeInMillis() - now; 779 // use Elapsed realtime so clock changes don't fool us. 780 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, 781 SystemClock.elapsedRealtime() + offset, 782 mPendingResetIntent); 783 } else { 784 if (VDBG) Slog.d(TAG, "no trusted time, not resetting period"); 785 } 786 } 787 } 788 789 // records bytecount data for a given time and accumulates it into larger time windows 790 // for logging and other purposes 791 // 792 // since time can be changed (user or network action) we will have to track the time of the 793 // last recording and deal with it. 794 private static class DataRecorder { 795 long[] mPeriodRxData; 796 long[] mPeriodTxData; 797 int mCurrentPeriod; 798 int mPeriodCount; 799 800 Calendar mPeriodStart; 801 Calendar mPeriodEnd; 802 803 ThrottleService mParent; 804 Context mContext; 805 String mImsi = null; 806 807 TelephonyManager mTelephonyManager; 808 809 DataRecorder(Context context, ThrottleService parent) { 810 mContext = context; 811 mParent = parent; 812 813 mTelephonyManager = (TelephonyManager)mContext.getSystemService( 814 Context.TELEPHONY_SERVICE); 815 816 synchronized (mParent) { 817 mPeriodCount = 6; 818 mPeriodRxData = new long[mPeriodCount]; 819 mPeriodTxData = new long[mPeriodCount]; 820 821 mPeriodStart = Calendar.getInstance(); 822 mPeriodEnd = Calendar.getInstance(); 823 824 retrieve(); 825 } 826 } 827 828 boolean setNextPeriod(Calendar start, Calendar end) { 829 // TODO - how would we deal with a dual-IMSI device? 830 checkForSubscriberId(); 831 boolean startNewPeriod = true; 832 833 if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) { 834 // same endpoints - keep collecting 835 if (VDBG) { 836 Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," + 837 end.getTimeInMillis() +") - ammending data"); 838 } 839 startNewPeriod = false; 840 } else { 841 if (VDBG) { 842 if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) { 843 Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," + 844 end.getTimeInMillis() + ") - old end was " + 845 mPeriodEnd.getTimeInMillis() + ", following"); 846 } else { 847 Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," + 848 end.getTimeInMillis() + ") replacing old (" + 849 mPeriodStart.getTimeInMillis() + "," + 850 mPeriodEnd.getTimeInMillis() + ")"); 851 } 852 } 853 synchronized (mParent) { 854 ++mCurrentPeriod; 855 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0; 856 mPeriodRxData[mCurrentPeriod] = 0; 857 mPeriodTxData[mCurrentPeriod] = 0; 858 } 859 } 860 setPeriodStart(start); 861 setPeriodEnd(end); 862 record(); 863 return startNewPeriod; 864 } 865 866 public long getPeriodEnd() { 867 synchronized (mParent) { 868 return mPeriodEnd.getTimeInMillis(); 869 } 870 } 871 872 private void setPeriodEnd(Calendar end) { 873 synchronized (mParent) { 874 mPeriodEnd = end; 875 } 876 } 877 878 public long getPeriodStart() { 879 synchronized (mParent) { 880 return mPeriodStart.getTimeInMillis(); 881 } 882 } 883 884 private void setPeriodStart(Calendar start) { 885 synchronized (mParent) { 886 mPeriodStart = start; 887 } 888 } 889 890 public int getPeriodCount() { 891 synchronized (mParent) { 892 return mPeriodCount; 893 } 894 } 895 896 private void zeroData(int field) { 897 synchronized (mParent) { 898 for(int period = 0; period<mPeriodCount; period++) { 899 mPeriodRxData[period] = 0; 900 mPeriodTxData[period] = 0; 901 } 902 mCurrentPeriod = 0; 903 } 904 905 } 906 907 // if time moves backward accumulate all read/write that's lost into the now 908 // otherwise time moved forward. 909 void addData(long bytesRead, long bytesWritten) { 910 checkForSubscriberId(); 911 912 synchronized (mParent) { 913 mPeriodRxData[mCurrentPeriod] += bytesRead; 914 mPeriodTxData[mCurrentPeriod] += bytesWritten; 915 } 916 record(); 917 } 918 919 private File getDataFile() { 920 File dataDir = Environment.getDataDirectory(); 921 File throttleDir = new File(dataDir, "system/throttle"); 922 throttleDir.mkdirs(); 923 String mImsi = mTelephonyManager.getSubscriberId(); 924 File dataFile; 925 if (mImsi == null) { 926 dataFile = useMRUFile(throttleDir); 927 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile); 928 } else { 929 String imsiHash = Integer.toString(mImsi.hashCode()); 930 dataFile = new File(throttleDir, imsiHash); 931 } 932 // touch the file so it's not LRU 933 dataFile.setLastModified(System.currentTimeMillis()); 934 checkAndDeleteLRUDataFile(throttleDir); 935 return dataFile; 936 } 937 938 // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling 939 private void checkForSubscriberId() { 940 if (mImsi != null) return; 941 942 mImsi = mTelephonyManager.getSubscriberId(); 943 if (mImsi == null) return; 944 945 if (VDBG) Slog.d(TAG, "finally have imsi - retreiving data"); 946 retrieve(); 947 } 948 949 private final static int MAX_SIMS_SUPPORTED = 3; 950 951 private void checkAndDeleteLRUDataFile(File dir) { 952 File[] files = dir.listFiles(); 953 954 if (files == null || files.length <= MAX_SIMS_SUPPORTED) return; 955 if (DBG) Slog.d(TAG, "Too many data files"); 956 do { 957 File oldest = null; 958 for (File f : files) { 959 if ((oldest == null) || (oldest.lastModified() > f.lastModified())) { 960 oldest = f; 961 } 962 } 963 if (oldest == null) return; 964 if (DBG) Slog.d(TAG, " deleting " + oldest); 965 oldest.delete(); 966 files = dir.listFiles(); 967 } while (files.length > MAX_SIMS_SUPPORTED); 968 } 969 970 private File useMRUFile(File dir) { 971 File newest = null; 972 File[] files = dir.listFiles(); 973 974 if (files != null) { 975 for (File f : files) { 976 if ((newest == null) || (newest.lastModified() < f.lastModified())) { 977 newest = f; 978 } 979 } 980 } 981 if (newest == null) { 982 newest = new File(dir, "temp"); 983 } 984 return newest; 985 } 986 987 988 private static final int DATA_FILE_VERSION = 1; 989 990 private void record() { 991 // 1 int version 992 // 1 int mPeriodCount 993 // 13*6 long[PERIOD_COUNT] mPeriodRxData 994 // 13*6 long[PERIOD_COUNT] mPeriodTxData 995 // 1 int mCurrentPeriod 996 // 13 long periodStartMS 997 // 13 long periodEndMS 998 // 200 chars max 999 StringBuilder builder = new StringBuilder(); 1000 builder.append(DATA_FILE_VERSION); 1001 builder.append(":"); 1002 builder.append(mPeriodCount); 1003 builder.append(":"); 1004 for(int i = 0; i < mPeriodCount; i++) { 1005 builder.append(mPeriodRxData[i]); 1006 builder.append(":"); 1007 } 1008 for(int i = 0; i < mPeriodCount; i++) { 1009 builder.append(mPeriodTxData[i]); 1010 builder.append(":"); 1011 } 1012 builder.append(mCurrentPeriod); 1013 builder.append(":"); 1014 builder.append(mPeriodStart.getTimeInMillis()); 1015 builder.append(":"); 1016 builder.append(mPeriodEnd.getTimeInMillis()); 1017 1018 BufferedWriter out = null; 1019 try { 1020 out = new BufferedWriter(new FileWriter(getDataFile()), 256); 1021 out.write(builder.toString()); 1022 } catch (IOException e) { 1023 Slog.e(TAG, "Error writing data file"); 1024 return; 1025 } finally { 1026 if (out != null) { 1027 try { 1028 out.close(); 1029 } catch (Exception e) {} 1030 } 1031 } 1032 } 1033 1034 private void retrieve() { 1035 // clean out any old data first. If we fail to read we don't want old stuff 1036 zeroData(0); 1037 1038 File f = getDataFile(); 1039 byte[] buffer; 1040 FileInputStream s = null; 1041 try { 1042 buffer = new byte[(int)f.length()]; 1043 s = new FileInputStream(f); 1044 s.read(buffer); 1045 } catch (IOException e) { 1046 Slog.e(TAG, "Error reading data file"); 1047 return; 1048 } finally { 1049 if (s != null) { 1050 try { 1051 s.close(); 1052 } catch (Exception e) {} 1053 } 1054 } 1055 String data = new String(buffer); 1056 if (data == null || data.length() == 0) { 1057 if (DBG) Slog.d(TAG, "data file empty"); 1058 return; 1059 } 1060 String[] parsed = data.split(":"); 1061 int parsedUsed = 0; 1062 if (parsed.length < 6) { 1063 Slog.e(TAG, "reading data file with insufficient length - ignoring"); 1064 return; 1065 } 1066 1067 int periodCount; 1068 long[] periodRxData; 1069 long[] periodTxData; 1070 int currentPeriod; 1071 Calendar periodStart; 1072 Calendar periodEnd; 1073 try { 1074 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) { 1075 Slog.e(TAG, "reading data file with bad version - ignoring"); 1076 return; 1077 } 1078 1079 periodCount = Integer.parseInt(parsed[parsedUsed++]); 1080 if (parsed.length != 5 + (2 * periodCount)) { 1081 Slog.e(TAG, "reading data file with bad length (" + parsed.length + 1082 " != " + (5 + (2 * periodCount)) + ") - ignoring"); 1083 return; 1084 } 1085 periodRxData = new long[periodCount]; 1086 for (int i = 0; i < periodCount; i++) { 1087 periodRxData[i] = Long.parseLong(parsed[parsedUsed++]); 1088 } 1089 periodTxData = new long[periodCount]; 1090 for (int i = 0; i < periodCount; i++) { 1091 periodTxData[i] = Long.parseLong(parsed[parsedUsed++]); 1092 } 1093 1094 currentPeriod = Integer.parseInt(parsed[parsedUsed++]); 1095 1096 periodStart = new GregorianCalendar(); 1097 periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++])); 1098 periodEnd = new GregorianCalendar(); 1099 periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++])); 1100 } catch (Exception e) { 1101 Slog.e(TAG, "Error parsing data file - ignoring"); 1102 return; 1103 } 1104 synchronized (mParent) { 1105 mPeriodCount = periodCount; 1106 mPeriodRxData = periodRxData; 1107 mPeriodTxData = periodTxData; 1108 mCurrentPeriod = currentPeriod; 1109 mPeriodStart = periodStart; 1110 mPeriodEnd = periodEnd; 1111 } 1112 } 1113 1114 long getPeriodRx(int which) { 1115 synchronized (mParent) { 1116 if (which > mPeriodCount) return 0; 1117 which = mCurrentPeriod - which; 1118 if (which < 0) which += mPeriodCount; 1119 return mPeriodRxData[which]; 1120 } 1121 } 1122 long getPeriodTx(int which) { 1123 synchronized (mParent) { 1124 if (which > mPeriodCount) return 0; 1125 which = mCurrentPeriod - which; 1126 if (which < 0) which += mPeriodCount; 1127 return mPeriodTxData[which]; 1128 } 1129 } 1130 } 1131 1132 @Override 1133 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1134 if (mContext.checkCallingOrSelfPermission( 1135 android.Manifest.permission.DUMP) 1136 != PackageManager.PERMISSION_GRANTED) { 1137 pw.println("Permission Denial: can't dump ThrottleService " + 1138 "from from pid=" + Binder.getCallingPid() + ", uid=" + 1139 Binder.getCallingUid()); 1140 return; 1141 } 1142 pw.println(); 1143 1144 pw.println("The threshold is " + mPolicyThreshold.get() + 1145 ", after which you experince throttling to " + 1146 mPolicyThrottleValue.get() + "kbps"); 1147 pw.println("Current period is " + 1148 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " + 1149 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 + 1150 " seconds."); 1151 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds"); 1152 pw.println("Current Throttle Index is " + mThrottleIndex.get()); 1153 pw.println("mMaxNtpCacheAge=" + mMaxNtpCacheAge); 1154 1155 for (int i = 0; i < mRecorder.getPeriodCount(); i++) { 1156 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" + 1157 mRecorder.getPeriodTx(i)); 1158 } 1159 } 1160} 1161