blob: 16fd104debc7a10989d87f4e0a0fcdde1b4c20b5 [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;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -070031import android.database.ContentObserver;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070032import android.net.IThrottleManager;
33import android.net.ThrottleManager;
34import android.os.Binder;
Robert Greenwaltb8912f52010-04-09 17:27:26 -070035import android.os.Environment;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070036import 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 Greenwaltb8912f52010-04-09 17:27:26 -070051import java.io.BufferedWriter;
52import java.io.File;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070053import java.io.FileDescriptor;
Robert Greenwaltb8912f52010-04-09 17:27:26 -070054import java.io.FileInputStream;
55import java.io.FileWriter;
56import java.io.IOException;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070057import java.io.PrintWriter;
58import java.util.Calendar;
59import java.util.GregorianCalendar;
60import java.util.Random;
61
62// TODO - add comments - reference the ThrottleManager for public API
63public class ThrottleService extends IThrottleManager.Stub {
64
65 private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing";
66
67 private static final String TAG = "ThrottleService";
68 private static boolean DBG = true;
69 private Handler mHandler;
70 private HandlerThread mThread;
71
72 private Context mContext;
73
Robert Greenwalt9e696c22010-04-01 14:45:18 -070074 private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070075 private static final int TESTING_RESET_PERIOD_SEC = 60 * 3;
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070076 private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070077
78 private static final int PERIOD_COUNT = 6;
79
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070080 private int mPolicyPollPeriodSec;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070081 private long mPolicyThreshold;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070082 private int mPolicyThrottleValue;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070083 private int mPolicyResetDay; // 1-28
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070084 private int mPolicyNotificationsAllowedMask;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070085
86 private long mLastRead; // read byte count from last poll
87 private long mLastWrite; // write byte count from last poll
88
89 private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL";
90 private static int POLL_REQUEST = 0;
91 private PendingIntent mPendingPollIntent;
92 private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET";
93 private static int RESET_REQUEST = 1;
94 private PendingIntent mPendingResetIntent;
95
96 private INetworkManagementService mNMService;
97 private AlarmManager mAlarmManager;
98 private NotificationManager mNotificationManager;
99
100 private DataRecorder mRecorder;
101
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700102 private String mIface;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700103
104 private static final int NOTIFICATION_WARNING = 2;
105 private static final int NOTIFICATION_ALL = 0xFFFFFFFF;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700106
107 private Notification mThrottlingNotification;
108 private boolean mWarningNotificationSent = false;
109
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700110 private SettingsObserver mSettingsObserver;
111
112 private int mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
113 private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
114 private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
115
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700116 public ThrottleService(Context context) {
117 if (DBG) Slog.d(TAG, "Starting ThrottleService");
118 mContext = context;
119
120 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
121 Intent pollIntent = new Intent(ACTION_POLL, null);
122 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
123 Intent resetIntent = new Intent(ACTION_RESET, null);
124 mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
125
126 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
127 mNMService = INetworkManagementService.Stub.asInterface(b);
128
129 mNotificationManager = (NotificationManager)mContext.getSystemService(
130 Context.NOTIFICATION_SERVICE);
131 }
132
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700133 private static class SettingsObserver extends ContentObserver {
134 private int mMsg;
135 private Handler mHandler;
136 SettingsObserver(Handler handler, int msg) {
137 super(handler);
138 mHandler = handler;
139 mMsg = msg;
140 }
141
142 void observe(Context context) {
143 ContentResolver resolver = context.getContentResolver();
144 resolver.registerContentObserver(Settings.Secure.getUriFor(
145 Settings.Secure.THROTTLE_POLLING_SEC), false, this);
146 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700147 Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700148 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700149 Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700150 resolver.registerContentObserver(Settings.Secure.getUriFor(
151 Settings.Secure.THROTTLE_RESET_DAY), false, this);
152 resolver.registerContentObserver(Settings.Secure.getUriFor(
153 Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this);
154 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700155 Settings.Secure.THROTTLE_HELP_URI), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700156 }
157
158 @Override
159 public void onChange(boolean selfChange) {
160 mHandler.obtainMessage(mMsg).sendToTarget();
161 }
162 }
163
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700164 private void enforceAccessPermission() {
165 mContext.enforceCallingOrSelfPermission(
166 android.Manifest.permission.ACCESS_NETWORK_STATE,
167 "ThrottleService");
168 }
169
170 public synchronized long getResetTime(String iface) {
171 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700172 if ((iface != null) &&
173 iface.equals(mIface) &&
174 (mRecorder != null)) {
175 mRecorder.getPeriodEnd();
176 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700177 return 0;
178 }
179 public synchronized long getPeriodStartTime(String iface) {
180 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700181 if ((iface != null) &&
182 iface.equals(mIface) &&
183 (mRecorder != null)) {
184 mRecorder.getPeriodStart();
185 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700186 return 0;
187 }
188 //TODO - a better name? getCliffByteCountThreshold?
189 public synchronized long getCliffThreshold(String iface, int cliff) {
190 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700191 if ((iface != null) && (cliff == 1) && iface.equals(mIface)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700192 return mPolicyThreshold;
193 }
194 return 0;
195 }
196 // TODO - a better name? getThrottleRate?
197 public synchronized int getCliffLevel(String iface, int cliff) {
198 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700199 if ((iface != null) && (cliff == 1) && iface.equals(mIface)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700200 return mPolicyThrottleValue;
201 }
202 return 0;
203 }
204
Irfan Sheriffc9b68512010-04-08 14:12:33 -0700205 public String getHelpUri() {
206 enforceAccessPermission();
207 return Settings.Secure.getString(mContext.getContentResolver(),
208 Settings.Secure.THROTTLE_HELP_URI);
209 }
210
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700211 public synchronized long getByteCount(String iface, int dir, int period, int ago) {
212 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700213 if ((iface != null) &&
214 iface.equals(mIface) &&
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700215 (period == ThrottleManager.PERIOD_CYCLE) &&
216 (mRecorder != null)) {
217 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
218 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
219 }
220 return 0;
221 }
222
223 // TODO - a better name - getCurrentThrottleRate?
224 public synchronized int getThrottle(String iface) {
225 enforceAccessPermission();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700226 if ((iface != null) && iface.equals(mIface) && (mThrottleIndex == 1)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700227 return mPolicyThrottleValue;
228 }
229 return 0;
230 }
231
232 void systemReady() {
233 if (DBG) Slog.d(TAG, "systemReady");
234 mContext.registerReceiver(
235 new BroadcastReceiver() {
236 @Override
237 public void onReceive(Context context, Intent intent) {
238 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
239 }
240 }, new IntentFilter(ACTION_POLL));
241
242 mContext.registerReceiver(
243 new BroadcastReceiver() {
244 @Override
245 public void onReceive(Context context, Intent intent) {
246 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
247 }
248 }, new IntentFilter(ACTION_RESET));
249
250 // use a new thread as we don't want to stall the system for file writes
251 mThread = new HandlerThread(TAG);
252 mThread.start();
253 mHandler = new MyHandler(mThread.getLooper());
254 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700255
256 mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
257 mSettingsObserver.observe(mContext);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700258 }
259
260
261 private static final int EVENT_REBOOT_RECOVERY = 0;
262 private static final int EVENT_POLICY_CHANGED = 1;
263 private static final int EVENT_POLL_ALARM = 2;
264 private static final int EVENT_RESET_ALARM = 3;
265 private class MyHandler extends Handler {
266 public MyHandler(Looper l) {
267 super(l);
268 }
269
270 @Override
271 public void handleMessage(Message msg) {
272 switch (msg.what) {
273 case EVENT_REBOOT_RECOVERY:
274 onRebootRecovery();
275 break;
276 case EVENT_POLICY_CHANGED:
277 onPolicyChanged();
278 break;
279 case EVENT_POLL_ALARM:
280 onPollAlarm();
281 break;
282 case EVENT_RESET_ALARM:
283 onResetAlarm();
284 }
285 }
286
287 private void onRebootRecovery() {
288 if (DBG) Slog.d(TAG, "onRebootRecovery");
289 // check for sim change TODO
290 // reregister for notification of policy change
291
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700292 mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700293
294 mRecorder = new DataRecorder(mContext, ThrottleService.this);
295
296 // get policy
297 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
298
299 // evaluate current conditions
300 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
301 }
302
303 private void onSimChange() {
304 // TODO
305 }
306
307 // check for new policy info (threshold limit/value/etc)
308 private void onPolicyChanged() {
309 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
310
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700311 int pollingPeriod = mContext.getResources().getInteger(
312 com.android.internal.R.integer.config_datause_polling_period_sec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700313 mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
314 Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
315
316 // TODO - remove testing stuff?
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700317 long defaultThreshold = mContext.getResources().getInteger(
318 com.android.internal.R.integer.config_datause_threshold_bytes);
319 int defaultValue = mContext.getResources().getInteger(
320 com.android.internal.R.integer.config_datause_throttle_kbitsps);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700321 synchronized (ThrottleService.this) {
322 mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700323 Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700324 mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700325 Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
326 if (testing) {
327 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
328 mPolicyThreshold = TESTING_THRESHOLD;
329 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700330 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700331
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700332 mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
333 Settings.Secure.THROTTLE_RESET_DAY, -1);
334 if (mPolicyResetDay == -1 ||
335 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
336 Random g = new Random();
337 mPolicyResetDay = 1 + g.nextInt(28); // 1-28
338 Settings.Secure.putInt(mContext.getContentResolver(),
339 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
340 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700341 mIface = mContext.getResources().getString(
342 com.android.internal.R.string.config_datause_iface);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700343 synchronized (ThrottleService.this) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700344 if (mIface == null) {
345 mPolicyThreshold = 0;
346 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700347 }
348
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700349 int defaultNotificationType = mContext.getResources().getInteger(
350 com.android.internal.R.integer.config_datause_notification_type);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700351 mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700352 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700353
354 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + mPolicyPollPeriodSec +
355 ", threshold=" + mPolicyThreshold + ", value=" + mPolicyThrottleValue +
356 ", resetDay=" + mPolicyResetDay + ", noteType=" +
357 mPolicyNotificationsAllowedMask);
358
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700359 onResetAlarm();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700360
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700361 onPollAlarm();
362
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700363 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
364 mContext.sendBroadcast(broadcast);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700365 }
366
367 private void onPollAlarm() {
368 long now = SystemClock.elapsedRealtime();
369 long next = now + mPolicyPollPeriodSec*1000;
370 long incRead = 0;
371 long incWrite = 0;
372 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700373 incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
374 incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700375 } catch (RemoteException e) {
376 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
377 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700378 // don't count this data if we're roaming.
379 boolean roaming = "true".equals(
380 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
381 if (!roaming) {
382 mRecorder.addData(incRead, incWrite);
383 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700384
385 long periodRx = mRecorder.getPeriodRx(0);
386 long periodTx = mRecorder.getPeriodTx(0);
387 long total = periodRx + periodTx;
388 if (DBG) {
Robert Greenwalt5f996892010-04-08 16:19:24 -0700389 Slog.d(TAG, "onPollAlarm - now =" + now + ", roaming =" + roaming +
390 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700391 }
392 mLastRead += incRead;
393 mLastWrite += incWrite;
394
395 checkThrottleAndPostNotification(total);
396
397 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
398 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
399 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
400 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, mRecorder.getPeriodStart());
401 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, mRecorder.getPeriodEnd());
402 mContext.sendStickyBroadcast(broadcast);
403
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700404 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700405 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent);
406 }
407
408 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700409 // is throttling enabled?
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700410 if (mPolicyThreshold == 0)
411 return;
412
413 // check if we need to throttle
414 if (currentTotal > mPolicyThreshold) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700415 if (mThrottleIndex != 1) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700416 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700417 mThrottleIndex = 1;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700418 }
419 if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!");
420 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700421 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700422 mPolicyThrottleValue, mPolicyThrottleValue);
423 } catch (Exception e) {
424 Slog.e(TAG, "error setting Throttle: " + e);
425 }
426
427 mNotificationManager.cancel(com.android.internal.R.drawable.
428 stat_sys_throttle_warning);
429
430 postNotification(com.android.internal.R.string.throttled_notification_title,
431 com.android.internal.R.string.throttled_notification_message,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700432 com.android.internal.R.drawable.stat_sys_throttled,
433 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700434
435 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
436 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue);
437 mContext.sendStickyBroadcast(broadcast);
438
439 } // else already up!
440 } else {
441 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
442 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700443 // pretend we only have 1/2 the time remaining that we actually do
444 // if our burn rate in the period so far would have us exceed the limit
445 // in that 1/2 window, warn the user.
446 // this gets more generous in the early to middle period and converges back
447 // to the limit as we move toward the period end.
448
449 // adding another factor - it must be greater than the total cap/4
450 // else we may get false alarms very early in the period.. in the first
451 // tenth of a percent of the period if we used more than a tenth of a percent
452 // of the cap we'd get a warning and that's not desired.
453 long start = mRecorder.getPeriodStart();
454 long end = mRecorder.getPeriodEnd();
455 long periodLength = end - start;
456 long now = System.currentTimeMillis();
457 long timeUsed = now - start;
458 long warningThreshold = 2*mPolicyThreshold*timeUsed/(timeUsed+periodLength);
459 if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) {
460 if (mWarningNotificationSent == false) {
461 mWarningNotificationSent = true;
462 mNotificationManager.cancel(com.android.internal.R.drawable.
463 stat_sys_throttle_warning);
464 postNotification(com.android.internal.R.string.
465 throttle_warning_notification_title,
466 com.android.internal.R.string.
467 throttle_warning_notification_message,
468 com.android.internal.R.drawable.stat_sys_throttle_warning,
469 0);
470 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700471 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700472 if (mWarningNotificationSent == true) {
473 mNotificationManager.cancel(com.android.internal.R.drawable.
474 stat_sys_throttle_warning);
475 mWarningNotificationSent =false;
476 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700477 }
478 }
479 }
480 }
481
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700482 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700483 Intent intent = new Intent();
484 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700485 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700486 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
487
488 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
489
490 Resources r = Resources.getSystem();
491 CharSequence title = r.getText(titleInt);
492 CharSequence message = r.getText(messageInt);
493 if (mThrottlingNotification == null) {
494 mThrottlingNotification = new Notification();
495 mThrottlingNotification.when = 0;
496 // TODO - fixup icon
497 mThrottlingNotification.icon = icon;
498 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700499 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700500 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700501 mThrottlingNotification.tickerText = title;
502 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
503
504 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
505 }
506
507
508 private synchronized void clearThrottleAndNotification() {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700509 if (mThrottleIndex != THROTTLE_INDEX_UNTHROTTLED) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700510 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700511 mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700512 }
513 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700514 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700515 } catch (Exception e) {
516 Slog.e(TAG, "error clearing Throttle: " + e);
517 }
518 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
519 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
520 mContext.sendStickyBroadcast(broadcast);
521 }
522 mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttle_warning);
523 mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttled);
524 mWarningNotificationSent = false;
525 }
526
527 private Calendar calculatePeriodEnd() {
528 Calendar end = GregorianCalendar.getInstance();
529 int day = end.get(Calendar.DAY_OF_MONTH);
530 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
531 end.set(Calendar.HOUR_OF_DAY, 0);
532 end.set(Calendar.MINUTE, 0);
533 if (day >= mPolicyResetDay) {
534 int month = end.get(Calendar.MONTH);
535 if (month == Calendar.DECEMBER) {
536 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
537 month = Calendar.JANUARY - 1;
538 }
539 end.set(Calendar.MONTH, month + 1);
540 }
541
542 // TODO - remove!
543 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
544 end = GregorianCalendar.getInstance();
545 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
546 }
547 return end;
548 }
549 private Calendar calculatePeriodStart(Calendar end) {
550 Calendar start = (Calendar)end.clone();
551 int month = end.get(Calendar.MONTH);
552 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
553 month = Calendar.DECEMBER + 1;
554 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
555 }
556 start.set(Calendar.MONTH, month - 1);
557
558 // TODO - remove!!
559 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
560 start = (Calendar)end.clone();
561 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
562 }
563 return start;
564 }
565
566 private void onResetAlarm() {
567 if (DBG) {
568 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
569 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
570 }
571
572 Calendar end = calculatePeriodEnd();
573 Calendar start = calculatePeriodStart(end);
574
575 clearThrottleAndNotification();
576
577 mRecorder.setNextPeriod(start,end);
578
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700579 mAlarmManager.cancel(mPendingResetIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700580 mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(),
581 mPendingResetIntent);
582 }
583 }
584
585 // records bytecount data for a given time and accumulates it into larger time windows
586 // for logging and other purposes
587 //
588 // since time can be changed (user or network action) we will have to track the time of the
589 // last recording and deal with it.
590 private static class DataRecorder {
591 long[] mPeriodRxData;
592 long[] mPeriodTxData;
593 int mCurrentPeriod;
594 int mPeriodCount;
595
596 Calendar mPeriodStart;
597 Calendar mPeriodEnd;
598
599 ThrottleService mParent;
600 Context mContext;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700601
602 DataRecorder(Context context, ThrottleService parent) {
603 mContext = context;
604 mParent = parent;
605
606 synchronized (mParent) {
607 mPeriodCount = 6;
608 mPeriodRxData = new long[mPeriodCount];
609 mPeriodTxData = new long[mPeriodCount];
610
611 mPeriodStart = Calendar.getInstance();
612 mPeriodEnd = Calendar.getInstance();
613
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700614 zeroData(0);
615 retrieve();
616 }
617 }
618
619 void setNextPeriod(Calendar start, Calendar end) {
620 if (DBG) {
621 Slog.d(TAG, "setting next period to " + start.getTimeInMillis() +
622 " --until-- " + end.getTimeInMillis());
623 }
624 // if we roll back in time to a previous period, toss out the current data
625 // if we roll forward to the next period, advance to the next
626
627 if (end.before(mPeriodStart)) {
628 if (DBG) {
629 Slog.d(TAG, " old start was " + mPeriodStart.getTimeInMillis() + ", wiping");
630 }
631 synchronized (mParent) {
632 mPeriodRxData[mCurrentPeriod] = 0;
633 mPeriodTxData[mCurrentPeriod] = 0;
634 }
635 } else if(start.after(mPeriodEnd)) {
636 if (DBG) {
637 Slog.d(TAG, " old end was " + mPeriodEnd.getTimeInMillis() + ", following");
638 }
639 synchronized (mParent) {
640 ++mCurrentPeriod;
641 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
642 mPeriodRxData[mCurrentPeriod] = 0;
643 mPeriodTxData[mCurrentPeriod] = 0;
644 }
645 } else {
646 if (DBG) Slog.d(TAG, " we fit - ammending to last period");
647 }
648 setPeriodStart(start);
649 setPeriodEnd(end);
650 record();
651 }
652
653 public long getPeriodEnd() {
654 synchronized (mParent) {
655 return mPeriodEnd.getTimeInMillis();
656 }
657 }
658
659 private void setPeriodEnd(Calendar end) {
660 synchronized (mParent) {
661 mPeriodEnd = end;
662 }
663 }
664
665 public long getPeriodStart() {
666 synchronized (mParent) {
667 return mPeriodStart.getTimeInMillis();
668 }
669 }
670
671 private void setPeriodStart(Calendar start) {
672 synchronized (mParent) {
673 mPeriodStart = start;
674 }
675 }
676
677 public int getPeriodCount() {
678 synchronized (mParent) {
679 return mPeriodCount;
680 }
681 }
682
683 private void zeroData(int field) {
684 synchronized (mParent) {
685 for(int period = 0; period<mPeriodCount; period++) {
686 mPeriodRxData[period] = 0;
687 mPeriodTxData[period] = 0;
688 }
689 mCurrentPeriod = 0;
690 }
691
692 }
693
694 // if time moves backward accumulate all read/write that's lost into the now
695 // otherwise time moved forward.
696 void addData(long bytesRead, long bytesWritten) {
697 synchronized (mParent) {
698 mPeriodRxData[mCurrentPeriod] += bytesRead;
699 mPeriodTxData[mCurrentPeriod] += bytesWritten;
700 }
701 record();
702 }
703
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700704 private File getDataFile() {
705 File dataDir = Environment.getDataDirectory();
706 File throttleDir = new File(dataDir, "system/throttle");
707 throttleDir.mkdirs();
708 File dataFile = new File(throttleDir, "data");
709 return dataFile;
710 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700711
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700712 private static final int DATA_FILE_VERSION = 1;
713
714 private void record() {
715 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700716 // 1 int mPeriodCount
717 // 13*6 long[PERIOD_COUNT] mPeriodRxData
718 // 13*6 long[PERIOD_COUNT] mPeriodTxData
719 // 1 int mCurrentPeriod
720 // 13 long periodStartMS
721 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700722 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700723 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700724 builder.append(DATA_FILE_VERSION);
725 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700726 builder.append(mPeriodCount);
727 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700728 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700729 builder.append(mPeriodRxData[i]);
730 builder.append(":");
731 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700732 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700733 builder.append(mPeriodTxData[i]);
734 builder.append(":");
735 }
736 builder.append(mCurrentPeriod);
737 builder.append(":");
738 builder.append(mPeriodStart.getTimeInMillis());
739 builder.append(":");
740 builder.append(mPeriodEnd.getTimeInMillis());
741 builder.append(":");
742
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700743 BufferedWriter out = null;
744 try {
745 out = new BufferedWriter(new FileWriter(getDataFile()),256);
746 out.write(builder.toString());
747 } catch (IOException e) {
748 Slog.e(TAG, "Error writing data file");
749 return;
750 } finally {
751 if (out != null) {
752 try {
753 out.close();
754 } catch (Exception e) {}
755 }
756 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700757 }
758
759 private void retrieve() {
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700760 File f = getDataFile();
761 byte[] buffer;
762 FileInputStream s = null;
763 try {
764 buffer = new byte[(int)f.length()];
765 s = new FileInputStream(f);
766 s.read(buffer);
767 } catch (IOException e) {
768 Slog.e(TAG, "Error reading data file");
769 return;
770 } finally {
771 if (s != null) {
772 try {
773 s.close();
774 } catch (Exception e) {}
775 }
776 }
777 String data = new String(buffer);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700778 if (data == null || data.length() == 0) return;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700779 synchronized (mParent) {
780 String[] parsed = data.split(":");
781 int parsedUsed = 0;
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700782 if (parsed.length < 6) {
783 Slog.e(TAG, "reading data file with insufficient length - ignoring");
784 return;
785 }
786
787 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
788 Slog.e(TAG, "reading data file with bad version - ignoring");
789 return;
790 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700791
792 mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
793 if (parsed.length != 4 + (2 * mPeriodCount)) return;
794
795 mPeriodRxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700796 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700797 mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
798 }
799 mPeriodTxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700800 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700801 mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
802 }
803 mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]);
804 mPeriodStart = new GregorianCalendar();
805 mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
806 mPeriodEnd = new GregorianCalendar();
807 mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
808 }
809 }
810
811 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700812 synchronized (mParent) {
813 if (which > mPeriodCount) return 0;
814 which = mCurrentPeriod - which;
815 if (which < 0) which += mPeriodCount;
816 return mPeriodRxData[which];
817 }
818 }
819 long getPeriodTx(int which) {
820 synchronized (mParent) {
821 if (which > mPeriodCount) return 0;
822 which = mCurrentPeriod - which;
823 if (which < 0) which += mPeriodCount;
824 return mPeriodTxData[which];
825 }
826 }
827 }
828
829 @Override
830 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
831 if (mContext.checkCallingOrSelfPermission(
832 android.Manifest.permission.DUMP)
833 != PackageManager.PERMISSION_GRANTED) {
834 pw.println("Permission Denial: can't dump ThrottleService " +
835 "from from pid=" + Binder.getCallingPid() + ", uid=" +
836 Binder.getCallingUid());
837 return;
838 }
839 pw.println();
840
841 pw.println("The threshold is " + mPolicyThreshold +
842 ", after which you experince throttling to " +
843 mPolicyThrottleValue + "kbps");
844 pw.println("Current period is " +
845 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
846 "and ends in " + (mRecorder.getPeriodEnd() - System.currentTimeMillis()) / 1000 +
847 " seconds.");
848 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
849 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
850 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
851 mRecorder.getPeriodTx(i));
852 }
853 }
854}