blob: 258f60e0259833366477b167b2f2a601a8ba9057 [file] [log] [blame]
/*
* Copyright (C) 2018 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.car.settings.wifi.details;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import com.android.car.settings.common.Logger;
import com.android.settingslib.wifi.AccessPoint;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Provides Wifi related info.
*/
public class WifiInfoProvider implements DefaultLifecycleObserver {
private static final Logger LOG = new Logger(WifiInfoProvider.class);
/**
* Observers of Wifi info changes.
*/
interface Listener {
/**
* Called when NetworkInfo and/or WifiInfo is changed.
*/
void onWifiChanged(NetworkInfo networkInfo, WifiInfo wifiInfo);
/**
* Called when network is lost.
*/
void onLost(Network network);
/**
* Called when NetworkCapabilities changed.
*/
void onCapabilitiesChanged(Network network, NetworkCapabilities nc);
/**
* Called when WifiConfiguration changed.
*/
void onWifiConfigurationChanged(WifiConfiguration wifiConfiguration,
NetworkInfo networkInfo, WifiInfo wifiInfo);
/**
* Called when LinkProperties changed.
*/
void onLinkPropertiesChanged(Network network, LinkProperties lp);
}
private final AccessPoint mAccessPoint;
private final Context mContext;
private final Set<Listener> mListeners = new HashSet<>();
private final WifiManager mWifiManager;
private final IntentFilter mFilter;
private final ConnectivityManager mConnectivityManager;
private final Handler mHandler;
@Nullable
private final Network mNetwork;
@Nullable
private LinkProperties mLinkProperties;
@Nullable
private NetworkCapabilities mNetworkCapabilities;
@Nullable
private WifiConfiguration mWifiConfig;
@Nullable
private NetworkInfo mNetworkInfo;
private WifiInfo mWifiInfo;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION:
LOG.d("Wifi Config changed.");
updateMatchingWifiConfig();
updateInfo();
for (Listener listener : getListenersCopy()) {
listener.onWifiConfigurationChanged(mWifiConfig, mNetworkInfo, mWifiInfo);
}
break;
case WifiManager.NETWORK_STATE_CHANGED_ACTION:
case WifiManager.RSSI_CHANGED_ACTION:
LOG.d("wifi changed.");
updateInfo();
for (Listener listener : getListenersCopy()) {
listener.onWifiChanged(mNetworkInfo, mWifiInfo);
}
break;
}
}
private void updateMatchingWifiConfig() {
// use getPrivilegedConfiguredNetworks() to get Passpoint & other ephemeral networks
for (WifiConfiguration wifiConfiguration :
mWifiManager.getPrivilegedConfiguredNetworks()) {
if (mAccessPoint.matches(wifiConfiguration)) {
mWifiConfig = wifiConfiguration;
break;
}
}
}
};
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities().addTransportType(TRANSPORT_WIFI).build();
// Must be run on the UI thread since it directly manipulates UI state.
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
if (network.equals(mNetwork) && !lp.equals(mLinkProperties)) {
mLinkProperties = lp;
for (Listener listener : getListenersCopy()) {
listener.onLinkPropertiesChanged(mNetwork, mLinkProperties);
}
}
}
private boolean hasCapabilityChanged(NetworkCapabilities nc, int cap) {
// If this is the first time we get NetworkCapabilities, report that something changed.
if (mNetworkCapabilities == null) return true;
// nc can never be null, see ConnectivityService#callCallbackForRequest.
return mNetworkCapabilities.hasCapability(cap) != nc.hasCapability(cap);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
// If the network just validated or lost Internet access, refresh network state.
if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) {
if (hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED)
|| hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL)) {
mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
}
mNetworkCapabilities = nc;
for (Listener listener : getListenersCopy()) {
listener.onCapabilitiesChanged(mNetwork, mNetworkCapabilities);
}
}
}
@Override
public void onLost(Network network) {
if (network.equals(mNetwork)) {
for (Listener listener : getListenersCopy()) {
listener.onLost(mNetwork);
}
}
}
};
WifiInfoProvider(Context context, AccessPoint accessPoint) {
mContext = context;
mAccessPoint = accessPoint;
mHandler = new Handler(Looper.getMainLooper());
mConnectivityManager =
mContext.getSystemService(ConnectivityManager.class);
mWifiManager = mContext.getSystemService(WifiManager.class);
mWifiConfig = mAccessPoint.getConfig();
mNetwork = mWifiManager.getCurrentNetwork();
mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork);
mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork);
updateInfo();
mFilter = new IntentFilter();
mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
}
/**
* Adds a {@link Listener} to be notified of changes to Wi-Fi information.
*/
public void addListener(Listener listener) {
mListeners.add(listener);
}
/**
* Removes a {@link Listener} so that it no longer receives updates.
*/
public void removeListener(Listener listener) {
mListeners.remove(listener);
}
/**
* Removes all {@link Listener} instances added via {@link #addListener(Listener)}.
*/
public void clearListeners() {
mListeners.clear();
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
LOG.d("onStart");
mContext.registerReceiver(mReceiver, mFilter);
mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback,
mHandler);
for (Listener listener : getListenersCopy()) {
listener.onWifiChanged(mNetworkInfo, mWifiInfo);
}
LOG.d("Done onStart");
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
LOG.d("onStop");
mContext.unregisterReceiver(mReceiver);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
LOG.d("done onStop");
}
/**
* Returns a {@link NetworkInfo} object containing connection status information about the
* current Wi-Fi network or {@code null} if no network is available.
*/
@Nullable
public NetworkInfo getNetworkInfo() {
return mNetworkInfo;
}
/**
* Returns dynamic information about the current Wi-Fi connection.
*/
public WifiInfo getWifiInfo() {
return mWifiInfo;
}
/**
* Returns the {@link Network} object of the current Wi-Fi network or {@code null} if no network
* is available.
*/
@Nullable
public Network getNetwork() {
return mNetwork;
}
/**
* Returns the {@link NetworkCapabilities} of the current Wi-Fi network or {@code null} if no
* network is available.
*/
@Nullable
public NetworkCapabilities getNetworkCapabilities() {
return mNetworkCapabilities;
}
/**
* Returns the {@link WifiConfiguration} of the current access point or {@code null} if
* unavailable.
*/
@Nullable
public WifiConfiguration getNetworkConfiguration() {
return mWifiConfig;
}
/**
* Returns the {@link LinkProperties} of the current Wi-Fi newtork or {@link null} if no network
* is available.
*/
@Nullable
public LinkProperties getLinkProperties() {
return mLinkProperties;
}
private Collection<Listener> getListenersCopy() {
return Collections.unmodifiableCollection(mListeners);
}
private void updateInfo() {
// No need to fetch LinkProperties and NetworkCapabilities, they are updated by the
// callbacks.
mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
mWifiInfo = mWifiManager.getConnectionInfo();
}
}