blob: 71557f5c8a22c8861db29ee686b317ad042c1770 [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
Robert Greenwalt9e696c22010-04-01 14:45:18 -070069 private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070070 private static final int TESTING_RESET_PERIOD_SEC = 60 * 3;
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070071 private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070072
73 private static final int PERIOD_COUNT = 6;
74
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070075 private int mPolicyPollPeriodSec;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070076 private long mPolicyThreshold;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070077 private int mPolicyThrottleValue;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070078 private int mPolicyResetDay; // 1-28
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070079 private int mPolicyNotificationsAllowedMask;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070080
81 private long mLastRead; // read byte count from last poll
82 private long mLastWrite; // write byte count from last poll
83
84 private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL";
85 private static int POLL_REQUEST = 0;
86 private PendingIntent mPendingPollIntent;
87 private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET";
88 private static int RESET_REQUEST = 1;
89 private PendingIntent mPendingResetIntent;
90
91 private INetworkManagementService mNMService;
92 private AlarmManager mAlarmManager;
93 private NotificationManager mNotificationManager;
94
95 private DataRecorder mRecorder;
96
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070097 private String mIface;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070098
99 private static final int NOTIFICATION_WARNING = 2;
100 private static final int NOTIFICATION_ALL = 0xFFFFFFFF;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700101
102 private Notification mThrottlingNotification;
103 private boolean mWarningNotificationSent = false;
104
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700105 private SettingsObserver mSettingsObserver;
106
107 private int mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
108 private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
109 private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
110
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700111 public ThrottleService(Context context) {
112 if (DBG) Slog.d(TAG, "Starting ThrottleService");
113 mContext = context;
114
115 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
116 Intent pollIntent = new Intent(ACTION_POLL, null);
117 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
118 Intent resetIntent = new Intent(ACTION_RESET, null);
119 mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
120
121 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
122 mNMService = INetworkManagementService.Stub.asInterface(b);
123
124 mNotificationManager = (NotificationManager)mContext.getSystemService(
125 Context.NOTIFICATION_SERVICE);
126 }
127
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700128 private static class SettingsObserver extends ContentObserver {
129 private int mMsg;
130 private Handler mHandler;
131 SettingsObserver(Handler handler, int msg) {
132 super(handler);
133 mHandler = handler;
134 mMsg = msg;
135 }
136
137 void observe(Context context) {
138 ContentResolver resolver = context.getContentResolver();
139 resolver.registerContentObserver(Settings.Secure.getUriFor(
140 Settings.Secure.THROTTLE_POLLING_SEC), false, this);
141 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700142 Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700143 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700144 Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700145 resolver.registerContentObserver(Settings.Secure.getUriFor(
146 Settings.Secure.THROTTLE_RESET_DAY), false, this);
147 resolver.registerContentObserver(Settings.Secure.getUriFor(
148 Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this);
149 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700150 Settings.Secure.THROTTLE_HELP_URI), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700151 }
152
153 @Override
154 public void onChange(boolean selfChange) {
155 mHandler.obtainMessage(mMsg).sendToTarget();
156 }
157 }
158
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700159 private void enforceAccessPermission() {
160 mContext.enforceCallingOrSelfPermission(
161 android.Manifest.permission.ACCESS_NETWORK_STATE,
162 "ThrottleService");
163 }
164
165 public synchronized long getResetTime(String iface) {
166 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700167 if ((iface != null) &&
168 iface.equals(mIface) &&
169 (mRecorder != null)) {
170 mRecorder.getPeriodEnd();
171 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700172 return 0;
173 }
174 public synchronized long getPeriodStartTime(String iface) {
175 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700176 if ((iface != null) &&
177 iface.equals(mIface) &&
178 (mRecorder != null)) {
179 mRecorder.getPeriodStart();
180 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700181 return 0;
182 }
183 //TODO - a better name? getCliffByteCountThreshold?
184 public synchronized long getCliffThreshold(String iface, int cliff) {
185 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700186 if ((iface != null) && (cliff == 1) && iface.equals(mIface)) {
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 Greenwaltd3bb93f2010-04-12 19:20:55 -0700194 if ((iface != null) && (cliff == 1) && iface.equals(mIface)) {
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();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700208 if ((iface != null) &&
209 iface.equals(mIface) &&
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700210 (period == ThrottleManager.PERIOD_CYCLE) &&
211 (mRecorder != null)) {
212 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
213 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
214 }
215 return 0;
216 }
217
218 // TODO - a better name - getCurrentThrottleRate?
219 public synchronized int getThrottle(String iface) {
220 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700221 if ((iface != null) && iface.equals(mIface) && (mThrottleIndex == 1)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700222 return mPolicyThrottleValue;
223 }
224 return 0;
225 }
226
227 void systemReady() {
228 if (DBG) Slog.d(TAG, "systemReady");
229 mContext.registerReceiver(
230 new BroadcastReceiver() {
231 @Override
232 public void onReceive(Context context, Intent intent) {
233 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
234 }
235 }, new IntentFilter(ACTION_POLL));
236
237 mContext.registerReceiver(
238 new BroadcastReceiver() {
239 @Override
240 public void onReceive(Context context, Intent intent) {
241 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
242 }
243 }, new IntentFilter(ACTION_RESET));
244
245 // use a new thread as we don't want to stall the system for file writes
246 mThread = new HandlerThread(TAG);
247 mThread.start();
248 mHandler = new MyHandler(mThread.getLooper());
249 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700250
251 mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
252 mSettingsObserver.observe(mContext);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700253 }
254
255
256 private static final int EVENT_REBOOT_RECOVERY = 0;
257 private static final int EVENT_POLICY_CHANGED = 1;
258 private static final int EVENT_POLL_ALARM = 2;
259 private static final int EVENT_RESET_ALARM = 3;
260 private class MyHandler extends Handler {
261 public MyHandler(Looper l) {
262 super(l);
263 }
264
265 @Override
266 public void handleMessage(Message msg) {
267 switch (msg.what) {
268 case EVENT_REBOOT_RECOVERY:
269 onRebootRecovery();
270 break;
271 case EVENT_POLICY_CHANGED:
272 onPolicyChanged();
273 break;
274 case EVENT_POLL_ALARM:
275 onPollAlarm();
276 break;
277 case EVENT_RESET_ALARM:
278 onResetAlarm();
279 }
280 }
281
282 private void onRebootRecovery() {
283 if (DBG) Slog.d(TAG, "onRebootRecovery");
284 // check for sim change TODO
285 // reregister for notification of policy change
286
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700287 mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700288
289 mRecorder = new DataRecorder(mContext, ThrottleService.this);
290
291 // get policy
292 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
293
294 // evaluate current conditions
295 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
296 }
297
298 private void onSimChange() {
299 // TODO
300 }
301
302 // check for new policy info (threshold limit/value/etc)
303 private void onPolicyChanged() {
304 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
305
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700306 int pollingPeriod = mContext.getResources().getInteger(
307 com.android.internal.R.integer.config_datause_polling_period_sec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700308 mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
309 Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
310
311 // TODO - remove testing stuff?
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700312 long defaultThreshold = mContext.getResources().getInteger(
313 com.android.internal.R.integer.config_datause_threshold_bytes);
314 int defaultValue = mContext.getResources().getInteger(
315 com.android.internal.R.integer.config_datause_throttle_kbitsps);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700316 synchronized (ThrottleService.this) {
317 mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700318 Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700319 mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700320 Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
321 if (testing) {
322 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
323 mPolicyThreshold = TESTING_THRESHOLD;
324 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700325 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700326
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700327 mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
328 Settings.Secure.THROTTLE_RESET_DAY, -1);
329 if (mPolicyResetDay == -1 ||
330 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
331 Random g = new Random();
332 mPolicyResetDay = 1 + g.nextInt(28); // 1-28
333 Settings.Secure.putInt(mContext.getContentResolver(),
334 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
335 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700336 mIface = mContext.getResources().getString(
337 com.android.internal.R.string.config_datause_iface);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700338 synchronized (ThrottleService.this) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700339 if (mIface == null) {
340 mPolicyThreshold = 0;
341 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700342 }
343
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700344 int defaultNotificationType = mContext.getResources().getInteger(
345 com.android.internal.R.integer.config_datause_notification_type);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700346 mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700347 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700348
349 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + mPolicyPollPeriodSec +
350 ", threshold=" + mPolicyThreshold + ", value=" + mPolicyThrottleValue +
351 ", resetDay=" + mPolicyResetDay + ", noteType=" +
352 mPolicyNotificationsAllowedMask);
353
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700354 onResetAlarm();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700355
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700356 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
357 mContext.sendBroadcast(broadcast);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700358 }
359
360 private void onPollAlarm() {
361 long now = SystemClock.elapsedRealtime();
362 long next = now + mPolicyPollPeriodSec*1000;
363 long incRead = 0;
364 long incWrite = 0;
365 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700366 incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
367 incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700368 } catch (RemoteException e) {
369 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
370 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700371 // don't count this data if we're roaming.
372 boolean roaming = "true".equals(
373 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
374 if (!roaming) {
375 mRecorder.addData(incRead, incWrite);
376 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700377
378 long periodRx = mRecorder.getPeriodRx(0);
379 long periodTx = mRecorder.getPeriodTx(0);
380 long total = periodRx + periodTx;
381 if (DBG) {
Robert Greenwalt5f996892010-04-08 16:19:24 -0700382 Slog.d(TAG, "onPollAlarm - now =" + now + ", roaming =" + roaming +
383 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700384 }
385 mLastRead += incRead;
386 mLastWrite += incWrite;
387
388 checkThrottleAndPostNotification(total);
389
390 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
391 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
392 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
393 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, mRecorder.getPeriodStart());
394 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, mRecorder.getPeriodEnd());
395 mContext.sendStickyBroadcast(broadcast);
396
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700397 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700398 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent);
399 }
400
401 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700402 // is throttling enabled?
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700403 if (mPolicyThreshold == 0)
404 return;
405
406 // check if we need to throttle
407 if (currentTotal > mPolicyThreshold) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700408 if (mThrottleIndex != 1) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700409 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700410 mThrottleIndex = 1;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700411 }
412 if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!");
413 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700414 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700415 mPolicyThrottleValue, mPolicyThrottleValue);
416 } catch (Exception e) {
417 Slog.e(TAG, "error setting Throttle: " + e);
418 }
419
420 mNotificationManager.cancel(com.android.internal.R.drawable.
421 stat_sys_throttle_warning);
422
423 postNotification(com.android.internal.R.string.throttled_notification_title,
424 com.android.internal.R.string.throttled_notification_message,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700425 com.android.internal.R.drawable.stat_sys_throttled,
426 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700427
428 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
429 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue);
430 mContext.sendStickyBroadcast(broadcast);
431
432 } // else already up!
433 } else {
434 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
435 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700436 // pretend we only have 1/2 the time remaining that we actually do
437 // if our burn rate in the period so far would have us exceed the limit
438 // in that 1/2 window, warn the user.
439 // this gets more generous in the early to middle period and converges back
440 // to the limit as we move toward the period end.
441
442 // adding another factor - it must be greater than the total cap/4
443 // else we may get false alarms very early in the period.. in the first
444 // tenth of a percent of the period if we used more than a tenth of a percent
445 // of the cap we'd get a warning and that's not desired.
446 long start = mRecorder.getPeriodStart();
447 long end = mRecorder.getPeriodEnd();
448 long periodLength = end - start;
449 long now = System.currentTimeMillis();
450 long timeUsed = now - start;
451 long warningThreshold = 2*mPolicyThreshold*timeUsed/(timeUsed+periodLength);
452 if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) {
453 if (mWarningNotificationSent == false) {
454 mWarningNotificationSent = true;
455 mNotificationManager.cancel(com.android.internal.R.drawable.
456 stat_sys_throttle_warning);
457 postNotification(com.android.internal.R.string.
458 throttle_warning_notification_title,
459 com.android.internal.R.string.
460 throttle_warning_notification_message,
461 com.android.internal.R.drawable.stat_sys_throttle_warning,
462 0);
463 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700464 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700465 if (mWarningNotificationSent == true) {
466 mNotificationManager.cancel(com.android.internal.R.drawable.
467 stat_sys_throttle_warning);
468 mWarningNotificationSent =false;
469 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700470 }
471 }
472 }
473 }
474
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700475 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700476 Intent intent = new Intent();
477 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700478 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700479 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
480
481 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
482
483 Resources r = Resources.getSystem();
484 CharSequence title = r.getText(titleInt);
485 CharSequence message = r.getText(messageInt);
486 if (mThrottlingNotification == null) {
487 mThrottlingNotification = new Notification();
488 mThrottlingNotification.when = 0;
489 // TODO - fixup icon
490 mThrottlingNotification.icon = icon;
491 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700492 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700493 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700494 mThrottlingNotification.tickerText = title;
495 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
496
497 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
498 }
499
500
501 private synchronized void clearThrottleAndNotification() {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700502 if (mThrottleIndex != THROTTLE_INDEX_UNTHROTTLED) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700503 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700504 mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700505 }
506 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700507 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700508 } catch (Exception e) {
509 Slog.e(TAG, "error clearing Throttle: " + e);
510 }
511 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
512 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
513 mContext.sendStickyBroadcast(broadcast);
514 }
515 mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttle_warning);
516 mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttled);
517 mWarningNotificationSent = false;
518 }
519
520 private Calendar calculatePeriodEnd() {
521 Calendar end = GregorianCalendar.getInstance();
522 int day = end.get(Calendar.DAY_OF_MONTH);
523 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
524 end.set(Calendar.HOUR_OF_DAY, 0);
525 end.set(Calendar.MINUTE, 0);
526 if (day >= mPolicyResetDay) {
527 int month = end.get(Calendar.MONTH);
528 if (month == Calendar.DECEMBER) {
529 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
530 month = Calendar.JANUARY - 1;
531 }
532 end.set(Calendar.MONTH, month + 1);
533 }
534
535 // TODO - remove!
536 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
537 end = GregorianCalendar.getInstance();
538 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
539 }
540 return end;
541 }
542 private Calendar calculatePeriodStart(Calendar end) {
543 Calendar start = (Calendar)end.clone();
544 int month = end.get(Calendar.MONTH);
545 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
546 month = Calendar.DECEMBER + 1;
547 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
548 }
549 start.set(Calendar.MONTH, month - 1);
550
551 // TODO - remove!!
552 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
553 start = (Calendar)end.clone();
554 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
555 }
556 return start;
557 }
558
559 private void onResetAlarm() {
560 if (DBG) {
561 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
562 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
563 }
564
565 Calendar end = calculatePeriodEnd();
566 Calendar start = calculatePeriodStart(end);
567
568 clearThrottleAndNotification();
569
570 mRecorder.setNextPeriod(start,end);
571
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700572 mAlarmManager.cancel(mPendingResetIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700573 mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(),
574 mPendingResetIntent);
575 }
576 }
577
578 // records bytecount data for a given time and accumulates it into larger time windows
579 // for logging and other purposes
580 //
581 // since time can be changed (user or network action) we will have to track the time of the
582 // last recording and deal with it.
583 private static class DataRecorder {
584 long[] mPeriodRxData;
585 long[] mPeriodTxData;
586 int mCurrentPeriod;
587 int mPeriodCount;
588
589 Calendar mPeriodStart;
590 Calendar mPeriodEnd;
591
592 ThrottleService mParent;
593 Context mContext;
594 SharedPreferences mSharedPreferences;
595
596 DataRecorder(Context context, ThrottleService parent) {
597 mContext = context;
598 mParent = parent;
599
600 synchronized (mParent) {
601 mPeriodCount = 6;
602 mPeriodRxData = new long[mPeriodCount];
603 mPeriodTxData = new long[mPeriodCount];
604
605 mPeriodStart = Calendar.getInstance();
606 mPeriodEnd = Calendar.getInstance();
607
608 mSharedPreferences = mContext.getSharedPreferences("ThrottleData",
609 android.content.Context.MODE_PRIVATE);
610
611 zeroData(0);
612 retrieve();
613 }
614 }
615
616 void setNextPeriod(Calendar start, Calendar end) {
617 if (DBG) {
618 Slog.d(TAG, "setting next period to " + start.getTimeInMillis() +
619 " --until-- " + end.getTimeInMillis());
620 }
621 // if we roll back in time to a previous period, toss out the current data
622 // if we roll forward to the next period, advance to the next
623
624 if (end.before(mPeriodStart)) {
625 if (DBG) {
626 Slog.d(TAG, " old start was " + mPeriodStart.getTimeInMillis() + ", wiping");
627 }
628 synchronized (mParent) {
629 mPeriodRxData[mCurrentPeriod] = 0;
630 mPeriodTxData[mCurrentPeriod] = 0;
631 }
632 } else if(start.after(mPeriodEnd)) {
633 if (DBG) {
634 Slog.d(TAG, " old end was " + mPeriodEnd.getTimeInMillis() + ", following");
635 }
636 synchronized (mParent) {
637 ++mCurrentPeriod;
638 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
639 mPeriodRxData[mCurrentPeriod] = 0;
640 mPeriodTxData[mCurrentPeriod] = 0;
641 }
642 } else {
643 if (DBG) Slog.d(TAG, " we fit - ammending to last period");
644 }
645 setPeriodStart(start);
646 setPeriodEnd(end);
647 record();
648 }
649
650 public long getPeriodEnd() {
651 synchronized (mParent) {
652 return mPeriodEnd.getTimeInMillis();
653 }
654 }
655
656 private void setPeriodEnd(Calendar end) {
657 synchronized (mParent) {
658 mPeriodEnd = end;
659 }
660 }
661
662 public long getPeriodStart() {
663 synchronized (mParent) {
664 return mPeriodStart.getTimeInMillis();
665 }
666 }
667
668 private void setPeriodStart(Calendar start) {
669 synchronized (mParent) {
670 mPeriodStart = start;
671 }
672 }
673
674 public int getPeriodCount() {
675 synchronized (mParent) {
676 return mPeriodCount;
677 }
678 }
679
680 private void zeroData(int field) {
681 synchronized (mParent) {
682 for(int period = 0; period<mPeriodCount; period++) {
683 mPeriodRxData[period] = 0;
684 mPeriodTxData[period] = 0;
685 }
686 mCurrentPeriod = 0;
687 }
688
689 }
690
691 // if time moves backward accumulate all read/write that's lost into the now
692 // otherwise time moved forward.
693 void addData(long bytesRead, long bytesWritten) {
694 synchronized (mParent) {
695 mPeriodRxData[mCurrentPeriod] += bytesRead;
696 mPeriodTxData[mCurrentPeriod] += bytesWritten;
697 }
698 record();
699 }
700
701 private void record() {
702 // serialize into a secure setting
703
704 // 1 int mPeriodCount
705 // 13*6 long[PERIOD_COUNT] mPeriodRxData
706 // 13*6 long[PERIOD_COUNT] mPeriodTxData
707 // 1 int mCurrentPeriod
708 // 13 long periodStartMS
709 // 13 long periodEndMS
710 // 199 chars max
711 StringBuilder builder = new StringBuilder();
712 builder.append(mPeriodCount);
713 builder.append(":");
714 for(int i=0; i < mPeriodCount; i++) {
715 builder.append(mPeriodRxData[i]);
716 builder.append(":");
717 }
718 for(int i=0; i < mPeriodCount; i++) {
719 builder.append(mPeriodTxData[i]);
720 builder.append(":");
721 }
722 builder.append(mCurrentPeriod);
723 builder.append(":");
724 builder.append(mPeriodStart.getTimeInMillis());
725 builder.append(":");
726 builder.append(mPeriodEnd.getTimeInMillis());
727 builder.append(":");
728
729 SharedPreferences.Editor editor = mSharedPreferences.edit();
730
731 editor.putString("Data", builder.toString());
732 editor.commit();
733 }
734
735 private void retrieve() {
736 String data = mSharedPreferences.getString("Data", "");
737// String data = Settings.Secure.getString(mContext.getContentResolver(),
738// Settings.Secure.THROTTLE_VALUE);
739 if (data == null || data.length() == 0) return;
740
741 synchronized (mParent) {
742 String[] parsed = data.split(":");
743 int parsedUsed = 0;
744 if (parsed.length < 6) return;
745
746 mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
747 if (parsed.length != 4 + (2 * mPeriodCount)) return;
748
749 mPeriodRxData = new long[mPeriodCount];
750 for(int i=0; i < mPeriodCount; i++) {
751 mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
752 }
753 mPeriodTxData = new long[mPeriodCount];
754 for(int i=0; i < mPeriodCount; i++) {
755 mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
756 }
757 mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]);
758 mPeriodStart = new GregorianCalendar();
759 mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
760 mPeriodEnd = new GregorianCalendar();
761 mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
762 }
763 }
764
765 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700766 synchronized (mParent) {
767 if (which > mPeriodCount) return 0;
768 which = mCurrentPeriod - which;
769 if (which < 0) which += mPeriodCount;
770 return mPeriodRxData[which];
771 }
772 }
773 long getPeriodTx(int which) {
774 synchronized (mParent) {
775 if (which > mPeriodCount) return 0;
776 which = mCurrentPeriod - which;
777 if (which < 0) which += mPeriodCount;
778 return mPeriodTxData[which];
779 }
780 }
781 }
782
783 @Override
784 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
785 if (mContext.checkCallingOrSelfPermission(
786 android.Manifest.permission.DUMP)
787 != PackageManager.PERMISSION_GRANTED) {
788 pw.println("Permission Denial: can't dump ThrottleService " +
789 "from from pid=" + Binder.getCallingPid() + ", uid=" +
790 Binder.getCallingUid());
791 return;
792 }
793 pw.println();
794
795 pw.println("The threshold is " + mPolicyThreshold +
796 ", after which you experince throttling to " +
797 mPolicyThrottleValue + "kbps");
798 pw.println("Current period is " +
799 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
800 "and ends in " + (mRecorder.getPeriodEnd() - System.currentTimeMillis()) / 1000 +
801 " seconds.");
802 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
803 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
804 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
805 mRecorder.getPeriodTx(i));
806 }
807 }
808}