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.Context;
20 import android.net.wifi.ITrafficStateCallback;
21 import android.net.wifi.WifiManager;
22 import android.os.RemoteCallbackList;
23 import android.os.RemoteException;
24 import android.util.Log;
25 import android.util.SparseBooleanArray;
26 
27 import com.android.wifi.resources.R;
28 
29 import java.io.FileDescriptor;
30 import java.io.PrintWriter;
31 
32 /**
33  * Polls for traffic stats and notifies the clients
34  */
35 public class WifiTrafficPoller {
36     private static final String TAG = "WifiTrafficPoller";
37 
38     private long mTxPkts = 0;
39     private long mRxPkts = 0;
40 
41     private int mLastActivity = -1;
42 
43     private final SparseBooleanArray mCallbackFirstInvocationTracker = new SparseBooleanArray();
44     private final RemoteCallbackList<ITrafficStateCallback> mRegisteredCallbacks;
45     private final Context mContext;
46 
WifiTrafficPoller(Context context)47     public WifiTrafficPoller(Context context) {
48         mContext = context;
49         mRegisteredCallbacks = new RemoteCallbackList<>();
50     }
51 
52     /**
53      * Add a new callback to the traffic poller.
54      */
addCallback(ITrafficStateCallback callback)55     public void addCallback(ITrafficStateCallback callback) {
56         mCallbackFirstInvocationTracker.put(callback.hashCode(), true);
57         if (!mRegisteredCallbacks.register(callback)) {
58             Log.e(TAG, "Failed to add callback");
59         }
60     }
61 
62     /**
63      * Remove an existing callback from the traffic poller.
64      */
removeCallback(ITrafficStateCallback callback)65     public void removeCallback(ITrafficStateCallback callback) {
66         mRegisteredCallbacks.unregister(callback);
67         mCallbackFirstInvocationTracker.delete(callback.hashCode());
68     }
69 
70     /**
71      * Notifies clients of data activity if the activity changed since the last update.
72      */
notifyOnDataActivity(long newTxPkts, long newRxPkts)73     public void notifyOnDataActivity(long newTxPkts, long newRxPkts) {
74         if (newTxPkts <= 0 && newRxPkts <= 0) {
75             return;
76         }
77 
78         long sent = newTxPkts - mTxPkts;
79         long received = newRxPkts - mRxPkts;
80         int dataActivity = WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
81         int txPacketThreshold = mContext.getResources().getInteger(
82                 R.integer.config_wifiTrafficPollerTxPacketThreshold);
83         int rxPacketThreshold = mContext.getResources().getInteger(
84                 R.integer.config_wifiTrafficPollerRxPacketThreshold);
85 
86         if (sent > txPacketThreshold) {
87             dataActivity |= WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
88         }
89         if (received > rxPacketThreshold) {
90             dataActivity |= WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
91         }
92 
93         int itemCount = mRegisteredCallbacks.beginBroadcast();
94         for (int i = 0; i < itemCount; i++) {
95             ITrafficStateCallback callback = mRegisteredCallbacks.getBroadcastItem(i);
96             int callbackIdentifier = callback.hashCode();
97             if (mCallbackFirstInvocationTracker.get(callbackIdentifier)
98                     || dataActivity != mLastActivity) {
99                 mCallbackFirstInvocationTracker.put(callbackIdentifier, false);
100                 try {
101                     callback.onStateChanged(dataActivity);
102                 } catch (RemoteException e) {
103                     // Failed to reach, skip
104                     // Client removal is handled in WifiService
105                 }
106             }
107         }
108         mRegisteredCallbacks.finishBroadcast();
109 
110         mTxPkts = newTxPkts;
111         mRxPkts = newRxPkts;
112         mLastActivity = dataActivity;
113     }
114 
115     /**
116      * Dump method for traffic poller.
117      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)118     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
119         pw.println("mTxPkts " + mTxPkts);
120         pw.println("mRxPkts " + mRxPkts);
121         pw.println("mLastActivity " + mLastActivity);
122         pw.println("mRegisteredCallbacks " + mRegisteredCallbacks.getRegisteredCallbackCount());
123     }
124 }
125