Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2013 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.wifi;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.net.NetworkInfo;
     24 import static android.net.NetworkInfo.DetailedState.CONNECTED;
     25 import android.net.TrafficStats;
     26 import android.net.wifi.WifiManager;
     27 import android.os.Messenger;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 import android.os.Handler;
     31 import android.os.Message;
     32 
     33 import java.io.FileDescriptor;
     34 import java.io.PrintWriter;
     35 import java.util.ArrayList;
     36 import java.util.List;
     37 import java.util.concurrent.atomic.AtomicBoolean;
     38 
     39 import com.android.internal.util.AsyncChannel;
     40 
     41 /* Polls for traffic stats and notifies the clients */
     42 final class WifiTrafficPoller {
     43     /**
     44      * Interval in milliseconds between polling for traffic
     45      * statistics
     46      */
     47     private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
     48 
     49     private static final int ENABLE_TRAFFIC_STATS_POLL  = 1;
     50     private static final int TRAFFIC_STATS_POLL         = 2;
     51     private static final int ADD_CLIENT                 = 3;
     52     private static final int REMOVE_CLIENT              = 4;
     53 
     54     private boolean mEnableTrafficStatsPoll = false;
     55     private int mTrafficStatsPollToken = 0;
     56     private long mTxPkts;
     57     private long mRxPkts;
     58     /* Tracks last reported data activity */
     59     private int mDataActivity;
     60 
     61     private final List<Messenger> mClients = new ArrayList<Messenger>();
     62     // err on the side of updating at boot since screen on broadcast may be missed
     63     // the first time
     64     private AtomicBoolean mScreenOn = new AtomicBoolean(true);
     65     private final TrafficHandler mTrafficHandler;
     66     private NetworkInfo mNetworkInfo;
     67     private final String mInterface;
     68 
     69     WifiTrafficPoller(Context context, String iface) {
     70         mInterface = iface;
     71         mTrafficHandler = new TrafficHandler();
     72 
     73         IntentFilter filter = new IntentFilter();
     74         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
     75         filter.addAction(Intent.ACTION_SCREEN_OFF);
     76         filter.addAction(Intent.ACTION_SCREEN_ON);
     77 
     78         context.registerReceiver(
     79                 new BroadcastReceiver() {
     80                     @Override
     81                     public void onReceive(Context context, Intent intent) {
     82                         if (intent.getAction().equals(
     83                                 WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
     84                             mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
     85                                     WifiManager.EXTRA_NETWORK_INFO);
     86                         } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
     87                             mScreenOn.set(false);
     88                         } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
     89                             mScreenOn.set(true);
     90                         }
     91                         evaluateTrafficStatsPolling();
     92                     }
     93                 }, filter);
     94     }
     95 
     96     void addClient(Messenger client) {
     97         Message.obtain(mTrafficHandler, ADD_CLIENT, client).sendToTarget();
     98     }
     99 
    100     void removeClient(Messenger client) {
    101         Message.obtain(mTrafficHandler, REMOVE_CLIENT, client).sendToTarget();
    102     }
    103 
    104 
    105     private class TrafficHandler extends Handler {
    106         public void handleMessage(Message msg) {
    107             switch (msg.what) {
    108                 case ENABLE_TRAFFIC_STATS_POLL:
    109                     mEnableTrafficStatsPoll = (msg.arg1 == 1);
    110                     mTrafficStatsPollToken++;
    111                     if (mEnableTrafficStatsPoll) {
    112                         notifyOnDataActivity();
    113                         sendMessageDelayed(Message.obtain(this, TRAFFIC_STATS_POLL,
    114                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
    115                     }
    116                     break;
    117                 case TRAFFIC_STATS_POLL:
    118                     if (msg.arg1 == mTrafficStatsPollToken) {
    119                         notifyOnDataActivity();
    120                         sendMessageDelayed(Message.obtain(this, TRAFFIC_STATS_POLL,
    121                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
    122                     }
    123                     break;
    124                 case ADD_CLIENT:
    125                     mClients.add((Messenger) msg.obj);
    126                     break;
    127                 case REMOVE_CLIENT:
    128                     mClients.remove(msg.obj);
    129                     break;
    130             }
    131 
    132         }
    133     }
    134 
    135     private void evaluateTrafficStatsPolling() {
    136         Message msg;
    137         if (mNetworkInfo == null) return;
    138         if (mNetworkInfo.getDetailedState() == CONNECTED && mScreenOn.get()) {
    139             msg = Message.obtain(mTrafficHandler,
    140                     ENABLE_TRAFFIC_STATS_POLL, 1, 0);
    141         } else {
    142             msg = Message.obtain(mTrafficHandler,
    143                     ENABLE_TRAFFIC_STATS_POLL, 0, 0);
    144         }
    145         msg.sendToTarget();
    146     }
    147 
    148     private void notifyOnDataActivity() {
    149         long sent, received;
    150         long preTxPkts = mTxPkts, preRxPkts = mRxPkts;
    151         int dataActivity = WifiManager.DATA_ACTIVITY_NONE;
    152 
    153         mTxPkts = TrafficStats.getTxPackets(mInterface);
    154         mRxPkts = TrafficStats.getRxPackets(mInterface);
    155 
    156         if (preTxPkts > 0 || preRxPkts > 0) {
    157             sent = mTxPkts - preTxPkts;
    158             received = mRxPkts - preRxPkts;
    159             if (sent > 0) {
    160                 dataActivity |= WifiManager.DATA_ACTIVITY_OUT;
    161             }
    162             if (received > 0) {
    163                 dataActivity |= WifiManager.DATA_ACTIVITY_IN;
    164             }
    165 
    166             if (dataActivity != mDataActivity && mScreenOn.get()) {
    167                 mDataActivity = dataActivity;
    168                 for (Messenger client : mClients) {
    169                     Message msg = Message.obtain();
    170                     msg.what = WifiManager.DATA_ACTIVITY_NOTIFICATION;
    171                     msg.arg1 = mDataActivity;
    172                     try {
    173                         client.send(msg);
    174                     } catch (RemoteException e) {
    175                         // Failed to reach, skip
    176                         // Client removal is handled in WifiService
    177                     }
    178                 }
    179             }
    180         }
    181     }
    182 
    183     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    184         pw.println("mEnableTrafficStatsPoll " + mEnableTrafficStatsPoll);
    185         pw.println("mTrafficStatsPollToken " + mTrafficStatsPollToken);
    186         pw.println("mTxPkts " + mTxPkts);
    187         pw.println("mRxPkts " + mRxPkts);
    188         pw.println("mDataActivity " + mDataActivity);
    189     }
    190 
    191 }
    192