Android10以降でQRコードを読み取ってWiFiを有効にする方法
Android10でWiFi周りでプライバシー/セキュリティ強化が入った。
- ネットワークスキャンには位置情報(高精度)のパーミッションが必要になった
- P2P接続、提案されたネットワークだけ必要な場合には位置情報パーミッションがいらないAPIが追加されているのでそちらを使う
WiFiNetworkSpecifier
- P2P接続のためのAPI
- ローカル Wi-Fi 経由で IoT 端末を簡単に管理し、設定、ダウンロード、印刷などのピアツーピア機能を利用できる
https://developer.android.com/guide/topics/connectivity/wifi-bootstrap?hl=ja
WifiNetworkSuggestion
WiFi形式のQRコードを読み取ったとき、WifiNetworkSpecifierとWifiNetworkSuggestionのどちらを使うようにすればよいか。
WiFi形式のQRコード
WIFI:S:TenChars;P:0123456789;T:WEP;;
使い分け
たぶん
- スマホから家のWiFiに繋ぐ → WifiNetworkSuggestion
- Switchとスマホを繋いで写真の共有をする → WiFiNetworkSpecifier
を使うと良い気がする
Android10からのAPIと、MLKitのBarcode.WiFiの暗号化規格
Android10からのAPI
WifiNetworkSpecifierとWifiNetworkSuggestionにPassphraseを設定するAPIは、
- setWapiPassphrase()
- setWpa2Passphrase()
- setWpa3Passphrase()
MLKitのBarcode.WiFi
MLKitのBarcode.WiFiのEncryptionTypeに定義されているTypeは
- OPEN
- WPA
- WEP
WEPは古い暗号化規格で、セキュリティが弱く、WifiConfigurationのときでもAPI28からdeprecatedになっていた。
WAPIは中国独自規格で国際標準になってない。
対応方針としては、
MLKitのBarcode.WiFiが、
- OPEN → passPhrase設定無しでOK
- WPA → setWpa2Passphrase()で設定
- WEP → 対応不可。ユーザーにメッセージ表示
が良さそう。
WifiNetworkSpecifier を使うことで特定のWifiに接続することができた。
(Pixel3は接続できたが、Xperia 5 II SOG02は接続できず。調査中)
WifiNetworkSuggestionはアプリに自動接続することを許可するAPI。インターネット接続に用いるものだが、QRコードの読み取り時の接続に使うのは以下の理由から向いてなさそう。
- ユーザーが明示的にWifiピッカーから切断するとブラックリストに入り24時間は接続できなくなるため
- QRコードの読み取り結果ではインターネット接続かP2P接続か判断できないため。P2P接続はWifiNetworkSpecifierが適している
WifiNetworkSpecifierだと機器との接続になり、インターネット接続できてない。
やはり、
- インターネット接続 → WifiNetworkSuggestion
- P2P接続 → WifiNetworkSpecifier
の使い分けは必要そう。
WifiNetworkSpecifierは機器との一時接続。
connectivityManager.unregisterNetworkCallback(this)
をすることで接続解除される。
Switchと接続し、写真をスマホに送る機能が動かない
事象
- Switchとの接続はできるが、Chromeで http://192.168.0.1/index.html を開いた際に 「インターネットに接続されていません」と表示されてしまう
- Switchとの接続を、端末のカメラアプリ(Xperia 5 II SOG02など)や、WiFi設定から手動で行った場合は読み込みできる
SwitchがWiFi接続されているときに、「インターネット未接続」と表示されている場合は画像共有結果が読み込まれる
ACTION_WIFI_ADD_NETWORKS
こちらを使うと、Switchでの接続から画像共有結果の読み込みが成功した。
ただし、API level 30〜なので、Android10では使えない。
今回は使えなかった、WifiNetworkSpecifierのコード
@RequiresApi(Build.VERSION_CODES.Q)
private fun connectWifiWithNetworkSpecifier(wifi: WiFi) {
if (wifi.ssid == null) {
return
}
if (wifi.encryptionType == Barcode.WiFi.TYPE_WEP) {
return
}
val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
.setSsid(wifi.ssid)
.apply {
if (wifi.encryptionType == Barcode.WiFi.TYPE_WPA && wifi.password != null) {
setWpa2Passphrase(wifi.password)
}
}
.build()
val networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setNetworkSpecifier(wifiNetworkSpecifier)
.build()
val connectivityManager = this.applicationContext.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
toast(R.string.connecting_to_wifi)
connectivityManager.bindProcessToNetwork(network)
}
override fun onUnavailable() {
super.onUnavailable()
toast(R.string.error_connecting_to_wifi)
}
}
// 接続実行
connectivityManager.requestNetwork(networkRequest, networkCallback)
}