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