blob: cb46d3faf7decfba673e1e0819f9a52cc46aa4d2 [file] [log] [blame]
/*
* Copyright (C) 2014 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.tv.settings.connectivity;
import android.annotation.SuppressLint;
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.EthernetManager;
import android.net.EthernetManager.InterfaceStateListener;
import android.net.IpConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import com.android.settingslib.utils.ThreadUtils;
import com.android.tv.settings.library.network.AccessPoint;
import com.android.tv.settings.library.network.WifiTracker;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Listens for changes to the current connectivity status.
*/
public class ConnectivityListener implements WifiTracker.WifiListener {
private static final String TAG = "ConnectivityListener";
private final Context mContext;
private final Listener mListener;
private WifiTracker mWifiTracker;
private final ConnectivityManager mConnectivityManager;
private final WifiManager mWifiManager;
private final EthernetManager mEthernetManager;
private WifiNetworkListener mWifiListener;
private final BroadcastReceiver mNetworkReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateConnectivityStatus();
if (mListener != null) {
mListener.onConnectivityChange();
}
}
};
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
mCellSignalStrength = signalStrength;
mListener.onConnectivityChange();
}
};
private SignalStrength mCellSignalStrength;
private int mNetworkType;
private String mWifiSsid;
private int mWifiSignalStrength;
private final Handler mHandler = new Handler();
private LinkProperties mLateLp = null;
private final InterfaceStateListener mEthernetListener;
private final ArrayMap<String, IpConfiguration> mAvailableInterfaces = new ArrayMap<>();
private final Handler mUiHandler = ThreadUtils.getUiThreadHandler();
public ConnectivityListener(Context context, Listener listener, Lifecycle lifecycle) {
mContext = context;
mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
mWifiManager = mContext.getSystemService(WifiManager.class);
mEthernetManager = mContext.getSystemService(EthernetManager.class);
mListener = listener;
if (mWifiManager != null) {
lifecycle.addObserver(new DefaultLifecycleObserver() {
@Override
public void onStart(LifecycleOwner owner) {
start();
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
stop();
}
});
mWifiTracker = new WifiTracker(context, this, lifecycle, mWifiManager,
mConnectivityManager);
}
mEthernetListener = (iface, state, role, configuration) -> {
if (state == EthernetManager.STATE_LINK_UP) {
mAvailableInterfaces.put(iface, configuration);
} else {
mAvailableInterfaces.remove(iface);
}
updateConnectivityStatus();
if (mListener != null) {
mListener.onConnectivityChange();
}
};
updateConnectivityStatus();
}
private void start() {
updateConnectivityStatus();
IntentFilter networkIntentFilter = new IntentFilter();
networkIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
networkIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiver(mNetworkReceiver, networkIntentFilter);
final TelephonyManager telephonyManager = mContext
.getSystemService(TelephonyManager.class);
if (telephonyManager != null) {
telephonyManager.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
mConnectivityManager.registerDefaultNetworkCallback(mNetworkCallback, mHandler);
if (mEthernetManager != null) {
mEthernetManager.addInterfaceStateListener(r -> mUiHandler.post(r),
mEthernetListener);
}
}
private void stop() {
mContext.unregisterReceiver(mNetworkReceiver);
mWifiListener = null;
final TelephonyManager telephonyManager = mContext
.getSystemService(TelephonyManager.class);
if (telephonyManager != null) {
telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
if (mEthernetManager != null) {
mEthernetManager.removeInterfaceStateListener(mEthernetListener);
}
}
public void setWifiListener(WifiNetworkListener wifiListener) {
mWifiListener = wifiListener;
}
public String getWifiIpAddress() {
if (isWifiConnected()) {
Network network = mWifiManager.getCurrentNetwork();
return formatIpAddresses(network);
} else {
return "";
}
}
/**
* Return the MAC address of the currently connected Wifi AP.
*/
@SuppressLint("HardwareIds")
public String getWifiMacAddress(AccessPoint ap) {
if (isWifiConnected() && mWifiManager.getConnectionInfo() != null) {
return mWifiManager.getConnectionInfo().getMacAddress();
}
if (ap != null) {
WifiConfiguration wifiConfig = ap.getConfig();
if (wifiConfig != null
&& isWifiMacAddressRandomized(ap)) {
return wifiConfig.getRandomizedMacAddress().toString();
}
}
// return device MAC address
final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
if (macAddresses != null && macAddresses.length > 0) {
return macAddresses[0];
}
Log.e(TAG, "Unable to get MAC address");
return "";
}
/**
* Return whether the connected Wifi supports MAC address randomization.
*/
public boolean isMacAddressRandomizationSupported() {
return mWifiManager.isConnectedMacRandomizationSupported();
}
/**
* Return whether the MAC address of the currently connected Wifi AP is randomized.
*/
public int getWifiMacRandomizationSetting(AccessPoint ap) {
if (ap == null || ap.getConfig() == null) {
return WifiConfiguration.RANDOMIZATION_NONE;
}
return ap.getConfig().macRandomizationSetting;
}
/**
* Return whether the randomized MAC address is used.
*/
public boolean isWifiMacAddressRandomized(AccessPoint ap) {
return getWifiMacRandomizationSetting(ap) != WifiConfiguration.RANDOMIZATION_NONE;
}
/**
* Apply the setting of whether to use MAC address randimization.
*/
public void applyMacRandomizationSetting(AccessPoint ap, boolean enable) {
if (ap != null && ap.getConfig() != null) {
ap.getConfig().macRandomizationSetting = enable
? WifiConfiguration.RANDOMIZATION_PERSISTENT
: WifiConfiguration.RANDOMIZATION_NONE;
mWifiManager.updateNetwork(ap.getConfig());
// To activate changing, we need to reconnect network. WiFi will auto connect to
// current network after disconnect(). Only needed when this is connected network.
final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
if (wifiInfo != null && wifiInfo.getNetworkId() == ap.getConfig().networkId) {
mWifiManager.disconnect();
}
}
}
public boolean isEthernetConnected() {
return mNetworkType == ConnectivityManager.TYPE_ETHERNET;
}
public boolean isWifiConnected() {
if (mNetworkType == ConnectivityManager.TYPE_WIFI) {
return true;
} else {
if (mWifiManager != null) {
WifiInfo connectionInfo = mWifiManager.getConnectionInfo();
return connectionInfo.getNetworkId() != -1;
}
}
return false;
}
public boolean isCellConnected() {
return mNetworkType == ConnectivityManager.TYPE_MOBILE;
}
private void ensureRunningOnUiThread() {
if (mUiHandler.getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException("Not running on the UI thread: "
+ Thread.currentThread().getName());
}
}
private boolean isEthernetEnabled() {
return mEthernetManager != null;
}
/**
* Return whether Ethernet port is available.
*/
public boolean isEthernetAvailable() {
ensureRunningOnUiThread();
return isEthernetEnabled() && (mAvailableInterfaces.size() > 0);
}
private Network getFirstEthernet() {
final Network[] networks = mConnectivityManager.getAllNetworks();
for (final Network network : networks) {
NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
return network;
}
}
return null;
}
private String formatIpAddresses(Network network) {
final LinkProperties linkProperties = mConnectivityManager.getLinkProperties(network);
if (linkProperties == null) {
return null;
}
final StringBuilder sb = new StringBuilder();
boolean gotAddress = false;
for (LinkAddress linkAddress : linkProperties.getLinkAddresses()) {
if (gotAddress) {
sb.append("\n");
}
sb.append(linkAddress.getAddress().getHostAddress());
gotAddress = true;
}
if (gotAddress) {
return sb.toString();
} else {
return null;
}
}
/**
* Returns the formatted IP addresses of the Ethernet connection or null
* if none available.
*/
public String getEthernetIpAddress() {
final Network network = getFirstEthernet();
if (network == null) {
return null;
}
return formatIpAddresses(network);
}
/**
* Get the current Ethernet interface name.
*/
public String getEthernetInterfaceName() {
ensureRunningOnUiThread();
if (mAvailableInterfaces.size() == 0) return null;
return mAvailableInterfaces.keyAt(0);
}
/**
* Get the current IP configuration of Ethernet interface.
*/
public IpConfiguration getEthernetIpConfiguration() {
ensureRunningOnUiThread();
if (mAvailableInterfaces.size() == 0) return null;
return mAvailableInterfaces.valueAt(0);
}
public int getWifiSignalStrength(int maxLevel) {
if (mWifiManager != null) {
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
return WifiManager.calculateSignalLevel(wifiInfo.getRssi(), maxLevel);
}
return 0;
}
public int getCellSignalStrength() {
if (isCellConnected() && mCellSignalStrength != null) {
return mCellSignalStrength.getLevel();
} else {
return 0;
}
}
/**
* Return a list of wifi networks. Ensure that if a wifi network is connected that it appears
* as the first item on the list.
*/
public List<AccessPoint> getAvailableNetworks() {
return mWifiTracker == null ? new ArrayList<>() : mWifiTracker.getAccessPoints();
}
public boolean isWifiEnabledOrEnabling() {
return mWifiManager != null
&& (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED
|| mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLING);
}
public void setWifiEnabled(boolean enable) {
if (mWifiManager != null) {
mWifiManager.setWifiEnabled(enable);
}
}
private void updateConnectivityStatus() {
NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
if (networkInfo == null) {
mNetworkType = ConnectivityManager.TYPE_NONE;
} else {
switch (networkInfo.getType()) {
case ConnectivityManager.TYPE_WIFI: {
if (mWifiManager == null) {
break;
}
// Determine if this is
// an open or secure wifi connection.
mNetworkType = ConnectivityManager.TYPE_WIFI;
String ssid = getSsid();
if (!TextUtils.equals(mWifiSsid, ssid)) {
mWifiSsid = ssid;
}
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
// Calculate the signal strength.
int signalStrength;
if (wifiInfo != null) {
// Calculate the signal strength between 0 and 3.
signalStrength = WifiManager.calculateSignalLevel(wifiInfo.getRssi(), 4);
} else {
signalStrength = 0;
}
if (mWifiSignalStrength != signalStrength) {
mWifiSignalStrength = signalStrength;
}
break;
}
case ConnectivityManager.TYPE_ETHERNET:
mNetworkType = ConnectivityManager.TYPE_ETHERNET;
break;
case ConnectivityManager.TYPE_MOBILE:
mNetworkType = ConnectivityManager.TYPE_MOBILE;
break;
default:
mNetworkType = ConnectivityManager.TYPE_NONE;
break;
}
}
}
@Override
public void onWifiStateChanged(int state) {
updateConnectivityStatus();
if (mListener != null) {
mListener.onConnectivityChange();
}
}
@Override
public void onConnectedChanged() {
updateConnectivityStatus();
if (mListener != null) {
mListener.onConnectivityChange();
}
}
@Override
public void onAccessPointsChanged() {
if (mWifiListener != null) {
mWifiListener.onWifiListChanged();
}
}
public void onIpAddrChanged() {
if (mListener != null) {
mListener.onConnectivityChange();
}
}
public interface Listener {
void onConnectivityChange();
}
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
if (mLateLp != null) {
if (!Objects.equals(mLateLp, lp)) {
if ((lp.hasIpv4Address() && !mLateLp.hasIpv4Address())
|| (lp.hasGlobalIpv6Address() && !mLateLp.hasGlobalIpv6Address())) {
onIpAddrChanged();
}
}
}
mLateLp = lp;
}
};
public interface WifiNetworkListener {
void onWifiListChanged();
}
/**
* Get the SSID of current connected network.
*
* @return SSID
*/
public String getSsid() {
if (mWifiManager == null) {
return null;
}
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
// Find the SSID of network.
String ssid = null;
if (wifiInfo != null) {
ssid = wifiInfo.getSSID();
if (ssid != null) {
ssid = sanitizeSsid(ssid);
}
}
return ssid;
}
public static String sanitizeSsid(@Nullable String string) {
return removeDoubleQuotes(string);
}
public static String removeDoubleQuotes(@Nullable String string) {
if (string == null) return null;
final int length = string.length();
if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
return string.substring(1, length - 1);
}
return string;
}
}