blob: 36931b23588cb7faf4a9dc8de1ee8d4e169ab139 [file] [log] [blame]
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001/*
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}