Android Q, programmatically connect to different WiFi AP for internet

Vinodh picture Vinodh · Jul 1, 2019 · Viewed 10.9k times · Source

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.

Answer

Anand Khinvasara picture Anand Khinvasara · Aug 9, 2019

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.