2

I have registered ConnectivityManager NetworkCallback as follows:

val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val builder = NetworkRequest.Builder()
            .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
cm.registerNetworkCallback(
                builder.build(),
                object : ConnectivityManager.NetworkCallback() {

                    override fun onAvailable(network: Network) {
                        super.onAvailable(network)
                        Log.i(TAG, "Network $network is available")
                    }

                    override fun onLosing(network: Network, maxMsToLive: Int) {
                        super.onLosing(network, maxMsToLive)
                        Log.i(TAG, "Network $network is losing after $maxMsToLive ms")
                    }

                    override fun onLost(network: Network) {
                        super.onLost(network)
                        Log.i(TAG, "Network $network is lost")
                    }

                    override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) {
                        super.onCapabilitiesChanged(network, caps)
                        Log.i(TAG, "Network $network capabilities changed: $caps")
                    }

                    override fun onLinkPropertiesChanged(network: Network, props: LinkProperties) {
                        super.onLinkPropertiesChanged(network, props)
                        Log.i(TAG, "Network $network link properties changed: $props")
                    }
                }
    }

When I turn hotspot on/off in my Android 11 device, none of the CallBack functions are called, i.e., I don't get any messages to logcat, when I turn WiFi hotspot on/off.

When hotspot is off, device has these addresses:

PAN_sprout:/ $ ip addr                                                        
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 46:85:2d:8f:9a:8e brd ff:ff:ff:ff:ff:ff
    inet6 fe80::4485:2dff:fe8f:9a8e/64 scope link
       valid_lft forever preferred_lft forever
3: ip_vti0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
    link/ipip 0.0.0.0 brd 0.0.0.0
4: ip6_vti0@NONE: <NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1
    link/tunnel6 :: brd ::
5: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
    link/sit 0.0.0.0 brd 0.0.0.0
6: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1
    link/tunnel6 :: brd ::
8: rmnet0: <UP,LOWER_UP> mtu 2000 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/[530]
9: rmnet_data0: <UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/[530]
    inet6 fe80::9e2:b3d5:84e6:14c/64 scope link
       valid_lft forever preferred_lft forever
10: rmnet_data1: <UP,LOWER_UP> mtu 1464 qdisc htb state UNKNOWN group default qlen 1000
    link/[530]
    inet 10.140.85.190/30 scope global rmnet_data1
       valid_lft forever preferred_lft forever
    inet6 2001:14bb:a0:5545:818d:5109:4166:b991/64 scope global dynamic mngtmpaddr
       valid_lft forever preferred_lft forever
    inet6 fe80::818d:5109:4166:b991/64 scope link
       valid_lft forever preferred_lft forever
...
25: r_rmnet_data8: <> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/[530]

and when I turn it on, device has these addresses:

PAN_sprout:/ $ ip addr                                                        
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 46:85:2d:8f:9a:8e brd ff:ff:ff:ff:ff:ff
    inet6 fe80::4485:2dff:fe8f:9a8e/64 scope link
       valid_lft forever preferred_lft forever
3: ip_vti0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
    link/ipip 0.0.0.0 brd 0.0.0.0
4: ip6_vti0@NONE: <NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1
    link/tunnel6 :: brd ::
5: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
    link/sit 0.0.0.0 brd 0.0.0.0
6: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1
    link/tunnel6 :: brd ::
8: rmnet0: <UP,LOWER_UP> mtu 2000 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/[530]
9: rmnet_data0: <UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/[530]
    inet6 fe80::9e2:b3d5:84e6:14c/64 scope link
       valid_lft forever preferred_lft forever
10: rmnet_data1: <UP,LOWER_UP> mtu 1464 qdisc htb state UNKNOWN group default qlen 1000
    link/[530]
    inet 10.140.85.190/30 scope global rmnet_data1
       valid_lft forever preferred_lft forever
    inet6 2001:14bb:a0:5545:818d:5109:4166:b991/64 scope global dynamic mngtmpaddr
       valid_lft forever preferred_lft forever
    inet6 fe80::818d:5109:4166:b991/64 scope link
       valid_lft forever preferred_lft forever
