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.annotation.NonNull;
20 import android.net.wifi.ITrafficStateCallback;
21 import android.net.wifi.WifiManager;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.os.RemoteException;
25 import android.util.Log;
26 
27 import com.android.server.wifi.util.ExternalCallbackTracker;
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 static class CallbackWrapper {
44         public final ITrafficStateCallback callback;
45         /**
46          * On the first invocation, the callback is invoked no matter if the data activity changed
47          * or not.
48          */
49         public boolean isFirstInvocation = true;
50 
CallbackWrapper(ITrafficStateCallback callback)51         CallbackWrapper(ITrafficStateCallback callback) {
52             this.callback = callback;
53         }
54     }
55 
56     private final ExternalCallbackTracker<CallbackWrapper> mRegisteredCallbacks;
57 
WifiTrafficPoller(@onNull Handler handler)58     public WifiTrafficPoller(@NonNull Handler handler) {
59         mRegisteredCallbacks = new ExternalCallbackTracker<>(handler);
60     }
61 
62     /**
63      * Add a new callback to the traffic poller.
64      */
addCallback(IBinder binder, ITrafficStateCallback callback, int callbackId)65     public void addCallback(IBinder binder, ITrafficStateCallback callback, int callbackId) {
66         if (!mRegisteredCallbacks.add(binder, new CallbackWrapper(callback), callbackId)) {
67             Log.e(TAG, "Failed to add callback");
68         }
69     }
70 
71     /**
72      * Remove an existing callback from the traffic poller.
73      */
removeCallback(int callbackId)74     public void removeCallback(int callbackId) {
75         mRegisteredCallbacks.remove(callbackId);
76     }
77 
78     /**
79      * Notifies clients of data activity if the activity changed since the last update.
80      */
notifyOnDataActivity(long newTxPkts, long newRxPkts)81     public void notifyOnDataActivity(long newTxPkts, long newRxPkts) {
82         if (newTxPkts <= 0 && newRxPkts <= 0) {
83             return;
84         }
85 
86         long sent = newTxPkts - mTxPkts;
87         long received = newRxPkts - mRxPkts;
88         int dataActivity = WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
89         if (sent > 0) {
90             dataActivity |= WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
91         }
92         if (received > 0) {
93             dataActivity |= WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
94         }
95 
96         for (CallbackWrapper wrapper : mRegisteredCallbacks.getCallbacks()) {
97             // if this callback hasn't been triggered before, or the data activity changed,
98             // notify the callback
99             if (wrapper.isFirstInvocation || dataActivity != mLastActivity) {
100                 wrapper.isFirstInvocation = false;
101                 try {
102                     wrapper.callback.onStateChanged(dataActivity);
103                 } catch (RemoteException e) {
104                     // Failed to reach, skip
105                     // Client removal is handled in WifiService
106                 }
107             }
108         }
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.getNumCallbacks());
123     }
124 }
125