blob: ccfc99bee083521e7bfabe0da8d30364f36df5f1 [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;
Robert Greenwalt39e163f2010-05-07 16:52:17 -070063import java.util.concurrent.atomic.AtomicInteger;
64import java.util.concurrent.atomic.AtomicLong;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070065import java.util.GregorianCalendar;
Robert Greenwalt7171ea82010-04-14 22:37:12 -070066import java.util.Properties;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070067import java.util.Random;
68
69// TODO - add comments - reference the ThrottleManager for public API
70public class ThrottleService extends IThrottleManager.Stub {
71
72 private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing";
73
74 private static final String TAG = "ThrottleService";
Robert Greenwaltbf7de392010-04-21 17:09:38 -070075 private static final boolean DBG = true;
76 private static final boolean VDBG = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070077 private Handler mHandler;
78 private HandlerThread mThread;
79
80 private Context mContext;
81
Robert Greenwaltfb9896b2010-04-22 15:39:38 -070082 private static final int INITIAL_POLL_DELAY_SEC = 90;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070083 private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
Robert Greenwalt7171ea82010-04-14 22:37:12 -070084 private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070085 private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070086
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070087 private int mPolicyPollPeriodSec;
Robert Greenwalt39e163f2010-05-07 16:52:17 -070088 private AtomicLong mPolicyThreshold;
89 private AtomicInteger mPolicyThrottleValue;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070090 private int mPolicyResetDay; // 1-28
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070091 private int mPolicyNotificationsAllowedMask;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070092
93 private long mLastRead; // read byte count from last poll
94 private long mLastWrite; // write byte count from last poll
95
96 private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL";
97 private static int POLL_REQUEST = 0;
98 private PendingIntent mPendingPollIntent;
99 private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET";
100 private static int RESET_REQUEST = 1;
101 private PendingIntent mPendingResetIntent;
102
103 private INetworkManagementService mNMService;
104 private AlarmManager mAlarmManager;
105 private NotificationManager mNotificationManager;
106
107 private DataRecorder mRecorder;
108
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700109 private String mIface;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700110
111 private static final int NOTIFICATION_WARNING = 2;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700112
113 private Notification mThrottlingNotification;
114 private boolean mWarningNotificationSent = false;
115
Robert Greenwaltfee46832010-05-06 12:25:13 -0700116 private InterfaceObserver mInterfaceObserver;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700117 private SettingsObserver mSettingsObserver;
118
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700119 private AtomicInteger mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700120 private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
121 private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
122
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700123 private static final String PROPERTIES_FILE = "/etc/gps.conf";
124 private String mNtpServer;
125 private boolean mNtpActive;
126
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700127 public ThrottleService(Context context) {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700128 if (VDBG) Slog.v(TAG, "Starting ThrottleService");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700129 mContext = context;
130
Robert Greenwalt24488bd2010-05-10 16:56:43 -0700131 mPolicyThreshold = new AtomicLong();
132 mPolicyThrottleValue = new AtomicInteger();
133 mThrottleIndex = new AtomicInteger();
134
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700135 mNtpActive = false;
136
Robert Greenwaltfee46832010-05-06 12:25:13 -0700137 mIface = mContext.getResources().getString(R.string.config_datause_iface);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700138 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
139 Intent pollIntent = new Intent(ACTION_POLL, null);
140 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
141 Intent resetIntent = new Intent(ACTION_RESET, null);
142 mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
143
144 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
145 mNMService = INetworkManagementService.Stub.asInterface(b);
146
147 mNotificationManager = (NotificationManager)mContext.getSystemService(
148 Context.NOTIFICATION_SERVICE);
149 }
150
Robert Greenwaltfee46832010-05-06 12:25:13 -0700151 private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
152 private int mMsg;
153 private Handler mHandler;
154 private String mIface;
155
156 InterfaceObserver(Handler handler, int msg, String iface) {
157 super();
158 mHandler = handler;
159 mMsg = msg;
160 mIface = iface;
161 }
162
Stan Chesnutt780dfa422011-01-03 08:43:57 -0800163 public void interfaceStatusChanged(String iface, boolean link) {
Robert Greenwaltfee46832010-05-06 12:25:13 -0700164 if (link) {
165 if (TextUtils.equals(iface, mIface)) {
166 mHandler.obtainMessage(mMsg).sendToTarget();
167 }
168 }
169 }
170
171 public void interfaceAdded(String iface) {
172 // TODO - an interface added in the UP state should also trigger a StatusChanged
173 // notification..
174 if (TextUtils.equals(iface, mIface)) {
175 mHandler.obtainMessage(mMsg).sendToTarget();
176 }
177 }
178
Stan Chesnutt780dfa422011-01-03 08:43:57 -0800179 public void interfaceLinkStateChanged(String iface, boolean linkState) {}
180
Robert Greenwaltfee46832010-05-06 12:25:13 -0700181 public void interfaceRemoved(String iface) {}
182 }
183
184
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700185 private static class SettingsObserver extends ContentObserver {
186 private int mMsg;
187 private Handler mHandler;
188 SettingsObserver(Handler handler, int msg) {
189 super(handler);
190 mHandler = handler;
191 mMsg = msg;
192 }
193
194 void observe(Context context) {
195 ContentResolver resolver = context.getContentResolver();
196 resolver.registerContentObserver(Settings.Secure.getUriFor(
197 Settings.Secure.THROTTLE_POLLING_SEC), false, this);
198 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700199 Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700200 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700201 Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700202 resolver.registerContentObserver(Settings.Secure.getUriFor(
203 Settings.Secure.THROTTLE_RESET_DAY), false, this);
204 resolver.registerContentObserver(Settings.Secure.getUriFor(
205 Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this);
206 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700207 Settings.Secure.THROTTLE_HELP_URI), false, this);
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700208 resolver.registerContentObserver(Settings.Secure.getUriFor(
209 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700210 }
211
212 @Override
213 public void onChange(boolean selfChange) {
214 mHandler.obtainMessage(mMsg).sendToTarget();
215 }
216 }
217
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700218 private void enforceAccessPermission() {
219 mContext.enforceCallingOrSelfPermission(
220 android.Manifest.permission.ACCESS_NETWORK_STATE,
221 "ThrottleService");
222 }
223
Robert Greenwalt05d06732010-04-19 11:10:38 -0700224 private long ntpToWallTime(long ntpTime) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700225 long bestNow = getBestTime(true); // do it quickly
Robert Greenwalt05d06732010-04-19 11:10:38 -0700226 long localNow = System.currentTimeMillis();
227 return localNow + (ntpTime - bestNow);
228 }
229
Irfan Sheriffcf282362010-04-16 16:53:20 -0700230 // TODO - fetch for the iface
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700231 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt05d06732010-04-19 11:10:38 -0700232
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700233 public long getResetTime(String iface) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700234 enforceAccessPermission();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700235 long resetTime = 0;
Irfan Sheriffcf282362010-04-16 16:53:20 -0700236 if (mRecorder != null) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700237 resetTime = mRecorder.getPeriodEnd();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700238 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700239 resetTime = ntpToWallTime(resetTime);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700240 return resetTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700241 }
Irfan Sheriffcf282362010-04-16 16:53:20 -0700242
243 // TODO - fetch for the iface
Robert Greenwalt05d06732010-04-19 11:10:38 -0700244 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700245 public long getPeriodStartTime(String iface) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700246 long startTime = 0;
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700247 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700248 if (mRecorder != null) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700249 startTime = mRecorder.getPeriodStart();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700250 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700251 startTime = ntpToWallTime(startTime);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700252 return startTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700253 }
254 //TODO - a better name? getCliffByteCountThreshold?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700255 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700256 public long getCliffThreshold(String iface, int cliff) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700257 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700258 if (cliff == 1) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700259 return mPolicyThreshold.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700260 }
261 return 0;
262 }
263 // TODO - a better name? getThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700264 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700265 public int getCliffLevel(String iface, int cliff) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700266 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700267 if (cliff == 1) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700268 return mPolicyThrottleValue.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700269 }
270 return 0;
271 }
272
Irfan Sheriffc9b68512010-04-08 14:12:33 -0700273 public String getHelpUri() {
274 enforceAccessPermission();
275 return Settings.Secure.getString(mContext.getContentResolver(),
276 Settings.Secure.THROTTLE_HELP_URI);
277 }
278
Irfan Sheriffcf282362010-04-16 16:53:20 -0700279 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700280 public long getByteCount(String iface, int dir, int period, int ago) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700281 enforceAccessPermission();
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700282 if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700283 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
284 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
285 }
286 return 0;
287 }
288
289 // TODO - a better name - getCurrentThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700290 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700291 public int getThrottle(String iface) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700292 enforceAccessPermission();
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700293 if (mThrottleIndex.get() == 1) {
294 return mPolicyThrottleValue.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700295 }
296 return 0;
297 }
298
299 void systemReady() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700300 if (VDBG) Slog.v(TAG, "systemReady");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700301 mContext.registerReceiver(
302 new BroadcastReceiver() {
303 @Override
304 public void onReceive(Context context, Intent intent) {
305 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
306 }
307 }, new IntentFilter(ACTION_POLL));
308
309 mContext.registerReceiver(
310 new BroadcastReceiver() {
311 @Override
312 public void onReceive(Context context, Intent intent) {
313 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
314 }
315 }, new IntentFilter(ACTION_RESET));
316
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700317 FileInputStream stream = null;
318 try {
319 Properties properties = new Properties();
320 File file = new File(PROPERTIES_FILE);
321 stream = new FileInputStream(file);
322 properties.load(stream);
323 mNtpServer = properties.getProperty("NTP_SERVER", null);
324 } catch (IOException e) {
325 Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
326 } finally {
327 if (stream != null) {
328 try {
329 stream.close();
330 } catch (Exception e) {}
331 }
332 }
Robert Greenwaltc76c15e2010-06-16 14:42:16 -0700333
334 // use a new thread as we don't want to stall the system for file writes
335 mThread = new HandlerThread(TAG);
336 mThread.start();
337 mHandler = new MyHandler(mThread.getLooper());
338 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
339
340 mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
341 try {
342 mNMService.registerObserver(mInterfaceObserver);
343 } catch (RemoteException e) {
344 Slog.e(TAG, "Could not register InterfaceObserver " + e);
345 }
346
347 mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
348 mSettingsObserver.observe(mContext);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700349 }
350
351
352 private static final int EVENT_REBOOT_RECOVERY = 0;
353 private static final int EVENT_POLICY_CHANGED = 1;
354 private static final int EVENT_POLL_ALARM = 2;
355 private static final int EVENT_RESET_ALARM = 3;
Robert Greenwaltfee46832010-05-06 12:25:13 -0700356 private static final int EVENT_IFACE_UP = 4;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700357 private class MyHandler extends Handler {
358 public MyHandler(Looper l) {
359 super(l);
360 }
361
362 @Override
363 public void handleMessage(Message msg) {
364 switch (msg.what) {
365 case EVENT_REBOOT_RECOVERY:
366 onRebootRecovery();
367 break;
368 case EVENT_POLICY_CHANGED:
369 onPolicyChanged();
370 break;
371 case EVENT_POLL_ALARM:
372 onPollAlarm();
373 break;
374 case EVENT_RESET_ALARM:
375 onResetAlarm();
Robert Greenwaltfee46832010-05-06 12:25:13 -0700376 break;
377 case EVENT_IFACE_UP:
378 onIfaceUp();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700379 }
380 }
381
382 private void onRebootRecovery() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700383 if (VDBG) Slog.v(TAG, "onRebootRecovery");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700384 // check for sim change TODO
385 // reregister for notification of policy change
386
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700387 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700388
389 mRecorder = new DataRecorder(mContext, ThrottleService.this);
390
391 // get policy
392 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
Robert Greenwaltfb9896b2010-04-22 15:39:38 -0700393
394 // if we poll now we won't have network connectivity or even imsi access
395 // queue up a poll to happen in a little while - after ntp and imsi are avail
396 // TODO - make this callback based (ie, listen for notificaitons)
397 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM),
398 INITIAL_POLL_DELAY_SEC * 1000);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700399 }
400
401 // check for new policy info (threshold limit/value/etc)
402 private void onPolicyChanged() {
403 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
404
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700405 int pollingPeriod = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700406 R.integer.config_datause_polling_period_sec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700407 mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
408 Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
409
410 // TODO - remove testing stuff?
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700411 long defaultThreshold = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700412 R.integer.config_datause_threshold_bytes);
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700413 int defaultValue = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700414 R.integer.config_datause_throttle_kbitsps);
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700415 long threshold = Settings.Secure.getLong(mContext.getContentResolver(),
416 Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
417 int value = Settings.Secure.getInt(mContext.getContentResolver(),
418 Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
419
420 mPolicyThreshold.set(threshold);
421 mPolicyThrottleValue.set(value);
422 if (testing) {
423 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
424 mPolicyThreshold.set(TESTING_THRESHOLD);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700425 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700426
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700427 mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
428 Settings.Secure.THROTTLE_RESET_DAY, -1);
429 if (mPolicyResetDay == -1 ||
430 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
431 Random g = new Random();
432 mPolicyResetDay = 1 + g.nextInt(28); // 1-28
433 Settings.Secure.putInt(mContext.getContentResolver(),
434 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
435 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700436 if (mIface == null) {
437 mPolicyThreshold.set(0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700438 }
439
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700440 int defaultNotificationType = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700441 R.integer.config_datause_notification_type);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700442 mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700443 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700444
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700445 mMaxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
446 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC, MAX_NTP_CACHE_AGE_SEC);
447
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700448 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700449 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700450 mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
451 ", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
452 ", noteType=" + mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" +
453 mMaxNtpCacheAgeSec);
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700454 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700455
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700456 // force updates
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700457 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700458
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700459 onResetAlarm();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700460
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700461 onPollAlarm();
462
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700463 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
464 mContext.sendBroadcast(broadcast);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700465 }
466
467 private void onPollAlarm() {
468 long now = SystemClock.elapsedRealtime();
469 long next = now + mPolicyPollPeriodSec*1000;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700470
471 checkForAuthoritativeTime();
472
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700473 long incRead = 0;
474 long incWrite = 0;
475 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700476 incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
477 incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700478 // handle iface resets - on some device the 3g iface comes and goes and gets
479 // totals reset to 0. Deal with it
480 if ((incRead < 0) || (incWrite < 0)) {
481 incRead += mLastRead;
482 incWrite += mLastWrite;
483 mLastRead = 0;
484 mLastWrite = 0;
485 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700486 } catch (RemoteException e) {
487 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
488 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700489 // don't count this data if we're roaming.
490 boolean roaming = "true".equals(
491 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
492 if (!roaming) {
493 mRecorder.addData(incRead, incWrite);
494 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700495
496 long periodRx = mRecorder.getPeriodRx(0);
497 long periodTx = mRecorder.getPeriodTx(0);
498 long total = periodRx + periodTx;
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700499 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700500 Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
Robert Greenwalt5f996892010-04-08 16:19:24 -0700501 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700502 }
503 mLastRead += incRead;
504 mLastWrite += incWrite;
505
506 checkThrottleAndPostNotification(total);
507
508 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
509 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
510 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700511 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
512 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700513 mContext.sendStickyBroadcast(broadcast);
514
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700515 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700516 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700517 }
518
Robert Greenwaltfee46832010-05-06 12:25:13 -0700519 private void onIfaceUp() {
520 // if we were throttled before, be sure and set it again - the iface went down
521 // (and may have disappeared all together) and these settings were lost
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700522 if (mThrottleIndex.get() == 1) {
Robert Greenwaltfee46832010-05-06 12:25:13 -0700523 try {
524 mNMService.setInterfaceThrottle(mIface, -1, -1);
525 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700526 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
Robert Greenwaltfee46832010-05-06 12:25:13 -0700527 } catch (Exception e) {
528 Slog.e(TAG, "error setting Throttle: " + e);
529 }
530 }
531 }
532
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700533 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700534 // is throttling enabled?
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700535 long threshold = mPolicyThreshold.get();
536 if (threshold == 0) {
Robert Greenwaltcce83372010-04-23 17:35:29 -0700537 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700538 return;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700539 }
540
541 // have we spoken with an ntp server yet?
542 // this is controversial, but we'd rather err towards not throttling
543 if ((mNtpServer != null) && !mNtpActive) {
544 return;
545 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700546
547 // check if we need to throttle
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700548 if (currentTotal > threshold) {
549 if (mThrottleIndex.get() != 1) {
550 mThrottleIndex.set(1);
551 if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700552 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700553 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700554 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700555 } catch (Exception e) {
556 Slog.e(TAG, "error setting Throttle: " + e);
557 }
558
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700559 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700560
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700561 postNotification(R.string.throttled_notification_title,
562 R.string.throttled_notification_message,
563 R.drawable.stat_sys_throttled,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700564 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700565
566 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700567 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
568 mPolicyThrottleValue.get());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700569 mContext.sendStickyBroadcast(broadcast);
570
571 } // else already up!
572 } else {
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700573 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700574 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
575 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700576 // pretend we only have 1/2 the time remaining that we actually do
577 // if our burn rate in the period so far would have us exceed the limit
578 // in that 1/2 window, warn the user.
579 // this gets more generous in the early to middle period and converges back
580 // to the limit as we move toward the period end.
581
582 // adding another factor - it must be greater than the total cap/4
583 // else we may get false alarms very early in the period.. in the first
584 // tenth of a percent of the period if we used more than a tenth of a percent
585 // of the cap we'd get a warning and that's not desired.
586 long start = mRecorder.getPeriodStart();
587 long end = mRecorder.getPeriodEnd();
588 long periodLength = end - start;
589 long now = System.currentTimeMillis();
590 long timeUsed = now - start;
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700591 long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength);
592 if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700593 if (mWarningNotificationSent == false) {
594 mWarningNotificationSent = true;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700595 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
596 postNotification(R.string.throttle_warning_notification_title,
597 R.string.throttle_warning_notification_message,
598 R.drawable.stat_sys_throttled,
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700599 0);
600 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700601 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700602 if (mWarningNotificationSent == true) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700603 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700604 mWarningNotificationSent =false;
605 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700606 }
607 }
608 }
609 }
610
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700611 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700612 Intent intent = new Intent();
613 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700614 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700615 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
616
617 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
618
619 Resources r = Resources.getSystem();
620 CharSequence title = r.getText(titleInt);
621 CharSequence message = r.getText(messageInt);
622 if (mThrottlingNotification == null) {
623 mThrottlingNotification = new Notification();
624 mThrottlingNotification.when = 0;
625 // TODO - fixup icon
626 mThrottlingNotification.icon = icon;
627 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700628 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700629 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700630 mThrottlingNotification.tickerText = title;
631 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
632
633 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
634 }
635
636
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700637 private void clearThrottleAndNotification() {
638 if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) {
639 mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700640 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700641 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700642 } catch (Exception e) {
643 Slog.e(TAG, "error clearing Throttle: " + e);
644 }
645 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
646 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
647 mContext.sendStickyBroadcast(broadcast);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700648 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
649 mWarningNotificationSent = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700650 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700651 }
652
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700653 private Calendar calculatePeriodEnd(long now) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700654 Calendar end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700655 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700656 int day = end.get(Calendar.DAY_OF_MONTH);
657 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
658 end.set(Calendar.HOUR_OF_DAY, 0);
659 end.set(Calendar.MINUTE, 0);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700660 end.set(Calendar.SECOND, 0);
661 end.set(Calendar.MILLISECOND, 0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700662 if (day >= mPolicyResetDay) {
663 int month = end.get(Calendar.MONTH);
664 if (month == Calendar.DECEMBER) {
665 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
666 month = Calendar.JANUARY - 1;
667 }
668 end.set(Calendar.MONTH, month + 1);
669 }
670
671 // TODO - remove!
672 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
673 end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700674 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700675 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
676 }
677 return end;
678 }
679 private Calendar calculatePeriodStart(Calendar end) {
680 Calendar start = (Calendar)end.clone();
681 int month = end.get(Calendar.MONTH);
682 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
683 month = Calendar.DECEMBER + 1;
684 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
685 }
686 start.set(Calendar.MONTH, month - 1);
687
688 // TODO - remove!!
689 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
690 start = (Calendar)end.clone();
691 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
692 }
693 return start;
694 }
695
696 private void onResetAlarm() {
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700697 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700698 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
699 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
700 }
701
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700702 long now = getBestTime(false);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700703
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700704 if (mNtpActive || (mNtpServer == null)) {
705 Calendar end = calculatePeriodEnd(now);
706 Calendar start = calculatePeriodStart(end);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700707
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700708 if (mRecorder.setNextPeriod(start, end)) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700709 onPollAlarm();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700710 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700711
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700712 mAlarmManager.cancel(mPendingResetIntent);
713 long offset = end.getTimeInMillis() - now;
714 // use Elapsed realtime so clock changes don't fool us.
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700715 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700716 SystemClock.elapsedRealtime() + offset,
717 mPendingResetIntent);
718 } else {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700719 if (VDBG) Slog.d(TAG, "no authoritative time - not resetting period");
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700720 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700721 }
722 }
723
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700724 private void checkForAuthoritativeTime() {
725 if (mNtpActive || (mNtpServer == null)) return;
726
Robert Greenwalt05d06732010-04-19 11:10:38 -0700727 // will try to get the ntp time and switch to it if found.
728 // will also cache the time so we don't fetch it repeatedly.
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700729 getBestTime(false);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700730 }
731
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700732 private static final int MAX_NTP_CACHE_AGE_SEC = 60 * 60 * 24; // 1 day
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700733 private static final int MAX_NTP_FETCH_WAIT = 20 * 1000;
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700734 private int mMaxNtpCacheAgeSec = MAX_NTP_CACHE_AGE_SEC;
Robert Greenwalt05d06732010-04-19 11:10:38 -0700735 private long cachedNtp;
736 private long cachedNtpTimestamp;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700737
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700738 // if the request is tied to UI and ANR's are a danger, request a fast result
739 // the regular polling should have updated the cached time recently using the
740 // slower method (!fast)
741 private long getBestTime(boolean fast) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700742 if (mNtpServer != null) {
743 if (mNtpActive) {
744 long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp;
Robert Greenwaltd1c3ea62010-05-26 10:37:48 -0700745 if (ntpAge < mMaxNtpCacheAgeSec * 1000 || fast) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700746 if (VDBG) Slog.v(TAG, "using cached time");
Robert Greenwalt05d06732010-04-19 11:10:38 -0700747 return cachedNtp + ntpAge;
748 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700749 }
Robert Greenwalt05d06732010-04-19 11:10:38 -0700750 SntpClient client = new SntpClient();
751 if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT)) {
752 cachedNtp = client.getNtpTime();
753 cachedNtpTimestamp = SystemClock.elapsedRealtime();
754 if (!mNtpActive) {
755 mNtpActive = true;
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700756 if (VDBG) Slog.d(TAG, "found Authoritative time - reseting alarm");
Robert Greenwalt05d06732010-04-19 11:10:38 -0700757 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
758 }
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700759 if (VDBG) Slog.v(TAG, "using Authoritative time: " + cachedNtp);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700760 return cachedNtp;
761 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700762 }
Robert Greenwalt05d06732010-04-19 11:10:38 -0700763 long time = System.currentTimeMillis();
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700764 if (VDBG) Slog.v(TAG, "using User time: " + time);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700765 mNtpActive = false;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700766 return time;
767 }
768
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700769 // records bytecount data for a given time and accumulates it into larger time windows
770 // for logging and other purposes
771 //
772 // since time can be changed (user or network action) we will have to track the time of the
773 // last recording and deal with it.
774 private static class DataRecorder {
775 long[] mPeriodRxData;
776 long[] mPeriodTxData;
777 int mCurrentPeriod;
778 int mPeriodCount;
779
780 Calendar mPeriodStart;
781 Calendar mPeriodEnd;
782
783 ThrottleService mParent;
784 Context mContext;
Robert Greenwalte6e98822010-04-15 08:27:14 -0700785 String mImsi = null;
786
787 TelephonyManager mTelephonyManager;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700788
789 DataRecorder(Context context, ThrottleService parent) {
790 mContext = context;
791 mParent = parent;
792
Robert Greenwalte6e98822010-04-15 08:27:14 -0700793 mTelephonyManager = (TelephonyManager)mContext.getSystemService(
794 Context.TELEPHONY_SERVICE);
795
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700796 synchronized (mParent) {
797 mPeriodCount = 6;
798 mPeriodRxData = new long[mPeriodCount];
799 mPeriodTxData = new long[mPeriodCount];
800
801 mPeriodStart = Calendar.getInstance();
802 mPeriodEnd = Calendar.getInstance();
803
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700804 retrieve();
805 }
806 }
807
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700808 boolean setNextPeriod(Calendar start, Calendar end) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700809 // TODO - how would we deal with a dual-IMSI device?
810 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700811 boolean startNewPeriod = true;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700812
Robert Greenwalt27fba672010-04-26 12:29:14 -0700813 if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) {
814 // same endpoints - keep collecting
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700815 if (VDBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700816 Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," +
817 end.getTimeInMillis() +") - ammending data");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700818 }
Robert Greenwalt27fba672010-04-26 12:29:14 -0700819 startNewPeriod = false;
820 } else {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700821 if (VDBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700822 if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) {
823 Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," +
824 end.getTimeInMillis() + ") - old end was " +
825 mPeriodEnd.getTimeInMillis() + ", following");
826 } else {
827 Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," +
828 end.getTimeInMillis() + ") replacing old (" +
829 mPeriodStart.getTimeInMillis() + "," +
830 mPeriodEnd.getTimeInMillis() + ")");
831 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700832 }
833 synchronized (mParent) {
834 ++mCurrentPeriod;
835 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
836 mPeriodRxData[mCurrentPeriod] = 0;
837 mPeriodTxData[mCurrentPeriod] = 0;
838 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700839 }
840 setPeriodStart(start);
841 setPeriodEnd(end);
842 record();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700843 return startNewPeriod;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700844 }
845
846 public long getPeriodEnd() {
847 synchronized (mParent) {
848 return mPeriodEnd.getTimeInMillis();
849 }
850 }
851
852 private void setPeriodEnd(Calendar end) {
853 synchronized (mParent) {
854 mPeriodEnd = end;
855 }
856 }
857
858 public long getPeriodStart() {
859 synchronized (mParent) {
860 return mPeriodStart.getTimeInMillis();
861 }
862 }
863
864 private void setPeriodStart(Calendar start) {
865 synchronized (mParent) {
866 mPeriodStart = start;
867 }
868 }
869
870 public int getPeriodCount() {
871 synchronized (mParent) {
872 return mPeriodCount;
873 }
874 }
875
876 private void zeroData(int field) {
877 synchronized (mParent) {
878 for(int period = 0; period<mPeriodCount; period++) {
879 mPeriodRxData[period] = 0;
880 mPeriodTxData[period] = 0;
881 }
882 mCurrentPeriod = 0;
883 }
884
885 }
886
887 // if time moves backward accumulate all read/write that's lost into the now
888 // otherwise time moved forward.
889 void addData(long bytesRead, long bytesWritten) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700890 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700891
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700892 synchronized (mParent) {
893 mPeriodRxData[mCurrentPeriod] += bytesRead;
894 mPeriodTxData[mCurrentPeriod] += bytesWritten;
895 }
896 record();
897 }
898
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700899 private File getDataFile() {
900 File dataDir = Environment.getDataDirectory();
901 File throttleDir = new File(dataDir, "system/throttle");
902 throttleDir.mkdirs();
Robert Greenwalte6e98822010-04-15 08:27:14 -0700903 String mImsi = mTelephonyManager.getSubscriberId();
904 File dataFile;
905 if (mImsi == null) {
906 dataFile = useMRUFile(throttleDir);
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700907 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700908 } else {
909 String imsiHash = Integer.toString(mImsi.hashCode());
910 dataFile = new File(throttleDir, imsiHash);
911 }
912 // touch the file so it's not LRU
913 dataFile.setLastModified(System.currentTimeMillis());
914 checkAndDeleteLRUDataFile(throttleDir);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700915 return dataFile;
916 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700917
Robert Greenwalte6e98822010-04-15 08:27:14 -0700918 // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling
919 private void checkForSubscriberId() {
920 if (mImsi != null) return;
921
922 mImsi = mTelephonyManager.getSubscriberId();
923 if (mImsi == null) return;
924
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700925 if (VDBG) Slog.d(TAG, "finally have imsi - retreiving data");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700926 retrieve();
927 }
928
929 private final static int MAX_SIMS_SUPPORTED = 3;
930
931 private void checkAndDeleteLRUDataFile(File dir) {
932 File[] files = dir.listFiles();
933
934 if (files.length <= MAX_SIMS_SUPPORTED) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700935 if (DBG) Slog.d(TAG, "Too many data files");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700936 do {
937 File oldest = null;
938 for (File f : files) {
939 if ((oldest == null) || (oldest.lastModified() > f.lastModified())) {
940 oldest = f;
941 }
942 }
943 if (oldest == null) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700944 if (DBG) Slog.d(TAG, " deleting " + oldest);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700945 oldest.delete();
946 files = dir.listFiles();
947 } while (files.length > MAX_SIMS_SUPPORTED);
948 }
949
950 private File useMRUFile(File dir) {
951 File newest = null;
952 File[] files = dir.listFiles();
953
954 for (File f : files) {
955 if ((newest == null) || (newest.lastModified() < f.lastModified())) {
956 newest = f;
957 }
958 }
959 if (newest == null) {
960 newest = new File(dir, "temp");
961 }
962 return newest;
963 }
964
965
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700966 private static final int DATA_FILE_VERSION = 1;
967
968 private void record() {
969 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700970 // 1 int mPeriodCount
971 // 13*6 long[PERIOD_COUNT] mPeriodRxData
972 // 13*6 long[PERIOD_COUNT] mPeriodTxData
973 // 1 int mCurrentPeriod
974 // 13 long periodStartMS
975 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700976 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700977 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700978 builder.append(DATA_FILE_VERSION);
979 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700980 builder.append(mPeriodCount);
981 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700982 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700983 builder.append(mPeriodRxData[i]);
984 builder.append(":");
985 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700986 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700987 builder.append(mPeriodTxData[i]);
988 builder.append(":");
989 }
990 builder.append(mCurrentPeriod);
991 builder.append(":");
992 builder.append(mPeriodStart.getTimeInMillis());
993 builder.append(":");
994 builder.append(mPeriodEnd.getTimeInMillis());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700995
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700996 BufferedWriter out = null;
997 try {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700998 out = new BufferedWriter(new FileWriter(getDataFile()), 256);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700999 out.write(builder.toString());
1000 } catch (IOException e) {
1001 Slog.e(TAG, "Error writing data file");
1002 return;
1003 } finally {
1004 if (out != null) {
1005 try {
1006 out.close();
1007 } catch (Exception e) {}
1008 }
1009 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001010 }
1011
1012 private void retrieve() {
Robert Greenwalt05d06732010-04-19 11:10:38 -07001013 // clean out any old data first. If we fail to read we don't want old stuff
1014 zeroData(0);
1015
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001016 File f = getDataFile();
1017 byte[] buffer;
1018 FileInputStream s = null;
1019 try {
1020 buffer = new byte[(int)f.length()];
1021 s = new FileInputStream(f);
1022 s.read(buffer);
1023 } catch (IOException e) {
1024 Slog.e(TAG, "Error reading data file");
1025 return;
1026 } finally {
1027 if (s != null) {
1028 try {
1029 s.close();
1030 } catch (Exception e) {}
1031 }
1032 }
1033 String data = new String(buffer);
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001034 if (data == null || data.length() == 0) {
1035 if (DBG) Slog.d(TAG, "data file empty");
1036 return;
1037 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001038 String[] parsed = data.split(":");
1039 int parsedUsed = 0;
1040 if (parsed.length < 6) {
1041 Slog.e(TAG, "reading data file with insufficient length - ignoring");
1042 return;
1043 }
1044
Robert Greenwalt9e3983f2010-05-11 07:06:13 -07001045 int periodCount;
1046 long[] periodRxData;
1047 long[] periodTxData;
1048 int currentPeriod;
1049 Calendar periodStart;
1050 Calendar periodEnd;
1051 try {
1052 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
1053 Slog.e(TAG, "reading data file with bad version - ignoring");
1054 return;
1055 }
1056
1057 periodCount = Integer.parseInt(parsed[parsedUsed++]);
1058 if (parsed.length != 5 + (2 * periodCount)) {
1059 Slog.e(TAG, "reading data file with bad length (" + parsed.length +
1060 " != " + (5 + (2 * periodCount)) + ") - ignoring");
1061 return;
1062 }
1063 periodRxData = new long[periodCount];
1064 for (int i = 0; i < periodCount; i++) {
1065 periodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
1066 }
1067 periodTxData = new long[periodCount];
1068 for (int i = 0; i < periodCount; i++) {
1069 periodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
1070 }
1071
1072 currentPeriod = Integer.parseInt(parsed[parsedUsed++]);
1073
1074 periodStart = new GregorianCalendar();
1075 periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1076 periodEnd = new GregorianCalendar();
1077 periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1078 } catch (Exception e) {
1079 Slog.e(TAG, "Error parsing data file - ignoring");
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001080 return;
1081 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001082 synchronized (mParent) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001083 mPeriodCount = periodCount;
1084 mPeriodRxData = periodRxData;
1085 mPeriodTxData = periodTxData;
Robert Greenwalt9e3983f2010-05-11 07:06:13 -07001086 mCurrentPeriod = currentPeriod;
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001087 mPeriodStart = periodStart;
1088 mPeriodEnd = periodEnd;
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001089 }
1090 }
1091
1092 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001093 synchronized (mParent) {
1094 if (which > mPeriodCount) return 0;
1095 which = mCurrentPeriod - which;
1096 if (which < 0) which += mPeriodCount;
1097 return mPeriodRxData[which];
1098 }
1099 }
1100 long getPeriodTx(int which) {
1101 synchronized (mParent) {
1102 if (which > mPeriodCount) return 0;
1103 which = mCurrentPeriod - which;
1104 if (which < 0) which += mPeriodCount;
1105 return mPeriodTxData[which];
1106 }
1107 }
1108 }
1109
1110 @Override
1111 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1112 if (mContext.checkCallingOrSelfPermission(
1113 android.Manifest.permission.DUMP)
1114 != PackageManager.PERMISSION_GRANTED) {
1115 pw.println("Permission Denial: can't dump ThrottleService " +
1116 "from from pid=" + Binder.getCallingPid() + ", uid=" +
1117 Binder.getCallingUid());
1118 return;
1119 }
1120 pw.println();
1121
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001122 pw.println("The threshold is " + mPolicyThreshold.get() +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001123 ", after which you experince throttling to " +
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001124 mPolicyThrottleValue.get() + "kbps");
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001125 pw.println("Current period is " +
1126 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001127 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001128 " seconds.");
1129 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001130 pw.println("Current Throttle Index is " + mThrottleIndex.get());
Robert Greenwaltd1055a22010-05-25 15:54:52 -07001131 pw.println("Max NTP Cache Age is " + mMaxNtpCacheAgeSec);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -07001132
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001133 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
1134 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
1135 mRecorder.getPeriodTx(i));
1136 }
1137 }
1138}