Wi-Fi スキャンの概要

WifiManager API による Wi-Fi スキャン機能を使用して、デバイスから参照可能な Wi-Fi アクセス ポイントのリストを取得できます。

Wi-Fi スキャン プロセス

スキャン プロセスには次の 3 つのステップがあります。

  1. SCAN_RESULTS_AVAILABLE_ACTIONブロードキャスト リスナーを登録する。このブロードキャストはスキャン リクエストが完了したときに呼び出され、成功 / 失敗のステータスを返します。Android 10(API レベル 29)以降を搭載するデバイスの場合は、プラットフォームまたは他のアプリによってデバイスで実行されたフル Wi-Fi スキャンに対してこのブロードキャストが送信されます。アプリは独自にスキャンを実行せずにブロードキャストを使用して、デバイス上のすべてのスキャンの完了を傍受できます。

  2. WifiManager.startScan() を使用してスキャンをリクエストする。 次のいずれかの理由により呼び出しが失敗する可能性があるため、メソッドの戻りステータスを必ず確認してください。

    • 短期間に大量のスキャンが集中したことにより、スキャンのリクエストがスロットリングされている。
    • デバイスはアイドル状態で、スキャンが無効になっている。
    • Wi-Fi ハードウェアがスキャンエラーを報告した。
  3. WifiManager.getScanResults() を使用してスキャン結果を取得する。 返されたスキャン結果は、最新の更新結果です。現在のスキャンが完了、または成功しなかった場合は、前回のスキャン結果が返されることがあります。 すなわち、成功した SCAN_RESULTS_AVAILABLE_ACTION のブロードキャストを受け取る前にこのメソッドを呼び出すと、古いスキャン結果が返される可能性があります。

次のコードは、上記のステップを実装する方法の例を示しています。

Kotlin

val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager

val wifiScanReceiver = object : BroadcastReceiver() {

  override fun onReceive(context: Context, intent: Intent) {
    val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
    if (success) {
      scanSuccess()
    } else {
      scanFailure()
    }
  }
}

val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.registerReceiver(wifiScanReceiver, intentFilter)

val success = wifiManager.startScan()
if (!success) {
  // scan failure handling
  scanFailure()
}

....

private fun scanSuccess() {
  val results = wifiManager.scanResults
  ... use new scan results ...
}

private fun scanFailure() {
  // handle failure: new scan did NOT succeed
  // consider using old scan results: these are the OLD results!
  val results = wifiManager.scanResults
  ... potentially use older scan results ...
}

Java

WifiManager wifiManager = (WifiManager)
                   context.getSystemService(Context.WIFI_SERVICE);

BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context c, Intent intent) {
    boolean success = intent.getBooleanExtra(
                       WifiManager.EXTRA_RESULTS_UPDATED, false);
    if (success) {
      scanSuccess();
    } else {
      // scan failure handling
      scanFailure();
    }
  }
};

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
context.registerReceiver(wifiScanReceiver, intentFilter);

boolean success = wifiManager.startScan();
if (!success) {
  // scan failure handling
  scanFailure();
}

....

private void scanSuccess() {
  List<ScanResult> results = wifiManager.getScanResults();
  ... use new scan results ...
}

private void scanFailure() {
  // handle failure: new scan did NOT succeed
  // consider using old scan results: these are the OLD results!
  List<ScanResult> results = wifiManager.getScanResults();
  ... potentially use older scan results ...
}

制限

Android 8.0(API レベル 26)では、権限と許可される Wi-Fi スキャンの頻度に関する制限が導入されました。

ネットワーク パフォーマンス、セキュリティ、電池寿命を改善するため、Android 9(API レベル 28)では権限に関する要件を厳格にし、Wi-Fi スキャンの頻度をさらに制限しました。

権限

Android 8.0 および Android 8.1:

WifiManager.getScanResults() の呼び出しが成功するには、次のうちいずれか 1 つの権限が必要です。

呼び出し元アプリにこれらの権限のいずれも付与されていない場合、呼び出しは SecurityException で失敗します。

代わりに、Android 8.0(API レベル 26)以降を搭載しているデバイスでは、CompanionDeviceManager を使用して、位置情報の利用許可を必要とすることなく、アプリの代理として付近のコンパニオン デバイスをスキャンできます。このオプションの詳細については、コンパニオン デバイスのペア設定をご覧ください。

Android 9:

WifiManager.startScan() の呼び出しが成功するには、次の条件をすべて満たす必要があります。

Android 10(API レベル 29)以降:

WifiManager.startScan() の呼び出しが成功するには、次の条件をすべて満たす必要があります。

  • アプリが Android 10(API レベル 29)以降の SDK を対象としている場合は、アプリに ACCESS_FINE_LOCATION 権限が付与されている。
  • アプリが Android 10(API レベル 29)より前の SDK を対象としている場合は、アプリに ACCESS_COARSE_LOCATION または ACCESS_FINE_LOCATION の権限が付与されている。
  • アプリに CHANGE_WIFI_STATE 権限が付与されている。
  • デバイスで([設定] > [位置情報] に移動して)位置情報サービスを有効にしている。

WifiManager.getScanResults() の呼び出しが成功するには、次の条件をすべて満たす必要があります。

  • アプリが Android 10(API レベル 29)以降の SDK を対象としている場合は、アプリに ACCESS_FINE_LOCATION 権限が付与されている。
  • アプリが Android 10(API レベル 29)より前の SDK を対象としている場合は、アプリに ACCESS_COARSE_LOCATION または ACCESS_FINE_LOCATION の権限が付与されている。
  • アプリに ACCESS_WIFI_STATE 権限が付与されている。
  • デバイスで([設定] > [位置情報] に移動して)位置情報サービスを有効にしている。

呼び出し元アプリがこれらの要件のすべてを満たしていない場合、呼び出しは SecurityException で失敗します。

スロットリング

WifiManager.startScan() を使用するスキャン頻度には次の制限が適用されます。

Android 8.0 および Android 8.1:

各バックグラウンド アプリは、30 分に 1 回スキャンを実行できます。

Android 9:

各フォアグラウンド アプリは、2 分間の間に 4 回スキャンを実行できます。これにより、短時間で大量のスキャンを行うことができます。

すべてのバックグラウンド アプリをまとめて、30 分に 1 回スキャンを実行できます。

Android 10 以降:

Android 9 と同じスロットリング制限が適用されます。ローカルテストでのスロットリングを([開発者向けオプション] > [ネットワーク] > [Wi-Fi スキャン スロットリング] に移動して)オフに切り替える新しいデベロッパー オプションが用意されています。