Android Q, programmatically connect to different WiFi AP for internet

16,853

Solution 1

Try calling bindProcessToNetwork() in onAvailable() callback to regain network connectivity, it works fine for me.

Connect to network:

    WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder();
    builder.setSsid("wifi-ap-ssid");
    builder.setWpa2Passphrase("wifi-ap-password");

    WifiNetworkSpecifier wifiNetworkSpecifier = builder.build();

    NetworkRequest.Builder networkRequestBuilder1 = new NetworkRequest.Builder();
    networkRequestBuilder1.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
    networkRequestBuilder1.setNetworkSpecifier(wifiNetworkSpecifier);

    NetworkRequest nr = networkRequestBuilder1.build();
    ConnectivityManager cm = (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
    ConnectivityManager.NetworkCallback networkCallback = new 
        ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            super.onAvailable(network);
            Log.d(TAG, "onAvailable:" + network);
            cm.bindProcessToNetwork(network);
        }
    });
    cm.requestNetwork(nr, networkCallback);

Disconnect from the bound network:

cm.unregisterNetworkCallback(networkCallback);

Solution 2

WifiNetworkSuggestion API is used to suggest the user about joining an AP(System will post a notification for user to join)

Use WifiNetworkSpecifier to send your requests. Use the network object provided in onAvailable().

WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder();
builder.setSsid("wifi-ap-ssid");
builder.setWpa2Passphrase("wifi-ap-password");

WifiNetworkSpecifier wifiNetworkSpecifier = builder.build();

NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
networkRequestBuilder1.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
networkRequestBuilder1.setNetworkSpecifier(wifiNetworkSpecifier);

NetworkRequest networkRequest = networkRequestBuilder.build();
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
cm.requestNetwork(networkRequest, networkCallback);
networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(@NonNull Network network) {
                //Use this network object to Send request. 
                //eg - Using OkHttp library to create a service request
                 //Service is an OkHttp interface where we define docs. Please read OkHttp docs
                 Service service = null;

                 OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
                okHttpBuilder.socketFactory(network.getSocketFactory());

                service = new Retrofit.Builder()                                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                        .addConverterFactory(GsonConverterFactory.create(gson))
                         .client(okHttpBuilder.build())
                         .build()
                         .create(Service.class);


               Observable<Object> observable = null;
               try {
                  if (service != null) {
                     observable = service.yourRestCall();
                  }
                  Subscriber<Object> sub = new Subscriber< Object >() {
                     @Override
                     public void onError(Throwable e) {
                        //Do on error
                     }

                     @Override
                     public void onNext(Object logs) {
                        //Do on next
                     }
                  };
                 if(observable != null) {
                     observable.subscribeOn(Schedulers.io())
                                          .observeOn(AndroidSchedulers.mainThread()).subscribe(sub);
                 }

                super.onAvailable(network);
            }
        };

After you are done using the Wifi access point do

connectivityManager.unregisterNetworkCallback(networkCallback);

From Google's Issue tracker by Google's Engineer:

The network suggestions API flow requires the user to approve the app (platform posts a notification to ask user for approval). Once the app is approved, the platform will consider all networks from the app in future auto-connection attempts. But, this API does not give you guarantees on when the device will connect to your AP for provisioning. So, WifiNetworkSuggestion is not the right API surface for the provided use-case (peer to peer instant connectivity).

Using WifiNetworkSpecifier establishes a local connection to the wifi access point as mentioned above. The default network will still be cellular in this case (we don't disrupt other app's internet connectivity). The app making the request should use the multi-network API's to route their traffic over the established connection. The |Network| object provided in the onAvailable() callback for the request is the handle that app needs to use for opening sockets over that local network (Look at https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.DatagramSocket) and other such API's available in the |Network| object surface.

Hope this helps.

Solution 3

As stated here, Android 10 made it intentionally so that the WifiNetworkSpecifier prevents actual internet connectivity. It is meant for peer to peer connections.

The WifiNetworkSuggestion API, however, provides internet connectivity and behaves similarly to the WifiNetworkSpecifier API. As long as the device is not currently connected to any Wifi network, the WifiNetworkSuggestion API will automatically connect to the specified network. The first time a device uses it, a notification will appear asking if the app can suggest networks. The user must accept this notification for the WifiNetworkSuggestion API to work.

I found that Android's provided code in the WifiNetworkSuggestion documentation had a few compile errors. Here is the code that I found to work:

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

// Optional extra suggesstion, you can delete this or add more
final WifiNetworkSuggestion suggestion2 = new WifiNetworkSuggestion.Builder()
.setSsid("SSID here 2")
.setWpa2Passphrase("password here 2")
.setIsAppInteractionRequired(true) // Optional (Needs location permission)
.build();

final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<WifiNetworkSuggestion>();
suggestionsList.add(suggestion1);
suggestionsList.add(suggestion2); // Optional extra suggestion
final WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
final int status = wifiManager.addNetworkSuggestions(suggestionsList);

