blob: 6ba96ae7043569c52fbd6ec7e55af515e257150a [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 Greenwalt8c7e6092010-04-14 17:31:20 -0700375 // handle iface resets - on some device the 3g iface comes and goes and gets
376 // totals reset to 0. Deal with it
377 if ((incRead < 0) || (incWrite < 0)) {
378 incRead += mLastRead;
379 incWrite += mLastWrite;
380 mLastRead = 0;
381 mLastWrite = 0;
382 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700383 } catch (RemoteException e) {
384 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
385 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700386 // don't count this data if we're roaming.
387 boolean roaming = "true".equals(
388 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
389 if (!roaming) {
390 mRecorder.addData(incRead, incWrite);
391 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700392
393 long periodRx = mRecorder.getPeriodRx(0);
394 long periodTx = mRecorder.getPeriodTx(0);
395 long total = periodRx + periodTx;
396 if (DBG) {
Robert Greenwalt5f996892010-04-08 16:19:24 -0700397 Slog.d(TAG, "onPollAlarm - now =" + now + ", roaming =" + roaming +
398 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700399 }
400 mLastRead += incRead;
401 mLastWrite += incWrite;
402
403 checkThrottleAndPostNotification(total);
404
405 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
406 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
407 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
408 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, mRecorder.getPeriodStart());
409 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, mRecorder.getPeriodEnd());
410 mContext.sendStickyBroadcast(broadcast);
411
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700412 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700413 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent);
414 }
415
416 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700417 // is throttling enabled?
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700418 if (mPolicyThreshold == 0)
419 return;
420
421 // check if we need to throttle
422 if (currentTotal > mPolicyThreshold) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700423 if (mThrottleIndex != 1) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700424 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700425 mThrottleIndex = 1;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700426 }
427 if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!");
428 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700429 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700430 mPolicyThrottleValue, mPolicyThrottleValue);
431 } catch (Exception e) {
432 Slog.e(TAG, "error setting Throttle: " + e);
433 }
434
435 mNotificationManager.cancel(com.android.internal.R.drawable.
Robert Greenwalt22b36442010-04-13 15:17:14 -0700436 stat_sys_throttled);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700437
438 postNotification(com.android.internal.R.string.throttled_notification_title,
439 com.android.internal.R.string.throttled_notification_message,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700440 com.android.internal.R.drawable.stat_sys_throttled,
441 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700442
443 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
444 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue);
445 mContext.sendStickyBroadcast(broadcast);
446
447 } // else already up!
448 } else {
449 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
450 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700451 // pretend we only have 1/2 the time remaining that we actually do
452 // if our burn rate in the period so far would have us exceed the limit
453 // in that 1/2 window, warn the user.
454 // this gets more generous in the early to middle period and converges back
455 // to the limit as we move toward the period end.
456
457 // adding another factor - it must be greater than the total cap/4
458 // else we may get false alarms very early in the period.. in the first
459 // tenth of a percent of the period if we used more than a tenth of a percent
460 // of the cap we'd get a warning and that's not desired.
461 long start = mRecorder.getPeriodStart();
462 long end = mRecorder.getPeriodEnd();
463 long periodLength = end - start;
464 long now = System.currentTimeMillis();
465 long timeUsed = now - start;
466 long warningThreshold = 2*mPolicyThreshold*timeUsed/(timeUsed+periodLength);
467 if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) {
468 if (mWarningNotificationSent == false) {
469 mWarningNotificationSent = true;
470 mNotificationManager.cancel(com.android.internal.R.drawable.
Robert Greenwalt22b36442010-04-13 15:17:14 -0700471 stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700472 postNotification(com.android.internal.R.string.
473 throttle_warning_notification_title,
474 com.android.internal.R.string.
475 throttle_warning_notification_message,
Robert Greenwalt22b36442010-04-13 15:17:14 -0700476 com.android.internal.R.drawable.stat_sys_throttled,
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700477 0);
478 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700479 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700480 if (mWarningNotificationSent == true) {
481 mNotificationManager.cancel(com.android.internal.R.drawable.
Robert Greenwalt22b36442010-04-13 15:17:14 -0700482 stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700483 mWarningNotificationSent =false;
484 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700485 }
486 }
487 }
488 }
489
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700490 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700491 Intent intent = new Intent();
492 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700493 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700494 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
495
496 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
497
498 Resources r = Resources.getSystem();
499 CharSequence title = r.getText(titleInt);
500 CharSequence message = r.getText(messageInt);
501 if (mThrottlingNotification == null) {
502 mThrottlingNotification = new Notification();
503 mThrottlingNotification.when = 0;
504 // TODO - fixup icon
505 mThrottlingNotification.icon = icon;
506 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700507 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700508 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700509 mThrottlingNotification.tickerText = title;
510 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
511
512 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
513 }
514
515
516 private synchronized void clearThrottleAndNotification() {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700517 if (mThrottleIndex != THROTTLE_INDEX_UNTHROTTLED) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700518 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700519 mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700520 }
521 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700522 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700523 } catch (Exception e) {
524 Slog.e(TAG, "error clearing Throttle: " + e);
525 }
526 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
527 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
528 mContext.sendStickyBroadcast(broadcast);
529 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700530 mNotificationManager.cancel(com.android.internal.R.drawable.stat_sys_throttled);
531 mWarningNotificationSent = false;
532 }
533
534 private Calendar calculatePeriodEnd() {
535 Calendar end = GregorianCalendar.getInstance();
536 int day = end.get(Calendar.DAY_OF_MONTH);
537 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
538 end.set(Calendar.HOUR_OF_DAY, 0);
539 end.set(Calendar.MINUTE, 0);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700540 end.set(Calendar.SECOND, 0);
541 end.set(Calendar.MILLISECOND, 0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700542 if (day >= mPolicyResetDay) {
543 int month = end.get(Calendar.MONTH);
544 if (month == Calendar.DECEMBER) {
545 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
546 month = Calendar.JANUARY - 1;
547 }
548 end.set(Calendar.MONTH, month + 1);
549 }
550
551 // TODO - remove!
552 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
553 end = GregorianCalendar.getInstance();
554 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
555 }
556 return end;
557 }
558 private Calendar calculatePeriodStart(Calendar end) {
559 Calendar start = (Calendar)end.clone();
560 int month = end.get(Calendar.MONTH);
561 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
562 month = Calendar.DECEMBER + 1;
563 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
564 }
565 start.set(Calendar.MONTH, month - 1);
566
567 // TODO - remove!!
568 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
569 start = (Calendar)end.clone();
570 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
571 }
572 return start;
573 }
574
575 private void onResetAlarm() {
576 if (DBG) {
577 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
578 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
579 }
580
581 Calendar end = calculatePeriodEnd();
582 Calendar start = calculatePeriodStart(end);
583
584 clearThrottleAndNotification();
585
586 mRecorder.setNextPeriod(start,end);
587
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700588 mAlarmManager.cancel(mPendingResetIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700589 mAlarmManager.set(AlarmManager.RTC_WAKEUP, end.getTimeInMillis(),
590 mPendingResetIntent);
591 }
592 }
593
594 // records bytecount data for a given time and accumulates it into larger time windows
595 // for logging and other purposes
596 //
597 // since time can be changed (user or network action) we will have to track the time of the
598 // last recording and deal with it.
599 private static class DataRecorder {
600 long[] mPeriodRxData;
601 long[] mPeriodTxData;
602 int mCurrentPeriod;
603 int mPeriodCount;
604
605 Calendar mPeriodStart;
606 Calendar mPeriodEnd;
607
608 ThrottleService mParent;
609 Context mContext;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700610
611 DataRecorder(Context context, ThrottleService parent) {
612 mContext = context;
613 mParent = parent;
614
615 synchronized (mParent) {
616 mPeriodCount = 6;
617 mPeriodRxData = new long[mPeriodCount];
618 mPeriodTxData = new long[mPeriodCount];
619
620 mPeriodStart = Calendar.getInstance();
621 mPeriodEnd = Calendar.getInstance();
622
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700623 zeroData(0);
624 retrieve();
625 }
626 }
627
628 void setNextPeriod(Calendar start, Calendar end) {
629 if (DBG) {
630 Slog.d(TAG, "setting next period to " + start.getTimeInMillis() +
631 " --until-- " + end.getTimeInMillis());
632 }
633 // if we roll back in time to a previous period, toss out the current data
634 // if we roll forward to the next period, advance to the next
635
636 if (end.before(mPeriodStart)) {
637 if (DBG) {
638 Slog.d(TAG, " old start was " + mPeriodStart.getTimeInMillis() + ", wiping");
639 }
640 synchronized (mParent) {
641 mPeriodRxData[mCurrentPeriod] = 0;
642 mPeriodTxData[mCurrentPeriod] = 0;
643 }
644 } else if(start.after(mPeriodEnd)) {
645 if (DBG) {
646 Slog.d(TAG, " old end was " + mPeriodEnd.getTimeInMillis() + ", following");
647 }
648 synchronized (mParent) {
649 ++mCurrentPeriod;
650 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
651 mPeriodRxData[mCurrentPeriod] = 0;
652 mPeriodTxData[mCurrentPeriod] = 0;
653 }
654 } else {
655 if (DBG) Slog.d(TAG, " we fit - ammending to last period");
656 }
657 setPeriodStart(start);
658 setPeriodEnd(end);
659 record();
660 }
661
662 public long getPeriodEnd() {
663 synchronized (mParent) {
664 return mPeriodEnd.getTimeInMillis();
665 }
666 }
667
668 private void setPeriodEnd(Calendar end) {
669 synchronized (mParent) {
670 mPeriodEnd = end;
671 }
672 }
673
674 public long getPeriodStart() {
675 synchronized (mParent) {
676 return mPeriodStart.getTimeInMillis();
677 }
678 }
679
680 private void setPeriodStart(Calendar start) {
681 synchronized (mParent) {
682 mPeriodStart = start;
683 }
684 }
685
686 public int getPeriodCount() {
687 synchronized (mParent) {
688 return mPeriodCount;
689 }
690 }
691
692 private void zeroData(int field) {
693 synchronized (mParent) {
694 for(int period = 0; period<mPeriodCount; period++) {
695 mPeriodRxData[period] = 0;
696 mPeriodTxData[period] = 0;
697 }
698 mCurrentPeriod = 0;
699 }
700
701 }
702
703 // if time moves backward accumulate all read/write that's lost into the now
704 // otherwise time moved forward.
705 void addData(long bytesRead, long bytesWritten) {
706 synchronized (mParent) {
707 mPeriodRxData[mCurrentPeriod] += bytesRead;
708 mPeriodTxData[mCurrentPeriod] += bytesWritten;
709 }
710 record();
711 }
712
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700713 private File getDataFile() {
714 File dataDir = Environment.getDataDirectory();
715 File throttleDir = new File(dataDir, "system/throttle");
716 throttleDir.mkdirs();
717 File dataFile = new File(throttleDir, "data");
718 return dataFile;
719 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700720
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700721 private static final int DATA_FILE_VERSION = 1;
722
723 private void record() {
724 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700725 // 1 int mPeriodCount
726 // 13*6 long[PERIOD_COUNT] mPeriodRxData
727 // 13*6 long[PERIOD_COUNT] mPeriodTxData
728 // 1 int mCurrentPeriod
729 // 13 long periodStartMS
730 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700731 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700732 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700733 builder.append(DATA_FILE_VERSION);
734 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700735 builder.append(mPeriodCount);
736 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700737 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700738 builder.append(mPeriodRxData[i]);
739 builder.append(":");
740 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700741 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700742 builder.append(mPeriodTxData[i]);
743 builder.append(":");
744 }
745 builder.append(mCurrentPeriod);
746 builder.append(":");
747 builder.append(mPeriodStart.getTimeInMillis());
748 builder.append(":");
749 builder.append(mPeriodEnd.getTimeInMillis());
750 builder.append(":");
751
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700752 BufferedWriter out = null;
753 try {
754 out = new BufferedWriter(new FileWriter(getDataFile()),256);
755 out.write(builder.toString());
756 } catch (IOException e) {
757 Slog.e(TAG, "Error writing data file");
758 return;
759 } finally {
760 if (out != null) {
761 try {
762 out.close();
763 } catch (Exception e) {}
764 }
765 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700766 }
767
768 private void retrieve() {
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700769 File f = getDataFile();
770 byte[] buffer;
771 FileInputStream s = null;
772 try {
773 buffer = new byte[(int)f.length()];
774 s = new FileInputStream(f);
775 s.read(buffer);
776 } catch (IOException e) {
777 Slog.e(TAG, "Error reading data file");
778 return;
779 } finally {
780 if (s != null) {
781 try {
782 s.close();
783 } catch (Exception e) {}
784 }
785 }
786 String data = new String(buffer);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700787 if (data == null || data.length() == 0) return;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700788 synchronized (mParent) {
789 String[] parsed = data.split(":");
790 int parsedUsed = 0;
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700791 if (parsed.length < 6) {
792 Slog.e(TAG, "reading data file with insufficient length - ignoring");
793 return;
794 }
795
796 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
797 Slog.e(TAG, "reading data file with bad version - ignoring");
798 return;
799 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700800
801 mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
802 if (parsed.length != 4 + (2 * mPeriodCount)) return;
803
804 mPeriodRxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700805 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700806 mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
807 }
808 mPeriodTxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700809 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700810 mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
811 }
812 mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]);
813 mPeriodStart = new GregorianCalendar();
814 mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
815 mPeriodEnd = new GregorianCalendar();
816 mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
817 }
818 }
819
820 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700821 synchronized (mParent) {
822 if (which > mPeriodCount) return 0;
823 which = mCurrentPeriod - which;
824 if (which < 0) which += mPeriodCount;
825 return mPeriodRxData[which];
826 }
827 }
828 long getPeriodTx(int which) {
829 synchronized (mParent) {
830 if (which > mPeriodCount) return 0;
831 which = mCurrentPeriod - which;
832 if (which < 0) which += mPeriodCount;
833 return mPeriodTxData[which];
834 }
835 }
836 }
837
838 @Override
839 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
840 if (mContext.checkCallingOrSelfPermission(
841 android.Manifest.permission.DUMP)
842 != PackageManager.PERMISSION_GRANTED) {
843 pw.println("Permission Denial: can't dump ThrottleService " +
844 "from from pid=" + Binder.getCallingPid() + ", uid=" +
845 Binder.getCallingUid());
846 return;
847 }
848 pw.println();
849
850 pw.println("The threshold is " + mPolicyThreshold +
851 ", after which you experince throttling to " +
852 mPolicyThrottleValue + "kbps");
853 pw.println("Current period is " +
854 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
855 "and ends in " + (mRecorder.getPeriodEnd() - System.currentTimeMillis()) / 1000 +
856 " seconds.");
857 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700858 pw.println("Current Throttle Index is " + mThrottleIndex);
859
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700860 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
861 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
862 mRecorder.getPeriodTx(i));
863 }
864 }
865}