blob: a93a6ee0bca800b5ab2d80711982fc9c495bf943 [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;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070023import android.content.BroadcastReceiver;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -070024import android.content.ContentResolver;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070025import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.PackageManager;
29import android.content.res.Resources;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -070030import android.database.ContentObserver;
Robert Greenwaltfee46832010-05-06 12:25:13 -070031import android.net.INetworkManagementEventObserver;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070032import android.net.IThrottleManager;
Robert Greenwalt7171ea82010-04-14 22:37:12 -070033import android.net.SntpClient;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070034import android.net.ThrottleManager;
35import android.os.Binder;
Robert Greenwaltb8912f52010-04-09 17:27:26 -070036import android.os.Environment;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070037import android.os.Handler;
38import android.os.HandlerThread;
39import android.os.IBinder;
40import android.os.INetworkManagementService;
41import android.os.Looper;
42import android.os.Message;
43import android.os.RemoteException;
44import android.os.ServiceManager;
45import android.os.SystemClock;
46import android.os.SystemProperties;
47import android.provider.Settings;
Robert Greenwalte6e98822010-04-15 08:27:14 -070048import android.telephony.TelephonyManager;
Robert Greenwaltfee46832010-05-06 12:25:13 -070049import android.text.TextUtils;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070050import android.util.Slog;
51
Robert Greenwalt7171ea82010-04-14 22:37:12 -070052import com.android.internal.R;
Robert Greenwalt5f996892010-04-08 16:19:24 -070053import com.android.internal.telephony.TelephonyProperties;
54
Robert Greenwaltb8912f52010-04-09 17:27:26 -070055import java.io.BufferedWriter;
56import java.io.File;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070057import java.io.FileDescriptor;
Robert Greenwaltb8912f52010-04-09 17:27:26 -070058import java.io.FileInputStream;
59import java.io.FileWriter;
60import java.io.IOException;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070061import java.io.PrintWriter;
62import java.util.Calendar;
63import java.util.GregorianCalendar;
Robert Greenwalt7171ea82010-04-14 22:37:12 -070064import java.util.Properties;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070065import java.util.Random;
66
67// TODO - add comments - reference the ThrottleManager for public API
68public class ThrottleService extends IThrottleManager.Stub {
69
70 private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing";
71
72 private static final String TAG = "ThrottleService";
Robert Greenwaltbf7de392010-04-21 17:09:38 -070073 private static final boolean DBG = true;
74 private static final boolean VDBG = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070075 private Handler mHandler;
76 private HandlerThread mThread;
77
78 private Context mContext;
79
Robert Greenwaltfb9896b2010-04-22 15:39:38 -070080 private static final int INITIAL_POLL_DELAY_SEC = 90;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070081 private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
Robert Greenwalt7171ea82010-04-14 22:37:12 -070082 private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070083 private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070084
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070085 private int mPolicyPollPeriodSec;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070086 private long mPolicyThreshold;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070087 private int mPolicyThrottleValue;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070088 private int mPolicyResetDay; // 1-28
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070089 private int mPolicyNotificationsAllowedMask;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070090
91 private long mLastRead; // read byte count from last poll
92 private long mLastWrite; // write byte count from last poll
93
94 private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL";
95 private static int POLL_REQUEST = 0;
96 private PendingIntent mPendingPollIntent;
97 private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET";
98 private static int RESET_REQUEST = 1;
99 private PendingIntent mPendingResetIntent;
100
101 private INetworkManagementService mNMService;
102 private AlarmManager mAlarmManager;
103 private NotificationManager mNotificationManager;
104
105 private DataRecorder mRecorder;
106
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700107 private String mIface;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700108
109 private static final int NOTIFICATION_WARNING = 2;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700110
111 private Notification mThrottlingNotification;
112 private boolean mWarningNotificationSent = false;
113
Robert Greenwaltfee46832010-05-06 12:25:13 -0700114 private InterfaceObserver mInterfaceObserver;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700115 private SettingsObserver mSettingsObserver;
116
117 private int mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
118 private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
119 private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
120
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700121 private static final String PROPERTIES_FILE = "/etc/gps.conf";
122 private String mNtpServer;
123 private boolean mNtpActive;
124
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700125 public ThrottleService(Context context) {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700126 if (VDBG) Slog.v(TAG, "Starting ThrottleService");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700127 mContext = context;
128
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700129 mNtpActive = false;
130
Robert Greenwaltfee46832010-05-06 12:25:13 -0700131 mIface = mContext.getResources().getString(R.string.config_datause_iface);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700132 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
133 Intent pollIntent = new Intent(ACTION_POLL, null);
134 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
135 Intent resetIntent = new Intent(ACTION_RESET, null);
136 mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
137
138 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
139 mNMService = INetworkManagementService.Stub.asInterface(b);
140
141 mNotificationManager = (NotificationManager)mContext.getSystemService(
142 Context.NOTIFICATION_SERVICE);
143 }
144
Robert Greenwaltfee46832010-05-06 12:25:13 -0700145 private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
146 private int mMsg;
147 private Handler mHandler;
148 private String mIface;
149
150 InterfaceObserver(Handler handler, int msg, String iface) {
151 super();
152 mHandler = handler;
153 mMsg = msg;
154 mIface = iface;
155 }
156
157 public void interfaceLinkStatusChanged(String iface, boolean link) {
158 if (link) {
159 if (TextUtils.equals(iface, mIface)) {
160 mHandler.obtainMessage(mMsg).sendToTarget();
161 }
162 }
163 }
164
165 public void interfaceAdded(String iface) {
166 // TODO - an interface added in the UP state should also trigger a StatusChanged
167 // notification..
168 if (TextUtils.equals(iface, mIface)) {
169 mHandler.obtainMessage(mMsg).sendToTarget();
170 }
171 }
172
173 public void interfaceRemoved(String iface) {}
174 }
175
176
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700177 private static class SettingsObserver extends ContentObserver {
178 private int mMsg;
179 private Handler mHandler;
180 SettingsObserver(Handler handler, int msg) {
181 super(handler);
182 mHandler = handler;
183 mMsg = msg;
184 }
185
186 void observe(Context context) {
187 ContentResolver resolver = context.getContentResolver();
188 resolver.registerContentObserver(Settings.Secure.getUriFor(
189 Settings.Secure.THROTTLE_POLLING_SEC), false, this);
190 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700191 Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700192 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700193 Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700194 resolver.registerContentObserver(Settings.Secure.getUriFor(
195 Settings.Secure.THROTTLE_RESET_DAY), false, this);
196 resolver.registerContentObserver(Settings.Secure.getUriFor(
197 Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this);
198 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700199 Settings.Secure.THROTTLE_HELP_URI), false, this);
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700200 resolver.registerContentObserver(Settings.Secure.getUriFor(
201 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700202 }
203
204 @Override
205 public void onChange(boolean selfChange) {
206 mHandler.obtainMessage(mMsg).sendToTarget();
207 }
208 }
209
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700210 private void enforceAccessPermission() {
211 mContext.enforceCallingOrSelfPermission(
212 android.Manifest.permission.ACCESS_NETWORK_STATE,
213 "ThrottleService");
214 }
215
Robert Greenwalt05d06732010-04-19 11:10:38 -0700216 private long ntpToWallTime(long ntpTime) {
217 long bestNow = getBestTime();
218 long localNow = System.currentTimeMillis();
219 return localNow + (ntpTime - bestNow);
220 }
221
Irfan Sheriffcf282362010-04-16 16:53:20 -0700222 // TODO - fetch for the iface
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700223 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt05d06732010-04-19 11:10:38 -0700224
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700225 public synchronized long getResetTime(String iface) {
226 enforceAccessPermission();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700227 long resetTime = 0;
Irfan Sheriffcf282362010-04-16 16:53:20 -0700228 if (mRecorder != null) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700229 resetTime = ntpToWallTime(mRecorder.getPeriodEnd());
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700230 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700231 return resetTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700232 }
Irfan Sheriffcf282362010-04-16 16:53:20 -0700233
234 // TODO - fetch for the iface
Robert Greenwalt05d06732010-04-19 11:10:38 -0700235 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700236 public synchronized long getPeriodStartTime(String iface) {
237 enforceAccessPermission();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700238 long startTime = 0;
Irfan Sheriffcf282362010-04-16 16:53:20 -0700239 if (mRecorder != null) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700240 startTime = ntpToWallTime(mRecorder.getPeriodStart());
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700241 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700242 return startTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700243 }
244 //TODO - a better name? getCliffByteCountThreshold?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700245 // TODO - fetch for the iface
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700246 public synchronized long getCliffThreshold(String iface, int cliff) {
247 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700248 if (cliff == 1) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700249 return mPolicyThreshold;
250 }
251 return 0;
252 }
253 // TODO - a better name? getThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700254 // TODO - fetch for the iface
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700255 public synchronized int getCliffLevel(String iface, int cliff) {
256 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700257 if (cliff == 1) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700258 return mPolicyThrottleValue;
259 }
260 return 0;
261 }
262
Irfan Sheriffc9b68512010-04-08 14:12:33 -0700263 public String getHelpUri() {
264 enforceAccessPermission();
265 return Settings.Secure.getString(mContext.getContentResolver(),
266 Settings.Secure.THROTTLE_HELP_URI);
267 }
268
Irfan Sheriffcf282362010-04-16 16:53:20 -0700269 // TODO - fetch for the iface
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700270 public synchronized long getByteCount(String iface, int dir, int period, int ago) {
271 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700272 if ((period == ThrottleManager.PERIOD_CYCLE) &&
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700273 (mRecorder != null)) {
274 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
275 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
276 }
277 return 0;
278 }
279
280 // TODO - a better name - getCurrentThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700281 // TODO - fetch for the iface
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700282 public synchronized int getThrottle(String iface) {
283 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700284 if (mThrottleIndex == 1) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700285 return mPolicyThrottleValue;
286 }
287 return 0;
288 }
289
290 void systemReady() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700291 if (VDBG) Slog.v(TAG, "systemReady");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700292 mContext.registerReceiver(
293 new BroadcastReceiver() {
294 @Override
295 public void onReceive(Context context, Intent intent) {
296 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
297 }
298 }, new IntentFilter(ACTION_POLL));
299
300 mContext.registerReceiver(
301 new BroadcastReceiver() {
302 @Override
303 public void onReceive(Context context, Intent intent) {
304 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
305 }
306 }, new IntentFilter(ACTION_RESET));
307
308 // use a new thread as we don't want to stall the system for file writes
309 mThread = new HandlerThread(TAG);
310 mThread.start();
311 mHandler = new MyHandler(mThread.getLooper());
312 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700313
Robert Greenwaltfee46832010-05-06 12:25:13 -0700314 mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
315 try {
316 mNMService.registerObserver(mInterfaceObserver);
317 } catch (RemoteException e) {
318 Slog.e(TAG, "Could not register InterfaceObserver " + e);
319 }
320
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700321 mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
322 mSettingsObserver.observe(mContext);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700323
324 FileInputStream stream = null;
325 try {
326 Properties properties = new Properties();
327 File file = new File(PROPERTIES_FILE);
328 stream = new FileInputStream(file);
329 properties.load(stream);
330 mNtpServer = properties.getProperty("NTP_SERVER", null);
331 } catch (IOException e) {
332 Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
333 } finally {
334 if (stream != null) {
335 try {
336 stream.close();
337 } catch (Exception e) {}
338 }
339 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700340 }
341
342
343 private static final int EVENT_REBOOT_RECOVERY = 0;
344 private static final int EVENT_POLICY_CHANGED = 1;
345 private static final int EVENT_POLL_ALARM = 2;
346 private static final int EVENT_RESET_ALARM = 3;
Robert Greenwaltfee46832010-05-06 12:25:13 -0700347 private static final int EVENT_IFACE_UP = 4;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700348 private class MyHandler extends Handler {
349 public MyHandler(Looper l) {
350 super(l);
351 }
352
353 @Override
354 public void handleMessage(Message msg) {
355 switch (msg.what) {
356 case EVENT_REBOOT_RECOVERY:
357 onRebootRecovery();
358 break;
359 case EVENT_POLICY_CHANGED:
360 onPolicyChanged();
361 break;
362 case EVENT_POLL_ALARM:
363 onPollAlarm();
364 break;
365 case EVENT_RESET_ALARM:
366 onResetAlarm();
Robert Greenwaltfee46832010-05-06 12:25:13 -0700367 break;
368 case EVENT_IFACE_UP:
369 onIfaceUp();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700370 }
371 }
372
373 private void onRebootRecovery() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700374 if (VDBG) Slog.v(TAG, "onRebootRecovery");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700375 // check for sim change TODO
376 // reregister for notification of policy change
377
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700378 mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700379
380 mRecorder = new DataRecorder(mContext, ThrottleService.this);
381
382 // get policy
383 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
Robert Greenwaltfb9896b2010-04-22 15:39:38 -0700384
385 // if we poll now we won't have network connectivity or even imsi access
386 // queue up a poll to happen in a little while - after ntp and imsi are avail
387 // TODO - make this callback based (ie, listen for notificaitons)
388 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM),
389 INITIAL_POLL_DELAY_SEC * 1000);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700390 }
391
392 // check for new policy info (threshold limit/value/etc)
393 private void onPolicyChanged() {
394 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
395
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700396 int pollingPeriod = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700397 R.integer.config_datause_polling_period_sec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700398 mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
399 Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
400
401 // TODO - remove testing stuff?
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700402 long defaultThreshold = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700403 R.integer.config_datause_threshold_bytes);
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700404 int defaultValue = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700405 R.integer.config_datause_throttle_kbitsps);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700406 synchronized (ThrottleService.this) {
407 mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700408 Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700409 mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700410 Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
411 if (testing) {
412 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
413 mPolicyThreshold = TESTING_THRESHOLD;
414 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700415 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700416
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700417 mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
418 Settings.Secure.THROTTLE_RESET_DAY, -1);
419 if (mPolicyResetDay == -1 ||
420 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
421 Random g = new Random();
422 mPolicyResetDay = 1 + g.nextInt(28); // 1-28
423 Settings.Secure.putInt(mContext.getContentResolver(),
424 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
425 }
426 synchronized (ThrottleService.this) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700427 if (mIface == null) {
428 mPolicyThreshold = 0;
429 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700430 }
431
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700432 int defaultNotificationType = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700433 R.integer.config_datause_notification_type);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700434 mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700435 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700436
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700437 mMaxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
438 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC, MAX_NTP_CACHE_AGE_SEC);
439
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700440 if (VDBG || (mPolicyThreshold != 0)) {
441 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
442 mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold + ", value=" +
443 mPolicyThrottleValue + ", resetDay=" + mPolicyResetDay + ", noteType=" +
444 mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" + mMaxNtpCacheAgeSec);
445 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700446
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700447 // force updates
448 mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
449
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700450 onResetAlarm();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700451
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700452 onPollAlarm();
453
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700454 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
455 mContext.sendBroadcast(broadcast);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700456 }
457
458 private void onPollAlarm() {
459 long now = SystemClock.elapsedRealtime();
460 long next = now + mPolicyPollPeriodSec*1000;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700461
462 checkForAuthoritativeTime();
463
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700464 long incRead = 0;
465 long incWrite = 0;
466 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700467 incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
468 incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700469 // handle iface resets - on some device the 3g iface comes and goes and gets
470 // totals reset to 0. Deal with it
471 if ((incRead < 0) || (incWrite < 0)) {
472 incRead += mLastRead;
473 incWrite += mLastWrite;
474 mLastRead = 0;
475 mLastWrite = 0;
476 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700477 } catch (RemoteException e) {
478 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
479 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700480 // don't count this data if we're roaming.
481 boolean roaming = "true".equals(
482 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
483 if (!roaming) {
484 mRecorder.addData(incRead, incWrite);
485 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700486
487 long periodRx = mRecorder.getPeriodRx(0);
488 long periodTx = mRecorder.getPeriodTx(0);
489 long total = periodRx + periodTx;
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700490 if (VDBG || (mPolicyThreshold != 0)) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700491 Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
Robert Greenwalt5f996892010-04-08 16:19:24 -0700492 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700493 }
494 mLastRead += incRead;
495 mLastWrite += incWrite;
496
497 checkThrottleAndPostNotification(total);
498
499 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
500 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
501 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700502 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
503 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700504 mContext.sendStickyBroadcast(broadcast);
505
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700506 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700507 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700508 }
509
Robert Greenwaltfee46832010-05-06 12:25:13 -0700510 private void onIfaceUp() {
511 // if we were throttled before, be sure and set it again - the iface went down
512 // (and may have disappeared all together) and these settings were lost
513 if (mThrottleIndex == 1) {
514 try {
515 mNMService.setInterfaceThrottle(mIface, -1, -1);
516 mNMService.setInterfaceThrottle(mIface,
517 mPolicyThrottleValue, mPolicyThrottleValue);
518 } catch (Exception e) {
519 Slog.e(TAG, "error setting Throttle: " + e);
520 }
521 }
522 }
523
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700524 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700525 // is throttling enabled?
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700526 if (mPolicyThreshold == 0) {
Robert Greenwaltcce83372010-04-23 17:35:29 -0700527 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700528 return;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700529 }
530
531 // have we spoken with an ntp server yet?
532 // this is controversial, but we'd rather err towards not throttling
533 if ((mNtpServer != null) && !mNtpActive) {
534 return;
535 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700536
537 // check if we need to throttle
538 if (currentTotal > mPolicyThreshold) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700539 if (mThrottleIndex != 1) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700540 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700541 mThrottleIndex = 1;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700542 }
543 if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!");
544 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700545 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700546 mPolicyThrottleValue, mPolicyThrottleValue);
547 } catch (Exception e) {
548 Slog.e(TAG, "error setting Throttle: " + e);
549 }
550
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700551 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700552
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700553 postNotification(R.string.throttled_notification_title,
554 R.string.throttled_notification_message,
555 R.drawable.stat_sys_throttled,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700556 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700557
558 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
559 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue);
560 mContext.sendStickyBroadcast(broadcast);
561
562 } // else already up!
563 } else {
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700564 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700565 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
566 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700567 // pretend we only have 1/2 the time remaining that we actually do
568 // if our burn rate in the period so far would have us exceed the limit
569 // in that 1/2 window, warn the user.
570 // this gets more generous in the early to middle period and converges back
571 // to the limit as we move toward the period end.
572
573 // adding another factor - it must be greater than the total cap/4
574 // else we may get false alarms very early in the period.. in the first
575 // tenth of a percent of the period if we used more than a tenth of a percent
576 // of the cap we'd get a warning and that's not desired.
577 long start = mRecorder.getPeriodStart();
578 long end = mRecorder.getPeriodEnd();
579 long periodLength = end - start;
580 long now = System.currentTimeMillis();
581 long timeUsed = now - start;
582 long warningThreshold = 2*mPolicyThreshold*timeUsed/(timeUsed+periodLength);
583 if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) {
584 if (mWarningNotificationSent == false) {
585 mWarningNotificationSent = true;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700586 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
587 postNotification(R.string.throttle_warning_notification_title,
588 R.string.throttle_warning_notification_message,
589 R.drawable.stat_sys_throttled,
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700590 0);
591 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700592 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700593 if (mWarningNotificationSent == true) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700594 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700595 mWarningNotificationSent =false;
596 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700597 }
598 }
599 }
600 }
601
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700602 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700603 Intent intent = new Intent();
604 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700605 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700606 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
607
608 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
609
610 Resources r = Resources.getSystem();
611 CharSequence title = r.getText(titleInt);
612 CharSequence message = r.getText(messageInt);
613 if (mThrottlingNotification == null) {
614 mThrottlingNotification = new Notification();
615 mThrottlingNotification.when = 0;
616 // TODO - fixup icon
617 mThrottlingNotification.icon = icon;
618 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700619 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700620 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700621 mThrottlingNotification.tickerText = title;
622 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
623
624 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
625 }
626
627
628 private synchronized void clearThrottleAndNotification() {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700629 if (mThrottleIndex != THROTTLE_INDEX_UNTHROTTLED) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700630 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700631 mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700632 }
633 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700634 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700635 } catch (Exception e) {
636 Slog.e(TAG, "error clearing Throttle: " + e);
637 }
638 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
639 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
640 mContext.sendStickyBroadcast(broadcast);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700641 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
642 mWarningNotificationSent = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700643 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700644 }
645
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700646 private Calendar calculatePeriodEnd(long now) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700647 Calendar end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700648 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700649 int day = end.get(Calendar.DAY_OF_MONTH);
650 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
651 end.set(Calendar.HOUR_OF_DAY, 0);
652 end.set(Calendar.MINUTE, 0);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700653 end.set(Calendar.SECOND, 0);
654 end.set(Calendar.MILLISECOND, 0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700655 if (day >= mPolicyResetDay) {
656 int month = end.get(Calendar.MONTH);
657 if (month == Calendar.DECEMBER) {
658 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
659 month = Calendar.JANUARY - 1;
660 }
661 end.set(Calendar.MONTH, month + 1);
662 }
663
664 // TODO - remove!
665 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
666 end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700667 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700668 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
669 }
670 return end;
671 }
672 private Calendar calculatePeriodStart(Calendar end) {
673 Calendar start = (Calendar)end.clone();
674 int month = end.get(Calendar.MONTH);
675 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
676 month = Calendar.DECEMBER + 1;
677 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
678 }
679 start.set(Calendar.MONTH, month - 1);
680
681 // TODO - remove!!
682 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
683 start = (Calendar)end.clone();
684 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
685 }
686 return start;
687 }
688
689 private void onResetAlarm() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700690 if (VDBG || (mPolicyThreshold != 0)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700691 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
692 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
693 }
694
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700695 long now = getBestTime();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700696
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700697 if (mNtpActive || (mNtpServer == null)) {
698 Calendar end = calculatePeriodEnd(now);
699 Calendar start = calculatePeriodStart(end);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700700
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700701 if (mRecorder.setNextPeriod(start, end)) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700702 onPollAlarm();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700703 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700704
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700705 mAlarmManager.cancel(mPendingResetIntent);
706 long offset = end.getTimeInMillis() - now;
707 // use Elapsed realtime so clock changes don't fool us.
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700708 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700709 SystemClock.elapsedRealtime() + offset,
710 mPendingResetIntent);
711 } else {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700712 if (VDBG) Slog.d(TAG, "no authoritative time - not resetting period");
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700713 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700714 }
715 }
716
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700717 private void checkForAuthoritativeTime() {
718 if (mNtpActive || (mNtpServer == null)) return;
719
Robert Greenwalt05d06732010-04-19 11:10:38 -0700720 // will try to get the ntp time and switch to it if found.
721 // will also cache the time so we don't fetch it repeatedly.
722 getBestTime();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700723 }
724
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700725 private static final int MAX_NTP_CACHE_AGE_SEC = 60 * 60 * 24; // 1 day
Robert Greenwalt05d06732010-04-19 11:10:38 -0700726 private static final int MAX_NTP_FETCH_WAIT = 10 * 1000;
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700727 private int mMaxNtpCacheAgeSec = MAX_NTP_CACHE_AGE_SEC;
Robert Greenwalt05d06732010-04-19 11:10:38 -0700728 private long cachedNtp;
729 private long cachedNtpTimestamp;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700730
Robert Greenwalt05d06732010-04-19 11:10:38 -0700731 private long getBestTime() {
732 if (mNtpServer != null) {
733 if (mNtpActive) {
734 long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp;
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700735 if (ntpAge < mMaxNtpCacheAgeSec * 1000) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700736 if (VDBG) Slog.v(TAG, "using cached time");
Robert Greenwalt05d06732010-04-19 11:10:38 -0700737 return cachedNtp + ntpAge;
738 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700739 }
Robert Greenwalt05d06732010-04-19 11:10:38 -0700740 SntpClient client = new SntpClient();
741 if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT)) {
742 cachedNtp = client.getNtpTime();
743 cachedNtpTimestamp = SystemClock.elapsedRealtime();
744 if (!mNtpActive) {
745 mNtpActive = true;
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700746 if (VDBG) Slog.d(TAG, "found Authoritative time - reseting alarm");
Robert Greenwalt05d06732010-04-19 11:10:38 -0700747 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
748 }
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700749 if (VDBG) Slog.v(TAG, "using Authoritative time: " + cachedNtp);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700750 return cachedNtp;
751 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700752 }
Robert Greenwalt05d06732010-04-19 11:10:38 -0700753 long time = System.currentTimeMillis();
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700754 if (VDBG) Slog.v(TAG, "using User time: " + time);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700755 mNtpActive = false;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700756 return time;
757 }
758
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700759 // records bytecount data for a given time and accumulates it into larger time windows
760 // for logging and other purposes
761 //
762 // since time can be changed (user or network action) we will have to track the time of the
763 // last recording and deal with it.
764 private static class DataRecorder {
765 long[] mPeriodRxData;
766 long[] mPeriodTxData;
767 int mCurrentPeriod;
768 int mPeriodCount;
769
770 Calendar mPeriodStart;
771 Calendar mPeriodEnd;
772
773 ThrottleService mParent;
774 Context mContext;
Robert Greenwalte6e98822010-04-15 08:27:14 -0700775 String mImsi = null;
776
777 TelephonyManager mTelephonyManager;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700778
779 DataRecorder(Context context, ThrottleService parent) {
780 mContext = context;
781 mParent = parent;
782
Robert Greenwalte6e98822010-04-15 08:27:14 -0700783 mTelephonyManager = (TelephonyManager)mContext.getSystemService(
784 Context.TELEPHONY_SERVICE);
785
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700786 synchronized (mParent) {
787 mPeriodCount = 6;
788 mPeriodRxData = new long[mPeriodCount];
789 mPeriodTxData = new long[mPeriodCount];
790
791 mPeriodStart = Calendar.getInstance();
792 mPeriodEnd = Calendar.getInstance();
793
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700794 retrieve();
795 }
796 }
797
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700798 boolean setNextPeriod(Calendar start, Calendar end) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700799 // TODO - how would we deal with a dual-IMSI device?
800 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700801 boolean startNewPeriod = true;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700802
Robert Greenwalt27fba672010-04-26 12:29:14 -0700803 if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) {
804 // same endpoints - keep collecting
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700805 if (VDBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700806 Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," +
807 end.getTimeInMillis() +") - ammending data");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700808 }
Robert Greenwalt27fba672010-04-26 12:29:14 -0700809 startNewPeriod = false;
810 } else {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700811 if (VDBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700812 if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) {
813 Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," +
814 end.getTimeInMillis() + ") - old end was " +
815 mPeriodEnd.getTimeInMillis() + ", following");
816 } else {
817 Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," +
818 end.getTimeInMillis() + ") replacing old (" +
819 mPeriodStart.getTimeInMillis() + "," +
820 mPeriodEnd.getTimeInMillis() + ")");
821 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700822 }
823 synchronized (mParent) {
824 ++mCurrentPeriod;
825 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
826 mPeriodRxData[mCurrentPeriod] = 0;
827 mPeriodTxData[mCurrentPeriod] = 0;
828 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700829 }
830 setPeriodStart(start);
831 setPeriodEnd(end);
832 record();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700833 return startNewPeriod;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700834 }
835
836 public long getPeriodEnd() {
837 synchronized (mParent) {
838 return mPeriodEnd.getTimeInMillis();
839 }
840 }
841
842 private void setPeriodEnd(Calendar end) {
843 synchronized (mParent) {
844 mPeriodEnd = end;
845 }
846 }
847
848 public long getPeriodStart() {
849 synchronized (mParent) {
850 return mPeriodStart.getTimeInMillis();
851 }
852 }
853
854 private void setPeriodStart(Calendar start) {
855 synchronized (mParent) {
856 mPeriodStart = start;
857 }
858 }
859
860 public int getPeriodCount() {
861 synchronized (mParent) {
862 return mPeriodCount;
863 }
864 }
865
866 private void zeroData(int field) {
867 synchronized (mParent) {
868 for(int period = 0; period<mPeriodCount; period++) {
869 mPeriodRxData[period] = 0;
870 mPeriodTxData[period] = 0;
871 }
872 mCurrentPeriod = 0;
873 }
874
875 }
876
877 // if time moves backward accumulate all read/write that's lost into the now
878 // otherwise time moved forward.
879 void addData(long bytesRead, long bytesWritten) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700880 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700881
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700882 synchronized (mParent) {
883 mPeriodRxData[mCurrentPeriod] += bytesRead;
884 mPeriodTxData[mCurrentPeriod] += bytesWritten;
885 }
886 record();
887 }
888
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700889 private File getDataFile() {
890 File dataDir = Environment.getDataDirectory();
891 File throttleDir = new File(dataDir, "system/throttle");
892 throttleDir.mkdirs();
Robert Greenwalte6e98822010-04-15 08:27:14 -0700893 String mImsi = mTelephonyManager.getSubscriberId();
894 File dataFile;
895 if (mImsi == null) {
896 dataFile = useMRUFile(throttleDir);
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700897 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700898 } else {
899 String imsiHash = Integer.toString(mImsi.hashCode());
900 dataFile = new File(throttleDir, imsiHash);
901 }
902 // touch the file so it's not LRU
903 dataFile.setLastModified(System.currentTimeMillis());
904 checkAndDeleteLRUDataFile(throttleDir);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700905 return dataFile;
906 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700907
Robert Greenwalte6e98822010-04-15 08:27:14 -0700908 // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling
909 private void checkForSubscriberId() {
910 if (mImsi != null) return;
911
912 mImsi = mTelephonyManager.getSubscriberId();
913 if (mImsi == null) return;
914
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700915 if (VDBG) Slog.d(TAG, "finally have imsi - retreiving data");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700916 retrieve();
917 }
918
919 private final static int MAX_SIMS_SUPPORTED = 3;
920
921 private void checkAndDeleteLRUDataFile(File dir) {
922 File[] files = dir.listFiles();
923
924 if (files.length <= MAX_SIMS_SUPPORTED) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700925 if (DBG) Slog.d(TAG, "Too many data files");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700926 do {
927 File oldest = null;
928 for (File f : files) {
929 if ((oldest == null) || (oldest.lastModified() > f.lastModified())) {
930 oldest = f;
931 }
932 }
933 if (oldest == null) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700934 if (DBG) Slog.d(TAG, " deleting " + oldest);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700935 oldest.delete();
936 files = dir.listFiles();
937 } while (files.length > MAX_SIMS_SUPPORTED);
938 }
939
940 private File useMRUFile(File dir) {
941 File newest = null;
942 File[] files = dir.listFiles();
943
944 for (File f : files) {
945 if ((newest == null) || (newest.lastModified() < f.lastModified())) {
946 newest = f;
947 }
948 }
949 if (newest == null) {
950 newest = new File(dir, "temp");
951 }
952 return newest;
953 }
954
955
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700956 private static final int DATA_FILE_VERSION = 1;
957
958 private void record() {
959 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700960 // 1 int mPeriodCount
961 // 13*6 long[PERIOD_COUNT] mPeriodRxData
962 // 13*6 long[PERIOD_COUNT] mPeriodTxData
963 // 1 int mCurrentPeriod
964 // 13 long periodStartMS
965 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700966 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700967 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700968 builder.append(DATA_FILE_VERSION);
969 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700970 builder.append(mPeriodCount);
971 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700972 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700973 builder.append(mPeriodRxData[i]);
974 builder.append(":");
975 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700976 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700977 builder.append(mPeriodTxData[i]);
978 builder.append(":");
979 }
980 builder.append(mCurrentPeriod);
981 builder.append(":");
982 builder.append(mPeriodStart.getTimeInMillis());
983 builder.append(":");
984 builder.append(mPeriodEnd.getTimeInMillis());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700985
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700986 BufferedWriter out = null;
987 try {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700988 out = new BufferedWriter(new FileWriter(getDataFile()), 256);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700989 out.write(builder.toString());
990 } catch (IOException e) {
991 Slog.e(TAG, "Error writing data file");
992 return;
993 } finally {
994 if (out != null) {
995 try {
996 out.close();
997 } catch (Exception e) {}
998 }
999 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001000 }
1001
1002 private void retrieve() {
Robert Greenwalt05d06732010-04-19 11:10:38 -07001003 // clean out any old data first. If we fail to read we don't want old stuff
1004 zeroData(0);
1005
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001006 File f = getDataFile();
1007 byte[] buffer;
1008 FileInputStream s = null;
1009 try {
1010 buffer = new byte[(int)f.length()];
1011 s = new FileInputStream(f);
1012 s.read(buffer);
1013 } catch (IOException e) {
1014 Slog.e(TAG, "Error reading data file");
1015 return;
1016 } finally {
1017 if (s != null) {
1018 try {
1019 s.close();
1020 } catch (Exception e) {}
1021 }
1022 }
1023 String data = new String(buffer);
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001024 if (data == null || data.length() == 0) {
1025 if (DBG) Slog.d(TAG, "data file empty");
1026 return;
1027 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001028 synchronized (mParent) {
1029 String[] parsed = data.split(":");
1030 int parsedUsed = 0;
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001031 if (parsed.length < 6) {
1032 Slog.e(TAG, "reading data file with insufficient length - ignoring");
1033 return;
1034 }
1035
1036 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
1037 Slog.e(TAG, "reading data file with bad version - ignoring");
1038 return;
1039 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001040
1041 mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001042 if (parsed.length != 5 + (2 * mPeriodCount)) {
Robert Greenwalt5a671d02010-06-07 16:43:16 -07001043 Slog.e(TAG, "reading data file with bad length (" + parsed.length +
1044 " != " + (5+(2*mPeriodCount)) + ") - ignoring");
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001045 return;
1046 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001047
1048 mPeriodRxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001049 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001050 mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
1051 }
1052 mPeriodTxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001053 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001054 mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
1055 }
1056 mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]);
1057 mPeriodStart = new GregorianCalendar();
1058 mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1059 mPeriodEnd = new GregorianCalendar();
1060 mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1061 }
1062 }
1063
1064 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001065 synchronized (mParent) {
1066 if (which > mPeriodCount) return 0;
1067 which = mCurrentPeriod - which;
1068 if (which < 0) which += mPeriodCount;
1069 return mPeriodRxData[which];
1070 }
1071 }
1072 long getPeriodTx(int which) {
1073 synchronized (mParent) {
1074 if (which > mPeriodCount) return 0;
1075 which = mCurrentPeriod - which;
1076 if (which < 0) which += mPeriodCount;
1077 return mPeriodTxData[which];
1078 }
1079 }
1080 }
1081
1082 @Override
1083 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1084 if (mContext.checkCallingOrSelfPermission(
1085 android.Manifest.permission.DUMP)
1086 != PackageManager.PERMISSION_GRANTED) {
1087 pw.println("Permission Denial: can't dump ThrottleService " +
1088 "from from pid=" + Binder.getCallingPid() + ", uid=" +
1089 Binder.getCallingUid());
1090 return;
1091 }
1092 pw.println();
1093
1094 pw.println("The threshold is " + mPolicyThreshold +
1095 ", after which you experince throttling to " +
1096 mPolicyThrottleValue + "kbps");
1097 pw.println("Current period is " +
1098 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001099 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001100 " seconds.");
1101 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
Robert Greenwalt8c7e6092010-04-14 17:31:20 -07001102 pw.println("Current Throttle Index is " + mThrottleIndex);
Robert Greenwaltd1055a22010-05-25 15:54:52 -07001103 pw.println("Max NTP Cache Age is " + mMaxNtpCacheAgeSec);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -07001104
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001105 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
1106 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
1107 mRecorder.getPeriodTx(i));
1108 }
1109 }
1110}