ThrottleService.java revision 9e696c29f06d45d2891e1d38fd8d9033a9e21bb9
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.app.Service; 24import android.content.BroadcastReceiver; 25import android.content.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.pm.PackageManager; 29import android.content.res.Resources; 30import android.content.SharedPreferences; 31import android.net.IThrottleManager; 32import android.net.ThrottleManager; 33import android.os.Binder; 34import android.os.Handler; 35import android.os.HandlerThread; 36import android.os.IBinder; 37import android.os.INetworkManagementService; 38import android.os.Looper; 39import android.os.Message; 40import android.os.RemoteException; 41import android.os.ServiceManager; 42import android.os.SystemClock; 43import android.os.SystemProperties; 44import android.provider.Settings; 45import android.util.Slog; 46 47import java.io.FileDescriptor; 48import java.io.PrintWriter; 49import java.util.Calendar; 50import java.util.GregorianCalendar; 51import java.util.Random; 52 53// TODO - add comments - reference the ThrottleManager for public API 54public class ThrottleService extends IThrottleManager.Stub { 55 56 private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing"; 57 58 private static final String TAG = "ThrottleService"; 59 private static boolean DBG = true; 60 private Handler mHandler; 61 private HandlerThread mThread; 62 63 private Context mContext; 64 65 private int mPolicyPollPeriodSec; 66 private static final int DEFAULT_POLLING_PERIOD_SEC = 60 * 10; 67 private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1; 68 69 private static final int TESTING_RESET_PERIOD_SEC = 60 * 3; 70 71 private static final int PERIOD_COUNT = 6; 72 73 private long mPolicyThreshold; 74 // TODO - remove testing stuff? 75 private static final long DEFAULT_TESTING_THRESHOLD = 1 * 1024 * 1024; 76 private static final long DEFAULT_THRESHOLD = 0; // off by default 77 78 private int mPolicyThrottleValue; 79 private static final int DEFAULT_THROTTLE_VALUE = 100; // 100 Kbps 80 81 private int mPolicyResetDay; // 1-28 82 83 private long mLastRead; // read byte count from last poll 84 private long mLastWrite; // write byte count from last poll 85 86 private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL"; 87 private static int POLL_REQUEST = 0; 88 private PendingIntent mPendingPollIntent; 89 private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET"; 90 private static int RESET_REQUEST = 1; 91 private PendingIntent mPendingResetIntent; 92 93 private INetworkManagementService mNMService; 94 private AlarmManager mAlarmManager; 95 private NotificationManager mNotificationManager; 96 97 private DataRecorder mRecorder; 98 99 private int mThrottleLevel; // 0 for none, 1 for first throttle val, 2 for next, etc 100 101 private String mPolicyIface; 102 103 private static final int NOTIFICATION_WARNING = 2; 104 private static final int NOTIFICATION_ALL = 0xFFFFFFFF; 105 private int mPolicyNotificationsAllowedMask; 106 107 private Notification mThrottlingNotification; 108 private boolean mWarningNotificationSent = false; 109 110 public ThrottleService(Context context) { 111 if (DBG) Slog.d(TAG, "Starting ThrottleService"); 112 mContext = context; 113 114 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 115 Intent pollIntent = new Intent(ACTION_POLL, null); 116 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); 117 Intent resetIntent = new Intent(ACTION_RESET, null); 118 mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0); 119 120 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); 121 mNMService = INetworkManagementService.Stub.asInterface(b); 122 123 mNotificationManager = (NotificationManager)mContext.getSystemService( 124 Context.NOTIFICATION_SERVICE); 125 } 126 127 private void enforceAccessPermission() { 128 mContext.enforceCallingOrSelfPermission( 129 android.Manifest.permission.ACCESS_NETWORK_STATE, 130 "ThrottleService"); 131 } 132 133 public synchronized long getResetTime(String iface) { 134 enforceAccessPermission(); 135 if (iface.equals(mPolicyIface) && (mRecorder != null)) mRecorder.getPeriodEnd(); 136 return 0; 137 } 138 public synchronized long getPeriodStartTime(String iface) { 139 enforceAccessPermission(); 140 if (iface.equals(mPolicyIface) && (mRecorder != null)) mRecorder.getPeriodStart(); 141 return 0; 142 } 143 //TODO - a better name? getCliffByteCountThreshold? 144 public synchronized long getCliffThreshold(String iface, int cliff) { 145 enforceAccessPermission(); 146 if ((cliff == 0) && iface.equals(mPolicyIface)) { 147 return mPolicyThreshold; 148 } 149 return 0; 150 } 151 // TODO - a better name? getThrottleRate? 152 public synchronized int getCliffLevel(String iface, int cliff) { 153 enforceAccessPermission(); 154 if ((cliff == 0) && iface.equals(mPolicyIface)) { 155 return mPolicyThrottleValue; 156 } 157 return 0; 158 } 159 160 public synchronized long getByteCount(String iface, int dir, int period, int ago) { 161 enforceAccessPermission(); 162 if (iface.equals(mPolicyIface) && 163 (period == ThrottleManager.PERIOD_CYCLE) && 164 (mRecorder != null)) { 165 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago); 166 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago); 167 } 168 return 0; 169 } 170 171 // TODO - a better name - getCurrentThrottleRate? 172 public synchronized int getThrottle(String iface) { 173 enforceAccessPermission(); 174 if (iface.equals(mPolicyIface) && (mThrottleLevel == 1)) { 175 return mPolicyThrottleValue; 176 } 177 return 0; 178 } 179 180 void systemReady() { 181 if (DBG) Slog.d(TAG, "systemReady"); 182 mContext.registerReceiver( 183 new BroadcastReceiver() { 184 @Override 185 public void onReceive(Context context, Intent intent) { 186 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget(); 187 } 188 }, new IntentFilter(ACTION_POLL)); 189 190 mContext.registerReceiver( 191 new BroadcastReceiver() { 192 @Override 193 public void onReceive(Context context, Intent intent) { 194 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget(); 195 } 196 }, new IntentFilter(ACTION_RESET)); 197 198 // use a new thread as we don't want to stall the system for file writes 199 mThread = new HandlerThread(TAG); 200 mThread.start(); 201 mHandler = new MyHandler(mThread.getLooper()); 202 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget(); 203 } 204 205 206 private static final int EVENT_REBOOT_RECOVERY = 0; 207 private static final int EVENT_POLICY_CHANGED = 1; 208 private static final int EVENT_POLL_ALARM = 2; 209 private static final int EVENT_RESET_ALARM = 3; 210 private class MyHandler extends Handler { 211 public MyHandler(Looper l) { 212 super(l); 213 } 214 215 @Override 216 public void handleMessage(Message msg) { 217 switch (msg.what) { 218 case EVENT_REBOOT_RECOVERY: 219 onRebootRecovery(); 220 break; 221 case EVENT_POLICY_CHANGED: 222 onPolicyChanged(); 223 break; 224 case EVENT_POLL_ALARM: 225 onPollAlarm(); 226 break; 227 case EVENT_RESET_ALARM: 228 onResetAlarm(); 229 } 230 } 231 232 private void onRebootRecovery() { 233 if (DBG) Slog.d(TAG, "onRebootRecovery"); 234 // check for sim change TODO 235 // reregister for notification of policy change 236 237 // register for roaming indication change 238 // check for roaming TODO 239 240 mRecorder = new DataRecorder(mContext, ThrottleService.this); 241 242 // get policy 243 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget(); 244 245 // evaluate current conditions 246 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget(); 247 } 248 249 private void onSimChange() { 250 // TODO 251 } 252 253 // check for new policy info (threshold limit/value/etc) 254 private void onPolicyChanged() { 255 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true"); 256 257 int pollingPeriod = DEFAULT_POLLING_PERIOD_SEC; 258 if (testing) pollingPeriod = TESTING_POLLING_PERIOD_SEC; 259 mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(), 260 Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod); 261 262 // TODO - remove testing stuff? 263 long defaultThreshold = DEFAULT_THRESHOLD; 264 if (testing) defaultThreshold = DEFAULT_TESTING_THRESHOLD; 265 synchronized (ThrottleService.this) { 266 mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(), 267 Settings.Secure.THROTTLE_THRESHOLD, defaultThreshold); 268 mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(), 269 Settings.Secure.THROTTLE_VALUE, DEFAULT_THROTTLE_VALUE); 270 } 271 mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(), 272 Settings.Secure.THROTTLE_RESET_DAY, -1); 273 if (mPolicyResetDay == -1 || 274 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) { 275 Random g = new Random(); 276 mPolicyResetDay = 1 + g.nextInt(28); // 1-28 277 Settings.Secure.putInt(mContext.getContentResolver(), 278 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay); 279 } 280 synchronized (ThrottleService.this) { 281 mPolicyIface = Settings.Secure.getString(mContext.getContentResolver(), 282 Settings.Secure.THROTTLE_IFACE); 283 // TODO - read default from resource so it's device-specific 284 if (mPolicyIface == null) mPolicyIface = "rmnet0"; 285 } 286 287 mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(), 288 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, NOTIFICATION_ALL); 289 290 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + mPolicyPollPeriodSec + 291 ", threshold=" + mPolicyThreshold + ", value=" + mPolicyThrottleValue + 292 ", resetDay=" + mPolicyResetDay + ", noteType=" + 293 mPolicyNotificationsAllowedMask); 294 295 Calendar end = calculatePeriodEnd(); 296 Calendar start = calculatePeriodStart(end); 297 298 mRecorder.setNextPeriod(start,end); 299 300 mAlarmManager.cancel(mPendingResetIntent); 301 mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(), 302 mPendingResetIntent); 303 } 304 305 private void onPollAlarm() { 306 long now = SystemClock.elapsedRealtime(); 307 long next = now + mPolicyPollPeriodSec*1000; 308 long incRead = 0; 309 long incWrite = 0; 310 try { 311 incRead = mNMService.getInterfaceRxCounter(mPolicyIface) - mLastRead; 312 incWrite = mNMService.getInterfaceTxCounter(mPolicyIface) - mLastWrite; 313 } catch (RemoteException e) { 314 Slog.e(TAG, "got remoteException in onPollAlarm:" + e); 315 } 316 317 mRecorder.addData(incRead, incWrite); 318 319 long periodRx = mRecorder.getPeriodRx(0); 320 long periodTx = mRecorder.getPeriodTx(0); 321 long total = periodRx + periodTx; 322 if (DBG) { 323 Slog.d(TAG, "onPollAlarm - now =" + now + ", read =" + incRead + 324 ", written =" + incWrite + ", new total =" + total); 325 } 326 mLastRead += incRead; 327 mLastWrite += incWrite; 328 329 checkThrottleAndPostNotification(total); 330 331 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION); 332 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx); 333 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx); 334 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, mRecorder.getPeriodStart()); 335 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, mRecorder.getPeriodEnd()); 336 mContext.sendStickyBroadcast(broadcast); 337 338 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent); 339 } 340 341 private void checkThrottleAndPostNotification(long currentTotal) { 342 // are we even doing this? 343 if (mPolicyThreshold == 0) 344 return; 345 346 // check if we need to throttle 347 if (currentTotal > mPolicyThreshold) { 348 if (mThrottleLevel != 1) { 349 synchronized (ThrottleService.this) { 350 mThrottleLevel = 1; 351 } 352 if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!"); 353 try { 354 mNMService.setInterfaceThrottle(mPolicyIface, 355 mPolicyThrottleValue, mPolicyThrottleValue); 356 } catch (Exception e) { 357 Slog.e(TAG, "error setting Throttle: " + e); 358 } 359 360 mNotificationManager.cancel(com.android.internal.R.drawable. 361 stat_sys_throttle_warning); 362 363 postNotification(com.android.internal.R.string.throttled_notification_title, 364 com.android.internal.R.string.throttled_notification_message, 365 com.android.internal.R.drawable.stat_sys_throttled); 366 367 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); 368 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue); 369 mContext.sendStickyBroadcast(broadcast); 370 371 } // else already up! 372 } else { 373 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) { 374 // check if we should warn about throttle 375 if (currentTotal > (mPolicyThreshold/2) && !mWarningNotificationSent) { 376 mWarningNotificationSent = true; 377 mNotificationManager.cancel(com.android.internal.R.drawable. 378 stat_sys_throttle_warning); 379 postNotification(com.android.internal.R.string. 380 throttle_warning_notification_title, 381 com.android.internal.R.string. 382 throttle_warning_notification_message, 383 com.android.internal.R.drawable.stat_sys_throttle_warning); 384 } else { 385 mWarningNotificationSent =false; 386 } 387 } 388 } 389 } 390 391 private void postNotification(int titleInt, int messageInt, int icon) { 392 Intent intent = new Intent(); 393 // TODO - fix up intent 394 intent.setClassName("com.android.settings", "com.android.settings.TetherSettings"); 395 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 396 397 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); 398 399 Resources r = Resources.getSystem(); 400 CharSequence title = r.getText(titleInt); 401 CharSequence message = r.getText(messageInt); 402 if (mThrottlingNotification == null) { 403 mThrottlingNotification = new Notification(); 404 mThrottlingNotification.when = 0; 405 // TODO - fixup icon 406 mThrottlingNotification.icon = icon; 407 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND; 408// mThrottlingNotification.flags = Notification.FLAG_ONGOING_EVENT; 409 } 410 mThrottlingNotification.tickerText = title; 411 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi); 412 413 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification); 414 } 415 416 417 private synchronized void clearThrottleAndNotification() { 418 if (mThrottleLevel == 1) { 419 synchronized (ThrottleService.this) { 420 mThrottleLevel = 0; 421 } 422 try { 423 mNMService.setInterfaceThrottle(mPolicyIface, -1, -1); 424 } catch (Exception e) { 425 Slog.e(TAG, "error clearing Throttle: " + e); 426 } 427 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION); 428 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1); 429 mContext.sendStickyBroadcast(broadcast); 430 } 431 mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttle_warning); 432 mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttled); 433 mWarningNotificationSent = false; 434 } 435 436 private Calendar calculatePeriodEnd() { 437 Calendar end = GregorianCalendar.getInstance(); 438 int day = end.get(Calendar.DAY_OF_MONTH); 439 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay); 440 end.set(Calendar.HOUR_OF_DAY, 0); 441 end.set(Calendar.MINUTE, 0); 442 if (day >= mPolicyResetDay) { 443 int month = end.get(Calendar.MONTH); 444 if (month == Calendar.DECEMBER) { 445 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1); 446 month = Calendar.JANUARY - 1; 447 } 448 end.set(Calendar.MONTH, month + 1); 449 } 450 451 // TODO - remove! 452 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { 453 end = GregorianCalendar.getInstance(); 454 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC); 455 } 456 return end; 457 } 458 private Calendar calculatePeriodStart(Calendar end) { 459 Calendar start = (Calendar)end.clone(); 460 int month = end.get(Calendar.MONTH); 461 if (end.get(Calendar.MONTH) == Calendar.JANUARY) { 462 month = Calendar.DECEMBER + 1; 463 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1); 464 } 465 start.set(Calendar.MONTH, month - 1); 466 467 // TODO - remove!! 468 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) { 469 start = (Calendar)end.clone(); 470 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC); 471 } 472 return start; 473 } 474 475 private void onResetAlarm() { 476 if (DBG) { 477 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) + 478 " bytes read and " + mRecorder.getPeriodTx(0) + " written"); 479 } 480 481 Calendar end = calculatePeriodEnd(); 482 Calendar start = calculatePeriodStart(end); 483 484 clearThrottleAndNotification(); 485 486 mRecorder.setNextPeriod(start,end); 487 488 mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(), 489 mPendingResetIntent); 490 } 491 } 492 493 // records bytecount data for a given time and accumulates it into larger time windows 494 // for logging and other purposes 495 // 496 // since time can be changed (user or network action) we will have to track the time of the 497 // last recording and deal with it. 498 private static class DataRecorder { 499 long[] mPeriodRxData; 500 long[] mPeriodTxData; 501 int mCurrentPeriod; 502 int mPeriodCount; 503 504 Calendar mPeriodStart; 505 Calendar mPeriodEnd; 506 507 ThrottleService mParent; 508 Context mContext; 509 SharedPreferences mSharedPreferences; 510 511 DataRecorder(Context context, ThrottleService parent) { 512 mContext = context; 513 mParent = parent; 514 515 synchronized (mParent) { 516 mPeriodCount = 6; 517 mPeriodRxData = new long[mPeriodCount]; 518 mPeriodTxData = new long[mPeriodCount]; 519 520 mPeriodStart = Calendar.getInstance(); 521 mPeriodEnd = Calendar.getInstance(); 522 523 mSharedPreferences = mContext.getSharedPreferences("ThrottleData", 524 android.content.Context.MODE_PRIVATE); 525 526 zeroData(0); 527 retrieve(); 528 } 529 } 530 531 void setNextPeriod(Calendar start, Calendar end) { 532 if (DBG) { 533 Slog.d(TAG, "setting next period to " + start.getTimeInMillis() + 534 " --until-- " + end.getTimeInMillis()); 535 } 536 // if we roll back in time to a previous period, toss out the current data 537 // if we roll forward to the next period, advance to the next 538 539 if (end.before(mPeriodStart)) { 540 if (DBG) { 541 Slog.d(TAG, " old start was " + mPeriodStart.getTimeInMillis() + ", wiping"); 542 } 543 synchronized (mParent) { 544 mPeriodRxData[mCurrentPeriod] = 0; 545 mPeriodTxData[mCurrentPeriod] = 0; 546 } 547 } else if(start.after(mPeriodEnd)) { 548 if (DBG) { 549 Slog.d(TAG, " old end was " + mPeriodEnd.getTimeInMillis() + ", following"); 550 } 551 synchronized (mParent) { 552 ++mCurrentPeriod; 553 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0; 554 mPeriodRxData[mCurrentPeriod] = 0; 555 mPeriodTxData[mCurrentPeriod] = 0; 556 } 557 } else { 558 if (DBG) Slog.d(TAG, " we fit - ammending to last period"); 559 } 560 setPeriodStart(start); 561 setPeriodEnd(end); 562 record(); 563 } 564 565 public long getPeriodEnd() { 566 synchronized (mParent) { 567 return mPeriodEnd.getTimeInMillis(); 568 } 569 } 570 571 private void setPeriodEnd(Calendar end) { 572 synchronized (mParent) { 573 mPeriodEnd = end; 574 } 575 } 576 577 public long getPeriodStart() { 578 synchronized (mParent) { 579 return mPeriodStart.getTimeInMillis(); 580 } 581 } 582 583 private void setPeriodStart(Calendar start) { 584 synchronized (mParent) { 585 mPeriodStart = start; 586 } 587 } 588 589 public int getPeriodCount() { 590 synchronized (mParent) { 591 return mPeriodCount; 592 } 593 } 594 595 private void zeroData(int field) { 596 synchronized (mParent) { 597 for(int period = 0; period<mPeriodCount; period++) { 598 mPeriodRxData[period] = 0; 599 mPeriodTxData[period] = 0; 600 } 601 mCurrentPeriod = 0; 602 } 603 604 } 605 606 // if time moves backward accumulate all read/write that's lost into the now 607 // otherwise time moved forward. 608 void addData(long bytesRead, long bytesWritten) { 609 synchronized (mParent) { 610 mPeriodRxData[mCurrentPeriod] += bytesRead; 611 mPeriodTxData[mCurrentPeriod] += bytesWritten; 612 } 613 record(); 614 } 615 616 private void record() { 617 // serialize into a secure setting 618 619 // 1 int mPeriodCount 620 // 13*6 long[PERIOD_COUNT] mPeriodRxData 621 // 13*6 long[PERIOD_COUNT] mPeriodTxData 622 // 1 int mCurrentPeriod 623 // 13 long periodStartMS 624 // 13 long periodEndMS 625 // 199 chars max 626 StringBuilder builder = new StringBuilder(); 627 builder.append(mPeriodCount); 628 builder.append(":"); 629 for(int i=0; i < mPeriodCount; i++) { 630 builder.append(mPeriodRxData[i]); 631 builder.append(":"); 632 } 633 for(int i=0; i < mPeriodCount; i++) { 634 builder.append(mPeriodTxData[i]); 635 builder.append(":"); 636 } 637 builder.append(mCurrentPeriod); 638 builder.append(":"); 639 builder.append(mPeriodStart.getTimeInMillis()); 640 builder.append(":"); 641 builder.append(mPeriodEnd.getTimeInMillis()); 642 builder.append(":"); 643 644 SharedPreferences.Editor editor = mSharedPreferences.edit(); 645 646 editor.putString("Data", builder.toString()); 647 editor.commit(); 648 } 649 650 private void retrieve() { 651 String data = mSharedPreferences.getString("Data", ""); 652// String data = Settings.Secure.getString(mContext.getContentResolver(), 653// Settings.Secure.THROTTLE_VALUE); 654 if (data == null || data.length() == 0) return; 655 656 synchronized (mParent) { 657 String[] parsed = data.split(":"); 658 int parsedUsed = 0; 659 if (parsed.length < 6) return; 660 661 mPeriodCount = Integer.parseInt(parsed[parsedUsed++]); 662 if (parsed.length != 4 + (2 * mPeriodCount)) return; 663 664 mPeriodRxData = new long[mPeriodCount]; 665 for(int i=0; i < mPeriodCount; i++) { 666 mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]); 667 } 668 mPeriodTxData = new long[mPeriodCount]; 669 for(int i=0; i < mPeriodCount; i++) { 670 mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]); 671 } 672 mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]); 673 mPeriodStart = new GregorianCalendar(); 674 mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++])); 675 mPeriodEnd = new GregorianCalendar(); 676 mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++])); 677 } 678 } 679 680 long getPeriodRx(int which) { 681 if (DBG) { // TODO - remove 682 Slog.d(TAG, "reading slot "+ which +" with current =" + mCurrentPeriod); 683 for(int x = 0; x<mPeriodCount; x++) { 684 Slog.d(TAG, " " + x + " = " + mPeriodRxData[x]); 685 } 686 } 687 synchronized (mParent) { 688 if (which > mPeriodCount) return 0; 689 which = mCurrentPeriod - which; 690 if (which < 0) which += mPeriodCount; 691 return mPeriodRxData[which]; 692 } 693 } 694 long getPeriodTx(int which) { 695 synchronized (mParent) { 696 if (which > mPeriodCount) return 0; 697 which = mCurrentPeriod - which; 698 if (which < 0) which += mPeriodCount; 699 return mPeriodTxData[which]; 700 } 701 } 702 } 703 704 @Override 705 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 706 if (mContext.checkCallingOrSelfPermission( 707 android.Manifest.permission.DUMP) 708 != PackageManager.PERMISSION_GRANTED) { 709 pw.println("Permission Denial: can't dump ThrottleService " + 710 "from from pid=" + Binder.getCallingPid() + ", uid=" + 711 Binder.getCallingUid()); 712 return; 713 } 714 pw.println(); 715 716 pw.println("The threshold is " + mPolicyThreshold + 717 ", after which you experince throttling to " + 718 mPolicyThrottleValue + "kbps"); 719 pw.println("Current period is " + 720 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " + 721 "and ends in " + (mRecorder.getPeriodEnd() - System.currentTimeMillis()) / 1000 + 722 " seconds."); 723 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds"); 724 for (int i = 0; i < mRecorder.getPeriodCount(); i++) { 725 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" + 726 mRecorder.getPeriodTx(i)); 727 } 728 } 729} 730