blob: f0b2210c33886741c3fb29313134dde88224acdc [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) {
126 if (DBG) Slog.d(TAG, "Starting ThrottleService");
127 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() {
291 if (DBG) Slog.d(TAG, "systemReady");
292 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() {
374 if (DBG) Slog.d(TAG, "onRebootRecovery");
375 // 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 Greenwalt9e696c22010-04-01 14:45:18 -0700440 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + mPolicyPollPeriodSec +
441 ", threshold=" + mPolicyThreshold + ", value=" + mPolicyThrottleValue +
442 ", resetDay=" + mPolicyResetDay + ", noteType=" +
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700443 mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" + mMaxNtpCacheAgeSec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700444
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700445 // force updates
446 mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
447
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700448 onResetAlarm();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700449
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700450 onPollAlarm();
451
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700452 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
453 mContext.sendBroadcast(broadcast);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700454 }
455
456 private void onPollAlarm() {
457 long now = SystemClock.elapsedRealtime();
458 long next = now + mPolicyPollPeriodSec*1000;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700459
460 checkForAuthoritativeTime();
461
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700462 long incRead = 0;
463 long incWrite = 0;
464 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700465 incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
466 incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700467 // handle iface resets - on some device the 3g iface comes and goes and gets
468 // totals reset to 0. Deal with it
469 if ((incRead < 0) || (incWrite < 0)) {
470 incRead += mLastRead;
471 incWrite += mLastWrite;
472 mLastRead = 0;
473 mLastWrite = 0;
474 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700475 } catch (RemoteException e) {
476 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
477 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700478 // don't count this data if we're roaming.
479 boolean roaming = "true".equals(
480 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
481 if (!roaming) {
482 mRecorder.addData(incRead, incWrite);
483 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700484
485 long periodRx = mRecorder.getPeriodRx(0);
486 long periodTx = mRecorder.getPeriodTx(0);
487 long total = periodRx + periodTx;
488 if (DBG) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700489 Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
Robert Greenwalt5f996892010-04-08 16:19:24 -0700490 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700491 }
492 mLastRead += incRead;
493 mLastWrite += incWrite;
494
495 checkThrottleAndPostNotification(total);
496
497 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
498 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
499 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700500 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
501 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700502 mContext.sendStickyBroadcast(broadcast);
503
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700504 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700505 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent);
506 }
507
Robert Greenwaltfee46832010-05-06 12:25:13 -0700508 private void onIfaceUp() {
509 // if we were throttled before, be sure and set it again - the iface went down
510 // (and may have disappeared all together) and these settings were lost
511 if (mThrottleIndex == 1) {
512 try {
513 mNMService.setInterfaceThrottle(mIface, -1, -1);
514 mNMService.setInterfaceThrottle(mIface,
515 mPolicyThrottleValue, mPolicyThrottleValue);
516 } catch (Exception e) {
517 Slog.e(TAG, "error setting Throttle: " + e);
518 }
519 }
520 }
521
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700522 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700523 // is throttling enabled?
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700524 if (mPolicyThreshold == 0) {
Robert Greenwaltcce83372010-04-23 17:35:29 -0700525 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700526 return;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700527 }
528
529 // have we spoken with an ntp server yet?
530 // this is controversial, but we'd rather err towards not throttling
531 if ((mNtpServer != null) && !mNtpActive) {
532 return;
533 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700534
535 // check if we need to throttle
536 if (currentTotal > mPolicyThreshold) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700537 if (mThrottleIndex != 1) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700538 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700539 mThrottleIndex = 1;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700540 }
541 if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!");
542 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700543 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700544 mPolicyThrottleValue, mPolicyThrottleValue);
545 } catch (Exception e) {
546 Slog.e(TAG, "error setting Throttle: " + e);
547 }
548
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700549 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700550
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700551 postNotification(R.string.throttled_notification_title,
552 R.string.throttled_notification_message,
553 R.drawable.stat_sys_throttled,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700554 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700555
556 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
557 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue);
558 mContext.sendStickyBroadcast(broadcast);
559
560 } // else already up!
561 } else {
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700562 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700563 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
564 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700565 // pretend we only have 1/2 the time remaining that we actually do
566 // if our burn rate in the period so far would have us exceed the limit
567 // in that 1/2 window, warn the user.
568 // this gets more generous in the early to middle period and converges back
569 // to the limit as we move toward the period end.
570
571 // adding another factor - it must be greater than the total cap/4
572 // else we may get false alarms very early in the period.. in the first
573 // tenth of a percent of the period if we used more than a tenth of a percent
574 // of the cap we'd get a warning and that's not desired.
575 long start = mRecorder.getPeriodStart();
576 long end = mRecorder.getPeriodEnd();
577 long periodLength = end - start;
578 long now = System.currentTimeMillis();
579 long timeUsed = now - start;
580 long warningThreshold = 2*mPolicyThreshold*timeUsed/(timeUsed+periodLength);
581 if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) {
582 if (mWarningNotificationSent == false) {
583 mWarningNotificationSent = true;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700584 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
585 postNotification(R.string.throttle_warning_notification_title,
586 R.string.throttle_warning_notification_message,
587 R.drawable.stat_sys_throttled,
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700588 0);
589 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700590 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700591 if (mWarningNotificationSent == true) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700592 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700593 mWarningNotificationSent =false;
594 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700595 }
596 }
597 }
598 }
599
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700600 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700601 Intent intent = new Intent();
602 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700603 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700604 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
605
606 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
607
608 Resources r = Resources.getSystem();
609 CharSequence title = r.getText(titleInt);
610 CharSequence message = r.getText(messageInt);
611 if (mThrottlingNotification == null) {
612 mThrottlingNotification = new Notification();
613 mThrottlingNotification.when = 0;
614 // TODO - fixup icon
615 mThrottlingNotification.icon = icon;
616 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700617 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700618 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700619 mThrottlingNotification.tickerText = title;
620 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
621
622 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
623 }
624
625
626 private synchronized void clearThrottleAndNotification() {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700627 if (mThrottleIndex != THROTTLE_INDEX_UNTHROTTLED) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700628 synchronized (ThrottleService.this) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700629 mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700630 }
631 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700632 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700633 } catch (Exception e) {
634 Slog.e(TAG, "error clearing Throttle: " + e);
635 }
636 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
637 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
638 mContext.sendStickyBroadcast(broadcast);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700639 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
640 mWarningNotificationSent = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700641 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700642 }
643
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700644 private Calendar calculatePeriodEnd(long now) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700645 Calendar end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700646 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700647 int day = end.get(Calendar.DAY_OF_MONTH);
648 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
649 end.set(Calendar.HOUR_OF_DAY, 0);
650 end.set(Calendar.MINUTE, 0);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700651 end.set(Calendar.SECOND, 0);
652 end.set(Calendar.MILLISECOND, 0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700653 if (day >= mPolicyResetDay) {
654 int month = end.get(Calendar.MONTH);
655 if (month == Calendar.DECEMBER) {
656 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
657 month = Calendar.JANUARY - 1;
658 }
659 end.set(Calendar.MONTH, month + 1);
660 }
661
662 // TODO - remove!
663 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
664 end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700665 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700666 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
667 }
668 return end;
669 }
670 private Calendar calculatePeriodStart(Calendar end) {
671 Calendar start = (Calendar)end.clone();
672 int month = end.get(Calendar.MONTH);
673 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
674 month = Calendar.DECEMBER + 1;
675 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
676 }
677 start.set(Calendar.MONTH, month - 1);
678
679 // TODO - remove!!
680 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
681 start = (Calendar)end.clone();
682 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
683 }
684 return start;
685 }
686
687 private void onResetAlarm() {
688 if (DBG) {
689 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
690 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
691 }
692
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700693 long now = getBestTime();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700694
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700695 if (mNtpActive || (mNtpServer == null)) {
696 Calendar end = calculatePeriodEnd(now);
697 Calendar start = calculatePeriodStart(end);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700698
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700699 if (mRecorder.setNextPeriod(start, end)) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700700 onPollAlarm();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700701 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700702
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700703 mAlarmManager.cancel(mPendingResetIntent);
704 long offset = end.getTimeInMillis() - now;
705 // use Elapsed realtime so clock changes don't fool us.
706 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
707 SystemClock.elapsedRealtime() + offset,
708 mPendingResetIntent);
709 } else {
710 if (DBG) Slog.d(TAG, "no authoritative time - not resetting period");
711 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700712 }
713 }
714
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700715 private void checkForAuthoritativeTime() {
716 if (mNtpActive || (mNtpServer == null)) return;
717
Robert Greenwalt05d06732010-04-19 11:10:38 -0700718 // will try to get the ntp time and switch to it if found.
719 // will also cache the time so we don't fetch it repeatedly.
720 getBestTime();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700721 }
722
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700723 private static final int MAX_NTP_CACHE_AGE_SEC = 60 * 60 * 24; // 1 day
Robert Greenwalt05d06732010-04-19 11:10:38 -0700724 private static final int MAX_NTP_FETCH_WAIT = 10 * 1000;
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700725 private int mMaxNtpCacheAgeSec = MAX_NTP_CACHE_AGE_SEC;
Robert Greenwalt05d06732010-04-19 11:10:38 -0700726 private long cachedNtp;
727 private long cachedNtpTimestamp;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700728
Robert Greenwalt05d06732010-04-19 11:10:38 -0700729 private long getBestTime() {
730 if (mNtpServer != null) {
731 if (mNtpActive) {
732 long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp;
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700733 if (ntpAge < mMaxNtpCacheAgeSec * 1000) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700734 if (VDBG) Slog.v(TAG, "using cached time");
Robert Greenwalt05d06732010-04-19 11:10:38 -0700735 return cachedNtp + ntpAge;
736 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700737 }
Robert Greenwalt05d06732010-04-19 11:10:38 -0700738 SntpClient client = new SntpClient();
739 if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT)) {
740 cachedNtp = client.getNtpTime();
741 cachedNtpTimestamp = SystemClock.elapsedRealtime();
742 if (!mNtpActive) {
743 mNtpActive = true;
744 if (DBG) Slog.d(TAG, "found Authoritative time - reseting alarm");
745 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
746 }
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700747 if (VDBG) Slog.v(TAG, "using Authoritative time: " + cachedNtp);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700748 return cachedNtp;
749 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700750 }
Robert Greenwalt05d06732010-04-19 11:10:38 -0700751 long time = System.currentTimeMillis();
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700752 if (VDBG) Slog.v(TAG, "using User time: " + time);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700753 mNtpActive = false;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700754 return time;
755 }
756
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700757 // records bytecount data for a given time and accumulates it into larger time windows
758 // for logging and other purposes
759 //
760 // since time can be changed (user or network action) we will have to track the time of the
761 // last recording and deal with it.
762 private static class DataRecorder {
763 long[] mPeriodRxData;
764 long[] mPeriodTxData;
765 int mCurrentPeriod;
766 int mPeriodCount;
767
768 Calendar mPeriodStart;
769 Calendar mPeriodEnd;
770
771 ThrottleService mParent;
772 Context mContext;
Robert Greenwalte6e98822010-04-15 08:27:14 -0700773 String mImsi = null;
774
775 TelephonyManager mTelephonyManager;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700776
777 DataRecorder(Context context, ThrottleService parent) {
778 mContext = context;
779 mParent = parent;
780
Robert Greenwalte6e98822010-04-15 08:27:14 -0700781 mTelephonyManager = (TelephonyManager)mContext.getSystemService(
782 Context.TELEPHONY_SERVICE);
783
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700784 synchronized (mParent) {
785 mPeriodCount = 6;
786 mPeriodRxData = new long[mPeriodCount];
787 mPeriodTxData = new long[mPeriodCount];
788
789 mPeriodStart = Calendar.getInstance();
790 mPeriodEnd = Calendar.getInstance();
791
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700792 retrieve();
793 }
794 }
795
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700796 boolean setNextPeriod(Calendar start, Calendar end) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700797 // TODO - how would we deal with a dual-IMSI device?
798 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700799 boolean startNewPeriod = true;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700800
Robert Greenwalt27fba672010-04-26 12:29:14 -0700801 if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) {
802 // same endpoints - keep collecting
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700803 if (DBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700804 Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," +
805 end.getTimeInMillis() +") - ammending data");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700806 }
Robert Greenwalt27fba672010-04-26 12:29:14 -0700807 startNewPeriod = false;
808 } else {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700809 if (DBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700810 if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) {
811 Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," +
812 end.getTimeInMillis() + ") - old end was " +
813 mPeriodEnd.getTimeInMillis() + ", following");
814 } else {
815 Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," +
816 end.getTimeInMillis() + ") replacing old (" +
817 mPeriodStart.getTimeInMillis() + "," +
818 mPeriodEnd.getTimeInMillis() + ")");
819 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700820 }
821 synchronized (mParent) {
822 ++mCurrentPeriod;
823 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
824 mPeriodRxData[mCurrentPeriod] = 0;
825 mPeriodTxData[mCurrentPeriod] = 0;
826 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700827 }
828 setPeriodStart(start);
829 setPeriodEnd(end);
830 record();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700831 return startNewPeriod;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700832 }
833
834 public long getPeriodEnd() {
835 synchronized (mParent) {
836 return mPeriodEnd.getTimeInMillis();
837 }
838 }
839
840 private void setPeriodEnd(Calendar end) {
841 synchronized (mParent) {
842 mPeriodEnd = end;
843 }
844 }
845
846 public long getPeriodStart() {
847 synchronized (mParent) {
848 return mPeriodStart.getTimeInMillis();
849 }
850 }
851
852 private void setPeriodStart(Calendar start) {
853 synchronized (mParent) {
854 mPeriodStart = start;
855 }
856 }
857
858 public int getPeriodCount() {
859 synchronized (mParent) {
860 return mPeriodCount;
861 }
862 }
863
864 private void zeroData(int field) {
865 synchronized (mParent) {
866 for(int period = 0; period<mPeriodCount; period++) {
867 mPeriodRxData[period] = 0;
868 mPeriodTxData[period] = 0;
869 }
870 mCurrentPeriod = 0;
871 }
872
873 }
874
875 // if time moves backward accumulate all read/write that's lost into the now
876 // otherwise time moved forward.
877 void addData(long bytesRead, long bytesWritten) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700878 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700879
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700880 synchronized (mParent) {
881 mPeriodRxData[mCurrentPeriod] += bytesRead;
882 mPeriodTxData[mCurrentPeriod] += bytesWritten;
883 }
884 record();
885 }
886
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700887 private File getDataFile() {
888 File dataDir = Environment.getDataDirectory();
889 File throttleDir = new File(dataDir, "system/throttle");
890 throttleDir.mkdirs();
Robert Greenwalte6e98822010-04-15 08:27:14 -0700891 String mImsi = mTelephonyManager.getSubscriberId();
892 File dataFile;
893 if (mImsi == null) {
894 dataFile = useMRUFile(throttleDir);
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700895 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700896 } else {
897 String imsiHash = Integer.toString(mImsi.hashCode());
898 dataFile = new File(throttleDir, imsiHash);
899 }
900 // touch the file so it's not LRU
901 dataFile.setLastModified(System.currentTimeMillis());
902 checkAndDeleteLRUDataFile(throttleDir);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700903 return dataFile;
904 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700905
Robert Greenwalte6e98822010-04-15 08:27:14 -0700906 // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling
907 private void checkForSubscriberId() {
908 if (mImsi != null) return;
909
910 mImsi = mTelephonyManager.getSubscriberId();
911 if (mImsi == null) return;
912
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700913 if (DBG) Slog.d(TAG, "finally have imsi - retreiving data");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700914 retrieve();
915 }
916
917 private final static int MAX_SIMS_SUPPORTED = 3;
918
919 private void checkAndDeleteLRUDataFile(File dir) {
920 File[] files = dir.listFiles();
921
922 if (files.length <= MAX_SIMS_SUPPORTED) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700923 if (DBG) Slog.d(TAG, "Too many data files");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700924 do {
925 File oldest = null;
926 for (File f : files) {
927 if ((oldest == null) || (oldest.lastModified() > f.lastModified())) {
928 oldest = f;
929 }
930 }
931 if (oldest == null) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700932 if (DBG) Slog.d(TAG, " deleting " + oldest);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700933 oldest.delete();
934 files = dir.listFiles();
935 } while (files.length > MAX_SIMS_SUPPORTED);
936 }
937
938 private File useMRUFile(File dir) {
939 File newest = null;
940 File[] files = dir.listFiles();
941
942 for (File f : files) {
943 if ((newest == null) || (newest.lastModified() < f.lastModified())) {
944 newest = f;
945 }
946 }
947 if (newest == null) {
948 newest = new File(dir, "temp");
949 }
950 return newest;
951 }
952
953
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700954 private static final int DATA_FILE_VERSION = 1;
955
956 private void record() {
957 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700958 // 1 int mPeriodCount
959 // 13*6 long[PERIOD_COUNT] mPeriodRxData
960 // 13*6 long[PERIOD_COUNT] mPeriodTxData
961 // 1 int mCurrentPeriod
962 // 13 long periodStartMS
963 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700964 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700965 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700966 builder.append(DATA_FILE_VERSION);
967 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700968 builder.append(mPeriodCount);
969 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700970 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700971 builder.append(mPeriodRxData[i]);
972 builder.append(":");
973 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700974 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700975 builder.append(mPeriodTxData[i]);
976 builder.append(":");
977 }
978 builder.append(mCurrentPeriod);
979 builder.append(":");
980 builder.append(mPeriodStart.getTimeInMillis());
981 builder.append(":");
982 builder.append(mPeriodEnd.getTimeInMillis());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700983
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700984 BufferedWriter out = null;
985 try {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700986 out = new BufferedWriter(new FileWriter(getDataFile()), 256);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700987 out.write(builder.toString());
988 } catch (IOException e) {
989 Slog.e(TAG, "Error writing data file");
990 return;
991 } finally {
992 if (out != null) {
993 try {
994 out.close();
995 } catch (Exception e) {}
996 }
997 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700998 }
999
1000 private void retrieve() {
Robert Greenwalt05d06732010-04-19 11:10:38 -07001001 // clean out any old data first. If we fail to read we don't want old stuff
1002 zeroData(0);
1003
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001004 File f = getDataFile();
1005 byte[] buffer;
1006 FileInputStream s = null;
1007 try {
1008 buffer = new byte[(int)f.length()];
1009 s = new FileInputStream(f);
1010 s.read(buffer);
1011 } catch (IOException e) {
1012 Slog.e(TAG, "Error reading data file");
1013 return;
1014 } finally {
1015 if (s != null) {
1016 try {
1017 s.close();
1018 } catch (Exception e) {}
1019 }
1020 }
1021 String data = new String(buffer);
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001022 if (data == null || data.length() == 0) {
1023 if (DBG) Slog.d(TAG, "data file empty");
1024 return;
1025 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001026 synchronized (mParent) {
1027 String[] parsed = data.split(":");
1028 int parsedUsed = 0;
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001029 if (parsed.length < 6) {
1030 Slog.e(TAG, "reading data file with insufficient length - ignoring");
1031 return;
1032 }
1033
1034 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
1035 Slog.e(TAG, "reading data file with bad version - ignoring");
1036 return;
1037 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001038
1039 mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001040 if (parsed.length != 5 + (2 * mPeriodCount)) {
Robert Greenwalt05d06732010-04-19 11:10:38 -07001041 Slog.e(TAG, "reading data file with bad length ("+parsed.length+" != "+(5 + (2*mPeriodCount))+") - ignoring");
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001042 return;
1043 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001044
1045 mPeriodRxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001046 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001047 mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
1048 }
1049 mPeriodTxData = new long[mPeriodCount];
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001050 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001051 mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
1052 }
1053 mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]);
1054 mPeriodStart = new GregorianCalendar();
1055 mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1056 mPeriodEnd = new GregorianCalendar();
1057 mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1058 }
1059 }
1060
1061 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001062 synchronized (mParent) {
1063 if (which > mPeriodCount) return 0;
1064 which = mCurrentPeriod - which;
1065 if (which < 0) which += mPeriodCount;
1066 return mPeriodRxData[which];
1067 }
1068 }
1069 long getPeriodTx(int which) {
1070 synchronized (mParent) {
1071 if (which > mPeriodCount) return 0;
1072 which = mCurrentPeriod - which;
1073 if (which < 0) which += mPeriodCount;
1074 return mPeriodTxData[which];
1075 }
1076 }
1077 }
1078
1079 @Override
1080 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1081 if (mContext.checkCallingOrSelfPermission(
1082 android.Manifest.permission.DUMP)
1083 != PackageManager.PERMISSION_GRANTED) {
1084 pw.println("Permission Denial: can't dump ThrottleService " +
1085 "from from pid=" + Binder.getCallingPid() + ", uid=" +
1086 Binder.getCallingUid());
1087 return;
1088 }
1089 pw.println();
1090
1091 pw.println("The threshold is " + mPolicyThreshold +
1092 ", after which you experince throttling to " +
1093 mPolicyThrottleValue + "kbps");
1094 pw.println("Current period is " +
1095 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001096 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001097 " seconds.");
1098 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
Robert Greenwalt8c7e6092010-04-14 17:31:20 -07001099 pw.println("Current Throttle Index is " + mThrottleIndex);
Robert Greenwaltd1055a22010-05-25 15:54:52 -07001100 pw.println("Max NTP Cache Age is " + mMaxNtpCacheAgeSec);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -07001101
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001102 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
1103 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
1104 mRecorder.getPeriodTx(i));
1105 }
1106 }
1107}