/*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
* the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2014 Digi International Inc., All Rights Reserved.
*/
package com.digi.android.wva.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Provides an abstraction over the Android networking APIs, for the purposes
* of adding the method {@link #shouldBeAllowedToConnect(Context)},
* which is used to indicate whether the Android device on which the application
* is running is either connected to a Wi-Fi network, or serving as a mobile
* Wi-Fi hotspot.
*/
public class NetworkUtils {
/**
* Because the Android emulator lacks support for using Wi-Fi connections,
* we need a way to let NetworkUtils bypass the actual network-connectivity-
* checking and allow the app, while running on the emulator, to connect
* to devices regardless of Android-specific network state. To that end,
* we know that the Android emulator, by default, has the build property
* ro.product.name set to "sdk" (or "sdk_x86" for HAXM builds), and presumably no
* real device will have that model name. Knowing that, we can simply check
* whether android.os.Build.PRODUCT.startsWith("sdk"), and we can use that
* to make {@link #shouldBeAllowedToConnect(Context)} always
* return true when the app is running on the emulator.
*/
private static final String EMULATOR_PRODUCT_NAME_PREFIX = "sdk";
/**
* No need for a public constructor, since NetworkUtils exists solely
* as a namespace for {@link #shouldBeAllowedToConnect(Context)}
*/
private NetworkUtils() { }
@SuppressWarnings("deprecation")
private static boolean connectedToWifi(Context context) {
boolean wifiConnected = false;
ConnectivityManager connManager =
(ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
NetworkInfo mWifi = connManager.getNetworkInfo(
ConnectivityManager.TYPE_WIFI);
wifiConnected = mWifi != null && mWifi.isConnected();
}
else {
Network[] networks = connManager.getAllNetworks();
NetworkInfo networkInfo;
for (Network mNetwork : networks) {
networkInfo = connManager.getNetworkInfo(mNetwork);
wifiConnected = (networkInfo != null && networkInfo.getType() ==
ConnectivityManager.TYPE_WIFI && networkInfo.isConnected());
}
}
return wifiConnected;
}
private static boolean tetheringActive(Context context) {
// Use Java reflection to get WiFi access point state
// stackoverflow.com/q/9065592
WifiManager wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
boolean isEnabled = false;
try {
Method wmMethod = wifi.getClass().getDeclaredMethod("isWifiApEnabled");
isEnabled = (Boolean) wmMethod.invoke(wifi);
} catch (NoSuchMethodException e) {
Log.e("NetworkUtils", "No WifiManager.isWifiApEnabled method!");
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return isEnabled;
}
/**
* Indicate if the Android device on which the application is running should
* be allowed to begin its network calls to connect to a WVA device. This
* is decided by detecting whether the device is <em>connected</em> to a
* Wi-Fi network, or serving as a Wi-Fi hotspot.
*
* @param context to be used when invoking WifiManager and ConnectivityManager
* methods to determine network connectivity
* @return true if attempted WVA networking should be permitted, given the
* current state of network connectivity
*/
public static boolean shouldBeAllowedToConnect(Context context) {
if (Build.PRODUCT.startsWith(EMULATOR_PRODUCT_NAME_PREFIX)) {
return true;
}
return connectedToWifi(context) || tetheringActive(context);
}
}