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