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.os.Handler;
     30 import android.os.Message;
     31 import android.util.Log;
     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 /* Polls for traffic stats and notifies the clients */
     40 final class WifiTrafficPoller {
     41 
     42     private boolean DBG = false;
     43     private boolean VDBG = false;
     44 
     45     private final String TAG = "WifiTrafficPoller";
     46     /**
     47      * Interval in milliseconds between polling for traffic
     48      * statistics
     49      */
     50     private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
     51 
     52     private static final int ENABLE_TRAFFIC_STATS_POLL  = 1;
     53     private static final int TRAFFIC_STATS_POLL         = 2;
     54     private static final int ADD_CLIENT                 = 3;
     55     private static final int REMOVE_CLIENT              = 4;
     56 
     57     private boolean mEnableTrafficStatsPoll = false;
     58     private int mTrafficStatsPollToken = 0;
     59     private long mTxPkts;
     60     private long mRxPkts;
     61     /* Tracks last reported data activity */
     62     private int mDataActivity;
     63 
     64     private final List<Messenger> mClients = new ArrayList<Messenger>();
     65     // err on the side of updating at boot since screen on broadcast may be missed
     66     // the first time
     67     private AtomicBoolean mScreenOn = new AtomicBoolean(true);
     68     private final TrafficHandler mTrafficHandler;
     69     private NetworkInfo mNetworkInfo;
     70     private final String mInterface;
     71 
     72     WifiTrafficPoller(Context context, String iface) {
     73         mInterface = iface;
     74         mTrafficHandler = new TrafficHandler();
     75 
     76         IntentFilter filter = new IntentFilter();
     77         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
     78         filter.addAction(Intent.ACTION_SCREEN_OFF);
     79         filter.addAction(Intent.ACTION_SCREEN_ON);
     80 
     81         context.registerReceiver(
     82                 new BroadcastReceiver() {
     83                     @Override
     84                     public void onReceive(Context context, Intent intent) {
     85                         if (intent.getAction().equals(
     86                                 WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
     87                             mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
     88                                     WifiManager.EXTRA_NETWORK_INFO);
     89                         } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
     90                             mScreenOn.set(false);
     91                         } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
     92                             mScreenOn.set(true);
     93                         }
     94                         evaluateTrafficStatsPolling();
     95                     }
     96                 }, filter);
     97     }
     98 
     99     void addClient(Messenger client) {
    100         Message.obtain(mTrafficHandler, ADD_CLIENT, client).sendToTarget();
    101     }
    102 
    103     void removeClient(Messenger client) {
    104         Message.obtain(mTrafficHandler, REMOVE_CLIENT, client).sendToTarget();
    105     }
    106 
    107     void enableVerboseLogging(int verbose) {
    108         if (verbose > 0 ) {
    109             DBG = true;
    110         } else {
    111             DBG = false;
    112         }
    113     }
    114 
    115     private class TrafficHandler extends Handler {
    116         public void handleMessage(Message msg) {
    117             switch (msg.what) {
    118                 case ENABLE_TRAFFIC_STATS_POLL:
    119                     mEnableTrafficStatsPoll = (msg.arg1 == 1);
    120                     if (DBG) {
    121                         Log.e(TAG, "ENABLE_TRAFFIC_STATS_POLL "
    122                                 + mEnableTrafficStatsPoll + " Token "
    123                                 + Integer.toString(mTrafficStatsPollToken));
    124                     }
    125                     mTrafficStatsPollToken++;
    126                     if (mEnableTrafficStatsPoll) {
    127                         notifyOnDataActivity();
    128                         sendMessageDelayed(Message.obtain(this, TRAFFIC_STATS_POLL,
    129                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
    130                     }
    131                     break;
    132                 case TRAFFIC_STATS_POLL:
    133                     if (VDBG) {
    134                         Log.e(TAG, "TRAFFIC_STATS_POLL "
    135                                 + mEnableTrafficStatsPoll + " Token "
    136                                 + Integer.toString(mTrafficStatsPollToken)
    137                                 + " num clients " + mClients.size());
    138                     }
    139                     if (msg.arg1 == mTrafficStatsPollToken) {
    140                         notifyOnDataActivity();
    141                         sendMessageDelayed(Message.obtain(this, TRAFFIC_STATS_POLL,
    142                                 mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
    143                     }
    144                     break;
    145                 case ADD_CLIENT:
    146                     mClients.add((Messenger) msg.obj);
    147                     if (DBG) {
    148                         Log.e(TAG, "ADD_CLIENT: "
    149                                 + Integer.toString(mClients.size()));
    150                     }
    151                     break;
    152                 case REMOVE_CLIENT:
    153                     mClients.remove(msg.obj);
    154                     break;
    155             }
    156 
    157         }
    158     }
    159 
    160     private void evaluateTrafficStatsPolling() {
    161         Message msg;
    162         if (mNetworkInfo == null) return;
    163         if (mNetworkInfo.getDetailedState() == CONNECTED && mScreenOn.get()) {
    164             msg = Message.obtain(mTrafficHandler,
    165                     ENABLE_TRAFFIC_STATS_POLL, 1, 0);
    166         } else {
    167             msg = Message.obtain(mTrafficHandler,
    168                     ENABLE_TRAFFIC_STATS_POLL, 0, 0);
    169         }
    170         msg.sendToTarget();
    171     }
    172 
    173     private void notifyOnDataActivity() {
    174         long sent, received;
    175         long preTxPkts = mTxPkts, preRxPkts = mRxPkts;
    176         int dataActivity = WifiManager.DATA_ACTIVITY_NONE;
    177 
    178         mTxPkts = TrafficStats.getTxPackets(mInterface);
    179         mRxPkts = TrafficStats.getRxPackets(mInterface);
    180 
    181         if (VDBG) {
    182             Log.e(TAG, " packet count Tx="
    183                     + Long.toString(mTxPkts)
    184                     + " Rx="
    185                     + Long.toString(mRxPkts));
    186         }
    187 
    188         if (preTxPkts > 0 || preRxPkts > 0) {
    189             sent = mTxPkts - preTxPkts;
    190             received = mRxPkts - preRxPkts;
    191             if (sent > 0) {
    192                 dataActivity |= WifiManager.DATA_ACTIVITY_OUT;
    193             }
    194             if (received > 0) {
    195                 dataActivity |= WifiManager.DATA_ACTIVITY_IN;
    196             }
    197 
    198             if (dataActivity != mDataActivity && mScreenOn.get()) {
    199                 mDataActivity = dataActivity;
    200                 if (DBG) {
    201                     Log.e(TAG, "notifying of data activity "
    202                             + Integer.toString(mDataActivity));
    203                 }
    204                 for (Messenger client : mClients) {
    205                     Message msg = Message.obtain();
    206                     msg.what = WifiManager.DATA_ACTIVITY_NOTIFICATION;
    207                     msg.arg1 = mDataActivity;
    208                     try {
    209                         client.send(msg);
    210                     } catch (RemoteException e) {
    211                         // Failed to reach, skip
    212                         // Client removal is handled in WifiService
    213                     }
    214                 }
    215             }
    216         }
    217     }
    218 
    219     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    220         pw.println("mEnableTrafficStatsPoll " + mEnableTrafficStatsPoll);
    221         pw.println("mTrafficStatsPollToken " + mTrafficStatsPollToken);
    222         pw.println("mTxPkts " + mTxPkts);
    223         pw.println("mRxPkts " + mRxPkts);
    224         pw.println("mDataActivity " + mDataActivity);
    225     }
    226 
    227 }
    228