...
25: r_rmnet_data8: <> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/[530]
176: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 7a:d1:a3:76:b8:8a brd ff:ff:ff:ff:ff:ff
    inet 192.168.214.149/24 brd 192.168.214.255 scope global wlan0
       valid_lft forever preferred_lft forever
    inet6 2001:14bb:a0:5545::5a/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::78d1:a3ff:fe76:b88a/64 scope link
       valid_lft forever preferred_lft forever

As you see, wlan0 was added, but no callback function was called. Callback functions do work otherwise, for example, when I turn cellular network on/off.

Question is: what do I need to do in order to get callback functions called when WiFi hotspot is turned on/off.

Edit: Based on Sergio Pardo's answer, I created this:

val hotSpotReceiver = object : BroadcastReceiver() {
            override fun onReceive(contxt: Context, intent: Intent) {
                val action = intent.action
                if ("android.net.wifi.WIFI_AP_STATE_CHANGED" == action) {
                    val state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0)
                    if (WifiManager.WIFI_STATE_ENABLED == state % 10)
                        Log.d(TAG, "HotSpot is enabled")
                    else
                        Log.d(TAG, "HotSpot is disabled")
                    }
                }
            }
        }
this.registerReceiver(hotSpotReceiver,
    IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED"))

It can be used to detect when WiFi hotspot is enabled or disabled and answers the title of the question. But it does not help in getting the NetworkCallbacks called.

The callbacks would have allowed me to learn hotspot's local IP address and interface name. Is there some other way to figure that out?

Edit: It turned out that when the above hotSpotReceiver logs "Hotspot is enabled", there is delay before the hotspot interface gets its IP addresses and routes. The missing network callbacks would help.

Juha
  • 349
  • 2
  • 11
  • Did you need a permission or some declaration in the AndroidManifest.xml for "android.net.wifi.WIFI_AP_STATE_CHANGED"? I'm trying it but it just does not get triggered. – JonasVautherin Nov 24 '22 at 17:43
  • 1
    I only have these two: android.permission.ACCESS_NETWORK_STATE and android.permission.ACCESS_WIFI_STATE. – Juha Nov 25 '22 at 10:22
  • Hmm same here, but it doesn't work for me for some reason. – JonasVautherin Nov 25 '22 at 13:26

2 Answers2

0

I think you are trying to use the wrong system service, and implementation later on. You should use "Context.WIFI_SERVICE" and this will get you a Wifi Manager.

val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as WifiManager

And then you can use that value to see if hotspot is on:

public static boolean isHotspotOn(final WifiManager manager) {
    try {
        final Method method = 
            manager.getClass().getDeclaredMethod("isWifiApEnabled");
        return (Boolean) method.invoke(manager);
    } catch (final Throwable ignored) {}

    return false;
}

If you also want to listen to the updates of the hotspot you should do it with the following IntentFilter

IntentFilter filter = new IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED");
Sergio Pardo
  • 774
  • 6
  • 17
  • Thanks for your reply. I'll give it a try. According to Android developer documentation, ConnectivityManager "Monitors network connections (Wi-Fi, GPRS, UMTS, etc.), Sends broadcast intents when network connectivity changes, ...". For sure when WiFi hotspot is turned on, there is network connectivity change and the device where the hotspot is located can start over the hotspot WiFi network communicate with other parties. If Connectivitymanager does not have anything to do with WiFI hotspot related connectivity changes, there is a big problem with official ConnectivityManager documentation. – Juha Sep 24 '21 at 18:21
  • Does "android.net.wifi.WIFI_AP_STATE_CHANGED" require any addition (permission/declaration) in the AndroidManifest.xml? I `registerReceiver` with the `IntentFilter`, but my `onReceive` never gets triggered... – JonasVautherin Nov 24 '22 at 17:44
0

Google's answer to my bug report:

This is a known limitation of all Android implementations as of Android 12. We are looking to improve this in the future but cannot share a timeline at this point.

Then they closed the bug report with status Won't Fix (Intended behavior). I'll try to escalate this since behavior that is against official API documentation and prevents app's response to network connectivity changes cannot be "Intended behavior".

Juha
  • 349
  • 2
  • 11