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