blob: d81dfdbc1b8dbe1d50923d12fddc51d4c41b0aa5 [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
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070019import com.android.internal.R;
20import com.android.internal.telephony.TelephonyProperties;
21
Robert Greenwalt9e696c22010-04-01 14:45:18 -070022import android.app.AlarmManager;
23import android.app.Notification;
24import android.app.NotificationManager;
25import android.app.PendingIntent;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070026import android.content.BroadcastReceiver;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -070027import android.content.ContentResolver;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070028import android.content.Context;
29import android.content.Intent;
30import android.content.IntentFilter;
31import android.content.pm.PackageManager;
32import android.content.res.Resources;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -070033import android.database.ContentObserver;
Robert Greenwaltfee46832010-05-06 12:25:13 -070034import android.net.INetworkManagementEventObserver;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070035import android.net.IThrottleManager;
Jeff Sharkey9a13f362011-04-26 16:25:36 -070036import android.net.NetworkStats;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070037import android.net.ThrottleManager;
38import android.os.Binder;
Robert Greenwaltb8912f52010-04-09 17:27:26 -070039import android.os.Environment;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070040import android.os.Handler;
41import android.os.HandlerThread;
42import android.os.IBinder;
43import android.os.INetworkManagementService;
44import android.os.Looper;
45import android.os.Message;
46import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.SystemClock;
49import android.os.SystemProperties;
50import android.provider.Settings;
Robert Greenwalte6e98822010-04-15 08:27:14 -070051import android.telephony.TelephonyManager;
Robert Greenwaltfee46832010-05-06 12:25:13 -070052import android.text.TextUtils;
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070053import android.util.NtpTrustedTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070054import android.util.Slog;
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070055import android.util.TrustedTime;
Robert Greenwalt5f996892010-04-08 16:19:24 -070056
Robert Greenwaltb8912f52010-04-09 17:27:26 -070057import java.io.BufferedWriter;
58import java.io.File;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070059import java.io.FileDescriptor;
Robert Greenwaltb8912f52010-04-09 17:27:26 -070060import java.io.FileInputStream;
61import java.io.FileWriter;
62import java.io.IOException;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070063import java.io.PrintWriter;
64import java.util.Calendar;
65import 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;
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070068import java.util.concurrent.atomic.AtomicInteger;
69import java.util.concurrent.atomic.AtomicLong;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070070
71// TODO - add comments - reference the ThrottleManager for public API
72public class ThrottleService extends IThrottleManager.Stub {
73
74 private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing";
75
76 private static final String TAG = "ThrottleService";
Robert Greenwaltbf7de392010-04-21 17:09:38 -070077 private static final boolean DBG = true;
78 private static final boolean VDBG = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070079 private Handler mHandler;
80 private HandlerThread mThread;
81
82 private Context mContext;
83
Robert Greenwaltfb9896b2010-04-22 15:39:38 -070084 private static final int INITIAL_POLL_DELAY_SEC = 90;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070085 private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
Robert Greenwalt7171ea82010-04-14 22:37:12 -070086 private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070087 private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070088
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070089 private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000;
90 private static final long MAX_NTP_FETCH_WAIT = 20 * 1000;
91
92 private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE;
93
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070094 private int mPolicyPollPeriodSec;
Robert Greenwalt39e163f2010-05-07 16:52:17 -070095 private AtomicLong mPolicyThreshold;
96 private AtomicInteger mPolicyThrottleValue;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070097 private int mPolicyResetDay; // 1-28
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070098 private int mPolicyNotificationsAllowedMask;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070099
100 private long mLastRead; // read byte count from last poll
101 private long mLastWrite; // write byte count from last poll
102
103 private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL";
104 private static int POLL_REQUEST = 0;
105 private PendingIntent mPendingPollIntent;
106 private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET";
107 private static int RESET_REQUEST = 1;
108 private PendingIntent mPendingResetIntent;
109
110 private INetworkManagementService mNMService;
111 private AlarmManager mAlarmManager;
112 private NotificationManager mNotificationManager;
113
114 private DataRecorder mRecorder;
115
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700116 private String mIface;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700117
118 private static final int NOTIFICATION_WARNING = 2;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700119
120 private Notification mThrottlingNotification;
121 private boolean mWarningNotificationSent = false;
122
Robert Greenwaltfee46832010-05-06 12:25:13 -0700123 private InterfaceObserver mInterfaceObserver;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700124 private SettingsObserver mSettingsObserver;
125
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700126 private AtomicInteger mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700127 private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
128 private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
129
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700130 private static final String PROPERTIES_FILE = "/etc/gps.conf";
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700131
132 private Intent mPollStickyBroadcast;
133
134 private TrustedTime mTime;
135
136 private static INetworkManagementService getNetworkManagementService() {
137 final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
138 return INetworkManagementService.Stub.asInterface(b);
139 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700140
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700141 public ThrottleService(Context context) {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700142 // TODO: move to using cached NtpTrustedTime
143 this(context, getNetworkManagementService(), new NtpTrustedTime(),
144 context.getResources().getString(R.string.config_datause_iface));
145 }
146
147 public ThrottleService(Context context, INetworkManagementService nmService, TrustedTime time,
148 String iface) {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700149 if (VDBG) Slog.v(TAG, "Starting ThrottleService");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700150 mContext = context;
151
Robert Greenwalt24488bd2010-05-10 16:56:43 -0700152 mPolicyThreshold = new AtomicLong();
153 mPolicyThrottleValue = new AtomicInteger();
154 mThrottleIndex = new AtomicInteger();
155
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700156 mIface = iface;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700157 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
158 Intent pollIntent = new Intent(ACTION_POLL, null);
159 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
160 Intent resetIntent = new Intent(ACTION_RESET, null);
161 mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
162
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700163 mNMService = nmService;
164 mTime = time;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700165
166 mNotificationManager = (NotificationManager)mContext.getSystemService(
167 Context.NOTIFICATION_SERVICE);
168 }
169
Robert Greenwaltfee46832010-05-06 12:25:13 -0700170 private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
171 private int mMsg;
172 private Handler mHandler;
173 private String mIface;
174
175 InterfaceObserver(Handler handler, int msg, String iface) {
176 super();
177 mHandler = handler;
178 mMsg = msg;
179 mIface = iface;
180 }
181
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700182 public void interfaceStatusChanged(String iface, boolean up) {
183 if (up) {
Robert Greenwaltfee46832010-05-06 12:25:13 -0700184 if (TextUtils.equals(iface, mIface)) {
185 mHandler.obtainMessage(mMsg).sendToTarget();
186 }
187 }
188 }
189
Mike J. Chenf59c7d02011-06-23 15:33:15 -0700190 public void interfaceLinkStateChanged(String iface, boolean up) {
191 }
192
Robert Greenwaltfee46832010-05-06 12:25:13 -0700193 public void interfaceAdded(String iface) {
194 // TODO - an interface added in the UP state should also trigger a StatusChanged
195 // notification..
196 if (TextUtils.equals(iface, mIface)) {
197 mHandler.obtainMessage(mMsg).sendToTarget();
198 }
199 }
200
201 public void interfaceRemoved(String iface) {}
202 }
203
204
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700205 private static class SettingsObserver extends ContentObserver {
206 private int mMsg;
207 private Handler mHandler;
208 SettingsObserver(Handler handler, int msg) {
209 super(handler);
210 mHandler = handler;
211 mMsg = msg;
212 }
213
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700214 void register(Context context) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700215 ContentResolver resolver = context.getContentResolver();
216 resolver.registerContentObserver(Settings.Secure.getUriFor(
217 Settings.Secure.THROTTLE_POLLING_SEC), false, this);
218 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700219 Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700220 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700221 Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700222 resolver.registerContentObserver(Settings.Secure.getUriFor(
223 Settings.Secure.THROTTLE_RESET_DAY), false, this);
224 resolver.registerContentObserver(Settings.Secure.getUriFor(
225 Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this);
226 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700227 Settings.Secure.THROTTLE_HELP_URI), false, this);
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700228 resolver.registerContentObserver(Settings.Secure.getUriFor(
229 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700230 }
231
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700232 void unregister(Context context) {
233 final ContentResolver resolver = context.getContentResolver();
234 resolver.unregisterContentObserver(this);
235 }
236
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700237 @Override
238 public void onChange(boolean selfChange) {
239 mHandler.obtainMessage(mMsg).sendToTarget();
240 }
241 }
242
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700243 private void enforceAccessPermission() {
244 mContext.enforceCallingOrSelfPermission(
245 android.Manifest.permission.ACCESS_NETWORK_STATE,
246 "ThrottleService");
247 }
248
Robert Greenwalt05d06732010-04-19 11:10:38 -0700249 private long ntpToWallTime(long ntpTime) {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700250 // get time quickly without worrying about trusted state
251 long bestNow = mTime.hasCache() ? mTime.currentTimeMillis()
252 : System.currentTimeMillis();
Robert Greenwalt05d06732010-04-19 11:10:38 -0700253 long localNow = System.currentTimeMillis();
254 return localNow + (ntpTime - bestNow);
255 }
256
Irfan Sheriffcf282362010-04-16 16:53:20 -0700257 // TODO - fetch for the iface
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700258 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt05d06732010-04-19 11:10:38 -0700259
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700260 public long getResetTime(String iface) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700261 enforceAccessPermission();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700262 long resetTime = 0;
Irfan Sheriffcf282362010-04-16 16:53:20 -0700263 if (mRecorder != null) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700264 resetTime = mRecorder.getPeriodEnd();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700265 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700266 resetTime = ntpToWallTime(resetTime);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700267 return resetTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700268 }
Irfan Sheriffcf282362010-04-16 16:53:20 -0700269
270 // TODO - fetch for the iface
Robert Greenwalt05d06732010-04-19 11:10:38 -0700271 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700272 public long getPeriodStartTime(String iface) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700273 long startTime = 0;
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700274 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700275 if (mRecorder != null) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700276 startTime = mRecorder.getPeriodStart();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700277 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700278 startTime = ntpToWallTime(startTime);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700279 return startTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700280 }
281 //TODO - a better name? getCliffByteCountThreshold?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700282 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700283 public long getCliffThreshold(String iface, int cliff) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700284 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700285 if (cliff == 1) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700286 return mPolicyThreshold.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700287 }
288 return 0;
289 }
290 // TODO - a better name? getThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700291 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700292 public int getCliffLevel(String iface, int cliff) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700293 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700294 if (cliff == 1) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700295 return mPolicyThrottleValue.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700296 }
297 return 0;
298 }
299
Irfan Sheriffc9b68512010-04-08 14:12:33 -0700300 public String getHelpUri() {
301 enforceAccessPermission();
302 return Settings.Secure.getString(mContext.getContentResolver(),
303 Settings.Secure.THROTTLE_HELP_URI);
304 }
305
Irfan Sheriffcf282362010-04-16 16:53:20 -0700306 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700307 public long getByteCount(String iface, int dir, int period, int ago) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700308 enforceAccessPermission();
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700309 if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700310 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
311 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
312 }
313 return 0;
314 }
315
316 // TODO - a better name - getCurrentThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700317 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700318 public int getThrottle(String iface) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700319 enforceAccessPermission();
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700320 if (mThrottleIndex.get() == 1) {
321 return mPolicyThrottleValue.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700322 }
323 return 0;
324 }
325
326 void systemReady() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700327 if (VDBG) Slog.v(TAG, "systemReady");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700328 mContext.registerReceiver(
329 new BroadcastReceiver() {
330 @Override
331 public void onReceive(Context context, Intent intent) {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700332 dispatchPoll();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700333 }
334 }, new IntentFilter(ACTION_POLL));
335
336 mContext.registerReceiver(
337 new BroadcastReceiver() {
338 @Override
339 public void onReceive(Context context, Intent intent) {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700340 dispatchReset();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700341 }
342 }, new IntentFilter(ACTION_RESET));
343
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700344 FileInputStream stream = null;
345 try {
346 Properties properties = new Properties();
347 File file = new File(PROPERTIES_FILE);
348 stream = new FileInputStream(file);
349 properties.load(stream);
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700350 final String ntpServer = properties.getProperty("NTP_SERVER", null);
351 if (mTime instanceof NtpTrustedTime) {
352 ((NtpTrustedTime) mTime).setNtpServer(ntpServer, MAX_NTP_FETCH_WAIT);
353 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700354 } catch (IOException e) {
355 Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
356 } finally {
357 if (stream != null) {
358 try {
359 stream.close();
360 } catch (Exception e) {}
361 }
362 }
Robert Greenwaltc76c15e2010-06-16 14:42:16 -0700363
364 // use a new thread as we don't want to stall the system for file writes
365 mThread = new HandlerThread(TAG);
366 mThread.start();
367 mHandler = new MyHandler(mThread.getLooper());
368 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
369
370 mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
371 try {
372 mNMService.registerObserver(mInterfaceObserver);
373 } catch (RemoteException e) {
374 Slog.e(TAG, "Could not register InterfaceObserver " + e);
375 }
376
377 mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700378 mSettingsObserver.register(mContext);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700379 }
380
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700381 void shutdown() {
382 // TODO: eventually connect with ShutdownThread to persist stats during
383 // graceful shutdown.
384
385 if (mThread != null) {
386 mThread.quit();
387 }
388
389 if (mSettingsObserver != null) {
390 mSettingsObserver.unregister(mContext);
391 }
392
393 if (mPollStickyBroadcast != null) {
394 mContext.removeStickyBroadcast(mPollStickyBroadcast);
395 }
396 }
397
398 void dispatchPoll() {
399 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
400 }
401
402 void dispatchReset() {
403 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
404 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700405
406 private static final int EVENT_REBOOT_RECOVERY = 0;
407 private static final int EVENT_POLICY_CHANGED = 1;
408 private static final int EVENT_POLL_ALARM = 2;
409 private static final int EVENT_RESET_ALARM = 3;
Robert Greenwaltfee46832010-05-06 12:25:13 -0700410 private static final int EVENT_IFACE_UP = 4;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700411 private class MyHandler extends Handler {
412 public MyHandler(Looper l) {
413 super(l);
414 }
415
416 @Override
417 public void handleMessage(Message msg) {
418 switch (msg.what) {
419 case EVENT_REBOOT_RECOVERY:
420 onRebootRecovery();
421 break;
422 case EVENT_POLICY_CHANGED:
423 onPolicyChanged();
424 break;
425 case EVENT_POLL_ALARM:
426 onPollAlarm();
427 break;
428 case EVENT_RESET_ALARM:
429 onResetAlarm();
Robert Greenwaltfee46832010-05-06 12:25:13 -0700430 break;
431 case EVENT_IFACE_UP:
432 onIfaceUp();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700433 }
434 }
435
436 private void onRebootRecovery() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700437 if (VDBG) Slog.v(TAG, "onRebootRecovery");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700438 // check for sim change TODO
439 // reregister for notification of policy change
440
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700441 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700442
443 mRecorder = new DataRecorder(mContext, ThrottleService.this);
444
445 // get policy
446 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
Robert Greenwaltfb9896b2010-04-22 15:39:38 -0700447
448 // if we poll now we won't have network connectivity or even imsi access
449 // queue up a poll to happen in a little while - after ntp and imsi are avail
450 // TODO - make this callback based (ie, listen for notificaitons)
451 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM),
452 INITIAL_POLL_DELAY_SEC * 1000);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700453 }
454
455 // check for new policy info (threshold limit/value/etc)
456 private void onPolicyChanged() {
457 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
458
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700459 int pollingPeriod = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700460 R.integer.config_datause_polling_period_sec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700461 mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
462 Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
463
464 // TODO - remove testing stuff?
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700465 long defaultThreshold = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700466 R.integer.config_datause_threshold_bytes);
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700467 int defaultValue = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700468 R.integer.config_datause_throttle_kbitsps);
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700469 long threshold = Settings.Secure.getLong(mContext.getContentResolver(),
470 Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
471 int value = Settings.Secure.getInt(mContext.getContentResolver(),
472 Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
473
474 mPolicyThreshold.set(threshold);
475 mPolicyThrottleValue.set(value);
476 if (testing) {
477 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
478 mPolicyThreshold.set(TESTING_THRESHOLD);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700479 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700480
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700481 mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
482 Settings.Secure.THROTTLE_RESET_DAY, -1);
483 if (mPolicyResetDay == -1 ||
484 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
485 Random g = new Random();
486 mPolicyResetDay = 1 + g.nextInt(28); // 1-28
487 Settings.Secure.putInt(mContext.getContentResolver(),
488 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
489 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700490 if (mIface == null) {
491 mPolicyThreshold.set(0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700492 }
493
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700494 int defaultNotificationType = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700495 R.integer.config_datause_notification_type);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700496 mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700497 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700498
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700499 final int maxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
500 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC,
501 (int) (MAX_NTP_CACHE_AGE / 1000));
502 mMaxNtpCacheAge = maxNtpCacheAgeSec * 1000;
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700503
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700504 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700505 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700506 mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
507 ", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700508 ", noteType=" + mPolicyNotificationsAllowedMask + ", mMaxNtpCacheAge=" +
509 mMaxNtpCacheAge);
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700510 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700511
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700512 // force updates
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700513 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700514
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700515 onResetAlarm();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700516
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700517 onPollAlarm();
518
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700519 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
520 mContext.sendBroadcast(broadcast);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700521 }
522
523 private void onPollAlarm() {
524 long now = SystemClock.elapsedRealtime();
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700525 long next = now + mPolicyPollPeriodSec * 1000;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700526
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700527 // when trusted cache outdated, try refreshing
528 if (mTime.getCacheAge() > mMaxNtpCacheAge) {
529 if (mTime.forceRefresh()) {
530 if (VDBG) Slog.d(TAG, "updated trusted time, reseting alarm");
531 dispatchReset();
532 }
533 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700534
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700535 long incRead = 0;
536 long incWrite = 0;
537 try {
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700538 final NetworkStats stats = mNMService.getNetworkStatsSummary();
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700539 final int index = stats.findIndex(
540 mIface, NetworkStats.UID_ALL, NetworkStats.TAG_NONE);
Jeff Sharkey9a13f362011-04-26 16:25:36 -0700541
542 if (index != -1) {
543 incRead = stats.rx[index] - mLastRead;
544 incWrite = stats.tx[index] - mLastWrite;
545 } else {
546 // missing iface, assume stats are 0
547 Slog.w(TAG, "unable to find stats for iface " + mIface);
548 }
549
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700550 // handle iface resets - on some device the 3g iface comes and goes and gets
551 // totals reset to 0. Deal with it
552 if ((incRead < 0) || (incWrite < 0)) {
553 incRead += mLastRead;
554 incWrite += mLastWrite;
555 mLastRead = 0;
556 mLastWrite = 0;
557 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700558 } catch (RemoteException e) {
559 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
560 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700561 // don't count this data if we're roaming.
562 boolean roaming = "true".equals(
563 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
564 if (!roaming) {
565 mRecorder.addData(incRead, incWrite);
566 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700567
568 long periodRx = mRecorder.getPeriodRx(0);
569 long periodTx = mRecorder.getPeriodTx(0);
570 long total = periodRx + periodTx;
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700571 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700572 Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
Robert Greenwalt5f996892010-04-08 16:19:24 -0700573 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700574 }
575 mLastRead += incRead;
576 mLastWrite += incWrite;
577
578 checkThrottleAndPostNotification(total);
579
580 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
581 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
582 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700583 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
584 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700585 mContext.sendStickyBroadcast(broadcast);
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700586 mPollStickyBroadcast = broadcast;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700587
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700588 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700589 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700590 }
591
Robert Greenwaltfee46832010-05-06 12:25:13 -0700592 private void onIfaceUp() {
593 // if we were throttled before, be sure and set it again - the iface went down
594 // (and may have disappeared all together) and these settings were lost
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700595 if (mThrottleIndex.get() == 1) {
Robert Greenwaltfee46832010-05-06 12:25:13 -0700596 try {
597 mNMService.setInterfaceThrottle(mIface, -1, -1);
598 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700599 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
Robert Greenwaltfee46832010-05-06 12:25:13 -0700600 } catch (Exception e) {
601 Slog.e(TAG, "error setting Throttle: " + e);
602 }
603 }
604 }
605
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700606 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700607 // is throttling enabled?
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700608 long threshold = mPolicyThreshold.get();
609 if (threshold == 0) {
Robert Greenwaltcce83372010-04-23 17:35:29 -0700610 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700611 return;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700612 }
613
614 // have we spoken with an ntp server yet?
615 // this is controversial, but we'd rather err towards not throttling
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700616 if (!mTime.hasCache()) {
617 Slog.w(TAG, "missing trusted time, skipping throttle check");
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700618 return;
619 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700620
621 // check if we need to throttle
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700622 if (currentTotal > threshold) {
623 if (mThrottleIndex.get() != 1) {
624 mThrottleIndex.set(1);
625 if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700626 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700627 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700628 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700629 } catch (Exception e) {
630 Slog.e(TAG, "error setting Throttle: " + e);
631 }
632
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700633 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700634
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700635 postNotification(R.string.throttled_notification_title,
636 R.string.throttled_notification_message,
637 R.drawable.stat_sys_throttled,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700638 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700639
640 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700641 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
642 mPolicyThrottleValue.get());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700643 mContext.sendStickyBroadcast(broadcast);
644
645 } // else already up!
646 } else {
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700647 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700648 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
649 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700650 // pretend we only have 1/2 the time remaining that we actually do
651 // if our burn rate in the period so far would have us exceed the limit
652 // in that 1/2 window, warn the user.
653 // this gets more generous in the early to middle period and converges back
654 // to the limit as we move toward the period end.
655
656 // adding another factor - it must be greater than the total cap/4
657 // else we may get false alarms very early in the period.. in the first
658 // tenth of a percent of the period if we used more than a tenth of a percent
659 // of the cap we'd get a warning and that's not desired.
660 long start = mRecorder.getPeriodStart();
661 long end = mRecorder.getPeriodEnd();
662 long periodLength = end - start;
663 long now = System.currentTimeMillis();
664 long timeUsed = now - start;
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700665 long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength);
666 if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700667 if (mWarningNotificationSent == false) {
668 mWarningNotificationSent = true;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700669 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
670 postNotification(R.string.throttle_warning_notification_title,
671 R.string.throttle_warning_notification_message,
672 R.drawable.stat_sys_throttled,
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700673 0);
674 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700675 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700676 if (mWarningNotificationSent == true) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700677 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700678 mWarningNotificationSent =false;
679 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700680 }
681 }
682 }
683 }
684
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700685 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700686 Intent intent = new Intent();
687 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700688 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700689 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
690
691 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
692
693 Resources r = Resources.getSystem();
694 CharSequence title = r.getText(titleInt);
695 CharSequence message = r.getText(messageInt);
696 if (mThrottlingNotification == null) {
697 mThrottlingNotification = new Notification();
698 mThrottlingNotification.when = 0;
699 // TODO - fixup icon
700 mThrottlingNotification.icon = icon;
701 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700702 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700703 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700704 mThrottlingNotification.tickerText = title;
705 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
706
707 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
708 }
709
710
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700711 private void clearThrottleAndNotification() {
712 if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) {
713 mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700714 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700715 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700716 } catch (Exception e) {
717 Slog.e(TAG, "error clearing Throttle: " + e);
718 }
719 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
720 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
721 mContext.sendStickyBroadcast(broadcast);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700722 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
723 mWarningNotificationSent = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700724 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700725 }
726
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700727 private Calendar calculatePeriodEnd(long now) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700728 Calendar end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700729 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700730 int day = end.get(Calendar.DAY_OF_MONTH);
731 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
732 end.set(Calendar.HOUR_OF_DAY, 0);
733 end.set(Calendar.MINUTE, 0);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700734 end.set(Calendar.SECOND, 0);
735 end.set(Calendar.MILLISECOND, 0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700736 if (day >= mPolicyResetDay) {
737 int month = end.get(Calendar.MONTH);
738 if (month == Calendar.DECEMBER) {
739 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
740 month = Calendar.JANUARY - 1;
741 }
742 end.set(Calendar.MONTH, month + 1);
743 }
744
745 // TODO - remove!
746 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
747 end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700748 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700749 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
750 }
751 return end;
752 }
753 private Calendar calculatePeriodStart(Calendar end) {
754 Calendar start = (Calendar)end.clone();
755 int month = end.get(Calendar.MONTH);
756 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
757 month = Calendar.DECEMBER + 1;
758 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
759 }
760 start.set(Calendar.MONTH, month - 1);
761
762 // TODO - remove!!
763 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
764 start = (Calendar)end.clone();
765 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
766 }
767 return start;
768 }
769
770 private void onResetAlarm() {
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700771 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700772 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
773 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
774 }
775
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700776 // when trusted cache outdated, try refreshing
777 if (mTime.getCacheAge() > mMaxNtpCacheAge) {
778 mTime.forceRefresh();
779 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700780
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700781 // as long as we have a trusted time cache, we always reset alarms,
782 // even if the refresh above failed.
783 if (mTime.hasCache()) {
784 final long now = mTime.currentTimeMillis();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700785 Calendar end = calculatePeriodEnd(now);
786 Calendar start = calculatePeriodStart(end);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700787
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700788 if (mRecorder.setNextPeriod(start, end)) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700789 onPollAlarm();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700790 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700791
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700792 mAlarmManager.cancel(mPendingResetIntent);
793 long offset = end.getTimeInMillis() - now;
794 // use Elapsed realtime so clock changes don't fool us.
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700795 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700796 SystemClock.elapsedRealtime() + offset,
797 mPendingResetIntent);
798 } else {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700799 if (VDBG) Slog.d(TAG, "no trusted time, not resetting period");
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700800 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700801 }
802 }
803
804 // records bytecount data for a given time and accumulates it into larger time windows
805 // for logging and other purposes
806 //
807 // since time can be changed (user or network action) we will have to track the time of the
808 // last recording and deal with it.
809 private static class DataRecorder {
810 long[] mPeriodRxData;
811 long[] mPeriodTxData;
812 int mCurrentPeriod;
813 int mPeriodCount;
814
815 Calendar mPeriodStart;
816 Calendar mPeriodEnd;
817
818 ThrottleService mParent;
819 Context mContext;
Robert Greenwalte6e98822010-04-15 08:27:14 -0700820 String mImsi = null;
821
822 TelephonyManager mTelephonyManager;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700823
824 DataRecorder(Context context, ThrottleService parent) {
825 mContext = context;
826 mParent = parent;
827
Robert Greenwalte6e98822010-04-15 08:27:14 -0700828 mTelephonyManager = (TelephonyManager)mContext.getSystemService(
829 Context.TELEPHONY_SERVICE);
830
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700831 synchronized (mParent) {
832 mPeriodCount = 6;
833 mPeriodRxData = new long[mPeriodCount];
834 mPeriodTxData = new long[mPeriodCount];
835
836 mPeriodStart = Calendar.getInstance();
837 mPeriodEnd = Calendar.getInstance();
838
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700839 retrieve();
840 }
841 }
842
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700843 boolean setNextPeriod(Calendar start, Calendar end) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700844 // TODO - how would we deal with a dual-IMSI device?
845 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700846 boolean startNewPeriod = true;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700847
Robert Greenwalt27fba672010-04-26 12:29:14 -0700848 if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) {
849 // same endpoints - keep collecting
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700850 if (VDBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700851 Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," +
852 end.getTimeInMillis() +") - ammending data");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700853 }
Robert Greenwalt27fba672010-04-26 12:29:14 -0700854 startNewPeriod = false;
855 } else {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700856 if (VDBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700857 if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) {
858 Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," +
859 end.getTimeInMillis() + ") - old end was " +
860 mPeriodEnd.getTimeInMillis() + ", following");
861 } else {
862 Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," +
863 end.getTimeInMillis() + ") replacing old (" +
864 mPeriodStart.getTimeInMillis() + "," +
865 mPeriodEnd.getTimeInMillis() + ")");
866 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700867 }
868 synchronized (mParent) {
869 ++mCurrentPeriod;
870 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
871 mPeriodRxData[mCurrentPeriod] = 0;
872 mPeriodTxData[mCurrentPeriod] = 0;
873 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700874 }
875 setPeriodStart(start);
876 setPeriodEnd(end);
877 record();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700878 return startNewPeriod;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700879 }
880
881 public long getPeriodEnd() {
882 synchronized (mParent) {
883 return mPeriodEnd.getTimeInMillis();
884 }
885 }
886
887 private void setPeriodEnd(Calendar end) {
888 synchronized (mParent) {
889 mPeriodEnd = end;
890 }
891 }
892
893 public long getPeriodStart() {
894 synchronized (mParent) {
895 return mPeriodStart.getTimeInMillis();
896 }
897 }
898
899 private void setPeriodStart(Calendar start) {
900 synchronized (mParent) {
901 mPeriodStart = start;
902 }
903 }
904
905 public int getPeriodCount() {
906 synchronized (mParent) {
907 return mPeriodCount;
908 }
909 }
910
911 private void zeroData(int field) {
912 synchronized (mParent) {
913 for(int period = 0; period<mPeriodCount; period++) {
914 mPeriodRxData[period] = 0;
915 mPeriodTxData[period] = 0;
916 }
917 mCurrentPeriod = 0;
918 }
919
920 }
921
922 // if time moves backward accumulate all read/write that's lost into the now
923 // otherwise time moved forward.
924 void addData(long bytesRead, long bytesWritten) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700925 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700926
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700927 synchronized (mParent) {
928 mPeriodRxData[mCurrentPeriod] += bytesRead;
929 mPeriodTxData[mCurrentPeriod] += bytesWritten;
930 }
931 record();
932 }
933
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700934 private File getDataFile() {
935 File dataDir = Environment.getDataDirectory();
936 File throttleDir = new File(dataDir, "system/throttle");
937 throttleDir.mkdirs();
Robert Greenwalte6e98822010-04-15 08:27:14 -0700938 String mImsi = mTelephonyManager.getSubscriberId();
939 File dataFile;
940 if (mImsi == null) {
941 dataFile = useMRUFile(throttleDir);
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700942 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700943 } else {
944 String imsiHash = Integer.toString(mImsi.hashCode());
945 dataFile = new File(throttleDir, imsiHash);
946 }
947 // touch the file so it's not LRU
948 dataFile.setLastModified(System.currentTimeMillis());
949 checkAndDeleteLRUDataFile(throttleDir);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700950 return dataFile;
951 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700952
Robert Greenwalte6e98822010-04-15 08:27:14 -0700953 // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling
954 private void checkForSubscriberId() {
955 if (mImsi != null) return;
956
957 mImsi = mTelephonyManager.getSubscriberId();
958 if (mImsi == null) return;
959
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700960 if (VDBG) Slog.d(TAG, "finally have imsi - retreiving data");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700961 retrieve();
962 }
963
964 private final static int MAX_SIMS_SUPPORTED = 3;
965
966 private void checkAndDeleteLRUDataFile(File dir) {
967 File[] files = dir.listFiles();
968
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700969 if (files == null || files.length <= MAX_SIMS_SUPPORTED) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700970 if (DBG) Slog.d(TAG, "Too many data files");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700971 do {
972 File oldest = null;
973 for (File f : files) {
974 if ((oldest == null) || (oldest.lastModified() > f.lastModified())) {
975 oldest = f;
976 }
977 }
978 if (oldest == null) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700979 if (DBG) Slog.d(TAG, " deleting " + oldest);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700980 oldest.delete();
981 files = dir.listFiles();
982 } while (files.length > MAX_SIMS_SUPPORTED);
983 }
984
985 private File useMRUFile(File dir) {
986 File newest = null;
987 File[] files = dir.listFiles();
988
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700989 if (files != null) {
990 for (File f : files) {
991 if ((newest == null) || (newest.lastModified() < f.lastModified())) {
992 newest = f;
993 }
Robert Greenwalte6e98822010-04-15 08:27:14 -0700994 }
995 }
996 if (newest == null) {
997 newest = new File(dir, "temp");
998 }
999 return newest;
1000 }
1001
1002
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001003 private static final int DATA_FILE_VERSION = 1;
1004
1005 private void record() {
1006 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001007 // 1 int mPeriodCount
1008 // 13*6 long[PERIOD_COUNT] mPeriodRxData
1009 // 13*6 long[PERIOD_COUNT] mPeriodTxData
1010 // 1 int mCurrentPeriod
1011 // 13 long periodStartMS
1012 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001013 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001014 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001015 builder.append(DATA_FILE_VERSION);
1016 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001017 builder.append(mPeriodCount);
1018 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001019 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001020 builder.append(mPeriodRxData[i]);
1021 builder.append(":");
1022 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001023 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001024 builder.append(mPeriodTxData[i]);
1025 builder.append(":");
1026 }
1027 builder.append(mCurrentPeriod);
1028 builder.append(":");
1029 builder.append(mPeriodStart.getTimeInMillis());
1030 builder.append(":");
1031 builder.append(mPeriodEnd.getTimeInMillis());
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001032
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001033 BufferedWriter out = null;
1034 try {
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001035 out = new BufferedWriter(new FileWriter(getDataFile()), 256);
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001036 out.write(builder.toString());
1037 } catch (IOException e) {
1038 Slog.e(TAG, "Error writing data file");
1039 return;
1040 } finally {
1041 if (out != null) {
1042 try {
1043 out.close();
1044 } catch (Exception e) {}
1045 }
1046 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001047 }
1048
1049 private void retrieve() {
Robert Greenwalt05d06732010-04-19 11:10:38 -07001050 // clean out any old data first. If we fail to read we don't want old stuff
1051 zeroData(0);
1052
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001053 File f = getDataFile();
1054 byte[] buffer;
1055 FileInputStream s = null;
1056 try {
1057 buffer = new byte[(int)f.length()];
1058 s = new FileInputStream(f);
1059 s.read(buffer);
1060 } catch (IOException e) {
1061 Slog.e(TAG, "Error reading data file");
1062 return;
1063 } finally {
1064 if (s != null) {
1065 try {
1066 s.close();
1067 } catch (Exception e) {}
1068 }
1069 }
1070 String data = new String(buffer);
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001071 if (data == null || data.length() == 0) {
1072 if (DBG) Slog.d(TAG, "data file empty");
1073 return;
1074 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001075 String[] parsed = data.split(":");
1076 int parsedUsed = 0;
1077 if (parsed.length < 6) {
1078 Slog.e(TAG, "reading data file with insufficient length - ignoring");
1079 return;
1080 }
1081
Robert Greenwalt9e3983f2010-05-11 07:06:13 -07001082 int periodCount;
1083 long[] periodRxData;
1084 long[] periodTxData;
1085 int currentPeriod;
1086 Calendar periodStart;
1087 Calendar periodEnd;
1088 try {
1089 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
1090 Slog.e(TAG, "reading data file with bad version - ignoring");
1091 return;
1092 }
1093
1094 periodCount = Integer.parseInt(parsed[parsedUsed++]);
1095 if (parsed.length != 5 + (2 * periodCount)) {
1096 Slog.e(TAG, "reading data file with bad length (" + parsed.length +
1097 " != " + (5 + (2 * periodCount)) + ") - ignoring");
1098 return;
1099 }
1100 periodRxData = new long[periodCount];
1101 for (int i = 0; i < periodCount; i++) {
1102 periodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
1103 }
1104 periodTxData = new long[periodCount];
1105 for (int i = 0; i < periodCount; i++) {
1106 periodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
1107 }
1108
1109 currentPeriod = Integer.parseInt(parsed[parsedUsed++]);
1110
1111 periodStart = new GregorianCalendar();
1112 periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1113 periodEnd = new GregorianCalendar();
1114 periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1115 } catch (Exception e) {
1116 Slog.e(TAG, "Error parsing data file - ignoring");
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001117 return;
1118 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001119 synchronized (mParent) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001120 mPeriodCount = periodCount;
1121 mPeriodRxData = periodRxData;
1122 mPeriodTxData = periodTxData;
Robert Greenwalt9e3983f2010-05-11 07:06:13 -07001123 mCurrentPeriod = currentPeriod;
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001124 mPeriodStart = periodStart;
1125 mPeriodEnd = periodEnd;
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001126 }
1127 }
1128
1129 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001130 synchronized (mParent) {
1131 if (which > mPeriodCount) return 0;
1132 which = mCurrentPeriod - which;
1133 if (which < 0) which += mPeriodCount;
1134 return mPeriodRxData[which];
1135 }
1136 }
1137 long getPeriodTx(int which) {
1138 synchronized (mParent) {
1139 if (which > mPeriodCount) return 0;
1140 which = mCurrentPeriod - which;
1141 if (which < 0) which += mPeriodCount;
1142 return mPeriodTxData[which];
1143 }
1144 }
1145 }
1146
1147 @Override
1148 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1149 if (mContext.checkCallingOrSelfPermission(
1150 android.Manifest.permission.DUMP)
1151 != PackageManager.PERMISSION_GRANTED) {
1152 pw.println("Permission Denial: can't dump ThrottleService " +
1153 "from from pid=" + Binder.getCallingPid() + ", uid=" +
1154 Binder.getCallingUid());
1155 return;
1156 }
1157 pw.println();
1158
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001159 pw.println("The threshold is " + mPolicyThreshold.get() +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001160 ", after which you experince throttling to " +
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001161 mPolicyThrottleValue.get() + "kbps");
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001162 pw.println("Current period is " +
1163 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001164 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001165 " seconds.");
1166 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001167 pw.println("Current Throttle Index is " + mThrottleIndex.get());
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -07001168 pw.println("mMaxNtpCacheAge=" + mMaxNtpCacheAge);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -07001169
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001170 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
1171 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
1172 mRecorder.getPeriodTx(i));
1173 }
1174 }
1175}