用於網際網路連線的 Wi-Fi Suggestion API

搭載 Android 10 (API 級別 29) 以上版本的裝置可讓應用程式新增裝置的網路憑證,以便自動連線至 Wi-Fi 存取點。你可以使用 WifiNetworkSuggestion 提供建議,指出要連線的網路。平台最終會根據應用程式和其他服務的輸入內容,選擇要接受的存取點。

在 Android 11 (API 級別 30) 以上版本中:

  • 架構會強制執行以 EAP-SIM 為基礎的企業建議 (EAP-SIM、EAP-AKA、EAP-AKA-PRIME) 的擁有權規定;只有電信業者簽署的應用程式才能使用這類建議。
  • 如果是電信業者簽署的應用程式提供的建議,架構會自動為這些建議指派與應用程式電信業者簽署相應的電信業者 ID。如果從裝置中移除對應的 SIM 卡,這類建議就會自動停用。

在 Android 12 (API 級別 31) 以上版本中:

  • 您可以啟用非持續性隨機 MAC 位址,定期重新隨機產生 MAC 位址,進一步保護隱私。使用 setMacRandomizationSetting 指定網路的隨機化程度。

  • isPasspointTermsAndConditionsSupported():「條款及細則」是 Passpoint 功能,可讓網路部署作業以安全的 Passpoint 網路取代使用開放網路的不安全網頁認證入口。如果使用者必須接受條款及細則,系統會顯示通知。如果應用程式建議的 Passpoint 網路設有條款及細則限制,就必須先呼叫這個 API,確認裝置支援這項功能。如果裝置不支援這項功能,就無法連上這個網路,必須建議使用替代或舊版網路。

  • isDecoratedIdentitySupported():使用前置字元裝飾驗證網路時,裝飾的身分前置字元可讓網路業者更新網路存取 ID (NAI),透過 AAA 網路內的多個 Proxy 執行明確的路由 (詳情請參閱 RFC 7542)。

    Android 12 實作這項功能,是為了符合 WBA 針對 PPS-MO 擴充功能的規格。如果應用程式建議的 Passpoint 網路需要裝飾身分,就必須先呼叫這個 API,確認裝置支援這項功能。如果裝置不支援這項功能,系統就不會裝飾身分,且可能無法通過網路驗證。

如要建立 Passpoint 建議,應用程式必須使用 PasspointConfigurationCredentialHomeSp 類別。這些類別說明 Passpoint 設定檔,該設定檔定義於 Wi-Fi 聯盟 Passpoint 規格中。

下列程式碼範例說明如何為一個開放式、一個 WPA2、一個 WPA3 網路和一個 Passpoint 網路提供憑證:

Kotlin

val suggestion1 = WifiNetworkSuggestion.Builder()
        .setSsid("test111111")
        .setIsAppInteractionRequired(true) // Optional (Needs location permission)
        .build();

val suggestion2 = WifiNetworkSuggestion.Builder()
        .setSsid("test222222")
        .setWpa2Passphrase("test123456")
        .setIsAppInteractionRequired(true) // Optional (Needs location permission)
        .build();

val suggestion3 = WifiNetworkSuggestion.Builder()
        .setSsid("test333333")
        .setWpa3Passphrase("test6789")
        .setIsAppInteractionRequired(true) // Optional (Needs location permission)
        .build();

val passpointConfig = PasspointConfiguration(); // configure passpointConfig to include a valid Passpoint configuration
val suggestion4 = WifiNetworkSuggestion.Builder()
        .setPasspointConfig(passpointConfig)
        .setIsAppInteractionRequired(true) // Optional (Needs location permission)
        .build();

val suggestionsList = listOf(suggestion1, suggestion2, suggestion3, suggestion4);

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

val status = wifiManager.addNetworkSuggestions(suggestionsList);
if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
    // do error handling here
}

// Optional (Wait for post connection broadcast to one of your suggestions)
val intentFilter = IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);

val broadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (!intent.action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
            return;
        }
        // do post connect processing here
    }
};
context.registerReceiver(broadcastReceiver, intentFilter);

Java

final WifiNetworkSuggestion suggestion1 =
  new WifiNetworkSuggestion.Builder()
  .setSsid("test111111")
  .setIsAppInteractionRequired(true) // Optional (Needs location permission)
  .build();

final WifiNetworkSuggestion suggestion2 =
  new WifiNetworkSuggestion.Builder()
  .setSsid("test222222")
  .setWpa2Passphrase("test123456")
  .setIsAppInteractionRequired(true) // Optional (Needs location permission)
  .build();

final WifiNetworkSuggestion suggestion3 =
  new WifiNetworkSuggestion.Builder()
  .setSsid("test333333")
  .setWpa3Passphrase("test6789")
  .setIsAppInteractionRequired(true) // Optional (Needs location permission)
  .build();

final PasspointConfiguration passpointConfig = new PasspointConfiguration();
// configure passpointConfig to include a valid Passpoint configuration

final WifiNetworkSuggestion suggestion4 =
  new WifiNetworkSuggestion.Builder()
  .setPasspointConfig(passpointConfig)
  .setIsAppInteractionRequired(true) // Optional (Needs location permission)
  .build();

final List<WifiNetworkSuggestion> suggestionsList =
  new ArrayList<WifiNetworkSuggestion> {{
    add(suggestion1);
    add(suggestion2);
    add(suggestion3);
    add(suggestion4);
  }};

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

final int status = wifiManager.addNetworkSuggestions(suggestionsList);
if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
// do error handling here…
}

// Optional (Wait for post connection broadcast to one of your suggestions)
final IntentFilter intentFilter =
  new IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);

final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    if (!intent.getAction().equals(
      WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
      return;
    }
    // do post connect processing here...
  }
};
context.registerReceiver(broadcastReceiver, intentFilter);

應用程式首次提供建議後,使用者會立即收到通知。通知類型取決於裝置執行的 Android 版本:

  • 在 Android 11 (API 級別 30) 以上版本中,如果應用程式在前台執行,使用者會看到對話方塊;如果應用程式在背景執行,使用者會看到通知。
  • 在 Android 10 (API 級別 29) 上,無論應用程式是在前景或背景執行,使用者都會看到通知。

平台連上其中一個網路建議時,設定會顯示將網路連線歸因於相應建議應用程式的文字。

處理使用者中斷連線

如果使用者連上網路建議後,透過 Wi-Fi 選擇器明確與其中一個網路中斷連線,則該網路仍在範圍內時,系統會忽略該網路。在這段期間,系統不會考慮自動連線至該網路,即使應用程式移除並重新新增對應的網路建議也一樣。如果使用者使用 Wi-Fi 選擇器明確連線至先前已中斷連線的網路,系統會立即考慮自動連線至該網路。

變更應用程式的核准狀態

使用者拒絕網路建議通知後,應用程式的 CHANGE_WIFI_STATE 權限就會遭到移除。使用者之後可以前往 Wi-Fi 控制選單 (依序點選「設定」>「應用程式和通知」>「特殊應用程式存取權」>「Wi-Fi 控制」> App name) 授予這項權限。