blob: 02332b7aa918815093e6468f176af947ccc2f7c7 [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;
36import android.net.ThrottleManager;
37import android.os.Binder;
Robert Greenwaltb8912f52010-04-09 17:27:26 -070038import android.os.Environment;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070039import android.os.Handler;
40import android.os.HandlerThread;
41import android.os.IBinder;
42import android.os.INetworkManagementService;
43import android.os.Looper;
44import android.os.Message;
45import android.os.RemoteException;
46import android.os.ServiceManager;
47import android.os.SystemClock;
48import android.os.SystemProperties;
49import android.provider.Settings;
Robert Greenwalte6e98822010-04-15 08:27:14 -070050import android.telephony.TelephonyManager;
Robert Greenwaltfee46832010-05-06 12:25:13 -070051import android.text.TextUtils;
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070052import android.util.NtpTrustedTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070053import android.util.Slog;
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070054import android.util.TrustedTime;
Robert Greenwalt5f996892010-04-08 16:19:24 -070055
Robert Greenwaltb8912f52010-04-09 17:27:26 -070056import java.io.BufferedWriter;
57import java.io.File;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070058import java.io.FileDescriptor;
Robert Greenwaltb8912f52010-04-09 17:27:26 -070059import java.io.FileInputStream;
60import java.io.FileWriter;
61import java.io.IOException;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070062import java.io.PrintWriter;
63import java.util.Calendar;
64import java.util.GregorianCalendar;
Robert Greenwalt7171ea82010-04-14 22:37:12 -070065import java.util.Properties;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070066import java.util.Random;
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070067import java.util.concurrent.atomic.AtomicInteger;
68import java.util.concurrent.atomic.AtomicLong;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070069
70// TODO - add comments - reference the ThrottleManager for public API
71public class ThrottleService extends IThrottleManager.Stub {
72
73 private static final String TESTING_ENABLED_PROPERTY = "persist.throttle.testing";
74
75 private static final String TAG = "ThrottleService";
Robert Greenwaltbf7de392010-04-21 17:09:38 -070076 private static final boolean DBG = true;
77 private static final boolean VDBG = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070078 private Handler mHandler;
79 private HandlerThread mThread;
80
81 private Context mContext;
82
Robert Greenwaltfb9896b2010-04-22 15:39:38 -070083 private static final int INITIAL_POLL_DELAY_SEC = 90;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070084 private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
Robert Greenwalt7171ea82010-04-14 22:37:12 -070085 private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070086 private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070087
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -070088 private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000;
89 private static final long MAX_NTP_FETCH_WAIT = 20 * 1000;
90
91 private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE;
92
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070093 private int mPolicyPollPeriodSec;
Robert Greenwalt39e163f2010-05-07 16:52:17 -070094 private AtomicLong mPolicyThreshold;
95 private AtomicInteger mPolicyThrottleValue;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070096 private int mPolicyResetDay; // 1-28
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -070097 private int mPolicyNotificationsAllowedMask;
Robert Greenwalt9e696c22010-04-01 14:45:18 -070098
99 private long mLastRead; // read byte count from last poll
100 private long mLastWrite; // write byte count from last poll
101
102 private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL";
103 private static int POLL_REQUEST = 0;
104 private PendingIntent mPendingPollIntent;
105 private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET";
106 private static int RESET_REQUEST = 1;
107 private PendingIntent mPendingResetIntent;
108
109 private INetworkManagementService mNMService;
110 private AlarmManager mAlarmManager;
111 private NotificationManager mNotificationManager;
112
113 private DataRecorder mRecorder;
114
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700115 private String mIface;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700116
117 private static final int NOTIFICATION_WARNING = 2;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700118
119 private Notification mThrottlingNotification;
120 private boolean mWarningNotificationSent = false;
121
Robert Greenwaltfee46832010-05-06 12:25:13 -0700122 private InterfaceObserver mInterfaceObserver;
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700123 private SettingsObserver mSettingsObserver;
124
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700125 private AtomicInteger mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700126 private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
127 private static final int THROTTLE_INDEX_UNTHROTTLED = 0;
128
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700129 private static final String PROPERTIES_FILE = "/etc/gps.conf";
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700130
131 private Intent mPollStickyBroadcast;
132
133 private TrustedTime mTime;
134
135 private static INetworkManagementService getNetworkManagementService() {
136 final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
137 return INetworkManagementService.Stub.asInterface(b);
138 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700139
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700140 public ThrottleService(Context context) {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700141 // TODO: move to using cached NtpTrustedTime
142 this(context, getNetworkManagementService(), new NtpTrustedTime(),
143 context.getResources().getString(R.string.config_datause_iface));
144 }
145
146 public ThrottleService(Context context, INetworkManagementService nmService, TrustedTime time,
147 String iface) {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700148 if (VDBG) Slog.v(TAG, "Starting ThrottleService");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700149 mContext = context;
150
Robert Greenwalt24488bd2010-05-10 16:56:43 -0700151 mPolicyThreshold = new AtomicLong();
152 mPolicyThrottleValue = new AtomicInteger();
153 mThrottleIndex = new AtomicInteger();
154
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700155 mIface = iface;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700156 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
157 Intent pollIntent = new Intent(ACTION_POLL, null);
158 mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
159 Intent resetIntent = new Intent(ACTION_RESET, null);
160 mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
161
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700162 mNMService = nmService;
163 mTime = time;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700164
165 mNotificationManager = (NotificationManager)mContext.getSystemService(
166 Context.NOTIFICATION_SERVICE);
167 }
168
Robert Greenwaltfee46832010-05-06 12:25:13 -0700169 private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
170 private int mMsg;
171 private Handler mHandler;
172 private String mIface;
173
174 InterfaceObserver(Handler handler, int msg, String iface) {
175 super();
176 mHandler = handler;
177 mMsg = msg;
178 mIface = iface;
179 }
180
Wink Saville1a7e6712011-01-09 12:16:38 -0800181 public void interfaceLinkStatusChanged(String iface, boolean link) {
Robert Greenwaltfee46832010-05-06 12:25:13 -0700182 if (link) {
183 if (TextUtils.equals(iface, mIface)) {
184 mHandler.obtainMessage(mMsg).sendToTarget();
185 }
186 }
187 }
188
189 public void interfaceAdded(String iface) {
190 // TODO - an interface added in the UP state should also trigger a StatusChanged
191 // notification..
192 if (TextUtils.equals(iface, mIface)) {
193 mHandler.obtainMessage(mMsg).sendToTarget();
194 }
195 }
196
197 public void interfaceRemoved(String iface) {}
198 }
199
200
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700201 private static class SettingsObserver extends ContentObserver {
202 private int mMsg;
203 private Handler mHandler;
204 SettingsObserver(Handler handler, int msg) {
205 super(handler);
206 mHandler = handler;
207 mMsg = msg;
208 }
209
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700210 void register(Context context) {
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700211 ContentResolver resolver = context.getContentResolver();
212 resolver.registerContentObserver(Settings.Secure.getUriFor(
213 Settings.Secure.THROTTLE_POLLING_SEC), false, this);
214 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700215 Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700216 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700217 Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700218 resolver.registerContentObserver(Settings.Secure.getUriFor(
219 Settings.Secure.THROTTLE_RESET_DAY), false, this);
220 resolver.registerContentObserver(Settings.Secure.getUriFor(
221 Settings.Secure.THROTTLE_NOTIFICATION_TYPE), false, this);
222 resolver.registerContentObserver(Settings.Secure.getUriFor(
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700223 Settings.Secure.THROTTLE_HELP_URI), false, this);
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700224 resolver.registerContentObserver(Settings.Secure.getUriFor(
225 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700226 }
227
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700228 void unregister(Context context) {
229 final ContentResolver resolver = context.getContentResolver();
230 resolver.unregisterContentObserver(this);
231 }
232
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700233 @Override
234 public void onChange(boolean selfChange) {
235 mHandler.obtainMessage(mMsg).sendToTarget();
236 }
237 }
238
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700239 private void enforceAccessPermission() {
240 mContext.enforceCallingOrSelfPermission(
241 android.Manifest.permission.ACCESS_NETWORK_STATE,
242 "ThrottleService");
243 }
244
Robert Greenwalt05d06732010-04-19 11:10:38 -0700245 private long ntpToWallTime(long ntpTime) {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700246 // get time quickly without worrying about trusted state
247 long bestNow = mTime.hasCache() ? mTime.currentTimeMillis()
248 : System.currentTimeMillis();
Robert Greenwalt05d06732010-04-19 11:10:38 -0700249 long localNow = System.currentTimeMillis();
250 return localNow + (ntpTime - bestNow);
251 }
252
Irfan Sheriffcf282362010-04-16 16:53:20 -0700253 // TODO - fetch for the iface
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700254 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt05d06732010-04-19 11:10:38 -0700255
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700256 public long getResetTime(String iface) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700257 enforceAccessPermission();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700258 long resetTime = 0;
Irfan Sheriffcf282362010-04-16 16:53:20 -0700259 if (mRecorder != null) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700260 resetTime = mRecorder.getPeriodEnd();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700261 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700262 resetTime = ntpToWallTime(resetTime);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700263 return resetTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700264 }
Irfan Sheriffcf282362010-04-16 16:53:20 -0700265
266 // TODO - fetch for the iface
Robert Greenwalt05d06732010-04-19 11:10:38 -0700267 // return time in the local, system wall time, correcting for the use of ntp
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700268 public long getPeriodStartTime(String iface) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700269 long startTime = 0;
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700270 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700271 if (mRecorder != null) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700272 startTime = mRecorder.getPeriodStart();
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700273 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700274 startTime = ntpToWallTime(startTime);
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700275 return startTime;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700276 }
277 //TODO - a better name? getCliffByteCountThreshold?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700278 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700279 public long getCliffThreshold(String iface, int cliff) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700280 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700281 if (cliff == 1) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700282 return mPolicyThreshold.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700283 }
284 return 0;
285 }
286 // TODO - a better name? getThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700287 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700288 public int getCliffLevel(String iface, int cliff) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700289 enforceAccessPermission();
Irfan Sheriffcf282362010-04-16 16:53:20 -0700290 if (cliff == 1) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700291 return mPolicyThrottleValue.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700292 }
293 return 0;
294 }
295
Irfan Sheriffc9b68512010-04-08 14:12:33 -0700296 public String getHelpUri() {
297 enforceAccessPermission();
298 return Settings.Secure.getString(mContext.getContentResolver(),
299 Settings.Secure.THROTTLE_HELP_URI);
300 }
301
Irfan Sheriffcf282362010-04-16 16:53:20 -0700302 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700303 public long getByteCount(String iface, int dir, int period, int ago) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700304 enforceAccessPermission();
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700305 if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700306 if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
307 if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
308 }
309 return 0;
310 }
311
312 // TODO - a better name - getCurrentThrottleRate?
Irfan Sheriffcf282362010-04-16 16:53:20 -0700313 // TODO - fetch for the iface
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700314 public int getThrottle(String iface) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700315 enforceAccessPermission();
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700316 if (mThrottleIndex.get() == 1) {
317 return mPolicyThrottleValue.get();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700318 }
319 return 0;
320 }
321
322 void systemReady() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700323 if (VDBG) Slog.v(TAG, "systemReady");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700324 mContext.registerReceiver(
325 new BroadcastReceiver() {
326 @Override
327 public void onReceive(Context context, Intent intent) {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700328 dispatchPoll();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700329 }
330 }, new IntentFilter(ACTION_POLL));
331
332 mContext.registerReceiver(
333 new BroadcastReceiver() {
334 @Override
335 public void onReceive(Context context, Intent intent) {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700336 dispatchReset();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700337 }
338 }, new IntentFilter(ACTION_RESET));
339
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700340 FileInputStream stream = null;
341 try {
342 Properties properties = new Properties();
343 File file = new File(PROPERTIES_FILE);
344 stream = new FileInputStream(file);
345 properties.load(stream);
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700346 final String ntpServer = properties.getProperty("NTP_SERVER", null);
347 if (mTime instanceof NtpTrustedTime) {
348 ((NtpTrustedTime) mTime).setNtpServer(ntpServer, MAX_NTP_FETCH_WAIT);
349 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700350 } catch (IOException e) {
351 Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
352 } finally {
353 if (stream != null) {
354 try {
355 stream.close();
356 } catch (Exception e) {}
357 }
358 }
Robert Greenwaltc76c15e2010-06-16 14:42:16 -0700359
360 // use a new thread as we don't want to stall the system for file writes
361 mThread = new HandlerThread(TAG);
362 mThread.start();
363 mHandler = new MyHandler(mThread.getLooper());
364 mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
365
366 mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
367 try {
368 mNMService.registerObserver(mInterfaceObserver);
369 } catch (RemoteException e) {
370 Slog.e(TAG, "Could not register InterfaceObserver " + e);
371 }
372
373 mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700374 mSettingsObserver.register(mContext);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700375 }
376
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700377 void shutdown() {
378 // TODO: eventually connect with ShutdownThread to persist stats during
379 // graceful shutdown.
380
381 if (mThread != null) {
382 mThread.quit();
383 }
384
385 if (mSettingsObserver != null) {
386 mSettingsObserver.unregister(mContext);
387 }
388
389 if (mPollStickyBroadcast != null) {
390 mContext.removeStickyBroadcast(mPollStickyBroadcast);
391 }
392 }
393
394 void dispatchPoll() {
395 mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
396 }
397
398 void dispatchReset() {
399 mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
400 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700401
402 private static final int EVENT_REBOOT_RECOVERY = 0;
403 private static final int EVENT_POLICY_CHANGED = 1;
404 private static final int EVENT_POLL_ALARM = 2;
405 private static final int EVENT_RESET_ALARM = 3;
Robert Greenwaltfee46832010-05-06 12:25:13 -0700406 private static final int EVENT_IFACE_UP = 4;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700407 private class MyHandler extends Handler {
408 public MyHandler(Looper l) {
409 super(l);
410 }
411
412 @Override
413 public void handleMessage(Message msg) {
414 switch (msg.what) {
415 case EVENT_REBOOT_RECOVERY:
416 onRebootRecovery();
417 break;
418 case EVENT_POLICY_CHANGED:
419 onPolicyChanged();
420 break;
421 case EVENT_POLL_ALARM:
422 onPollAlarm();
423 break;
424 case EVENT_RESET_ALARM:
425 onResetAlarm();
Robert Greenwaltfee46832010-05-06 12:25:13 -0700426 break;
427 case EVENT_IFACE_UP:
428 onIfaceUp();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700429 }
430 }
431
432 private void onRebootRecovery() {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700433 if (VDBG) Slog.v(TAG, "onRebootRecovery");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700434 // check for sim change TODO
435 // reregister for notification of policy change
436
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700437 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700438
439 mRecorder = new DataRecorder(mContext, ThrottleService.this);
440
441 // get policy
442 mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
Robert Greenwaltfb9896b2010-04-22 15:39:38 -0700443
444 // if we poll now we won't have network connectivity or even imsi access
445 // queue up a poll to happen in a little while - after ntp and imsi are avail
446 // TODO - make this callback based (ie, listen for notificaitons)
447 mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM),
448 INITIAL_POLL_DELAY_SEC * 1000);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700449 }
450
451 // check for new policy info (threshold limit/value/etc)
452 private void onPolicyChanged() {
453 boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
454
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700455 int pollingPeriod = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700456 R.integer.config_datause_polling_period_sec);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700457 mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
458 Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
459
460 // TODO - remove testing stuff?
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700461 long defaultThreshold = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700462 R.integer.config_datause_threshold_bytes);
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700463 int defaultValue = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700464 R.integer.config_datause_throttle_kbitsps);
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700465 long threshold = Settings.Secure.getLong(mContext.getContentResolver(),
466 Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
467 int value = Settings.Secure.getInt(mContext.getContentResolver(),
468 Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
469
470 mPolicyThreshold.set(threshold);
471 mPolicyThrottleValue.set(value);
472 if (testing) {
473 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
474 mPolicyThreshold.set(TESTING_THRESHOLD);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700475 }
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700476
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700477 mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
478 Settings.Secure.THROTTLE_RESET_DAY, -1);
479 if (mPolicyResetDay == -1 ||
480 ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
481 Random g = new Random();
482 mPolicyResetDay = 1 + g.nextInt(28); // 1-28
483 Settings.Secure.putInt(mContext.getContentResolver(),
484 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
485 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700486 if (mIface == null) {
487 mPolicyThreshold.set(0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700488 }
489
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700490 int defaultNotificationType = mContext.getResources().getInteger(
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700491 R.integer.config_datause_notification_type);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700492 mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700493 Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700494
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700495 final int maxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
496 Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC,
497 (int) (MAX_NTP_CACHE_AGE / 1000));
498 mMaxNtpCacheAge = maxNtpCacheAgeSec * 1000;
Robert Greenwaltd1055a22010-05-25 15:54:52 -0700499
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700500 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700501 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700502 mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
503 ", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700504 ", noteType=" + mPolicyNotificationsAllowedMask + ", mMaxNtpCacheAge=" +
505 mMaxNtpCacheAge);
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700506 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700507
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700508 // force updates
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700509 mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700510
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700511 onResetAlarm();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700512
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700513 onPollAlarm();
514
Robert Greenwalt81aa0971d2010-04-09 09:36:09 -0700515 Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
516 mContext.sendBroadcast(broadcast);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700517 }
518
519 private void onPollAlarm() {
520 long now = SystemClock.elapsedRealtime();
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700521 long next = now + mPolicyPollPeriodSec * 1000;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700522
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700523 // when trusted cache outdated, try refreshing
524 if (mTime.getCacheAge() > mMaxNtpCacheAge) {
525 if (mTime.forceRefresh()) {
526 if (VDBG) Slog.d(TAG, "updated trusted time, reseting alarm");
527 dispatchReset();
528 }
529 }
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700530
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700531 long incRead = 0;
532 long incWrite = 0;
533 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700534 incRead = mNMService.getInterfaceRxCounter(mIface) - mLastRead;
535 incWrite = mNMService.getInterfaceTxCounter(mIface) - mLastWrite;
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700536 // handle iface resets - on some device the 3g iface comes and goes and gets
537 // totals reset to 0. Deal with it
538 if ((incRead < 0) || (incWrite < 0)) {
539 incRead += mLastRead;
540 incWrite += mLastWrite;
541 mLastRead = 0;
542 mLastWrite = 0;
543 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700544 } catch (RemoteException e) {
545 Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
546 }
Robert Greenwalt5f996892010-04-08 16:19:24 -0700547 // don't count this data if we're roaming.
548 boolean roaming = "true".equals(
549 SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
550 if (!roaming) {
551 mRecorder.addData(incRead, incWrite);
552 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700553
554 long periodRx = mRecorder.getPeriodRx(0);
555 long periodTx = mRecorder.getPeriodTx(0);
556 long total = periodRx + periodTx;
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700557 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700558 Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
Robert Greenwalt5f996892010-04-08 16:19:24 -0700559 ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700560 }
561 mLastRead += incRead;
562 mLastWrite += incWrite;
563
564 checkThrottleAndPostNotification(total);
565
566 Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
567 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
568 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
Robert Greenwalt05d06732010-04-19 11:10:38 -0700569 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
570 broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700571 mContext.sendStickyBroadcast(broadcast);
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700572 mPollStickyBroadcast = broadcast;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700573
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700574 mAlarmManager.cancel(mPendingPollIntent);
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700575 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700576 }
577
Robert Greenwaltfee46832010-05-06 12:25:13 -0700578 private void onIfaceUp() {
579 // if we were throttled before, be sure and set it again - the iface went down
580 // (and may have disappeared all together) and these settings were lost
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700581 if (mThrottleIndex.get() == 1) {
Robert Greenwaltfee46832010-05-06 12:25:13 -0700582 try {
583 mNMService.setInterfaceThrottle(mIface, -1, -1);
584 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700585 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
Robert Greenwaltfee46832010-05-06 12:25:13 -0700586 } catch (Exception e) {
587 Slog.e(TAG, "error setting Throttle: " + e);
588 }
589 }
590 }
591
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700592 private void checkThrottleAndPostNotification(long currentTotal) {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700593 // is throttling enabled?
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700594 long threshold = mPolicyThreshold.get();
595 if (threshold == 0) {
Robert Greenwaltcce83372010-04-23 17:35:29 -0700596 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700597 return;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700598 }
599
600 // have we spoken with an ntp server yet?
601 // this is controversial, but we'd rather err towards not throttling
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700602 if (!mTime.hasCache()) {
603 Slog.w(TAG, "missing trusted time, skipping throttle check");
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700604 return;
605 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700606
607 // check if we need to throttle
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700608 if (currentTotal > threshold) {
609 if (mThrottleIndex.get() != 1) {
610 mThrottleIndex.set(1);
611 if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700612 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700613 mNMService.setInterfaceThrottle(mIface,
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700614 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700615 } catch (Exception e) {
616 Slog.e(TAG, "error setting Throttle: " + e);
617 }
618
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700619 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700620
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700621 postNotification(R.string.throttled_notification_title,
622 R.string.throttled_notification_message,
623 R.drawable.stat_sys_throttled,
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700624 Notification.FLAG_ONGOING_EVENT);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700625
626 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700627 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
628 mPolicyThrottleValue.get());
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700629 mContext.sendStickyBroadcast(broadcast);
630
631 } // else already up!
632 } else {
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700633 clearThrottleAndNotification();
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700634 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
635 // check if we should warn about throttle
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700636 // pretend we only have 1/2 the time remaining that we actually do
637 // if our burn rate in the period so far would have us exceed the limit
638 // in that 1/2 window, warn the user.
639 // this gets more generous in the early to middle period and converges back
640 // to the limit as we move toward the period end.
641
642 // adding another factor - it must be greater than the total cap/4
643 // else we may get false alarms very early in the period.. in the first
644 // tenth of a percent of the period if we used more than a tenth of a percent
645 // of the cap we'd get a warning and that's not desired.
646 long start = mRecorder.getPeriodStart();
647 long end = mRecorder.getPeriodEnd();
648 long periodLength = end - start;
649 long now = System.currentTimeMillis();
650 long timeUsed = now - start;
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700651 long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength);
652 if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700653 if (mWarningNotificationSent == false) {
654 mWarningNotificationSent = true;
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700655 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
656 postNotification(R.string.throttle_warning_notification_title,
657 R.string.throttle_warning_notification_message,
658 R.drawable.stat_sys_throttled,
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700659 0);
660 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700661 } else {
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700662 if (mWarningNotificationSent == true) {
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700663 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
Robert Greenwalte2c0ce02010-04-09 12:31:46 -0700664 mWarningNotificationSent =false;
665 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700666 }
667 }
668 }
669 }
670
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700671 private void postNotification(int titleInt, int messageInt, int icon, int flags) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700672 Intent intent = new Intent();
673 // TODO - fix up intent
Robert Greenwalt2a7b7302010-04-12 14:56:31 -0700674 intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700675 intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
676
677 PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
678
679 Resources r = Resources.getSystem();
680 CharSequence title = r.getText(titleInt);
681 CharSequence message = r.getText(messageInt);
682 if (mThrottlingNotification == null) {
683 mThrottlingNotification = new Notification();
684 mThrottlingNotification.when = 0;
685 // TODO - fixup icon
686 mThrottlingNotification.icon = icon;
687 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700688 }
Robert Greenwaltc87dc6d2010-04-08 16:00:26 -0700689 mThrottlingNotification.flags = flags;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700690 mThrottlingNotification.tickerText = title;
691 mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
692
693 mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
694 }
695
696
Robert Greenwalt39e163f2010-05-07 16:52:17 -0700697 private void clearThrottleAndNotification() {
698 if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) {
699 mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700700 try {
Robert Greenwaltd3bb93f2010-04-12 19:20:55 -0700701 mNMService.setInterfaceThrottle(mIface, -1, -1);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700702 } catch (Exception e) {
703 Slog.e(TAG, "error clearing Throttle: " + e);
704 }
705 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
706 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
707 mContext.sendStickyBroadcast(broadcast);
Robert Greenwalt5bf16d62010-04-23 13:15:44 -0700708 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
709 mWarningNotificationSent = false;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700710 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700711 }
712
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700713 private Calendar calculatePeriodEnd(long now) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700714 Calendar end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700715 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700716 int day = end.get(Calendar.DAY_OF_MONTH);
717 end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
718 end.set(Calendar.HOUR_OF_DAY, 0);
719 end.set(Calendar.MINUTE, 0);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -0700720 end.set(Calendar.SECOND, 0);
721 end.set(Calendar.MILLISECOND, 0);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700722 if (day >= mPolicyResetDay) {
723 int month = end.get(Calendar.MONTH);
724 if (month == Calendar.DECEMBER) {
725 end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
726 month = Calendar.JANUARY - 1;
727 }
728 end.set(Calendar.MONTH, month + 1);
729 }
730
731 // TODO - remove!
732 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
733 end = GregorianCalendar.getInstance();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700734 end.setTimeInMillis(now);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700735 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
736 }
737 return end;
738 }
739 private Calendar calculatePeriodStart(Calendar end) {
740 Calendar start = (Calendar)end.clone();
741 int month = end.get(Calendar.MONTH);
742 if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
743 month = Calendar.DECEMBER + 1;
744 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
745 }
746 start.set(Calendar.MONTH, month - 1);
747
748 // TODO - remove!!
749 if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
750 start = (Calendar)end.clone();
751 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
752 }
753 return start;
754 }
755
756 private void onResetAlarm() {
Robert Greenwalt687f2a02010-06-08 10:10:54 -0700757 if (VDBG || (mPolicyThreshold.get() != 0)) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700758 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
759 " bytes read and " + mRecorder.getPeriodTx(0) + " written");
760 }
761
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700762 // when trusted cache outdated, try refreshing
763 if (mTime.getCacheAge() > mMaxNtpCacheAge) {
764 mTime.forceRefresh();
765 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700766
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700767 // as long as we have a trusted time cache, we always reset alarms,
768 // even if the refresh above failed.
769 if (mTime.hasCache()) {
770 final long now = mTime.currentTimeMillis();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700771 Calendar end = calculatePeriodEnd(now);
772 Calendar start = calculatePeriodStart(end);
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700773
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700774 if (mRecorder.setNextPeriod(start, end)) {
Robert Greenwalt05d06732010-04-19 11:10:38 -0700775 onPollAlarm();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700776 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700777
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700778 mAlarmManager.cancel(mPendingResetIntent);
779 long offset = end.getTimeInMillis() - now;
780 // use Elapsed realtime so clock changes don't fool us.
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700781 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700782 SystemClock.elapsedRealtime() + offset,
783 mPendingResetIntent);
784 } else {
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700785 if (VDBG) Slog.d(TAG, "no trusted time, not resetting period");
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700786 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700787 }
788 }
789
790 // records bytecount data for a given time and accumulates it into larger time windows
791 // for logging and other purposes
792 //
793 // since time can be changed (user or network action) we will have to track the time of the
794 // last recording and deal with it.
795 private static class DataRecorder {
796 long[] mPeriodRxData;
797 long[] mPeriodTxData;
798 int mCurrentPeriod;
799 int mPeriodCount;
800
801 Calendar mPeriodStart;
802 Calendar mPeriodEnd;
803
804 ThrottleService mParent;
805 Context mContext;
Robert Greenwalte6e98822010-04-15 08:27:14 -0700806 String mImsi = null;
807
808 TelephonyManager mTelephonyManager;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700809
810 DataRecorder(Context context, ThrottleService parent) {
811 mContext = context;
812 mParent = parent;
813
Robert Greenwalte6e98822010-04-15 08:27:14 -0700814 mTelephonyManager = (TelephonyManager)mContext.getSystemService(
815 Context.TELEPHONY_SERVICE);
816
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700817 synchronized (mParent) {
818 mPeriodCount = 6;
819 mPeriodRxData = new long[mPeriodCount];
820 mPeriodTxData = new long[mPeriodCount];
821
822 mPeriodStart = Calendar.getInstance();
823 mPeriodEnd = Calendar.getInstance();
824
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700825 retrieve();
826 }
827 }
828
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700829 boolean setNextPeriod(Calendar start, Calendar end) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700830 // TODO - how would we deal with a dual-IMSI device?
831 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700832 boolean startNewPeriod = true;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700833
Robert Greenwalt27fba672010-04-26 12:29:14 -0700834 if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) {
835 // same endpoints - keep collecting
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700836 if (VDBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700837 Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," +
838 end.getTimeInMillis() +") - ammending data");
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700839 }
Robert Greenwalt27fba672010-04-26 12:29:14 -0700840 startNewPeriod = false;
841 } else {
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700842 if (VDBG) {
Robert Greenwalt27fba672010-04-26 12:29:14 -0700843 if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) {
844 Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," +
845 end.getTimeInMillis() + ") - old end was " +
846 mPeriodEnd.getTimeInMillis() + ", following");
847 } else {
848 Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," +
849 end.getTimeInMillis() + ") replacing old (" +
850 mPeriodStart.getTimeInMillis() + "," +
851 mPeriodEnd.getTimeInMillis() + ")");
852 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700853 }
854 synchronized (mParent) {
855 ++mCurrentPeriod;
856 if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
857 mPeriodRxData[mCurrentPeriod] = 0;
858 mPeriodTxData[mCurrentPeriod] = 0;
859 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700860 }
861 setPeriodStart(start);
862 setPeriodEnd(end);
863 record();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700864 return startNewPeriod;
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700865 }
866
867 public long getPeriodEnd() {
868 synchronized (mParent) {
869 return mPeriodEnd.getTimeInMillis();
870 }
871 }
872
873 private void setPeriodEnd(Calendar end) {
874 synchronized (mParent) {
875 mPeriodEnd = end;
876 }
877 }
878
879 public long getPeriodStart() {
880 synchronized (mParent) {
881 return mPeriodStart.getTimeInMillis();
882 }
883 }
884
885 private void setPeriodStart(Calendar start) {
886 synchronized (mParent) {
887 mPeriodStart = start;
888 }
889 }
890
891 public int getPeriodCount() {
892 synchronized (mParent) {
893 return mPeriodCount;
894 }
895 }
896
897 private void zeroData(int field) {
898 synchronized (mParent) {
899 for(int period = 0; period<mPeriodCount; period++) {
900 mPeriodRxData[period] = 0;
901 mPeriodTxData[period] = 0;
902 }
903 mCurrentPeriod = 0;
904 }
905
906 }
907
908 // if time moves backward accumulate all read/write that's lost into the now
909 // otherwise time moved forward.
910 void addData(long bytesRead, long bytesWritten) {
Robert Greenwalte6e98822010-04-15 08:27:14 -0700911 checkForSubscriberId();
Robert Greenwalt7171ea82010-04-14 22:37:12 -0700912
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700913 synchronized (mParent) {
914 mPeriodRxData[mCurrentPeriod] += bytesRead;
915 mPeriodTxData[mCurrentPeriod] += bytesWritten;
916 }
917 record();
918 }
919
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700920 private File getDataFile() {
921 File dataDir = Environment.getDataDirectory();
922 File throttleDir = new File(dataDir, "system/throttle");
923 throttleDir.mkdirs();
Robert Greenwalte6e98822010-04-15 08:27:14 -0700924 String mImsi = mTelephonyManager.getSubscriberId();
925 File dataFile;
926 if (mImsi == null) {
927 dataFile = useMRUFile(throttleDir);
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700928 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700929 } else {
930 String imsiHash = Integer.toString(mImsi.hashCode());
931 dataFile = new File(throttleDir, imsiHash);
932 }
933 // touch the file so it's not LRU
934 dataFile.setLastModified(System.currentTimeMillis());
935 checkAndDeleteLRUDataFile(throttleDir);
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700936 return dataFile;
937 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700938
Robert Greenwalte6e98822010-04-15 08:27:14 -0700939 // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling
940 private void checkForSubscriberId() {
941 if (mImsi != null) return;
942
943 mImsi = mTelephonyManager.getSubscriberId();
944 if (mImsi == null) return;
945
Robert Greenwalt5a671d02010-06-07 16:43:16 -0700946 if (VDBG) Slog.d(TAG, "finally have imsi - retreiving data");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700947 retrieve();
948 }
949
950 private final static int MAX_SIMS_SUPPORTED = 3;
951
952 private void checkAndDeleteLRUDataFile(File dir) {
953 File[] files = dir.listFiles();
954
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700955 if (files == null || files.length <= MAX_SIMS_SUPPORTED) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700956 if (DBG) Slog.d(TAG, "Too many data files");
Robert Greenwalte6e98822010-04-15 08:27:14 -0700957 do {
958 File oldest = null;
959 for (File f : files) {
960 if ((oldest == null) || (oldest.lastModified() > f.lastModified())) {
961 oldest = f;
962 }
963 }
964 if (oldest == null) return;
Robert Greenwaltbf7de392010-04-21 17:09:38 -0700965 if (DBG) Slog.d(TAG, " deleting " + oldest);
Robert Greenwalte6e98822010-04-15 08:27:14 -0700966 oldest.delete();
967 files = dir.listFiles();
968 } while (files.length > MAX_SIMS_SUPPORTED);
969 }
970
971 private File useMRUFile(File dir) {
972 File newest = null;
973 File[] files = dir.listFiles();
974
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -0700975 if (files != null) {
976 for (File f : files) {
977 if ((newest == null) || (newest.lastModified() < f.lastModified())) {
978 newest = f;
979 }
Robert Greenwalte6e98822010-04-15 08:27:14 -0700980 }
981 }
982 if (newest == null) {
983 newest = new File(dir, "temp");
984 }
985 return newest;
986 }
987
988
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700989 private static final int DATA_FILE_VERSION = 1;
990
991 private void record() {
992 // 1 int version
Robert Greenwalt9e696c22010-04-01 14:45:18 -0700993 // 1 int mPeriodCount
994 // 13*6 long[PERIOD_COUNT] mPeriodRxData
995 // 13*6 long[PERIOD_COUNT] mPeriodTxData
996 // 1 int mCurrentPeriod
997 // 13 long periodStartMS
998 // 13 long periodEndMS
Robert Greenwaltb8912f52010-04-09 17:27:26 -0700999 // 200 chars max
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001000 StringBuilder builder = new StringBuilder();
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001001 builder.append(DATA_FILE_VERSION);
1002 builder.append(":");
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001003 builder.append(mPeriodCount);
1004 builder.append(":");
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001005 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001006 builder.append(mPeriodRxData[i]);
1007 builder.append(":");
1008 }
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001009 for(int i = 0; i < mPeriodCount; i++) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001010 builder.append(mPeriodTxData[i]);
1011 builder.append(":");
1012 }
1013 builder.append(mCurrentPeriod);
1014 builder.append(":");
1015 builder.append(mPeriodStart.getTimeInMillis());
1016 builder.append(":");
1017 builder.append(mPeriodEnd.getTimeInMillis());
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001018
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001019 BufferedWriter out = null;
1020 try {
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001021 out = new BufferedWriter(new FileWriter(getDataFile()), 256);
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001022 out.write(builder.toString());
1023 } catch (IOException e) {
1024 Slog.e(TAG, "Error writing data file");
1025 return;
1026 } finally {
1027 if (out != null) {
1028 try {
1029 out.close();
1030 } catch (Exception e) {}
1031 }
1032 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001033 }
1034
1035 private void retrieve() {
Robert Greenwalt05d06732010-04-19 11:10:38 -07001036 // clean out any old data first. If we fail to read we don't want old stuff
1037 zeroData(0);
1038
Robert Greenwaltb8912f52010-04-09 17:27:26 -07001039 File f = getDataFile();
1040 byte[] buffer;
1041 FileInputStream s = null;
1042 try {
1043 buffer = new byte[(int)f.length()];
1044 s = new FileInputStream(f);
1045 s.read(buffer);
1046 } catch (IOException e) {
1047 Slog.e(TAG, "Error reading data file");
1048 return;
1049 } finally {
1050 if (s != null) {
1051 try {
1052 s.close();
1053 } catch (Exception e) {}
1054 }
1055 }
1056 String data = new String(buffer);
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001057 if (data == null || data.length() == 0) {
1058 if (DBG) Slog.d(TAG, "data file empty");
1059 return;
1060 }
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001061 String[] parsed = data.split(":");
1062 int parsedUsed = 0;
1063 if (parsed.length < 6) {
1064 Slog.e(TAG, "reading data file with insufficient length - ignoring");
1065 return;
1066 }
1067
Robert Greenwalt9e3983f2010-05-11 07:06:13 -07001068 int periodCount;
1069 long[] periodRxData;
1070 long[] periodTxData;
1071 int currentPeriod;
1072 Calendar periodStart;
1073 Calendar periodEnd;
1074 try {
1075 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
1076 Slog.e(TAG, "reading data file with bad version - ignoring");
1077 return;
1078 }
1079
1080 periodCount = Integer.parseInt(parsed[parsedUsed++]);
1081 if (parsed.length != 5 + (2 * periodCount)) {
1082 Slog.e(TAG, "reading data file with bad length (" + parsed.length +
1083 " != " + (5 + (2 * periodCount)) + ") - ignoring");
1084 return;
1085 }
1086 periodRxData = new long[periodCount];
1087 for (int i = 0; i < periodCount; i++) {
1088 periodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
1089 }
1090 periodTxData = new long[periodCount];
1091 for (int i = 0; i < periodCount; i++) {
1092 periodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
1093 }
1094
1095 currentPeriod = Integer.parseInt(parsed[parsedUsed++]);
1096
1097 periodStart = new GregorianCalendar();
1098 periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1099 periodEnd = new GregorianCalendar();
1100 periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
1101 } catch (Exception e) {
1102 Slog.e(TAG, "Error parsing data file - ignoring");
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001103 return;
1104 }
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001105 synchronized (mParent) {
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001106 mPeriodCount = periodCount;
1107 mPeriodRxData = periodRxData;
1108 mPeriodTxData = periodTxData;
Robert Greenwalt9e3983f2010-05-11 07:06:13 -07001109 mCurrentPeriod = currentPeriod;
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001110 mPeriodStart = periodStart;
1111 mPeriodEnd = periodEnd;
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001112 }
1113 }
1114
1115 long getPeriodRx(int which) {
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001116 synchronized (mParent) {
1117 if (which > mPeriodCount) return 0;
1118 which = mCurrentPeriod - which;
1119 if (which < 0) which += mPeriodCount;
1120 return mPeriodRxData[which];
1121 }
1122 }
1123 long getPeriodTx(int which) {
1124 synchronized (mParent) {
1125 if (which > mPeriodCount) return 0;
1126 which = mCurrentPeriod - which;
1127 if (which < 0) which += mPeriodCount;
1128 return mPeriodTxData[which];
1129 }
1130 }
1131 }
1132
1133 @Override
1134 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1135 if (mContext.checkCallingOrSelfPermission(
1136 android.Manifest.permission.DUMP)
1137 != PackageManager.PERMISSION_GRANTED) {
1138 pw.println("Permission Denial: can't dump ThrottleService " +
1139 "from from pid=" + Binder.getCallingPid() + ", uid=" +
1140 Binder.getCallingUid());
1141 return;
1142 }
1143 pw.println();
1144
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001145 pw.println("The threshold is " + mPolicyThreshold.get() +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001146 ", after which you experince throttling to " +
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001147 mPolicyThrottleValue.get() + "kbps");
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001148 pw.println("Current period is " +
1149 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
Robert Greenwalt7171ea82010-04-14 22:37:12 -07001150 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001151 " seconds.");
1152 pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
Robert Greenwalt39e163f2010-05-07 16:52:17 -07001153 pw.println("Current Throttle Index is " + mThrottleIndex.get());
Jeff Sharkeyb7342ac2011-04-25 23:44:11 -07001154 pw.println("mMaxNtpCacheAge=" + mMaxNtpCacheAge);
Robert Greenwalt8c7e6092010-04-14 17:31:20 -07001155
Robert Greenwalt9e696c22010-04-01 14:45:18 -07001156 for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
1157 pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
1158 mRecorder.getPeriodTx(i));
1159 }
1160 }
1161}