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