ThrottleService.java revision 5ac72a29593ab9a20337a2225df52bdf4754be02
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.Secure.getUriFor( 215 Settings.Secure.THROTTLE_POLLING_SEC), false, this); 216 resolver.registerContentObserver(Settings.Secure.getUriFor( 217 Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this); 218 resolver.registerContentObserver(Settings.Secure.getUriFor( 219 Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this); 220 resolver.registerContentObserver(Settings.Secure.getUriFor( 221 Settings.Secure.THROTTLE_RESET_DAY), false, this); 222 resolver.registerContentObserver(Settings.Secure.getUriFor( 223 Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this); 224 resolver.registerContentObserver(Settings.Secure.getUriFor( 225 Settings.Secure.THROTTLE_HELP_URI), false, this); 226 resolver.registerContentObserver(Settings.Secure.getUriFor( 227 Settings.Secure.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.Secure.getString(mContext.getContentResolver(), 301 Settings.Secure.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.Secure.getInt(mContext.getContentResolver(), 440 Settings.Secure.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.Secure.getLong(mContext.getContentResolver(), 448 Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold); 449 int value = Settings.Secure.getInt(mContext.getContentResolver(), 450 Settings.Secure.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.Secure.getInt(mContext.getContentResolver(), 460 Settings.Secure.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.Secure.putInt(mContext.getContentResolver(), 466 Settings.Secure.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.Secure.getInt(mContext.getContentResolver(), 475 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType); 476 477 final int maxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(), 478 Settings.Secure.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.getActivity(mContext, 0, intent, 0); 674 675 Resources r = Resources.getSystem(); 676 CharSequence title = r.getText(titleInt); 677 CharSequence message = r.getText(messageInt); 678 if (mThrottlingNotification == null) { 679 mThrottlingNotification = new Notification(); 680 mThrottlingNotification.when = 0; 681 // TODO - fixup icon 682 mThrottlingNotification.icon = icon; 683 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND; 684 } 685 mThrottlingNotification.flags = flags; 686 mThrottlingNotification.tickerText = title; 687 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi); 688 689 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification); 690 } 691 692 693 private void clearThrottleAndNotification() { 694 if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) { 695 mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED); 696 try { 697 mNMService.setInterfaceThrottle(mIface, -1, -1); 698 } catch (Exception e) { 699 Slog.e(TAG, "error clearing Throttle: " + e); 700 } 701 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); 702 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1); 703 mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL); 704 mNotificationManager.cancel(R.drawable.stat_sys_throttled); 705 mWarningNotificationSent = false; 706 } 707 } 708 709 private Calendar calculatePeriodEnd(long now) { 710 Calendar end = GregorianCalendar.getInstance(); 711 end.setTimeInMillis(now); 712 int day = end.get(Calendar.DAY_OF_MONTH); 713 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay); 714 end.set(Calendar.HOUR_OF_DAY, 0); 715 end.set(Calendar.MINUTE, 0); 716 end.set(Calendar.SECOND, 0); 717 end.set(Calendar.MILLISECOND, 0); 718 if (day >= mPolicyResetDay) { 719 int month = end.get(Calendar.MONTH); 720 if (month == Calendar.DECEMBER) { 721 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1); 722 month = Calendar.JANUARY - 1; 723 } 724 end.set(Calendar.MONTH, month + 1); 725 } 726 727 // TODO - remove! 728 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { 729 end = GregorianCalendar.getInstance(); 730 end.setTimeInMillis(now); 731 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC); 732 } 733 return end; 734 } 735 private Calendar calculatePeriodStart(Calendar end) { 736 Calendar start = (Calendar)end.clone(); 737 int month = end.get(Calendar.MONTH); 738 if (end.get(Calendar.MONTH) == Calendar.JANUARY) { 739 month = Calendar.DECEMBER + 1; 740 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1); 741 } 742 start.set(Calendar.MONTH, month - 1); 743 744 // TODO - remove!! 745 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { 746 start = (Calendar)end.clone(); 747 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC); 748 } 749 return start; 750 } 751 752 private void onResetAlarm() { 753 if (VDBG || (mPolicyThreshold.get() != 0)) { 754 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) + 755 " bytes read and " + mRecorder.getPeriodTx(0) + " written"); 756 } 757 758 // when trusted cache outdated, try refreshing 759 if (mTime.getCacheAge() > mMaxNtpCacheAge) { 760 mTime.forceRefresh(); 761 } 762 763 // as long as we have a trusted time cache, we always reset alarms, 764 // even if the refresh above failed. 765 if (mTime.hasCache()) { 766 final long now = mTime.currentTimeMillis(); 767 Calendar end = calculatePeriodEnd(now); 768 Calendar start = calculatePeriodStart(end); 769 770 if (mRecorder.setNextPeriod(start, end)) { 771 onPollAlarm(); 772 } 773 774 mAlarmManager.cancel(mPendingResetIntent); 775 long offset = end.getTimeInMillis() - now; 776 // use Elapsed realtime so clock changes don't fool us. 777 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, 778 SystemClock.elapsedRealtime() + offset, 779 mPendingResetIntent); 780 } else { 781 if (VDBG) Slog.d(TAG, "no trusted time, not resetting period"); 782 } 783 } 784 } 785 786 // records bytecount data for a given time and accumulates it into larger time windows 787 // for logging and other purposes 788 // 789 // since time can be changed (user or network action) we will have to track the time of the 790 // last recording and deal with it. 791 private static class DataRecorder { 792 long[] mPeriodRxData; 793 long[] mPeriodTxData; 794 int mCurrentPeriod; 795 int mPeriodCount; 796 797 Calendar mPeriodStart; 798 Calendar mPeriodEnd; 799 800 ThrottleService mParent; 801 Context mContext; 802 String mImsi = null; 803 804 TelephonyManager mTelephonyManager; 805 806 DataRecorder(Context context, ThrottleService parent) { 807 mContext = context; 808 mParent = parent; 809 810 mTelephonyManager = (TelephonyManager)mContext.getSystemService( 811 Context.TELEPHONY_SERVICE); 812 813 synchronized (mParent) { 814 mPeriodCount = 6; 815 mPeriodRxData = new long[mPeriodCount]; 816 mPeriodTxData = new long[mPeriodCount]; 817 818 mPeriodStart = Calendar.getInstance(); 819 mPeriodEnd = Calendar.getInstance(); 820 821 retrieve(); 822 } 823 } 824 825 boolean setNextPeriod(Calendar start, Calendar end) { 826 // TODO - how would we deal with a dual-IMSI device? 827 checkForSubscriberId(); 828 boolean startNewPeriod = true; 829 830 if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) { 831 // same endpoints - keep collecting 832 if (VDBG) { 833 Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," + 834 end.getTimeInMillis() +") - ammending data"); 835 } 836 startNewPeriod = false; 837 } else { 838 if (VDBG) { 839 if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) { 840 Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," + 841 end.getTimeInMillis() + ") - old end was " + 842 mPeriodEnd.getTimeInMillis() + ", following"); 843 } else { 844 Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," + 845 end.getTimeInMillis() + ") replacing old (" + 846 mPeriodStart.getTimeInMillis() + "," + 847 mPeriodEnd.getTimeInMillis() + ")"); 848 } 849 } 850 synchronized (mParent) { 851 ++mCurrentPeriod; 852 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0; 853 mPeriodRxData[mCurrentPeriod] = 0; 854 mPeriodTxData[mCurrentPeriod] = 0; 855 } 856 } 857 setPeriodStart(start); 858 setPeriodEnd(end); 859 record(); 860 return startNewPeriod; 861 } 862 863 public long getPeriodEnd() { 864 synchronized (mParent) { 865 return mPeriodEnd.getTimeInMillis(); 866 } 867 } 868 869 private void setPeriodEnd(Calendar end) { 870 synchronized (mParent) { 871 mPeriodEnd = end; 872 } 873 } 874 875 public long getPeriodStart() { 876 synchronized (mParent) { 877 return mPeriodStart.getTimeInMillis(); 878 } 879 } 880 881 private void setPeriodStart(Calendar start) { 882 synchronized (mParent) { 883 mPeriodStart = start; 884 } 885 } 886 887 public int getPeriodCount() { 888 synchronized (mParent) { 889 return mPeriodCount; 890 } 891 } 892 893 private void zeroData(int field) { 894 synchronized (mParent) { 895 for(int period = 0; period<mPeriodCount; period++) { 896 mPeriodRxData[period] = 0; 897 mPeriodTxData[period] = 0; 898 } 899 mCurrentPeriod = 0; 900 } 901 902 } 903 904 // if time moves backward accumulate all read/write that's lost into the now 905 // otherwise time moved forward. 906 void addData(long bytesRead, long bytesWritten) { 907 checkForSubscriberId(); 908 909 synchronized (mParent) { 910 mPeriodRxData[mCurrentPeriod] += bytesRead; 911 mPeriodTxData[mCurrentPeriod] += bytesWritten; 912 } 913 record(); 914 } 915 916 private File getDataFile() { 917 File dataDir = Environment.getDataDirectory(); 918 File throttleDir = new File(dataDir, "system/throttle"); 919 throttleDir.mkdirs(); 920 String mImsi = mTelephonyManager.getSubscriberId(); 921 File dataFile; 922 if (mImsi == null) { 923 dataFile = useMRUFile(throttleDir); 924 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile); 925 } else { 926 String imsiHash = Integer.toString(mImsi.hashCode()); 927 dataFile = new File(throttleDir, imsiHash); 928 } 929 // touch the file so it's not LRU 930 dataFile.setLastModified(System.currentTimeMillis()); 931 checkAndDeleteLRUDataFile(throttleDir); 932 return dataFile; 933 } 934 935 // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling 936 private void checkForSubscriberId() { 937 if (mImsi != null) return; 938 939 mImsi = mTelephonyManager.getSubscriberId(); 940 if (mImsi == null) return; 941 942 if (VDBG) Slog.d(TAG, "finally have imsi - retreiving data"); 943 retrieve(); 944 } 945 946 private final static int MAX_SIMS_SUPPORTED = 3; 947 948 private void checkAndDeleteLRUDataFile(File dir) { 949 File[] files = dir.listFiles(); 950 951 if (files == null || files.length <= MAX_SIMS_SUPPORTED) return; 952 if (DBG) Slog.d(TAG, "Too many data files"); 953 do { 954 File oldest = null; 955 for (File f : files) { 956 if ((oldest == null) || (oldest.lastModified() > f.lastModified())) { 957 oldest = f; 958 } 959 } 960 if (oldest == null) return; 961 if (DBG) Slog.d(TAG, " deleting " + oldest); 962 oldest.delete(); 963 files = dir.listFiles(); 964 } while (files.length > MAX_SIMS_SUPPORTED); 965 } 966 967 private File useMRUFile(File dir) { 968 File newest = null; 969 File[] files = dir.listFiles(); 970 971 if (files != null) { 972 for (File f : files) { 973 if ((newest == null) || (newest.lastModified() < f.lastModified())) { 974 newest = f; 975 } 976 } 977 } 978 if (newest == null) { 979 newest = new File(dir, "temp"); 980 } 981 return newest; 982 } 983 984 985 private static final int DATA_FILE_VERSION = 1; 986 987 private void record() { 988 // 1 int version 989 // 1 int mPeriodCount 990 // 13*6 long[PERIOD_COUNT] mPeriodRxData 991 // 13*6 long[PERIOD_COUNT] mPeriodTxData 992 // 1 int mCurrentPeriod 993 // 13 long periodStartMS 994 // 13 long periodEndMS 995 // 200 chars max 996 StringBuilder builder = new StringBuilder(); 997 builder.append(DATA_FILE_VERSION); 998 builder.append(":"); 999 builder.append(mPeriodCount); 1000 builder.append(":"); 1001 for(int i = 0; i < mPeriodCount; i++) { 1002 builder.append(mPeriodRxData[i]); 1003 builder.append(":"); 1004 } 1005 for(int i = 0; i < mPeriodCount; i++) { 1006 builder.append(mPeriodTxData[i]); 1007 builder.append(":"); 1008 } 1009 builder.append(mCurrentPeriod); 1010 builder.append(":"); 1011 builder.append(mPeriodStart.getTimeInMillis()); 1012 builder.append(":"); 1013 builder.append(mPeriodEnd.getTimeInMillis()); 1014 1015 BufferedWriter out = null; 1016 try { 1017 out = new BufferedWriter(new FileWriter(getDataFile()), 256); 1018 out.write(builder.toString()); 1019 } catch (IOException e) { 1020 Slog.e(TAG, "Error writing data file"); 1021 return; 1022 } finally { 1023 if (out != null) { 1024 try { 1025 out.close(); 1026 } catch (Exception e) {} 1027 } 1028 } 1029 } 1030 1031 private void retrieve() { 1032 // clean out any old data first. If we fail to read we don't want old stuff 1033 zeroData(0); 1034 1035 File f = getDataFile(); 1036 byte[] buffer; 1037 FileInputStream s = null; 1038 try { 1039 buffer = new byte[(int)f.length()]; 1040 s = new FileInputStream(f); 1041 s.read(buffer); 1042 } catch (IOException e) { 1043 Slog.e(TAG, "Error reading data file"); 1044 return; 1045 } finally { 1046 if (s != null) { 1047 try { 1048 s.close(); 1049 } catch (Exception e) {} 1050 } 1051 } 1052 String data = new String(buffer); 1053 if (data == null || data.length() == 0) { 1054 if (DBG) Slog.d(TAG, "data file empty"); 1055 return; 1056 } 1057 String[] parsed = data.split(":"); 1058 int parsedUsed = 0; 1059 if (parsed.length < 6) { 1060 Slog.e(TAG, "reading data file with insufficient length - ignoring"); 1061 return; 1062 } 1063 1064 int periodCount; 1065 long[] periodRxData; 1066 long[] periodTxData; 1067 int currentPeriod; 1068 Calendar periodStart; 1069 Calendar periodEnd; 1070 try { 1071 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) { 1072 Slog.e(TAG, "reading data file with bad version - ignoring"); 1073 return; 1074 } 1075 1076 periodCount = Integer.parseInt(parsed[parsedUsed++]); 1077 if (parsed.length != 5 + (2 * periodCount)) { 1078 Slog.e(TAG, "reading data file with bad length (" + parsed.length + 1079 " != " + (5 + (2 * periodCount)) + ") - ignoring"); 1080 return; 1081 } 1082 periodRxData = new long[periodCount]; 1083 for (int i = 0; i < periodCount; i++) { 1084 periodRxData[i] = Long.parseLong(parsed[parsedUsed++]); 1085 } 1086 periodTxData = new long[periodCount]; 1087 for (int i = 0; i < periodCount; i++) { 1088 periodTxData[i] = Long.parseLong(parsed[parsedUsed++]); 1089 } 1090 1091 currentPeriod = Integer.parseInt(parsed[parsedUsed++]); 1092 1093 periodStart = new GregorianCalendar(); 1094 periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++])); 1095 periodEnd = new GregorianCalendar(); 1096 periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++])); 1097 } catch (Exception e) { 1098 Slog.e(TAG, "Error parsing data file - ignoring"); 1099 return; 1100 } 1101 synchronized (mParent) { 1102 mPeriodCount = periodCount; 1103 mPeriodRxData = periodRxData; 1104 mPeriodTxData = periodTxData; 1105 mCurrentPeriod = currentPeriod; 1106 mPeriodStart = periodStart; 1107 mPeriodEnd = periodEnd; 1108 } 1109 } 1110 1111 long getPeriodRx(int which) { 1112 synchronized (mParent) { 1113 if (which > mPeriodCount) return 0; 1114 which = mCurrentPeriod - which; 1115 if (which < 0) which += mPeriodCount; 1116 return mPeriodRxData[which]; 1117 } 1118 } 1119 long getPeriodTx(int which) { 1120 synchronized (mParent) { 1121 if (which > mPeriodCount) return 0; 1122 which = mCurrentPeriod - which; 1123 if (which < 0) which += mPeriodCount; 1124 return mPeriodTxData[which]; 1125 } 1126 } 1127 } 1128 1129 @Override 1130 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1131 if (mContext.checkCallingOrSelfPermission( 1132 android.Manifest.permission.DUMP) 1133 != PackageManager.PERMISSION_GRANTED) { 1134 pw.println("Permission Denial: can't dump ThrottleService " + 1135 "from from pid=" + Binder.getCallingPid() + ", uid=" + 1136 Binder.getCallingUid()); 1137 return; 1138 } 1139 pw.println(); 1140 1141 pw.println("The threshold is " + mPolicyThreshold.get() + 1142 ", after which you experince throttling to " + 1143 mPolicyThrottleValue.get() + "kbps"); 1144 pw.println("Current period is " + 1145 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " + 1146 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 + 1147 " seconds."); 1148 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds"); 1149 pw.println("Current Throttle Index is " + mThrottleIndex.get()); 1150 pw.println("mMaxNtpCacheAge=" + mMaxNtpCacheAge); 1151 1152 for (int i = 0; i < mRecorder.getPeriodCount(); i++) { 1153 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" + 1154 mRecorder.getPeriodTx(i)); 1155 } 1156 } 1157} 1158