Разработчик Android: как определить, когда точка доступа Wi-Fi включена/выключена

#android #hotspot #android-connectivitymanager

Вопрос:

Я зарегистрировал ConnectivityManager сетевой обратный вызов следующим образом:

 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")
                    }
                }
    }
 

Когда я включаю/выключаю точку доступа на своем устройстве Android 11, ни одна из функций обратного вызова не вызывается, т. Е. Я не получаю никаких сообщений в logcat, когда я включаю/выключаю точку доступа Wi-Fi.

Когда точка доступа отключена, у устройства есть эти адреса:

 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
 

Как вы видите, был добавлен wlan0, но функция обратного вызова не вызывалась. Функции обратного вызова работают иначе, например, когда я включаю/выключаю сотовую сеть.

Вопрос в следующем: что мне нужно сделать, чтобы функции обратного вызова вызывались при включении/выключении точки доступа Wi-Fi.

Редактировать: Основываясь на ответе Серджио Пардо, я создал это:

 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"))
 

Он может быть использован для определения того, включена или отключена точка доступа Wi-Fi, и отвечает на название вопроса. Но это не помогает в вызове обратных вызовов сети.

Обратные вызовы позволили бы мне узнать локальный IP-адрес точки доступа и имя интерфейса. Есть ли какой-то другой способ выяснить это?

Редактировать: Оказалось, что, когда указанный выше hotSpotReceiver регистрирует «Точка доступа включена», возникает задержка, прежде чем интерфейс точки доступа получит свои IP-адреса и маршруты. Отсутствующие сетевые обратные вызовы помогли бы.

Ответ №1:

Я думаю, что вы пытаетесь использовать неправильную системную службу и ее последующую реализацию. Вы должны использовать «Контекст.WIFI_SERVICE», и это даст вам менеджер Wi-Fi.

 val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as WifiManager
 

И затем вы можете использовать это значение, чтобы узнать, включена ли точка доступа:

 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;
}
 

Если вы также хотите слушать обновления точки доступа, вам следует сделать это с помощью следующего фильтра намерений

 IntentFilter filter = new IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED");
 

Комментарии:

1. Спасибо за ваш ответ. Я попробую это сделать. Согласно документации разработчика Android, ConnectivityManager «Отслеживает сетевые подключения (Wi-Fi, GPRS, UMTS и т. Д.), Отправляет намерения вещания при изменении сетевого подключения…». Наверняка, когда точка доступа Wi-Fi включена, происходит изменение сетевого подключения, и устройство, на котором расположена точка доступа, может начать через сеть Wi-Fi точки доступа общаться с другими сторонами. Если Connectivitymanager не имеет никакого отношения к изменениям подключения, связанным с точкой доступа Wi-Fi, существует большая проблема с официальной документацией ConnectivityManager.

Ответ №2:

Ответ Google на мой отчет об ошибке:

Это известное ограничение всех реализаций Android начиная с Android 12. Мы стремимся улучшить это в будущем, но на данный момент не можем поделиться сроками.

Затем они закрыли отчет об ошибке со статусом Won't Fix (Intended behavior) . Я постараюсь усилить это, поскольку поведение, противоречащее официальной документации API и предотвращающее реакцию приложения на изменения сетевого подключения, не может быть «Предполагаемым поведением».