if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
     // Error handling
}

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;
        }
        // Post connection
    }
};
getApplicationContext().registerReceiver(broadcastReceiver, intentFilter);
Share:
16,853
Vinodh
Author by

Vinodh

Android application development.

Updated on June 06, 2022

Comments

  • Vinodh
    Vinodh about 2 years

    As in Android Q, several WiFi APIs are restricted. I am trying to use alternate APIs to connect to different Wifi AP for internet.

    Below is my code :

        WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder();
        builder.setSsid("wifi-ap-ssid");
        builder.setWpa2Passphrase("wifi-ap-password");
    
        WifiNetworkSpecifier wifiNetworkSpecifier = builder.build();
    
        NetworkRequest.Builder networkRequestBuilder1 = new NetworkRequest.Builder();
        networkRequestBuilder1.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
        networkRequestBuilder1.setNetworkSpecifier(wifiNetworkSpecifier);
    
        NetworkRequest nr = networkRequestBuilder1.build();
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        cm.requestNetwork(nr, callback);
    

    This allows me to connect but Internet is disabled. This is working as defined in Android docs.

    Alternate way i tried is below :

        WifiNetworkSuggestion.Builder wifiNetworkSuggestionBuilder1 = new WifiNetworkSuggestion.Builder();
        wifiNetworkSuggestionBuilder1.setSsid("wifi-ap-ssid");
        wifiNetworkSuggestionBuilder1.setWpa2Passphrase("wifi-ap-password");
        WifiNetworkSuggestion wifiNetworkSuggestion = wifiNetworkSuggestionBuilder1.build();
        List<WifiNetworkSuggestion> list = new ArrayList<>();
        list.add(wifiNetworkSuggestion);
        wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        wifiManager.removeNetworkSuggestions(new ArrayList<WifiNetworkSuggestion>());
        wifiManager.addNetworkSuggestions(list);
    

    declared permission in Manifest :

    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    

    Using this didn't change anything in behavior.

    Please let know sequence of APIs to connect successfully to different Wifi AP with internet capability.

    • Anand Khinvasara
      Anand Khinvasara almost 5 years
      There is an open ticket with google regarding this. I would recommend you guys to comment and voice over this ticket as it would help get google's attention. issuetracker.google.com/issues/138335744
    • Vinodh
      Vinodh almost 5 years
      @AnandKhinvasara : As, These APIs are not giving internet capability. I am displaying a popup for user to go to settings and connect to AP manually. Hope, this alternate way can work for your usecase.
    • Anand Khinvasara
      Anand Khinvasara almost 5 years
      I know but its not a good solution. Google should fix it.
    • Vinodh
      Vinodh almost 5 years
      @AnandKhinvasara : Agreed.
    • Anand Khinvasara
      Anand Khinvasara almost 5 years
      I got it to work. Please check my answer.
    • Anand Khinvasara
      Anand Khinvasara almost 5 years
      Did that work for you?
    • R.singh
      R.singh over 2 years
      @AnandKhinvasara Did that work for you? Where is your answer?
  • Vinodh
    Vinodh almost 5 years
    Does it work on the connections established using WifiNetworkSpecifier ?
  • Vinodh
    Vinodh almost 5 years
    Thanks for your reply. But, I see network suggestions is not providing internet capability. Do you have any alternate work around for this ?
  • Anand Khinvasara
    Anand Khinvasara almost 5 years
    For me status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS is always true. Thus I cant make it work
  • Aristide13
    Aristide13 over 4 years
    With this code, I get the authorization window with the right AP ... but when I press connect it does not work. Obtaining ip address then reconnection to the normal network. An idea ? Thank you
  • Russell C.
    Russell C. about 4 years
    Hi, @Anand Khinvasara, would you be able to post the full code for this? I'm having a lot of problems with no internet when connecting programmatically. Thanks!
  • Faizan Mir
    Faizan Mir about 4 years
    Ok So I have tried the WifiNetworkSpecifier ... I have this issue that when a network is selected the device wont connect to the network ,I have tried the documentation code too , is there a solution for local networks without internet connection
  • Inception
    Inception almost 4 years
    Does that mean I am forced to use WiFiNetworkSuggestion to connect to WiFi and actually use the internet? I am able to connect to WiFi using WiFiNetworkSpecifier but no actual internet access.
  • Russell C.
    Russell C. almost 4 years
    Yes, you will need to use WifiNetworkSuggestion. WifiNetworkSpecifier is meant for if you want to connect to a network for some purpose other than internet access.
  • Sergiy Belozorov
    Sergiy Belozorov about 3 years
    Saved my day! Thanks a bunch.
  • mono
    mono about 2 years
    This answer does not solve the problem This connection method does not have access to the Internet
  • FABiO
    FABiO about 2 years
    You saved my day, thanks of lot! Regards!