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