/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.util.Log;
import android.util.Pair;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Intended Purpose/Behavior of the class upon completion:
* Essentially this class automates a user toggling 'Airplane Mode' when WiFi "won't work".
* IF each available saved network has failed connecting more times than the FAILURE_THRESHOLD
* THEN Watchdog will restart Supplicant, wifi driver and return WifiStateMachine to InitialState.
*
*/
public class WifiLastResortWatchdog {
private static final String TAG = "WifiLastResortWatchdog";
private static final boolean VDBG = false;
/**
* Cached WifiConfigurations of available networks seen within MAX_BSSID_AGE scan results
* Key:BSSID, Value:Counters of failure types
*/
private Map mRecentAvailableNetworks = new HashMap<>();
// Maximum number of scan results received since we last saw a BSSID.
// If it is not seen before this limit is reached, the network is culled
public static final int MAX_BSSID_AGE = 10;
/**
* Refreshes recentAvailableNetworks with the latest available networks
* Adds new networks, removes old ones that have timed out. Should be called after Wifi
* framework decides what networks it is potentially connecting to.
* @param availableNetworkFailureCounts ScanDetail & Config list of potential connection
* candidates
*/
public void updateAvailableNetworks(
List> availableNetworkFailureCounts) {
// Add new networks to mRecentAvailableNetworks
if (availableNetworkFailureCounts != null) {
for (Pair pair : availableNetworkFailureCounts) {
ScanResult scanResult = pair.first.getScanResult();
if (scanResult == null) continue;
String key = scanResult.BSSID;
// Cache the scanResult & WifiConfig
AvailableNetworkFailureCount availableNetworkFailureCount =
mRecentAvailableNetworks.get(key);
if (availableNetworkFailureCount != null) {
// We've already cached this, refresh timeout count & config
availableNetworkFailureCount.config = pair.second;
} else {
// New network is available
availableNetworkFailureCount = new AvailableNetworkFailureCount(pair.second);
availableNetworkFailureCount.Ssid = pair.first.getSSID();
}
// If we saw a network, set its Age to -1 here, next incrementation will set it to 0
availableNetworkFailureCount.age = -1;
mRecentAvailableNetworks.put(key, availableNetworkFailureCount);
}
}
// Iterate through available networks updating timeout counts & removing networks.
Iterator> it =
mRecentAvailableNetworks.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
if (entry.getValue().age < MAX_BSSID_AGE - 1) {
entry.getValue().age++;
} else {
it.remove();
}
}
if (VDBG) Log.v(TAG, toString());
}
/**
* Gets the buffer of recently available networks
*/
Map getRecentAvailableNetworks() {
return mRecentAvailableNetworks;
}
/**
* Prints all networks & counts within mRecentAvailableNetworks to string
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("WifiLastResortWatchdog: " + mRecentAvailableNetworks.size() + " networks...");
for (Map.Entry entry
: mRecentAvailableNetworks.entrySet()) {
sb.append("\n " + entry.getKey() + ": " + entry.getValue());
}
return sb.toString();
}
static class AvailableNetworkFailureCount {
/**
* WifiConfiguration associated with this network. Can be null for Ephemeral networks
*/
public WifiConfiguration config;
/**
* SSID of the network (from ScanDetail)
*/
public String Ssid = "";
/**
* Number of times network has failed for this reason
*/
public int associationRejection = 0;
/**
* Number of times network has failed for this reason
*/
public int authenticationRejection = 0;
/**
* Number of times network has failed for this reason
*/
public int dhcpFailure = 0;
/**
* Number of scanResults since this network was last seen
*/
public int age = 0;
AvailableNetworkFailureCount(WifiConfiguration config) {
config = config;
}
void resetCounts() {
associationRejection = 0;
authenticationRejection = 0;
dhcpFailure = 0;
}
public String toString() {
return Ssid + ", HasEverConnected: " + ((config != null)
? config.getNetworkSelectionStatus().getHasEverConnected() : false)
+ ", Failures: {"
+ "Assoc: " + associationRejection
+ ", Auth: " + authenticationRejection
+ ", Dhcp: " + dhcpFailure
+ "}"
+ ", Age: " + age;
}
}
}