blob: 1974f0e63bb32e0f308b77c0da7945587e2d2c07 [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) {
128 if (DBG) Slog.d(TAG, "Starting ThrottleService");
129 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
163 public void interfaceLinkStatusChanged(String iface, boolean link) {
164 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
179 public void interfaceRemoved(String iface) {}
180 }
181
182
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700183 private static class SettingsObserver extends ContentObserver {
184 private int mMsg;
185 private Handler mHandler;
186 SettingsObserver(Handler handler, int msg) {
187 super(handler);
188 mHandler = handler;
189 mMsg = msg;
190 }
191
192 void observe(Context context) {
193 ContentResolver resolver = context.getContentResolver();
194 resolver.registerContentObserver(Settings.Secure.getUriFor(
195 Settings.Secure.THROTTLE_POLLING_SEC), false, this);
196 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700197 Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700198 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700199 Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700200 resolver.registerContentObserver(Settings.Secure.getUriFor(
201 Settings.Secure.THROTTLE_RESET_DAY), false, this);
202 resolver.registerContentObserver(Settings.Secure.getUriFor(
203 Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this);
204 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700205 Settings.Secure.THROTTLE_HELP_URI), false, this);
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700206 resolver.registerContentObserver(Settings.Secure.getUriFor(
207 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700208 }
209
210 @Override
211 public void onChange(boolean selfChange) {
212 mHandler.obtainMessage(mMsg).sendToTarget();
213 }
214 }
215
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700216 private void enforceAccessPermission() {
217 mContext.enforceCallingOrSelfPermission(
218 android.Manifest.permission.ACCESS_NETWORK_STATE,
219 "ThrottleService");
220 }
221
Robert Greenwalt05d06732010-04-19 11:10:38 -0700222 private long ntpToWallTime(long ntpTime) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700223 long bestNow = getBestTime(true); // do it quickly
Robert Greenwalt05d06732010-04-19 11:10:38 -0700224 long localNow = System.currentTimeMillis();
225 return localNow + (ntpTime - bestNow);
226 }
227
Irfan Sheriffcf282362010-04-16 16:53:20 -0700228 // TODO - fetch for the iface
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700229 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt05d06732010-04-19 11:10:38 -0700230
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700231 public long getResetTime(String iface) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700232 enforceAccessPermission();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700233 long resetTime = 0;
Irfan Sheriffcf282362010-04-16 16:53:20 -0700234 if (mRecorder != null) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700235 resetTime = mRecorder.getPeriodEnd();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700236 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700237 resetTime = ntpToWallTime(resetTime);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700238 return resetTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700239 }
Irfan Sheriffcf282362010-04-16 16:53:20 -0700240
241 // TODO - fetch for the iface
Robert Greenwalt05d06732010-04-19 11:10:38 -0700242 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700243 public long getPeriodStartTime(String iface) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700244 long startTime = 0;
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700245 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700246 if (mRecorder != null) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700247 startTime = mRecorder.getPeriodStart();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700248 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700249 startTime = ntpToWallTime(startTime);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700250 return startTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700251 }
252 //TODO - a better name? getCliffByteCountThreshold?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700253 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700254 public long getCliffThreshold(String iface, int cliff) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700255 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700256 if (cliff == 1) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700257 return mPolicyThreshold.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700258 }
259 return 0;
260 }
261 // TODO - a better name? getThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700262 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700263 public int getCliffLevel(String iface, int cliff) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700264 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700265 if (cliff == 1) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700266 return mPolicyThrottleValue.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700267 }
268 return 0;
269 }
270
Irfan Sheriffc9b68512010-04-08 14:12:33 -0700271 public String getHelpUri() {
272 enforceAccessPermission();
273 return Settings.Secure.getString(mContext.getContentResolver(),
274 Settings.Secure.THROTTLE_HELP_URI);
275 }
276
Irfan Sheriffcf282362010-04-16 16:53:20 -0700277 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700278 public long getByteCount(String iface, int dir, int period, int ago) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700279 enforceAccessPermission();
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700280 if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700281 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
282 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
283 }
284 return 0;
285 }
286
287 // TODO - a better name - getCurrentThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700288 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700289 public int getThrottle(String iface) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700290 enforceAccessPermission();
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700291 if (mThrottleIndex.get() == 1) {
292 return mPolicyThrottleValue.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700293 }
294 return 0;
295 }
296
297 void systemReady() {
298 if (DBG) Slog.d(TAG, "systemReady");
299 mContext.registerReceiver(
300 new BroadcastReceiver() {
301 @Override
302 public void onReceive(Context context, Intent intent) {
303 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
304 }
305 }, new IntentFilter(ACTION_POLL));
306
307 mContext.registerReceiver(
308 new BroadcastReceiver() {
309 @Override
310 public void onReceive(Context context, Intent intent) {
311 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
312 }
313 }, new IntentFilter(ACTION_RESET));
314
315 // use a new thread as we don't want to stall the system for file writes
316 mThread = new HandlerThread(TAG);
317 mThread.start();
318 mHandler = new MyHandler(mThread.getLooper());
319 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700320
Robert Greenwaltfee46832010-05-06 12:25:13 -0700321 mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
322 try {
323 mNMService.registerObserver(mInterfaceObserver);
324 } catch (RemoteException e) {
325 Slog.e(TAG, "Could not register InterfaceObserver " + e);
326 }
327
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700328 mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
329 mSettingsObserver.observe(mContext);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700330
331 FileInputStream stream = null;
332 try {
333 Properties properties = new Properties();
334 File file = new File(PROPERTIES_FILE);
335 stream = new FileInputStream(file);
336 properties.load(stream);
337 mNtpServer = properties.getProperty("NTP_SERVER", null);
338 } catch (IOException e) {
339 Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
340 } finally {
341 if (stream != null) {
342 try {
343 stream.close();
344 } catch (Exception e) {}
345 }
346 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700347 }
348
349
350 private static final int EVENT_REBOOT_RECOVERY = 0;
351 private static final int EVENT_POLICY_CHANGED = 1;
352 private static final int EVENT_POLL_ALARM = 2;
353 private static final int EVENT_RESET_ALARM = 3;
Robert Greenwaltfee46832010-05-06 12:25:13 -0700354 private static final int EVENT_IFACE_UP = 4;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700355 private class MyHandler extends Handler {
356 public MyHandler(Looper l) {
357 super(l);
358 }
359
360 @Override
361 public void handleMessage(Message msg) {
362 switch (msg.what) {
363 case EVENT_REBOOT_RECOVERY:
364 onRebootRecovery();
365 break;
366 case EVENT_POLICY_CHANGED:
367 onPolicyChanged();
368 break;
369 case EVENT_POLL_ALARM:
370 onPollAlarm();
371 break;
372 case EVENT_RESET_ALARM:
373 onResetAlarm();
Robert Greenwaltfee46832010-05-06 12:25:13 -0700374 break;
375 case EVENT_IFACE_UP:
376 onIfaceUp();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700377 }
378 }
379
380 private void onRebootRecovery() {
381 if (DBG) Slog.d(TAG, "onRebootRecovery");
382 // check for sim change TODO
383 // reregister for notification of policy change
384
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700385 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700386
387 mRecorder = new DataRecorder(mContext, ThrottleService.this);
388
389 // get policy
390 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
Robert Greenwaltfb9896b2010-04-22 15:39:38 -0700391
392 // if we poll now we won't have network connectivity or even imsi access
393 // queue up a poll to happen in a little while - after ntp and imsi are avail
394 // TODO - make this callback based (ie, listen for notificaitons)
395 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM),
396 INITIAL_POLL_DELAY_SEC * 1000);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700397 }
398
399 // check for new policy info (threshold limit/value/etc)
400 private void onPolicyChanged() {
401 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
402
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700403 int pollingPeriod = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700404 R.integer.config_datause_polling_period_sec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700405 mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
406 Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
407
408 // TODO - remove testing stuff?
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700409 long defaultThreshold = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700410 R.integer.config_datause_threshold_bytes);
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700411 int defaultValue = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700412 R.integer.config_datause_throttle_kbitsps);
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700413 long threshold = Settings.Secure.getLong(mContext.getContentResolver(),
414 Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
415 int value = Settings.Secure.getInt(mContext.getContentResolver(),
416 Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
417
418 mPolicyThreshold.set(threshold);
419 mPolicyThrottleValue.set(value);
420 if (testing) {
421 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
422 mPolicyThreshold.set(TESTING_THRESHOLD);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700423 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700424
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700425 mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
426 Settings.Secure.THROTTLE_RESET_DAY, -1);
427 if (mPolicyResetDay == -1 ||
428 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
429 Random g = new Random();
430 mPolicyResetDay = 1 + g.nextInt(28); // 1-28
431 Settings.Secure.putInt(mContext.getContentResolver(),
432 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
433 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700434 if (mIface == null) {
435 mPolicyThreshold.set(0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700436 }
437
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700438 int defaultNotificationType = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700439 R.integer.config_datause_notification_type);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700440 mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700441 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700442
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700443 mMaxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
444 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC, MAX_NTP_CACHE_AGE_SEC);
445
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700446 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" + mPolicyPollPeriodSec +
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700447 ", threshold=" + mPolicyThreshold.get() + ", value=" +
448 mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay + ", noteType=" +
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700449 mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" + mMaxNtpCacheAgeSec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700450
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700451 // force updates
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700452 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700453
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700454 onResetAlarm();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700455
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700456 onPollAlarm();
457
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700458 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
459 mContext.sendBroadcast(broadcast);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700460 }
461
462 private void onPollAlarm() {
463 long now = SystemClock.elapsedRealtime();
464 long next = now + mPolicyPollPeriodSec*1000;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700465
466 checkForAuthoritativeTime();
467
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700468 long incRead = 0;
469 long incWrite = 0;
470 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700471 incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
472 incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700473 // handle iface resets - on some device the 3g iface comes and goes and gets
474 // totals reset to 0. Deal with it
475 if ((incRead < 0) || (incWrite < 0)) {
476 incRead += mLastRead;
477 incWrite += mLastWrite;
478 mLastRead = 0;
479 mLastWrite = 0;
480 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700481 } catch (RemoteException e) {
482 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
483 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700484 // don't count this data if we're roaming.
485 boolean roaming = "true".equals(
486 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
487 if (!roaming) {
488 mRecorder.addData(incRead, incWrite);
489 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700490
491 long periodRx = mRecorder.getPeriodRx(0);
492 long periodTx = mRecorder.getPeriodTx(0);
493 long total = periodRx + periodTx;
494 if (DBG) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700495 Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
Robert Greenwalt5f996892010-04-08 16:19:24 -0700496 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700497 }
498 mLastRead += incRead;
499 mLastWrite += incWrite;
500
501 checkThrottleAndPostNotification(total);
502
503 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
504 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
505 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700506 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
507 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700508 mContext.sendStickyBroadcast(broadcast);
509
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700510 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700511 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, next, mPendingPollIntent);
512 }
513
Robert Greenwaltfee46832010-05-06 12:25:13 -0700514 private void onIfaceUp() {
515 // if we were throttled before, be sure and set it again - the iface went down
516 // (and may have disappeared all together) and these settings were lost
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700517 if (mThrottleIndex.get() == 1) {
Robert Greenwaltfee46832010-05-06 12:25:13 -0700518 try {
519 mNMService.setInterfaceThrottle(mIface, -1, -1);
520 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700521 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
Robert Greenwaltfee46832010-05-06 12:25:13 -0700522 } catch (Exception e) {
523 Slog.e(TAG, "error setting Throttle: " + e);
524 }
525 }
526 }
527
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700528 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700529 // is throttling enabled?
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700530 long threshold = mPolicyThreshold.get();
531 if (threshold == 0) {
Robert Greenwaltcce83372010-04-23 17:35:29 -0700532 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700533 return;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700534 }
535
536 // have we spoken with an ntp server yet?
537 // this is controversial, but we'd rather err towards not throttling
538 if ((mNtpServer != null) && !mNtpActive) {
539 return;
540 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700541
542 // check if we need to throttle
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700543 if (currentTotal > threshold) {
544 if (mThrottleIndex.get() != 1) {
545 mThrottleIndex.set(1);
546 if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700547 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700548 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700549 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700550 } catch (Exception e) {
551 Slog.e(TAG, "error setting Throttle: " + e);
552 }
553
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700554 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700555
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700556 postNotification(R.string.throttled_notification_title,
557 R.string.throttled_notification_message,
558 R.drawable.stat_sys_throttled,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700559 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700560
561 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700562 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
563 mPolicyThrottleValue.get());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700564 mContext.sendStickyBroadcast(broadcast);
565
566 } // else already up!
567 } else {
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700568 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700569 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
570 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700571 // pretend we only have 1/2 the time remaining that we actually do
572 // if our burn rate in the period so far would have us exceed the limit
573 // in that 1/2 window, warn the user.
574 // this gets more generous in the early to middle period and converges back
575 // to the limit as we move toward the period end.
576
577 // adding another factor - it must be greater than the total cap/4
578 // else we may get false alarms very early in the period.. in the first
579 // tenth of a percent of the period if we used more than a tenth of a percent
580 // of the cap we'd get a warning and that's not desired.
581 long start = mRecorder.getPeriodStart();
582 long end = mRecorder.getPeriodEnd();
583 long periodLength = end - start;
584 long now = System.currentTimeMillis();
585 long timeUsed = now - start;
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700586 long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength);
587 if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700588 if (mWarningNotificationSent == false) {
589 mWarningNotificationSent = true;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700590 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
591 postNotification(R.string.throttle_warning_notification_title,
592 R.string.throttle_warning_notification_message,
593 R.drawable.stat_sys_throttled,
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700594 0);
595 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700596 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700597 if (mWarningNotificationSent == true) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700598 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700599 mWarningNotificationSent =false;
600 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700601 }
602 }
603 }
604 }
605
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700606 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700607 Intent intent = new Intent();
608 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700609 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700610 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
611
612 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
613
614 Resources r = Resources.getSystem();
615 CharSequence title = r.getText(titleInt);
616 CharSequence message = r.getText(messageInt);
617 if (mThrottlingNotification == null) {
618 mThrottlingNotification = new Notification();
619 mThrottlingNotification.when = 0;
620 // TODO - fixup icon
621 mThrottlingNotification.icon = icon;
622 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700623 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700624 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700625 mThrottlingNotification.tickerText = title;
626 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
627
628 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
629 }
630
631
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700632 private void clearThrottleAndNotification() {
633 if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) {
634 mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700635 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700636 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700637 } catch (Exception e) {
638 Slog.e(TAG, "error clearing Throttle: " + e);
639 }
640 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
641 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
642 mContext.sendStickyBroadcast(broadcast);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700643 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
644 mWarningNotificationSent = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700645 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700646 }
647
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700648 private Calendar calculatePeriodEnd(long now) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700649 Calendar end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700650 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700651 int day = end.get(Calendar.DAY_OF_MONTH);
652 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
653 end.set(Calendar.HOUR_OF_DAY, 0);
654 end.set(Calendar.MINUTE, 0);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700655 end.set(Calendar.SECOND, 0);
656 end.set(Calendar.MILLISECOND, 0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700657 if (day >= mPolicyResetDay) {
658 int month = end.get(Calendar.MONTH);
659 if (month == Calendar.DECEMBER) {
660 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
661 month = Calendar.JANUARY - 1;
662 }
663 end.set(Calendar.MONTH, month + 1);
664 }
665
666 // TODO - remove!
667 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
668 end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700669 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700670 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
671 }
672 return end;
673 }
674 private Calendar calculatePeriodStart(Calendar end) {
675 Calendar start = (Calendar)end.clone();
676 int month = end.get(Calendar.MONTH);
677 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
678 month = Calendar.DECEMBER + 1;
679 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
680 }
681 start.set(Calendar.MONTH, month - 1);
682
683 // TODO - remove!!
684 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
685 start = (Calendar)end.clone();
686 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
687 }
688 return start;
689 }
690
691 private void onResetAlarm() {
692 if (DBG) {
693 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
694 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
695 }
696
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700697 long now = getBestTime(false);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700698
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700699 if (mNtpActive || (mNtpServer == null)) {
700 Calendar end = calculatePeriodEnd(now);
701 Calendar start = calculatePeriodStart(end);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700702
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700703 if (mRecorder.setNextPeriod(start, end)) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700704 onPollAlarm();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700705 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700706
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700707 mAlarmManager.cancel(mPendingResetIntent);
708 long offset = end.getTimeInMillis() - now;
709 // use Elapsed realtime so clock changes don't fool us.
710 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
711 SystemClock.elapsedRealtime() + offset,
712 mPendingResetIntent);
713 } else {
714 if (DBG) Slog.d(TAG, "no authoritative time - not resetting period");
715 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700716 }
717 }
718
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700719 private void checkForAuthoritativeTime() {
720 if (mNtpActive || (mNtpServer == null)) return;
721
Robert Greenwalt05d06732010-04-19 11:10:38 -0700722 // will try to get the ntp time and switch to it if found.
723 // will also cache the time so we don't fetch it repeatedly.
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700724 getBestTime(false);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700725 }
726
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700727 private static final int MAX_NTP_CACHE_AGE_SEC = 60 * 60 * 24; // 1 day
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700728 private static final int MAX_NTP_FETCH_WAIT = 20 * 1000;
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700729 private int mMaxNtpCacheAgeSec = MAX_NTP_CACHE_AGE_SEC;
Robert Greenwalt05d06732010-04-19 11:10:38 -0700730 private long cachedNtp;
731 private long cachedNtpTimestamp;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700732
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700733 // if the request is tied to UI and ANR's are a danger, request a fast result
734 // the regular polling should have updated the cached time recently using the
735 // slower method (!fast)
736 private long getBestTime(boolean fast) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700737 if (mNtpServer != null) {
738 if (mNtpActive) {
739 long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp;
Robert Greenwaltd1c3ea62010-05-26 10:37:48 -0700740 if (ntpAge < mMaxNtpCacheAgeSec * 1000 || fast) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700741 if (VDBG) Slog.v(TAG, "using cached time");
Robert Greenwalt05d06732010-04-19 11:10:38 -0700742 return cachedNtp + ntpAge;
743 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700744 }
Robert Greenwalt05d06732010-04-19 11:10:38 -0700745 SntpClient client = new SntpClient();
746 if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT)) {
747 cachedNtp = client.getNtpTime();
748 cachedNtpTimestamp = SystemClock.elapsedRealtime();
749 if (!mNtpActive) {
750 mNtpActive = true;
751 if (DBG) Slog.d(TAG, "found Authoritative time - reseting alarm");
752 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
753 }
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700754 if (VDBG) Slog.v(TAG, "using Authoritative time: " + cachedNtp);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700755 return cachedNtp;
756 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700757 }
Robert Greenwalt05d06732010-04-19 11:10:38 -0700758 long time = System.currentTimeMillis();
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700759 if (VDBG) Slog.v(TAG, "using User time: " + time);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700760 mNtpActive = false;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700761 return time;
762 }
763
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700764 // records bytecount data for a given time and accumulates it into larger time windows
765 // for logging and other purposes
766 //
767 // since time can be changed (user or network action) we will have to track the time of the
768 // last recording and deal with it.
769 private static class DataRecorder {
770 long[] mPeriodRxData;
771 long[] mPeriodTxData;
772 int mCurrentPeriod;
773 int mPeriodCount;
774
775 Calendar mPeriodStart;
776 Calendar mPeriodEnd;
777
778 ThrottleService mParent;
779 Context mContext;
Robert Greenwalte6e98822010-04-15 08:27:14 -0700780 String mImsi = null;
781
782 TelephonyManager mTelephonyManager;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700783
784 DataRecorder(Context context, ThrottleService parent) {
785 mContext = context;
786 mParent = parent;
787
Robert Greenwalte6e98822010-04-15 08:27:14 -0700788 mTelephonyManager = (TelephonyManager)mContext.getSystemService(
789 Context.TELEPHONY_SERVICE);
790
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700791 synchronized (mParent) {
792 mPeriodCount = 6;
793 mPeriodRxData = new long[mPeriodCount];
794 mPeriodTxData = new long[mPeriodCount];
795
796 mPeriodStart = Calendar.getInstance();
797 mPeriodEnd = Calendar.getInstance();
798
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700799 retrieve();
800 }
801 }
802
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700803 boolean setNextPeriod(Calendar start, Calendar end) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700804 // TODO - how would we deal with a dual-IMSI device?
805 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700806 boolean startNewPeriod = true;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700807
Robert Greenwalt27fba672010-04-26 12:29:14 -0700808 if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) {
809 // same endpoints - keep collecting
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700810 if (DBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700811 Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," +
812 end.getTimeInMillis() +") - ammending data");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700813 }
Robert Greenwalt27fba672010-04-26 12:29:14 -0700814 startNewPeriod = false;
815 } else {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700816 if (DBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700817 if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) {
818 Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," +
819 end.getTimeInMillis() + ") - old end was " +
820 mPeriodEnd.getTimeInMillis() + ", following");
821 } else {
822 Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," +
823 end.getTimeInMillis() + ") replacing old (" +
824 mPeriodStart.getTimeInMillis() + "," +
825 mPeriodEnd.getTimeInMillis() + ")");
826 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700827 }
828 synchronized (mParent) {
829 ++mCurrentPeriod;
830 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
831 mPeriodRxData[mCurrentPeriod] = 0;
832 mPeriodTxData[mCurrentPeriod] = 0;
833 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700834 }
835 setPeriodStart(start);
836 setPeriodEnd(end);
837 record();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700838 return startNewPeriod;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700839 }
840
841 public long getPeriodEnd() {
842 synchronized (mParent) {
843 return mPeriodEnd.getTimeInMillis();
844 }
845 }
846
847 private void setPeriodEnd(Calendar end) {
848 synchronized (mParent) {
849 mPeriodEnd = end;
850 }
851 }
852
853 public long getPeriodStart() {
854 synchronized (mParent) {
855 return mPeriodStart.getTimeInMillis();
856 }
857 }
858
859 private void setPeriodStart(Calendar start) {
860 synchronized (mParent) {
861 mPeriodStart = start;
862 }
863 }
864
865 public int getPeriodCount() {
866 synchronized (mParent) {
867 return mPeriodCount;
868 }
869 }
870
871 private void zeroData(int field) {
872 synchronized (mParent) {
873 for(int period = 0; period<mPeriodCount; period++) {
874 mPeriodRxData[period] = 0;
875 mPeriodTxData[period] = 0;
876 }
877 mCurrentPeriod = 0;
878 }
879
880 }
881
882 // if time moves backward accumulate all read/write that's lost into the now
883 // otherwise time moved forward.
884 void addData(long bytesRead, long bytesWritten) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700885 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700886
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700887 synchronized (mParent) {
888 mPeriodRxData[mCurrentPeriod] += bytesRead;
889 mPeriodTxData[mCurrentPeriod] += bytesWritten;
890 }
891 record();
892 }
893
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700894 private File getDataFile() {
895 File dataDir = Environment.getDataDirectory();
896 File throttleDir = new File(dataDir, "system/throttle");
897 throttleDir.mkdirs();
Robert Greenwalte6e98822010-04-15 08:27:14 -0700898 String mImsi = mTelephonyManager.getSubscriberId();
899 File dataFile;
900 if (mImsi == null) {
901 dataFile = useMRUFile(throttleDir);
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700902 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700903 } else {
904 String imsiHash = Integer.toString(mImsi.hashCode());
905 dataFile = new File(throttleDir, imsiHash);
906 }
907 // touch the file so it's not LRU
908 dataFile.setLastModified(System.currentTimeMillis());
909 checkAndDeleteLRUDataFile(throttleDir);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700910 return dataFile;
911 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700912
Robert Greenwalte6e98822010-04-15 08:27:14 -0700913 // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling
914 private void checkForSubscriberId() {
915 if (mImsi != null) return;
916
917 mImsi = mTelephonyManager.getSubscriberId();
918 if (mImsi == null) return;
919
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700920 if (DBG) Slog.d(TAG, "finally have imsi - retreiving data");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700921 retrieve();
922 }
923
924 private final static int MAX_SIMS_SUPPORTED = 3;
925
926 private void checkAndDeleteLRUDataFile(File dir) {
927 File[] files = dir.listFiles();
928
929 if (files.length <= MAX_SIMS_SUPPORTED) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700930 if (DBG) Slog.d(TAG, "Too many data files");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700931 do {
932 File oldest = null;
933 for (File f : files) {
934 if ((oldest == null) || (oldest.lastModified() > f.lastModified())) {
935 oldest = f;
936 }
937 }
938 if (oldest == null) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700939 if (DBG) Slog.d(TAG, " deleting " + oldest);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700940 oldest.delete();
941 files = dir.listFiles();
942 } while (files.length > MAX_SIMS_SUPPORTED);
943 }
944
945 private File useMRUFile(File dir) {
946 File newest = null;
947 File[] files = dir.listFiles();
948
949 for (File f : files) {
950 if ((newest == null) || (newest.lastModified() < f.lastModified())) {
951 newest = f;
952 }
953 }
954 if (newest == null) {
955 newest = new File(dir, "temp");
956 }
957 return newest;
958 }
959
960
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700961 private static final int DATA_FILE_VERSION = 1;
962
963 private void record() {
964 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700965 // 1 int mPeriodCount
966 // 13*6 long[PERIOD_COUNT] mPeriodRxData
967 // 13*6 long[PERIOD_COUNT] mPeriodTxData
968 // 1 int mCurrentPeriod
969 // 13 long periodStartMS
970 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700971 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700972 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700973 builder.append(DATA_FILE_VERSION);
974 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700975 builder.append(mPeriodCount);
976 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700977 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700978 builder.append(mPeriodRxData[i]);
979 builder.append(":");
980 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700981 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700982 builder.append(mPeriodTxData[i]);
983 builder.append(":");
984 }
985 builder.append(mCurrentPeriod);
986 builder.append(":");
987 builder.append(mPeriodStart.getTimeInMillis());
988 builder.append(":");
989 builder.append(mPeriodEnd.getTimeInMillis());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700990
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700991 BufferedWriter out = null;
992 try {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700993 out = new BufferedWriter(new FileWriter(getDataFile()), 256);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700994 out.write(builder.toString());
995 } catch (IOException e) {
996 Slog.e(TAG, "Error writing data file");
997 return;
998 } finally {
999 if (out != null) {
1000 try {
1001 out.close();
1002 } catch (Exception e) {}
1003 }
1004 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001005 }
1006
1007 private void retrieve() {
Robert Greenwalt05d06732010-04-19 11:10:38 -07001008 // clean out any old data first. If we fail to read we don't want old stuff
1009 zeroData(0);
1010
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001011 File f = getDataFile();
1012 byte[] buffer;
1013 FileInputStream s = null;
1014 try {
1015 buffer = new byte[(int)f.length()];
1016 s = new FileInputStream(f);
1017 s.read(buffer);
1018 } catch (IOException e) {
1019 Slog.e(TAG, "Error reading data file");
1020 return;
1021 } finally {
1022 if (s != null) {
1023 try {
1024 s.close();
1025 } catch (Exception e) {}
1026 }
1027 }
1028 String data = new String(buffer);
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001029 if (data == null || data.length() == 0) {
1030 if (DBG) Slog.d(TAG, "data file empty");
1031 return;
1032 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001033 String[] parsed = data.split(":");
1034 int parsedUsed = 0;
1035 if (parsed.length < 6) {
1036 Slog.e(TAG, "reading data file with insufficient length - ignoring");
1037 return;
1038 }
1039
Robert Greenwalt9e3983f2010-05-11 07:06:13 -07001040 int periodCount;
1041 long[] periodRxData;
1042 long[] periodTxData;
1043 int currentPeriod;
1044 Calendar periodStart;
1045 Calendar periodEnd;
1046 try {
1047 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
1048 Slog.e(TAG, "reading data file with bad version - ignoring");
1049 return;
1050 }
1051
1052 periodCount = Integer.parseInt(parsed[parsedUsed++]);
1053 if (parsed.length != 5 + (2 * periodCount)) {
1054 Slog.e(TAG, "reading data file with bad length (" + parsed.length +
1055 " != " + (5 + (2 * periodCount)) + ") - ignoring");
1056 return;
1057 }
1058 periodRxData = new long[periodCount];
1059 for (int i = 0; i < periodCount; i++) {
1060 periodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
1061 }
1062 periodTxData = new long[periodCount];
1063 for (int i = 0; i < periodCount; i++) {
1064 periodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
1065 }
1066
1067 currentPeriod = Integer.parseInt(parsed[parsedUsed++]);
1068
1069 periodStart = new GregorianCalendar();
1070 periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1071 periodEnd = new GregorianCalendar();
1072 periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1073 } catch (Exception e) {
1074 Slog.e(TAG, "Error parsing data file - ignoring");
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001075 return;
1076 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001077 synchronized (mParent) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001078 mPeriodCount = periodCount;
1079 mPeriodRxData = periodRxData;
1080 mPeriodTxData = periodTxData;
Robert Greenwalt9e3983f2010-05-11 07:06:13 -07001081 mCurrentPeriod = currentPeriod;
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001082 mPeriodStart = periodStart;
1083 mPeriodEnd = periodEnd;
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001084 }
1085 }
1086
1087 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001088 synchronized (mParent) {
1089 if (which > mPeriodCount) return 0;
1090 which = mCurrentPeriod - which;
1091 if (which < 0) which += mPeriodCount;
1092 return mPeriodRxData[which];
1093 }
1094 }
1095 long getPeriodTx(int which) {
1096 synchronized (mParent) {
1097 if (which > mPeriodCount) return 0;
1098 which = mCurrentPeriod - which;
1099 if (which < 0) which += mPeriodCount;
1100 return mPeriodTxData[which];
1101 }
1102 }
1103 }
1104
1105 @Override
1106 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1107 if (mContext.checkCallingOrSelfPermission(
1108 android.Manifest.permission.DUMP)
1109 != PackageManager.PERMISSION_GRANTED) {
1110 pw.println("Permission Denial: can't dump ThrottleService " +
1111 "from from pid=" + Binder.getCallingPid() + ", uid=" +
1112 Binder.getCallingUid());
1113 return;
1114 }
1115 pw.println();
1116
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001117 pw.println("The threshold is " + mPolicyThreshold.get() +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001118 ", after which you experince throttling to " +
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001119 mPolicyThrottleValue.get() + "kbps");
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001120 pw.println("Current period is " +
1121 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001122 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001123 " seconds.");
1124 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001125 pw.println("Current Throttle Index is " + mThrottleIndex.get());
Robert Greenwaltd1055a22010-05-25 15:54:52 -07001126 pw.println("Max NTP Cache Age is " + mMaxNtpCacheAgeSec);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -07001127
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001128 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
1129 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
1130 mRecorder.getPeriodTx(i));
1131 }
1132 }
1133}