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