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