はじめに (対象読者・この記事でわかること)
この記事は、Androidアプリ開発に興味があるJavaプログラマー、またはネットワーク設定情報をアプリ内で利用したいと考えている開発者を対象としています。
この記事を読むことで、Android端末のWi-Fi接続情報からサブネットマスク(ネットマスク)を取得する方法がわかります。IPアドレスやゲートウェイなど、他のネットワーク情報と合わせて取得する方法も紹介するので、ネットワーク診断ツールや社内管理アプリなどの実装に活用できます。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。 - Androidアプリ開発の基本的な知識(Activity、Permissionなど) - Javaの基本的な文法(変数、メソッド、例外処理) - ネットワークの基礎知識(IPアドレス、サブネットマスクの意味)
Androidでサブネットマスク取得が必要な理由
Android端末のIP設定情報は、通常「設定」アプリから確認できますが、アプリ内で自動的に取得したい場合があります。
たとえば、社内のセキュリティアプリで「同一サブネット内の機器のみ通信を許可する」ような場合や、カスタムDNS診断ツールで「ネットワーク設定を一括表示」する機能を実装する際に、サブネットマスクが必要になります。
Android 10以降で非推奨となったWifiInfo.getIpAddress()だけでなく、新しいAPIでも取得可能な手法を押さえておくと、長期運用されるアプリの保守が楽になります。
Javaでサブネットマスクを取得する実装手順
ここでは、Android 9(API 28)以降で動作するJavaコードを用いて、サブネットマスクを取得する方法を解説します。
必要な権限と、WifiManager・LinkAddress・InterfaceAddressを使った取得方法をステップごとに説明します。
ステップ1:AndroidManifestに権限を追加
ネットワーク状態を読み取るには、ACCESS_NETWORK_STATEとACCESS_WIFI_STATEの2つの権限が必要です。
Android 10以降で正確なIP情報を取得するには、ACCESS_FINE_LOCATIONも追加します。
Xml<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.networkinfo"> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- 必要に応じて --> <uses-permission android:name="android.permission.INTERNET"/> </manifest>
ステップ2:WifiManagerとNetworkInterfaceを使った取得処理
以下のメソッドは、Wi-Fi接続中のサブネットマスクをスラッシュ表記(例:24)で返します。
NetworkInterfaceを列挙し、Wi-Fiインタフェース(大抵はwlan0)を特定してInterfaceAddressからプレフィックス長を取得します。
Javapublic static int getSubnetPrefixLength(Context ctx) { WifiManager wm = (WifiManager) ctx.getSystemService(Context.WIFI_SERVICE); if (wm == null || !wm.isWifiEnabled()) return -1; try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface ni = interfaces.nextElement(); if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) continue; // Android 9以降の名前は「wlan0」が多い if (!ni.getName().startsWith("wlan")) continue; for (InterfaceAddress addr : ni.getInterfaceAddresses()) { InetAddress inet = addr.getAddress(); if (inet instanceof Inet4Address) { return addr.getNetworkPrefixLength(); // 24 が返るケースが多い } } } } catch (SocketException e) { Log.e("NetworkInfo", "Cannot list interfaces", e); } return -1; }
ステップ3:結果をUIやログに表示
取得したプレフィックス長をもとに、サブネットマスクのドット表記(255.255.255.0)に変換して表示できます。
Javaint prefix = getSubnetPrefixLength(this); if (prefix != -1) { int mask = 0xFFFFFFFF << (32 - prefix); String dottedMask = String.format(Locale.US, "%d.%d.%d.%d", (mask >> 24) & 0xFF, (mask >> 16) & 0xFF, (mask >> 8) & 0xFF, mask & 0xFF); Log.i("SubnetMask", "Prefix: " + prefix + " Mask: " + dottedMask); }
ハマった点やエラー解決
-
位置情報権限がないと
NetworkInterfaceが空で返る
Android 10以降、Wi-FiのSSIDやIPを取得する際はACCESS_FINE_LOCATIONが必須です。かつ、ユーザーに対して実行時権限リクエストも必要です。 -
VPN接続時に
wlan0でなくtun0が優先される
VPN使用中はWi-Fiインタフェースが非アクティブになるため、取得できません。VPN判定を入れておくと親切です。 -
デュアルスタック環境でIPv6が先に来る
InterfaceAddressのリストはIPv6を先に含むことがあるため、instanceof Inet4Addressでフィルタリングしてください。
解決策
上記1.については、Activityで以下のように実行時権限をリクエストします。
Javaif (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 100); }
VPN使用中はConnectivityManager#getActiveNetwork()でTRANSPORT_VPNをチェックし、適切にメッセージを出してください。
まとめ
本記事では、AndroidでJavaを使いWi-Fi接続中のサブネットマスクを取得する方法を解説しました。
- 必要な権限は
ACCESS_FINE_LOCATIONも含め3つ NetworkInterfaceとInterfaceAddressでプレフィックス長を取得- IPv4かどうかを判定し、マスクをドット表記に変換
この記事を通して、ネットワーク情報を活用したAndroidアプリを実装する基礎が身につきました。
次回は、取得したサブネットマスクを使って同一セグメントの機器を自動スキャンする方法を紹介します。
参考資料
- Android Developers 公式ドキュメント - WifiManager
- Android Developers 公式ドキュメント - NetworkInterface
- InterfaceAddressのプレフィックス長解説 - Oracle公式
