📶

ConnectivityManagerをつかってSSIDを取ろうとすると<unknown>になる

2023/02/09に公開

背景

WifiManager.getConnectionInfo が deprecatedになっていていた.

そこでConnecitivityManagerを利用して置き換えてみたのだけど、下記のように取得すると ssid = <unknown ssid> となってしまう.

fun requestWifiInfo(context: Context) {
    val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val wifi = (manager.getNetworkCapabilities(manager.activeNetwork)?.transportInfo as? WifiInfo) ?: return
        println(wifi.ssid) // <unknown ssid>
        println(wifi.bssid) // 02:00:00:00:00:00
    }
}

解決策

2点がポイントだった.

  • permissionに必要なpermissionを追加する.
  • FLAG_INCLUDE_LOCATION_INFO を NetworkCallbackの引数に渡してリクエストし、onCapabilitiesChangedで正しい値を取得する.

permission

AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

code

fun requestWifiInfo(context: Context, onResult: (WifiInfo) -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        requestNetworkCapability(manager, onResult)
    } else {
        val manager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
        val info: WifiInfo = manager.connectionInfo
        onResult(info)
    }
}

@RequiresApi(Build.VERSION_CODES.S)
fun requestNetworkCapability(manager: ConnectivityManager, onResult: (WifiInfo) -> Unit) {
    val request = NetworkRequest.Builder()
        .addTransportType(TRANSPORT_WIFI)
        .build()

    // FLAG_INCLUDE_LOCATION_INFOを指定しないとWifiInfoが取得できない
    val callback = object : NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
        override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
        ) {
            super.onCapabilitiesChanged(network, networkCapabilities)
            val wifi =
                (networkCapabilities.transportInfo as? WifiInfo) ?: return@onCapabilitiesChanged
	    // ここで得られたwifiのssid情報は正しく得られる.
            onResult(wifi)
        }

        override fun onAvailable(network: Network) {
            super.onAvailable(network)
            val wifi = (manager.getNetworkCapabilities(network)?.transportInfo as? WifiInfo)
                ?: return@onAvailable
            // ここで得られたwifiのssid情報はunknownになるが...
        }
    }
    manager.registerNetworkCallback(request, callback)
    manager.requestNetwork(request, callback)
}

なぜ?

ssidなどのwifi情報は位置情報にセンシティブなデータだとして、必要とされる権限が強くなった.
このせいでssidを取得するまでの手順がややこしくなっている. (とくにAPI Level 33~)

参考

Discussion