Home | History | Annotate | Download | only in server
      1 /*
      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 
     17 package com.android.server;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.Notification;
     21 import android.app.NotificationManager;
     22 import android.app.PendingIntent;
     23 import android.content.BroadcastReceiver;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.PackageManager;
     29 import android.content.res.Resources;
     30 import android.database.ContentObserver;
     31 import android.net.INetworkManagementEventObserver;
     32 import android.net.IThrottleManager;
     33 import android.net.NetworkStats;
     34 import android.net.ThrottleManager;
     35 import android.os.Binder;
     36 import android.os.Environment;
     37 import android.os.Handler;
     38 import android.os.HandlerThread;
     39 import android.os.IBinder;
     40 import android.os.INetworkManagementService;
     41 import android.os.Looper;
     42 import android.os.Message;
     43 import android.os.RemoteException;
     44 import android.os.ServiceManager;
     45 import android.os.SystemClock;
     46 import android.os.SystemProperties;
     47 import android.provider.Settings;
     48 import android.telephony.TelephonyManager;
     49 import android.text.TextUtils;
     50 import android.util.NtpTrustedTime;
     51 import android.util.Slog;
     52 import android.util.TrustedTime;
     53 
     54 import com.android.internal.R;
     55 import com.android.internal.telephony.TelephonyProperties;
     56 
     57 import java.io.BufferedWriter;
     58 import java.io.File;
     59 import java.io.FileDescriptor;
     60 import java.io.FileInputStream;
     61 import java.io.FileWriter;
     62 import java.io.IOException;
     63 import java.io.PrintWriter;
     64 import java.util.Calendar;
     65 import java.util.GregorianCalendar;
     66 import java.util.Random;
     67 import java.util.concurrent.atomic.AtomicInteger;
     68 import java.util.concurrent.atomic.AtomicLong;
     69 
     70 // TODO - add comments - reference the ThrottleManager for public API
     71 public 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";
     76     private static final boolean DBG = true;
     77     private static final boolean VDBG = false;
     78     private Handler mHandler;
     79     private HandlerThread mThread;
     80 
     81     private Context mContext;
     82 
     83     private static final int INITIAL_POLL_DELAY_SEC = 90;
     84     private static final int TESTING_POLLING_PERIOD_SEC = 60 * 1;
     85     private static final int TESTING_RESET_PERIOD_SEC = 60 * 10;
     86     private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
     87 
     88     private static final long MAX_NTP_CACHE_AGE = 24 * 60 * 60 * 1000;
     89 
     90     private long mMaxNtpCacheAge = MAX_NTP_CACHE_AGE;
     91 
     92     private int mPolicyPollPeriodSec;
     93     private AtomicLong mPolicyThreshold;
     94     private AtomicInteger mPolicyThrottleValue;
     95     private int mPolicyResetDay; // 1-28
     96     private int mPolicyNotificationsAllowedMask;
     97 
     98     private long mLastRead; // read byte count from last poll
     99     private long mLastWrite; // write byte count from last poll
    100 
    101     private static final String ACTION_POLL = "com.android.server.ThrottleManager.action.POLL";
    102     private static int POLL_REQUEST = 0;
    103     private PendingIntent mPendingPollIntent;
    104     private static final String ACTION_RESET = "com.android.server.ThorottleManager.action.RESET";
    105     private static int RESET_REQUEST = 1;
    106     private PendingIntent mPendingResetIntent;
    107 
    108     private INetworkManagementService mNMService;
    109     private AlarmManager mAlarmManager;
    110     private NotificationManager mNotificationManager;
    111 
    112     private DataRecorder mRecorder;
    113 
    114     private String mIface;
    115 
    116     private static final int NOTIFICATION_WARNING   = 2;
    117 
    118     private Notification mThrottlingNotification;
    119     private boolean mWarningNotificationSent = false;
    120 
    121     private InterfaceObserver mInterfaceObserver;
    122     private SettingsObserver mSettingsObserver;
    123 
    124     private AtomicInteger mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
    125     private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
    126     private static final int THROTTLE_INDEX_UNTHROTTLED   =  0;
    127 
    128     private Intent mPollStickyBroadcast;
    129 
    130     private TrustedTime mTime;
    131 
    132     private static INetworkManagementService getNetworkManagementService() {
    133         final IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
    134         return INetworkManagementService.Stub.asInterface(b);
    135     }
    136 
    137     public ThrottleService(Context context) {
    138         this(context, getNetworkManagementService(), NtpTrustedTime.getInstance(context),
    139                 context.getResources().getString(R.string.config_datause_iface));
    140     }
    141 
    142     public ThrottleService(Context context, INetworkManagementService nmService, TrustedTime time,
    143             String iface) {
    144         if (VDBG) Slog.v(TAG, "Starting ThrottleService");
    145         mContext = context;
    146 
    147         mPolicyThreshold = new AtomicLong();
    148         mPolicyThrottleValue = new AtomicInteger();
    149         mThrottleIndex = new AtomicInteger();
    150 
    151         mIface = iface;
    152         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
    153         Intent pollIntent = new Intent(ACTION_POLL, null);
    154         mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
    155         Intent resetIntent = new Intent(ACTION_RESET, null);
    156         mPendingResetIntent = PendingIntent.getBroadcast(mContext, RESET_REQUEST, resetIntent, 0);
    157 
    158         mNMService = nmService;
    159         mTime = time;
    160 
    161         mNotificationManager = (NotificationManager)mContext.getSystemService(
    162                 Context.NOTIFICATION_SERVICE);
    163     }
    164 
    165     private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
    166         private int mMsg;
    167         private Handler mHandler;
    168         private String mIface;
    169 
    170         InterfaceObserver(Handler handler, int msg, String iface) {
    171             super();
    172             mHandler = handler;
    173             mMsg = msg;
    174             mIface = iface;
    175         }
    176 
    177         public void interfaceStatusChanged(String iface, boolean up) {
    178             if (up) {
    179                 if (TextUtils.equals(iface, mIface)) {
    180                     mHandler.obtainMessage(mMsg).sendToTarget();
    181                 }
    182             }
    183         }
    184 
    185         public void interfaceLinkStateChanged(String iface, boolean up) {
    186         }
    187 
    188         public void interfaceAdded(String iface) {
    189             // TODO - an interface added in the UP state should also trigger a StatusChanged
    190             // notification..
    191             if (TextUtils.equals(iface, mIface)) {
    192                 mHandler.obtainMessage(mMsg).sendToTarget();
    193             }
    194         }
    195 
    196         public void interfaceRemoved(String iface) {}
    197         public void limitReached(String limitName, String iface) {}
    198     }
    199 
    200 
    201     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 
    210         void register(Context context) {
    211             ContentResolver resolver = context.getContentResolver();
    212             resolver.registerContentObserver(Settings.Secure.getUriFor(
    213                     Settings.Secure.THROTTLE_POLLING_SEC), false, this);
    214             resolver.registerContentObserver(Settings.Secure.getUriFor(
    215                     Settings.Secure.THROTTLE_THRESHOLD_BYTES), false, this);
    216             resolver.registerContentObserver(Settings.Secure.getUriFor(
    217                     Settings.Secure.THROTTLE_VALUE_KBITSPS), false, this);
    218             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(
    223                     Settings.Secure.THROTTLE_HELP_URI), false, this);
    224             resolver.registerContentObserver(Settings.Secure.getUriFor(
    225                     Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC), false, this);
    226         }
    227 
    228         void unregister(Context context) {
    229             final ContentResolver resolver = context.getContentResolver();
    230             resolver.unregisterContentObserver(this);
    231         }
    232 
    233         @Override
    234         public void onChange(boolean selfChange) {
    235             mHandler.obtainMessage(mMsg).sendToTarget();
    236         }
    237     }
    238 
    239     private void enforceAccessPermission() {
    240         mContext.enforceCallingOrSelfPermission(
    241                 android.Manifest.permission.ACCESS_NETWORK_STATE,
    242                 "ThrottleService");
    243     }
    244 
    245     private long ntpToWallTime(long ntpTime) {
    246         // get time quickly without worrying about trusted state
    247         long bestNow = mTime.hasCache() ? mTime.currentTimeMillis()
    248                 : System.currentTimeMillis();
    249         long localNow = System.currentTimeMillis();
    250         return localNow + (ntpTime - bestNow);
    251     }
    252 
    253     // TODO - fetch for the iface
    254     // return time in the local, system wall time, correcting for the use of ntp
    255 
    256     public long getResetTime(String iface) {
    257         enforceAccessPermission();
    258         long resetTime = 0;
    259         if (mRecorder != null) {
    260             resetTime = mRecorder.getPeriodEnd();
    261         }
    262         resetTime = ntpToWallTime(resetTime);
    263         return resetTime;
    264     }
    265 
    266     // TODO - fetch for the iface
    267     // return time in the local, system wall time, correcting for the use of ntp
    268     public long getPeriodStartTime(String iface) {
    269         long startTime = 0;
    270         enforceAccessPermission();
    271         if (mRecorder != null) {
    272             startTime = mRecorder.getPeriodStart();
    273         }
    274         startTime = ntpToWallTime(startTime);
    275         return startTime;
    276     }
    277     //TODO - a better name?  getCliffByteCountThreshold?
    278     // TODO - fetch for the iface
    279     public long getCliffThreshold(String iface, int cliff) {
    280         enforceAccessPermission();
    281         if (cliff == 1) {
    282            return mPolicyThreshold.get();
    283         }
    284         return 0;
    285     }
    286     // TODO - a better name? getThrottleRate?
    287     // TODO - fetch for the iface
    288     public int getCliffLevel(String iface, int cliff) {
    289         enforceAccessPermission();
    290         if (cliff == 1) {
    291             return mPolicyThrottleValue.get();
    292         }
    293         return 0;
    294     }
    295 
    296     public String getHelpUri() {
    297         enforceAccessPermission();
    298         return Settings.Secure.getString(mContext.getContentResolver(),
    299                     Settings.Secure.THROTTLE_HELP_URI);
    300     }
    301 
    302     // TODO - fetch for the iface
    303     public long getByteCount(String iface, int dir, int period, int ago) {
    304         enforceAccessPermission();
    305         if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) {
    306             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?
    313     // TODO - fetch for the iface
    314     public int getThrottle(String iface) {
    315         enforceAccessPermission();
    316         if (mThrottleIndex.get() == 1) {
    317             return mPolicyThrottleValue.get();
    318         }
    319         return 0;
    320     }
    321 
    322     void systemReady() {
    323         if (VDBG) Slog.v(TAG, "systemReady");
    324         mContext.registerReceiver(
    325             new BroadcastReceiver() {
    326                 @Override
    327                 public void onReceive(Context context, Intent intent) {
    328                     dispatchPoll();
    329                 }
    330             }, new IntentFilter(ACTION_POLL));
    331 
    332         mContext.registerReceiver(
    333             new BroadcastReceiver() {
    334                 @Override
    335                 public void onReceive(Context context, Intent intent) {
    336                     dispatchReset();
    337                 }
    338             }, new IntentFilter(ACTION_RESET));
    339 
    340         // use a new thread as we don't want to stall the system for file writes
    341         mThread = new HandlerThread(TAG);
    342         mThread.start();
    343         mHandler = new MyHandler(mThread.getLooper());
    344         mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
    345 
    346         mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
    347         try {
    348             mNMService.registerObserver(mInterfaceObserver);
    349         } catch (RemoteException e) {
    350             Slog.e(TAG, "Could not register InterfaceObserver " + e);
    351         }
    352 
    353         mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
    354         mSettingsObserver.register(mContext);
    355     }
    356 
    357     void shutdown() {
    358         // TODO: eventually connect with ShutdownThread to persist stats during
    359         // graceful shutdown.
    360 
    361         if (mThread != null) {
    362             mThread.quit();
    363         }
    364 
    365         if (mSettingsObserver != null) {
    366             mSettingsObserver.unregister(mContext);
    367         }
    368 
    369         if (mPollStickyBroadcast != null) {
    370             mContext.removeStickyBroadcast(mPollStickyBroadcast);
    371         }
    372     }
    373 
    374     void dispatchPoll() {
    375         mHandler.obtainMessage(EVENT_POLL_ALARM).sendToTarget();
    376     }
    377 
    378     void dispatchReset() {
    379         mHandler.obtainMessage(EVENT_RESET_ALARM).sendToTarget();
    380     }
    381 
    382     private static final int EVENT_REBOOT_RECOVERY = 0;
    383     private static final int EVENT_POLICY_CHANGED  = 1;
    384     private static final int EVENT_POLL_ALARM      = 2;
    385     private static final int EVENT_RESET_ALARM     = 3;
    386     private static final int EVENT_IFACE_UP        = 4;
    387     private class MyHandler extends Handler {
    388         public MyHandler(Looper l) {
    389             super(l);
    390         }
    391 
    392         @Override
    393         public void handleMessage(Message msg) {
    394             switch (msg.what) {
    395             case EVENT_REBOOT_RECOVERY:
    396                 onRebootRecovery();
    397                 break;
    398             case EVENT_POLICY_CHANGED:
    399                 onPolicyChanged();
    400                 break;
    401             case EVENT_POLL_ALARM:
    402                 onPollAlarm();
    403                 break;
    404             case EVENT_RESET_ALARM:
    405                 onResetAlarm();
    406                 break;
    407             case EVENT_IFACE_UP:
    408                 onIfaceUp();
    409             }
    410         }
    411 
    412         private void onRebootRecovery() {
    413             if (VDBG) Slog.v(TAG, "onRebootRecovery");
    414             // check for sim change TODO
    415             // reregister for notification of policy change
    416 
    417             mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
    418 
    419             mRecorder = new DataRecorder(mContext, ThrottleService.this);
    420 
    421             // get policy
    422             mHandler.obtainMessage(EVENT_POLICY_CHANGED).sendToTarget();
    423 
    424             // if we poll now we won't have network connectivity or even imsi access
    425             // queue up a poll to happen in a little while - after ntp and imsi are avail
    426             // TODO - make this callback based (ie, listen for notificaitons)
    427             mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_POLL_ALARM),
    428                     INITIAL_POLL_DELAY_SEC * 1000);
    429         }
    430 
    431         // check for new policy info (threshold limit/value/etc)
    432         private void onPolicyChanged() {
    433             boolean testing = SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true");
    434 
    435             int pollingPeriod = mContext.getResources().getInteger(
    436                     R.integer.config_datause_polling_period_sec);
    437             mPolicyPollPeriodSec = Settings.Secure.getInt(mContext.getContentResolver(),
    438                     Settings.Secure.THROTTLE_POLLING_SEC, pollingPeriod);
    439 
    440             // TODO - remove testing stuff?
    441             long defaultThreshold = mContext.getResources().getInteger(
    442                     R.integer.config_datause_threshold_bytes);
    443             int defaultValue = mContext.getResources().getInteger(
    444                     R.integer.config_datause_throttle_kbitsps);
    445             long threshold = Settings.Secure.getLong(mContext.getContentResolver(),
    446                     Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
    447             int value = Settings.Secure.getInt(mContext.getContentResolver(),
    448                     Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
    449 
    450             mPolicyThreshold.set(threshold);
    451             mPolicyThrottleValue.set(value);
    452             if (testing) {
    453                 mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
    454                 mPolicyThreshold.set(TESTING_THRESHOLD);
    455             }
    456 
    457             mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
    458                     Settings.Secure.THROTTLE_RESET_DAY, -1);
    459             if (mPolicyResetDay == -1 ||
    460                     ((mPolicyResetDay < 1) || (mPolicyResetDay > 28))) {
    461                 Random g = new Random();
    462                 mPolicyResetDay = 1 + g.nextInt(28); // 1-28
    463                 Settings.Secure.putInt(mContext.getContentResolver(),
    464                 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
    465             }
    466             if (mIface == null) {
    467                 mPolicyThreshold.set(0);
    468             }
    469 
    470             int defaultNotificationType = mContext.getResources().getInteger(
    471                     R.integer.config_datause_notification_type);
    472             mPolicyNotificationsAllowedMask = Settings.Secure.getInt(mContext.getContentResolver(),
    473                     Settings.Secure.THROTTLE_NOTIFICATION_TYPE, defaultNotificationType);
    474 
    475             final int maxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
    476                     Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC,
    477                     (int) (MAX_NTP_CACHE_AGE / 1000));
    478             mMaxNtpCacheAge = maxNtpCacheAgeSec * 1000;
    479 
    480             if (VDBG || (mPolicyThreshold.get() != 0)) {
    481                 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
    482                         mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
    483                         ", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
    484                         ", noteType=" + mPolicyNotificationsAllowedMask + ", mMaxNtpCacheAge=" +
    485                         mMaxNtpCacheAge);
    486             }
    487 
    488             // force updates
    489             mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
    490 
    491             onResetAlarm();
    492 
    493             onPollAlarm();
    494 
    495             Intent broadcast = new Intent(ThrottleManager.POLICY_CHANGED_ACTION);
    496             mContext.sendBroadcast(broadcast);
    497         }
    498 
    499         private void onPollAlarm() {
    500             long now = SystemClock.elapsedRealtime();
    501             long next = now + mPolicyPollPeriodSec * 1000;
    502 
    503             // when trusted cache outdated, try refreshing
    504             if (mTime.getCacheAge() > mMaxNtpCacheAge) {
    505                 if (mTime.forceRefresh()) {
    506                     if (VDBG) Slog.d(TAG, "updated trusted time, reseting alarm");
    507                     dispatchReset();
    508                 }
    509             }
    510 
    511             long incRead = 0;
    512             long incWrite = 0;
    513             try {
    514                 final NetworkStats stats = mNMService.getNetworkStatsSummary();
    515                 final int index = stats.findIndex(mIface, NetworkStats.UID_ALL,
    516                         NetworkStats.SET_DEFAULT, NetworkStats.TAG_NONE);
    517 
    518                 if (index != -1) {
    519                     final NetworkStats.Entry entry = stats.getValues(index, null);
    520                     incRead = entry.rxBytes - mLastRead;
    521                     incWrite = entry.txBytes - mLastWrite;
    522                 } else {
    523                     // missing iface, assume stats are 0
    524                     Slog.w(TAG, "unable to find stats for iface " + mIface);
    525                 }
    526 
    527                 // handle iface resets - on some device the 3g iface comes and goes and gets
    528                 // totals reset to 0.  Deal with it
    529                 if ((incRead < 0) || (incWrite < 0)) {
    530                     incRead += mLastRead;
    531                     incWrite += mLastWrite;
    532                     mLastRead = 0;
    533                     mLastWrite = 0;
    534                 }
    535             } catch (IllegalStateException e) {
    536                 Slog.e(TAG, "problem during onPollAlarm: " + e);
    537             } catch (RemoteException e) {
    538                 Slog.e(TAG, "problem during onPollAlarm: " + e);
    539             }
    540 
    541             // don't count this data if we're roaming.
    542             boolean roaming = "true".equals(
    543                     SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
    544             if (!roaming) {
    545                 mRecorder.addData(incRead, incWrite);
    546             }
    547 
    548             long periodRx = mRecorder.getPeriodRx(0);
    549             long periodTx = mRecorder.getPeriodTx(0);
    550             long total = periodRx + periodTx;
    551             if (VDBG || (mPolicyThreshold.get() != 0)) {
    552                 Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
    553                         ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
    554             }
    555             mLastRead += incRead;
    556             mLastWrite += incWrite;
    557 
    558             checkThrottleAndPostNotification(total);
    559 
    560             Intent broadcast = new Intent(ThrottleManager.THROTTLE_POLL_ACTION);
    561             broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_READ, periodRx);
    562             broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_WRITE, periodTx);
    563             broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_START, getPeriodStartTime(mIface));
    564             broadcast.putExtra(ThrottleManager.EXTRA_CYCLE_END, getResetTime(mIface));
    565             mContext.sendStickyBroadcast(broadcast);
    566             mPollStickyBroadcast = broadcast;
    567 
    568             mAlarmManager.cancel(mPendingPollIntent);
    569             mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
    570         }
    571 
    572         private void onIfaceUp() {
    573             // if we were throttled before, be sure and set it again - the iface went down
    574             // (and may have disappeared all together) and these settings were lost
    575             if (mThrottleIndex.get() == 1) {
    576                 try {
    577                     mNMService.setInterfaceThrottle(mIface, -1, -1);
    578                     mNMService.setInterfaceThrottle(mIface,
    579                             mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
    580                 } catch (Exception e) {
    581                     Slog.e(TAG, "error setting Throttle: " + e);
    582                 }
    583             }
    584         }
    585 
    586         private void checkThrottleAndPostNotification(long currentTotal) {
    587             // is throttling enabled?
    588             long threshold = mPolicyThreshold.get();
    589             if (threshold == 0) {
    590                 clearThrottleAndNotification();
    591                 return;
    592             }
    593 
    594             // have we spoken with an ntp server yet?
    595             // this is controversial, but we'd rather err towards not throttling
    596             if (!mTime.hasCache()) {
    597                 Slog.w(TAG, "missing trusted time, skipping throttle check");
    598                 return;
    599             }
    600 
    601             // check if we need to throttle
    602             if (currentTotal > threshold) {
    603                 if (mThrottleIndex.get() != 1) {
    604                     mThrottleIndex.set(1);
    605                     if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!");
    606                     try {
    607                         mNMService.setInterfaceThrottle(mIface,
    608                                 mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
    609                     } catch (Exception e) {
    610                         Slog.e(TAG, "error setting Throttle: " + e);
    611                     }
    612 
    613                     mNotificationManager.cancel(R.drawable.stat_sys_throttled);
    614 
    615                     postNotification(R.string.throttled_notification_title,
    616                             R.string.throttled_notification_message,
    617                             R.drawable.stat_sys_throttled,
    618                             Notification.FLAG_ONGOING_EVENT);
    619 
    620                     Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
    621                     broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
    622                             mPolicyThrottleValue.get());
    623                     mContext.sendStickyBroadcast(broadcast);
    624 
    625                 } // else already up!
    626             } else {
    627                 clearThrottleAndNotification();
    628                 if ((mPolicyNotificationsAllowedMask & NOTIFICATION_WARNING) != 0) {
    629                     // check if we should warn about throttle
    630                     // pretend we only have 1/2 the time remaining that we actually do
    631                     // if our burn rate in the period so far would have us exceed the limit
    632                     // in that 1/2 window, warn the user.
    633                     // this gets more generous in the early to middle period and converges back
    634                     // to the limit as we move toward the period end.
    635 
    636                     // adding another factor - it must be greater than the total cap/4
    637                     // else we may get false alarms very early in the period..  in the first
    638                     // tenth of a percent of the period if we used more than a tenth of a percent
    639                     // of the cap we'd get a warning and that's not desired.
    640                     long start = mRecorder.getPeriodStart();
    641                     long end = mRecorder.getPeriodEnd();
    642                     long periodLength = end - start;
    643                     long now = System.currentTimeMillis();
    644                     long timeUsed = now - start;
    645                     long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength);
    646                     if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) {
    647                         if (mWarningNotificationSent == false) {
    648                             mWarningNotificationSent = true;
    649                             mNotificationManager.cancel(R.drawable.stat_sys_throttled);
    650                             postNotification(R.string.throttle_warning_notification_title,
    651                                     R.string.throttle_warning_notification_message,
    652                                     R.drawable.stat_sys_throttled,
    653                                     0);
    654                         }
    655                     } else {
    656                         if (mWarningNotificationSent == true) {
    657                             mNotificationManager.cancel(R.drawable.stat_sys_throttled);
    658                             mWarningNotificationSent =false;
    659                         }
    660                     }
    661                 }
    662             }
    663         }
    664 
    665         private void postNotification(int titleInt, int messageInt, int icon, int flags) {
    666             Intent intent = new Intent();
    667             // TODO - fix up intent
    668             intent.setClassName("com.android.phone", "com.android.phone.DataUsage");
    669             intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
    670 
    671             PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
    672 
    673             Resources r = Resources.getSystem();
    674             CharSequence title = r.getText(titleInt);
    675             CharSequence message = r.getText(messageInt);
    676             if (mThrottlingNotification == null) {
    677                 mThrottlingNotification = new Notification();
    678                 mThrottlingNotification.when = 0;
    679                 // TODO -  fixup icon
    680                 mThrottlingNotification.icon = icon;
    681                 mThrottlingNotification.defaults &= ~Notification.DEFAULT_SOUND;
    682             }
    683             mThrottlingNotification.flags = flags;
    684             mThrottlingNotification.tickerText = title;
    685             mThrottlingNotification.setLatestEventInfo(mContext, title, message, pi);
    686 
    687             mNotificationManager.notify(mThrottlingNotification.icon, mThrottlingNotification);
    688         }
    689 
    690 
    691         private void clearThrottleAndNotification() {
    692             if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) {
    693                 mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED);
    694                 try {
    695                     mNMService.setInterfaceThrottle(mIface, -1, -1);
    696                 } catch (Exception e) {
    697                     Slog.e(TAG, "error clearing Throttle: " + e);
    698                 }
    699                 Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
    700                 broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, -1);
    701                 mContext.sendStickyBroadcast(broadcast);
    702                 mNotificationManager.cancel(R.drawable.stat_sys_throttled);
    703                 mWarningNotificationSent = false;
    704             }
    705         }
    706 
    707         private Calendar calculatePeriodEnd(long now) {
    708             Calendar end = GregorianCalendar.getInstance();
    709             end.setTimeInMillis(now);
    710             int day = end.get(Calendar.DAY_OF_MONTH);
    711             end.set(Calendar.DAY_OF_MONTH, mPolicyResetDay);
    712             end.set(Calendar.HOUR_OF_DAY, 0);
    713             end.set(Calendar.MINUTE, 0);
    714             end.set(Calendar.SECOND, 0);
    715             end.set(Calendar.MILLISECOND, 0);
    716             if (day >= mPolicyResetDay) {
    717                 int month = end.get(Calendar.MONTH);
    718                 if (month == Calendar.DECEMBER) {
    719                     end.set(Calendar.YEAR, end.get(Calendar.YEAR) + 1);
    720                     month = Calendar.JANUARY - 1;
    721                 }
    722                 end.set(Calendar.MONTH, month + 1);
    723             }
    724 
    725             // TODO - remove!
    726             if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
    727                 end = GregorianCalendar.getInstance();
    728                 end.setTimeInMillis(now);
    729                 end.add(Calendar.SECOND, TESTING_RESET_PERIOD_SEC);
    730             }
    731             return end;
    732         }
    733         private Calendar calculatePeriodStart(Calendar end) {
    734             Calendar start = (Calendar)end.clone();
    735             int month = end.get(Calendar.MONTH);
    736             if (end.get(Calendar.MONTH) == Calendar.JANUARY) {
    737                 month = Calendar.DECEMBER + 1;
    738                 start.set(Calendar.YEAR, start.get(Calendar.YEAR) - 1);
    739             }
    740             start.set(Calendar.MONTH, month - 1);
    741 
    742             // TODO - remove!!
    743             if (SystemProperties.get(TESTING_ENABLED_PROPERTY).equals("true")) {
    744                 start = (Calendar)end.clone();
    745                 start.add(Calendar.SECOND, -TESTING_RESET_PERIOD_SEC);
    746             }
    747             return start;
    748         }
    749 
    750         private void onResetAlarm() {
    751             if (VDBG || (mPolicyThreshold.get() != 0)) {
    752                 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
    753                         " bytes read and " + mRecorder.getPeriodTx(0) + " written");
    754             }
    755 
    756             // when trusted cache outdated, try refreshing
    757             if (mTime.getCacheAge() > mMaxNtpCacheAge) {
    758                 mTime.forceRefresh();
    759             }
    760 
    761             // as long as we have a trusted time cache, we always reset alarms,
    762             // even if the refresh above failed.
    763             if (mTime.hasCache()) {
    764                 final long now = mTime.currentTimeMillis();
    765                 Calendar end = calculatePeriodEnd(now);
    766                 Calendar start = calculatePeriodStart(end);
    767 
    768                 if (mRecorder.setNextPeriod(start, end)) {
    769                     onPollAlarm();
    770                 }
    771 
    772                 mAlarmManager.cancel(mPendingResetIntent);
    773                 long offset = end.getTimeInMillis() - now;
    774                 // use Elapsed realtime so clock changes don't fool us.
    775                 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
    776                         SystemClock.elapsedRealtime() + offset,
    777                         mPendingResetIntent);
    778             } else {
    779                 if (VDBG) Slog.d(TAG, "no trusted time, not resetting period");
    780             }
    781         }
    782     }
    783 
    784     // records bytecount data for a given time and accumulates it into larger time windows
    785     // for logging and other purposes
    786     //
    787     // since time can be changed (user or network action) we will have to track the time of the
    788     // last recording and deal with it.
    789     private static class DataRecorder {
    790         long[] mPeriodRxData;
    791         long[] mPeriodTxData;
    792         int mCurrentPeriod;
    793         int mPeriodCount;
    794 
    795         Calendar mPeriodStart;
    796         Calendar mPeriodEnd;
    797 
    798         ThrottleService mParent;
    799         Context mContext;
    800         String mImsi = null;
    801 
    802         TelephonyManager mTelephonyManager;
    803 
    804         DataRecorder(Context context, ThrottleService parent) {
    805             mContext = context;
    806             mParent = parent;
    807 
    808             mTelephonyManager = (TelephonyManager)mContext.getSystemService(
    809                     Context.TELEPHONY_SERVICE);
    810 
    811             synchronized (mParent) {
    812                 mPeriodCount = 6;
    813                 mPeriodRxData = new long[mPeriodCount];
    814                 mPeriodTxData = new long[mPeriodCount];
    815 
    816                 mPeriodStart = Calendar.getInstance();
    817                 mPeriodEnd = Calendar.getInstance();
    818 
    819                 retrieve();
    820             }
    821         }
    822 
    823         boolean setNextPeriod(Calendar start, Calendar end) {
    824             // TODO - how would we deal with a dual-IMSI device?
    825             checkForSubscriberId();
    826             boolean startNewPeriod = true;
    827 
    828             if (start.equals(mPeriodStart) && end.equals(mPeriodEnd)) {
    829                 // same endpoints - keep collecting
    830                 if (VDBG) {
    831                     Slog.d(TAG, "same period (" + start.getTimeInMillis() + "," +
    832                             end.getTimeInMillis() +") - ammending data");
    833                 }
    834                 startNewPeriod = false;
    835             } else {
    836                 if (VDBG) {
    837                     if(start.equals(mPeriodEnd) || start.after(mPeriodEnd)) {
    838                         Slog.d(TAG, "next period (" + start.getTimeInMillis() + "," +
    839                                 end.getTimeInMillis() + ") - old end was " +
    840                                 mPeriodEnd.getTimeInMillis() + ", following");
    841                     } else {
    842                         Slog.d(TAG, "new period (" + start.getTimeInMillis() + "," +
    843                                 end.getTimeInMillis() + ") replacing old (" +
    844                                 mPeriodStart.getTimeInMillis() + "," +
    845                                 mPeriodEnd.getTimeInMillis() + ")");
    846                     }
    847                 }
    848                 synchronized (mParent) {
    849                     ++mCurrentPeriod;
    850                     if (mCurrentPeriod >= mPeriodCount) mCurrentPeriod = 0;
    851                     mPeriodRxData[mCurrentPeriod] = 0;
    852                     mPeriodTxData[mCurrentPeriod] = 0;
    853                 }
    854             }
    855             setPeriodStart(start);
    856             setPeriodEnd(end);
    857             record();
    858             return startNewPeriod;
    859         }
    860 
    861         public long getPeriodEnd() {
    862             synchronized (mParent) {
    863                 return mPeriodEnd.getTimeInMillis();
    864             }
    865         }
    866 
    867         private void setPeriodEnd(Calendar end) {
    868             synchronized (mParent) {
    869                 mPeriodEnd = end;
    870             }
    871         }
    872 
    873         public long getPeriodStart() {
    874             synchronized (mParent) {
    875                 return mPeriodStart.getTimeInMillis();
    876             }
    877         }
    878 
    879         private void setPeriodStart(Calendar start) {
    880             synchronized (mParent) {
    881                 mPeriodStart = start;
    882             }
    883         }
    884 
    885         public int getPeriodCount() {
    886             synchronized (mParent) {
    887                 return mPeriodCount;
    888             }
    889         }
    890 
    891         private void zeroData(int field) {
    892             synchronized (mParent) {
    893                 for(int period = 0; period<mPeriodCount; period++) {
    894                     mPeriodRxData[period] = 0;
    895                     mPeriodTxData[period] = 0;
    896                 }
    897                 mCurrentPeriod = 0;
    898             }
    899 
    900         }
    901 
    902         // if time moves backward accumulate all read/write that's lost into the now
    903         // otherwise time moved forward.
    904         void addData(long bytesRead, long bytesWritten) {
    905             checkForSubscriberId();
    906 
    907             synchronized (mParent) {
    908                 mPeriodRxData[mCurrentPeriod] += bytesRead;
    909                 mPeriodTxData[mCurrentPeriod] += bytesWritten;
    910             }
    911             record();
    912         }
    913 
    914         private File getDataFile() {
    915             File dataDir = Environment.getDataDirectory();
    916             File throttleDir = new File(dataDir, "system/throttle");
    917             throttleDir.mkdirs();
    918             String mImsi = mTelephonyManager.getSubscriberId();
    919             File dataFile;
    920             if (mImsi == null) {
    921                 dataFile = useMRUFile(throttleDir);
    922                 if (VDBG) Slog.v(TAG, "imsi not available yet, using " + dataFile);
    923             } else {
    924                 String imsiHash = Integer.toString(mImsi.hashCode());
    925                 dataFile = new File(throttleDir, imsiHash);
    926             }
    927             // touch the file so it's not LRU
    928             dataFile.setLastModified(System.currentTimeMillis());
    929             checkAndDeleteLRUDataFile(throttleDir);
    930             return dataFile;
    931         }
    932 
    933         // TODO - get broadcast (TelephonyIntents.ACTION_SIM_STATE_CHANGED) instead of polling
    934         private void checkForSubscriberId() {
    935             if (mImsi != null) return;
    936 
    937             mImsi = mTelephonyManager.getSubscriberId();
    938             if (mImsi == null) return;
    939 
    940             if (VDBG) Slog.d(TAG, "finally have imsi - retreiving data");
    941             retrieve();
    942         }
    943 
    944         private final static int MAX_SIMS_SUPPORTED = 3;
    945 
    946         private void checkAndDeleteLRUDataFile(File dir) {
    947             File[] files = dir.listFiles();
    948 
    949             if (files == null || files.length <= MAX_SIMS_SUPPORTED) return;
    950             if (DBG) Slog.d(TAG, "Too many data files");
    951             do {
    952                 File oldest = null;
    953                 for (File f : files) {
    954                     if ((oldest == null) || (oldest.lastModified() > f.lastModified())) {
    955                         oldest = f;
    956                     }
    957                 }
    958                 if (oldest == null) return;
    959                 if (DBG) Slog.d(TAG, " deleting " + oldest);
    960                 oldest.delete();
    961                 files = dir.listFiles();
    962             } while (files.length > MAX_SIMS_SUPPORTED);
    963         }
    964 
    965         private File useMRUFile(File dir) {
    966             File newest = null;
    967             File[] files = dir.listFiles();
    968 
    969             if (files != null) {
    970                 for (File f : files) {
    971                     if ((newest == null) || (newest.lastModified() < f.lastModified())) {
    972                         newest = f;
    973                     }
    974                 }
    975             }
    976             if (newest == null) {
    977                 newest = new File(dir, "temp");
    978             }
    979             return newest;
    980         }
    981 
    982 
    983         private static final int DATA_FILE_VERSION = 1;
    984 
    985         private void record() {
    986             // 1 int version
    987             // 1 int mPeriodCount
    988             // 13*6 long[PERIOD_COUNT] mPeriodRxData
    989             // 13*6 long[PERIOD_COUNT] mPeriodTxData
    990             // 1  int mCurrentPeriod
    991             // 13 long periodStartMS
    992             // 13 long periodEndMS
    993             // 200 chars max
    994             StringBuilder builder = new StringBuilder();
    995             builder.append(DATA_FILE_VERSION);
    996             builder.append(":");
    997             builder.append(mPeriodCount);
    998             builder.append(":");
    999             for(int i = 0; i < mPeriodCount; i++) {
   1000                 builder.append(mPeriodRxData[i]);
   1001                 builder.append(":");
   1002             }
   1003             for(int i = 0; i < mPeriodCount; i++) {
   1004                 builder.append(mPeriodTxData[i]);
   1005                 builder.append(":");
   1006             }
   1007             builder.append(mCurrentPeriod);
   1008             builder.append(":");
   1009             builder.append(mPeriodStart.getTimeInMillis());
   1010             builder.append(":");
   1011             builder.append(mPeriodEnd.getTimeInMillis());
   1012 
   1013             BufferedWriter out = null;
   1014             try {
   1015                 out = new BufferedWriter(new FileWriter(getDataFile()), 256);
   1016                 out.write(builder.toString());
   1017             } catch (IOException e) {
   1018                 Slog.e(TAG, "Error writing data file");
   1019                 return;
   1020             } finally {
   1021                 if (out != null) {
   1022                     try {
   1023                         out.close();
   1024                     } catch (Exception e) {}
   1025                 }
   1026             }
   1027         }
   1028 
   1029         private void retrieve() {
   1030             // clean out any old data first.  If we fail to read we don't want old stuff
   1031             zeroData(0);
   1032 
   1033             File f = getDataFile();
   1034             byte[] buffer;
   1035             FileInputStream s = null;
   1036             try {
   1037                 buffer = new byte[(int)f.length()];
   1038                 s = new FileInputStream(f);
   1039                 s.read(buffer);
   1040             } catch (IOException e) {
   1041                 Slog.e(TAG, "Error reading data file");
   1042                 return;
   1043             } finally {
   1044                 if (s != null) {
   1045                     try {
   1046                         s.close();
   1047                     } catch (Exception e) {}
   1048                 }
   1049             }
   1050             String data = new String(buffer);
   1051             if (data == null || data.length() == 0) {
   1052                 if (DBG) Slog.d(TAG, "data file empty");
   1053                 return;
   1054             }
   1055             String[] parsed = data.split(":");
   1056             int parsedUsed = 0;
   1057             if (parsed.length < 6) {
   1058                 Slog.e(TAG, "reading data file with insufficient length - ignoring");
   1059                 return;
   1060             }
   1061 
   1062             int periodCount;
   1063             long[] periodRxData;
   1064             long[] periodTxData;
   1065             int currentPeriod;
   1066             Calendar periodStart;
   1067             Calendar periodEnd;
   1068             try {
   1069                 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
   1070                     Slog.e(TAG, "reading data file with bad version - ignoring");
   1071                     return;
   1072                 }
   1073 
   1074                 periodCount = Integer.parseInt(parsed[parsedUsed++]);
   1075                 if (parsed.length != 5 + (2 * periodCount)) {
   1076                     Slog.e(TAG, "reading data file with bad length (" + parsed.length +
   1077                             " != " + (5 + (2 * periodCount)) + ") - ignoring");
   1078                     return;
   1079                 }
   1080                 periodRxData = new long[periodCount];
   1081                 for (int i = 0; i < periodCount; i++) {
   1082                     periodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
   1083                 }
   1084                 periodTxData = new long[periodCount];
   1085                 for (int i = 0; i < periodCount; i++) {
   1086                     periodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
   1087                 }
   1088 
   1089                 currentPeriod = Integer.parseInt(parsed[parsedUsed++]);
   1090 
   1091                 periodStart = new GregorianCalendar();
   1092                 periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
   1093                 periodEnd = new GregorianCalendar();
   1094                 periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
   1095             } catch (Exception e) {
   1096                 Slog.e(TAG, "Error parsing data file - ignoring");
   1097                 return;
   1098             }
   1099             synchronized (mParent) {
   1100                 mPeriodCount = periodCount;
   1101                 mPeriodRxData = periodRxData;
   1102                 mPeriodTxData = periodTxData;
   1103                 mCurrentPeriod = currentPeriod;
   1104                 mPeriodStart = periodStart;
   1105                 mPeriodEnd = periodEnd;
   1106             }
   1107         }
   1108 
   1109         long getPeriodRx(int which) {
   1110             synchronized (mParent) {
   1111                 if (which > mPeriodCount) return 0;
   1112                 which = mCurrentPeriod - which;
   1113                 if (which < 0) which += mPeriodCount;
   1114                 return mPeriodRxData[which];
   1115             }
   1116         }
   1117         long getPeriodTx(int which) {
   1118             synchronized (mParent) {
   1119                 if (which > mPeriodCount) return 0;
   1120                 which = mCurrentPeriod - which;
   1121                 if (which < 0) which += mPeriodCount;
   1122                 return mPeriodTxData[which];
   1123             }
   1124         }
   1125     }
   1126 
   1127     @Override
   1128     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1129         if (mContext.checkCallingOrSelfPermission(
   1130                 android.Manifest.permission.DUMP)
   1131                 != PackageManager.PERMISSION_GRANTED) {
   1132             pw.println("Permission Denial: can't dump ThrottleService " +
   1133                     "from from pid=" + Binder.getCallingPid() + ", uid=" +
   1134                     Binder.getCallingUid());
   1135             return;
   1136         }
   1137         pw.println();
   1138 
   1139         pw.println("The threshold is " + mPolicyThreshold.get() +
   1140                 ", after which you experince throttling to " +
   1141                 mPolicyThrottleValue.get() + "kbps");
   1142         pw.println("Current period is " +
   1143                 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
   1144                 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
   1145                 " seconds.");
   1146         pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
   1147         pw.println("Current Throttle Index is " + mThrottleIndex.get());
   1148         pw.println("mMaxNtpCacheAge=" + mMaxNtpCacheAge);
   1149 
   1150         for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
   1151             pw.println(" Period[" + i + "] - read:" + mRecorder.getPeriodRx(i) + ", written:" +
   1152                     mRecorder.getPeriodTx(i));
   1153         }
   1154     }
   1155 }
   1156