blob: 9333dd828892ebe859bad7f3da67cc9ad70e3d56 [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.
Robert Greenwalt22b36442010-04-13 15:17:14 -0700428 stat_sys_throttled);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700429
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.
Robert Greenwalt22b36442010-04-13 15:17:14 -0700463 stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700464 postNotification(com.android.internal.R.string.
465 throttle_warning_notification_title,
466 com.android.internal.R.string.
467 throttle_warning_notification_message,
Robert Greenwalt22b36442010-04-13 15:17:14 -0700468 com.android.internal.R.drawable.stat_sys_throttled,
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700469 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.
Robert Greenwalt22b36442010-04-13 15:17:14 -0700474 stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700475 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 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700522 mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttled);
523 mWarningNotificationSent = false;
524 }
525
526 private Calendar calculatePeriodEnd() {
527 Calendar end = GregorianCalendar.getInstance();
528 int day = end.get(Calendar.DAY_OF_MONTH);
529 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
530 end.set(Calendar.HOUR_OF_DAY, 0);
531 end.set(Calendar.MINUTE, 0);
532 if (day >= mPolicyResetDay) {
533 int month = end.get(Calendar.MONTH);
534 if (month == Calendar.DECEMBER) {
535 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
536 month = Calendar.JANUARY - 1;
537 }
538 end.set(Calendar.MONTH, month + 1);
539 }
540
541 // TODO - remove!
542 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
543 end = GregorianCalendar.getInstance();
544 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
545 }
546 return end;
547 }
548 private Calendar calculatePeriodStart(Calendar end) {
549 Calendar start = (Calendar)end.clone();
550 int month = end.get(Calendar.MONTH);
551 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
552 month = Calendar.DECEMBER + 1;
553 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
554 }
555 start.set(Calendar.MONTH, month - 1);
556
557 // TODO - remove!!
558 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
559 start = (Calendar)end.clone();
560 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
561 }
562 return start;
563 }
564
565 private void onResetAlarm() {
566 if (DBG) {
567 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
568 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
569 }
570
571 Calendar end = calculatePeriodEnd();
572 Calendar start = calculatePeriodStart(end);
573
574 clearThrottleAndNotification();
575
576 mRecorder.setNextPeriod(start,end);
577
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700578 mAlarmManager.cancel(mPendingResetIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700579 mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(),
580 mPendingResetIntent);
581 }
582 }
583
584 // records bytecount data for a given time and accumulates it into larger time windows
585 // for logging and other purposes
586 //
587 // since time can be changed (user or network action) we will have to track the time of the
588 // last recording and deal with it.
589 private static class DataRecorder {
590 long[] mPeriodRxData;
591 long[] mPeriodTxData;
592 int mCurrentPeriod;
593 int mPeriodCount;
594
595 Calendar mPeriodStart;
596 Calendar mPeriodEnd;
597
598 ThrottleService mParent;
599 Context mContext;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700600
601 DataRecorder(Context context, ThrottleService parent) {
602 mContext = context;
603 mParent = parent;
604
605 synchronized (mParent) {
606 mPeriodCount = 6;
607 mPeriodRxData = new long[mPeriodCount];
608 mPeriodTxData = new long[mPeriodCount];
609
610 mPeriodStart = Calendar.getInstance();
611 mPeriodEnd = Calendar.getInstance();
612
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700613 zeroData(0);
614 retrieve();
615 }
616 }
617
618 void setNextPeriod(Calendar start, Calendar end) {
619 if (DBG) {
620 Slog.d(TAG, "setting next period to " + start.getTimeInMillis() +
621 " --until-- " + end.getTimeInMillis());
622 }
623 // if we roll back in time to a previous period, toss out the current data
624 // if we roll forward to the next period, advance to the next
625
626 if (end.before(mPeriodStart)) {
627 if (DBG) {
628 Slog.d(TAG, " old start was " + mPeriodStart.getTimeInMillis() + ", wiping");
629 }
630 synchronized (mParent) {
631 mPeriodRxData[mCurrentPeriod] = 0;
632 mPeriodTxData[mCurrentPeriod] = 0;
633 }
634 } else if(start.after(mPeriodEnd)) {
635 if (DBG) {
636 Slog.d(TAG, " old end was " + mPeriodEnd.getTimeInMillis() + ", following");
637 }
638 synchronized (mParent) {
639 ++mCurrentPeriod;
640 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
641 mPeriodRxData[mCurrentPeriod] = 0;
642 mPeriodTxData[mCurrentPeriod] = 0;
643 }
644 } else {
645 if (DBG) Slog.d(TAG, " we fit - ammending to last period");
646 }
647 setPeriodStart(start);
648 setPeriodEnd(end);
649 record();
650 }
651
652 public long getPeriodEnd() {
653 synchronized (mParent) {
654 return mPeriodEnd.getTimeInMillis();
655 }
656 }
657
658 private void setPeriodEnd(Calendar end) {
659 synchronized (mParent) {
660 mPeriodEnd = end;
661 }
662 }
663
664 public long getPeriodStart() {
665 synchronized (mParent) {
666 return mPeriodStart.getTimeInMillis();
667 }
668 }
669
670 private void setPeriodStart(Calendar start) {
671 synchronized (mParent) {
672 mPeriodStart = start;
673 }
674 }
675
676 public int getPeriodCount() {
677 synchronized (mParent) {
678 return mPeriodCount;
679 }
680 }
681
682 private void zeroData(int field) {
683 synchronized (mParent) {
684 for(int period = 0; period<mPeriodCount; period++) {
685 mPeriodRxData[period] = 0;
686 mPeriodTxData[period] = 0;
687 }
688 mCurrentPeriod = 0;
689 }
690
691 }
692
693 // if time moves backward accumulate all read/write that's lost into the now
694 // otherwise time moved forward.
695 void addData(long bytesRead, long bytesWritten) {
696 synchronized (mParent) {
697 mPeriodRxData[mCurrentPeriod] += bytesRead;
698 mPeriodTxData[mCurrentPeriod] += bytesWritten;
699 }
700 record();
701 }
702
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700703 private File getDataFile() {
704 File dataDir = Environment.getDataDirectory();
705 File throttleDir = new File(dataDir, "system/throttle");
706 throttleDir.mkdirs();
707 File dataFile = new File(throttleDir, "data");
708 return dataFile;
709 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700710
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700711 private static final int DATA_FILE_VERSION = 1;
712
713 private void record() {
714 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700715 // 1 int mPeriodCount
716 // 13*6 long[PERIOD_COUNT] mPeriodRxData
717 // 13*6 long[PERIOD_COUNT] mPeriodTxData
718 // 1 int mCurrentPeriod
719 // 13 long periodStartMS
720 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700721 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700722 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700723 builder.append(DATA_FILE_VERSION);
724 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700725 builder.append(mPeriodCount);
726 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700727 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700728 builder.append(mPeriodRxData[i]);
729 builder.append(":");
730 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700731 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700732 builder.append(mPeriodTxData[i]);
733 builder.append(":");
734 }
735 builder.append(mCurrentPeriod);
736 builder.append(":");
737 builder.append(mPeriodStart.getTimeInMillis());
738 builder.append(":");
739 builder.append(mPeriodEnd.getTimeInMillis());
740 builder.append(":");
741
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700742 BufferedWriter out = null;
743 try {
744 out = new BufferedWriter(new FileWriter(getDataFile()),256);
745 out.write(builder.toString());
746 } catch (IOException e) {
747 Slog.e(TAG, "Error writing data file");
748 return;
749 } finally {
750 if (out != null) {
751 try {
752 out.close();
753 } catch (Exception e) {}
754 }
755 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700756 }
757
758 private void retrieve() {
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700759 File f = getDataFile();
760 byte[] buffer;
761 FileInputStream s = null;
762 try {
763 buffer = new byte[(int)f.length()];
764 s = new FileInputStream(f);
765 s.read(buffer);
766 } catch (IOException e) {
767 Slog.e(TAG, "Error reading data file");
768 return;
769 } finally {
770 if (s != null) {
771 try {
772 s.close();
773 } catch (Exception e) {}
774 }
775 }
776 String data = new String(buffer);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700777 if (data == null || data.length() == 0) return;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700778 synchronized (mParent) {
779 String[] parsed = data.split(":");
780 int parsedUsed = 0;
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700781 if (parsed.length < 6) {
782 Slog.e(TAG, "reading data file with insufficient length - ignoring");
783 return;
784 }
785
786 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
787 Slog.e(TAG, "reading data file with bad version - ignoring");
788 return;
789 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700790
791 mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
792 if (parsed.length != 4 + (2 * mPeriodCount)) return;
793
794 mPeriodRxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700795 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700796 mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
797 }
798 mPeriodTxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700799 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700800 mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
801 }
802 mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]);
803 mPeriodStart = new GregorianCalendar();
804 mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
805 mPeriodEnd = new GregorianCalendar();
806 mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
807 }
808 }
809
810 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700811 synchronized (mParent) {
812 if (which > mPeriodCount) return 0;
813 which = mCurrentPeriod - which;
814 if (which < 0) which += mPeriodCount;
815 return mPeriodRxData[which];
816 }
817 }
818 long getPeriodTx(int which) {
819 synchronized (mParent) {
820 if (which > mPeriodCount) return 0;
821 which = mCurrentPeriod - which;
822 if (which < 0) which += mPeriodCount;
823 return mPeriodTxData[which];
824 }
825 }
826 }
827
828 @Override
829 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
830 if (mContext.checkCallingOrSelfPermission(
831 android.Manifest.permission.DUMP)
832 != PackageManager.PERMISSION_GRANTED) {
833 pw.println("Permission Denial: can't dump ThrottleService " +
834 "from from pid=" + Binder.getCallingPid() + ", uid=" +
835 Binder.getCallingUid());
836 return;
837 }
838 pw.println();
839
840 pw.println("The threshold is " + mPolicyThreshold +
841 ", after which you experince throttling to " +
842 mPolicyThrottleValue + "kbps");
843 pw.println("Current period is " +
844 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
845 "and ends in " + (mRecorder.getPeriodEnd() - System.currentTimeMillis()) / 1000 +
846 " seconds.");
847 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
848 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
849 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
850 mRecorder.getPeriodTx(i));
851 }
852 }
853}