WiFi Hotspot Development on Android

Posted by mediamind on Tue, 08 Mar 2022 08:01:38 +0100

WiFi hotspot Android Development

What is WiFi

Wi Fi is a family of wireless network protocols, which is based on IEEE 802.11 protocol. Wi Fi works seamlessly with its wired sister Ethernet. Conditional devices can be interconnected through wireless access points, including wired devices and the Internet. Based on different IEEE 802.11 protocol standards, there are different WiFi versions.

Wi Fi stations communicate by sending packets to each other, which are modulated and demodulated by carriers. The site has a 48 bit MAC address, which is used to identify the purpose and source of each packet. Wi Fi establishes a link level connection, which can be defined by the destination and source address.

The channel is half duplex and can be used by different networks in time sharing. When communication occurs on the same channel, any information sent by a machine will be received by all, even if there is only one destination address. The network interface card (NIC) only interrupts the CPU when it receives the appropriate message and ignores the message that is not for its purpose.

  • Infrastructure is the most commonly used mode, and all communication passes through a basic site.
  • Ad hoc mode means that the devices talk to each other directly without having to talk to the AP first. In more complex protocols, nodes may forward messages and continuously track how to reach other nodes. Some devices can share Internet connections through ad hoc, thus becoming a hotspot (virtual router).

What is a WiFi hotspot

WiFi hotspot is an Internet access point (AP) that allows computers, mobile phones and other devices to connect to WiFi. It uses routing through WLAN to connect to Internet service providers. Hotspots are created through WiFi. The hotspot is a Tethering mode, and other Tethering modes include Bluetooth and USB.

WiFi is used between wireless devices and APS, while hotspots use AP devices connected to routes.

Wi Fi is a kind of WLAN.

SoftAP

https://en.wikipedia.org/wiki/SoftAP

Software enabled access point enables a device that is not used as a route to become a wireless access point, which is equivalent to virtual router. SoftAP is mainly used to set on some Wi Fi products without display or input devices. The general steps are as follows:

  1. Connect Wi Fi devices to enable SoftAP Wi Fi hotspot
  2. The user downloads the app of a specific product on the smartphone. The app uses the underlying operating system (android or iOS) to connect to the SoftAP hotspot, or guides the user to connect manually
  3. The app requests the user's private Wi Fi network name (SSID) and passkey
  4. The app sends SSID and passkey to the device through the SoftAP network
  5. The device ends the SoftAP network and joins the user's private Wi Fi network

It should be noted that different operating systems and hardware handle SoftAP in different ways, so the user experience will be very different, especially for Android Software and hardware.

stay Android open source website In, I think Android's Wi Fi hotspot is Soft AP. stay Android open source website In, I think Android's Wi Fi hotspot is Soft AP. In fact, when the user uses the UI to turn on the WiFi hotspot, the system's startTether() function will be called in turn, one of which is tethering startTethering—>enableTetheringInternal—>setWifiTethering. In setWifiTethering, the startSoftAp function of WifiManager is called. Finally, call the cpp function HostapdManager::StartHostapd(). See details Sorting out Android 9.0 WiFi hotspot opening process.

Wi-Fi Certified Passpoint (Hotspot 2.0)

https://www.everythingrf.com/community/what-is-passpoint-or-hotspot-2-0
https://developer.android.com/guide/topics/connectivity/passpoint

Launched by Wi Fi Alliance (WFA) in 2012, it allows mobile devices to automatically connect to WiFi hotspots certified by Passpoint. This technology allows mobile devices to automatically join Wi Fi user services when users enter the hotspot 2.0 area, so as to provide better bandwidth and on-demand services for end users and reduce the traffic of operators' infrastructure. It is based on the IEEE 802.11u standard. Devices that support 802.11u will automatically connect and roam if they subscribe to the Hotspots 2 service.

The Passpoint connection is protected by WPA 2 security.

Before passing point, users need to repeat the login process when the hotspot is changed. When Passpoint connects to a hotspot, the user credentials are transmitted to all hotspots in the network to prevent users from finding and authenticating devices in order to connect to a new hotspot.

Android version 2.2 adds wifi hotspot function

Wi Fi on

Required permissions

These permissions belong to normal level install time permissions, which will be displayed in androidmanifest XML is displayed in permission.

The Wi-Fi Control permission allows apps that have been granted it to "turn Wi-Fi on or off, scan and connect to Wi-Fi networks, add or remove networks, or start a local-only hotspot."

Hotspot on

reference resources:

https://my.oschina.net/u/4382384/blog/4289594

https://www.jianshu.com/p/9dbb02c3e21f

https://blog.csdn.net/VNanyesheshou/article/details/82147110

<uses-permission android:name="android.permission.INTERNET" />     
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />     
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />     
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />     
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />     
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />      

Android <6.0, API<23,setWifiApEnabled

jurisdiction

Mobile phones below 6.0 can use the following code to detect permissions

ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED;

There is no need for Android mainifest XML location permission, and manually open GPS permission. You can set the user name ssid and password of WiFi hotspot here. Of course, you can also set the form of no password.

method

WifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);

public static boolean configApState(Context context, String apName) {
    WifiManager wifimanager = (WifiManager) context.getSystemService(context.WIFI_SERVICE);
    WifiConfiguration wificonfiguration = null;
    try {
        wificonfiguration = new WifiConfiguration();
        wificonfiguration.SSID = apName;
        if (isApOn(context)) {
            wifimanager.setWifiEnabled(false);
            disableAp(context);
        }
        Method method = wifimanager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
        method.invoke(wifimanager, wificonfiguration, !isApOn(context));
        return true;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

6.0<=Android <=7.0, 23<=API<=24,setWifiApEnabled

jurisdiction

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { //6.0 API > = 23, dynamic application
    Intent intent = new
    Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + context.getPackageName()));
    context.startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS);
} else {
    ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_SETTINGS}, REQUEST_CODE_WRITE_SETTINGS);
        }

ActivityCompat.requestPermissions() vs. context.startActivityForResult()

And you need to apply for write in Android manifest_ SETTINGS":

<uses-permission android:name="android.permission.WRITE_SETTINGS"  >

Check permissions:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //6.0 API>=23
        permission = Settings.System.canWrite(context);
    } else {
        permission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED;
    }

method

ditto

Android == 7.1,API==25,com.android.settings.TetherSettings

Starting from 7.1, the original interface WiFi manager The setwifi apenabled method in Java is no longer used to open WiFi hotspots. At this time, we can only use a opportunistic method to prompt the user to start the hotspot. We need to jump to the hotspot configuration page, and then we will come back and judge whether the hotspot is enabled in onActivityResult

method

private void showRequestApDialogOnN_MR1() {
    final CustomDialog dialog = new CustomDialog(this);
    dialog.setMessage("android7.1 The above system does not support automatic opening of hotspots,You need to turn on the hotspot manually");
    dialog.setPositiveButton("To open", new CustomDialog.onPositiveOnclickListener() {
        @Override
        public void onYesClick() {
            dialog.dismiss();
            openAP();
        }
    });
    dialog.setNegativeButton("sign out", new CustomDialog.onNoOnclickListener() {
        @Override
        public void onNoClick() {
            finishBack();
            dialog.dismiss();
        }
    });
    dialog.show();
}
//Open the portable hotspot interface of the system  
private void openAP() {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_MAIN);
    ComponentName com = new ComponentName("com.android.settings", "com.android.settings.TetherSettings");
    intent.setComponent(com);
    startActivityForResult(intent, 1000);
}

Judge whether it is on

//Judge whether the user has enabled the hotspot getWiFiAPConfig(); This method is not used to get the information of wifi hotspots on this machine
  @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_WRITE_SETTINGS && Settings.System.canWrite(this)) {
            init();
        } else if (requestCode == 1000) {
            if (!ApMgr.isApOn(this)) {
                showRequestApDialogOnN_MR1();
            } else {
                getWiFiAPConfig();
            }
        }
    }

Android>=8.0, API>=26, WifiManager.startLocalOnlyHotspot ()

jurisdiction

Access required_ FINE_ LOCATION:

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

And manually turn on the GPS permission. Similarly, use onActivityResult to detect whether the GPS permission has been turned on.

method

How to turn on hot spots:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
var wifiManager:WifiManager = getApplicationContext().getSystemService(Context.WIFI_SERVICE) as WifiManager
    try {
        wifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
                @TargetApi(Build.VERSION_CODES.O)
                @Override
                public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
                    super.onStarted(reservation);
                    Log.i(TAG, "uild.VERSION.SDK_INT >= Build.VERSION_CODES.O initWifiAp onStarted");
                    WifiConfiguration wifiConfiguration = reservation.getWifiConfiguration();
                    ssid = wifiConfiguration.SSID;
                    password = wifiConfiguration.preSharedKey
                    Log.i(TAG, "ssid is:" + ssid + "password is:" + password);
                    mHandler.obtainMessage(2018, wifiConfiguration).sendToTarget();
                }

                @Override
                public void onStopped() {
                    super.onStopped();
                    Log.i(TAG, "uild.VERSION.SDK_INT >= Build.VERSION_CODES.O initWifiAp onStopped");
                }

                @Override
                public void onFailed(int reason) {
                    super.onFailed(reason);
                    Log.i(TAG, "uild.VERSION.SDK_INT >= Build.VERSION_CODES.O initWifiAp onFailed and reason isL" + reason);
                }

            }, mHandler);
        
    } catch (e: Exception) {
        e.printStackTrace();
        Log.i(TAG, "open wifi Ap fail");
        openWifiAPFail();
    }
}

ssid and password are randomly generated by WiFi configuration and cannot be set by developers.

After the hotspot is turned on, the IP address of the mobile hotspot server is fixed, and the address is 192.168.43.1

Turn off hotspot Code:

var reserva:WifiManager.LocalOnlyHotspotReservation? = null

if (reserva != null){
     reserva!!.close();
 }

Android>=8.0, API>=26, ConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI,...)

jurisdiction

This API is a system API and cannot be called directly. You need to:

Modify the source code of ConnectivityManager and change it to teaching_ Remove the relevant hidden flags in WiFi field, startTethering method and OnStartTetheringCallback class, and then compile a jar package separately and copy the jar package to the project

method

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { //7.1 api25
    Field iConnMgrField = connManager.getClass().getDeclaredField("mService");
    iConnMgrField.setAccessible(true);
    Object iConnMgr = iConnMgrField.get(connManager);
    Class<?> iConnMgrClass = Class.forName(iConnMgr.getClass().getName());
    Method startTethering = iConnMgrClass.getMethod("startTethering", int.class, ResultReceiver.class, boolean.class);
    startTethering.invoke(iConnMgr, TETHERING_WIFI, new ResultReceiver(new Handler()) {
        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            super.onReceiveResult(resultCode, resultData);
        }
    }, true);
    return true;

} else {
    Method method = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE);
    return (boolean) method.invoke(wifiManager, config, true);
}

Android>=10, API>=30,connectivityManager.requestNetwork

method

final NetworkSpecifier specifier =
      new Builder()
      .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX))
      .setBssidPattern(MacAddress.fromString("10:03:23:00:00:00"),
                       MacAddress.fromString("ff:ff:ff:00:00:00"))
      .build()
 final NetworkRequest request =
      new NetworkRequest.Builder()
      .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
      .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
      .setNetworkSpecifier(specifier)
      .build();
 final ConnectivityManager connectivityManager =
      context.getSystemService(Context.CONNECTIVITY_SERVICE);
 final NetworkCallback networkCallback = new NetworkCallback() {
      ...
      {@literal @}Override
      void onAvailable(...) {}
      // etc.
 };
 connectivityManager.requestNetwork(request, networkCallback);

appendix

WifiManager.startLocalOnlyHotspot (API>=26)

https://developer.android.com/reference/android/net/wifi/WifiManager#startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback,%20android.os.Handler)

  • Requires Manifest.permission.CHANGE_WIFI_STATE and
  • Manifest.permission.ACCESS_FINE_LOCATION

Request a local only hotspot so that the application can communicate with devices in the same location connected to the created hotspot. When multiple applications are successfully registered at the same time, the underlying hotspot is shared. LocalOnlyHotspotCallback#onStarted(LocalOnlyHotspotReservation) Called when the hotspot can be used by the application.

Each application can create an active call to the method. WifiManager. LocalOnlyHotspotCallback. The onstarted (Android. Net. WiFi. WiFi manager. Localonlyhospotreservation) callback provides the requester with localonlyhospotreservation, including SoftApConfiguration, including SSID, security type and confidential information required to connect to hotspot. Communicating this information is up to the application.

If localonlyhospot cannot be created, the localonlyhospotcallback#onfailed (int) method will be called. Examples of failures include network errors or an operating mode that cannot coexist. For example, if a user is using Wifi Tethering to provide upstream for other devices, localonlyhospot will not be enabled because it cannot coexist. Possible error codes include: localonlyhospotcallback#error_ NO_ CHANNEL, LocalOnlyHotspotCallback#ERROR_ GENERIC, LocalOnlyHotspotCallback#ERROR_ INCOMPATIBLE_ Mode and localonlyhospotcallback#error_ TETHERING_ DISALLOWED.

Internally, the request is logged to prevent the hotspot from being destroyed while the application is still using it. By wifi manager LocalOnlyHotspot Callback. The callonlyhospotreservation object passed by onstarted (Android. Net. wifi. wifi. Localonlyhospotreservation) call should be closed when localonlyhospot no longer needs to use localonlyhospotreservation #close() Because the hotspot may be shared by multiple applications, removing the last registered application request will destroy the hotspot. This means that the application should not listen to the broadcast containing wifi status information after using the hotspot to determine whether the hotspot is stopped. In addition, as long as localonlyhospotreservation #close() is called, the application will not receive any callback.

The application should know that the user can also stop localonlyhospot through the system UI, which can not guarantee that the hotspot will remain active when there is a requested application. The requester is notified of this through localonlyhospotcallback#onsstopped(). Other situations where hotspots are turned off are also possible (e.g. emergency mode). Application developers should be aware that hotspots may suddenly stop, but they will be notified when they register correctly.

Applications should know that the network will be shared by other applications. Applications need to protect their data in the network. (e.g. TLS)

WifiManager.setWifiEnabled (API<29)

https://developer.android.com/reference/android/net/wifi/WifiManager#setWifiEnabled(boolean)

Enable or disable Wi-Fi.

  • Applications must have the Manifest.permission.CHANGE_WIFI_STATE permission to toggle wifi.
  • @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)

https://github.com/AndroidSDKSources/android-sdk-sources-for-api-level-26/blob/master/android/net/wifi/WifiManager.java#L1824

Instead of being used as a WiFi binding, you should use connectivitymanager #starttethering (int, Boolean, connectivitymanager #onstarttethering callback) You can also use WiFi manager #startlocalonlyhospot (localonlyhospotcallback) as the hotspot of the communication link of devices in the same location.

  • require android.Manifest.permission.TETHER_PRIVILEGED
  • @hide, @SystemApi

Call the setWifiApEnabled method of WiFi manager to set the hotspot:

try {
    Method method = mWifManager.getClass().getMethod(
        "setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE);
    boolean enable = (Boolean) method.invoke(mWifManager, config, true);

    if (enable) {
        Log.d("WiFi", "Hotspot turned on");
    } else {
        Log.d("WiFi", "Failed to create hotspot");
    }
} catch (Exception e) {
    e.printStackTrace();
}

ConnectivityManager.startTethering (7.19.0,2528)

Check the tether entry and turn on tether when it meets the conditions. Including WIFI, USB and BLUETOOTH.

  • @Hide (before API 28, @ hide method can still be accessed through Java reflection. This annotation only indicates that the method / class / domain is not included in the API document.)
  • @SystemApi cannot be called through Java reflection

https://android.googlesource.com/platform/frameworks/base/+/25bf8f5/core/java/android/net/ConnectivityManager.java#2010

/**
     * Runs tether provisioning for the given type if needed and then starts tethering if
     * the check succeeds. If no carrier provisioning is required for tethering, tethering is
     * enabled immediately. If provisioning fails, tethering will not be enabled. It also
     * schedules tether provisioning re-checks if appropriate.
     *
     * @param type The type of tethering to start. Must be one of
     *         {@link ConnectivityManager.TETHERING_WIFI},
     *         {@link ConnectivityManager.TETHERING_USB}, or
     *         {@link ConnectivityManager.TETHERING_BLUETOOTH}.
     * @param showProvisioningUi a boolean indicating to show the provisioning app UI if there
     *         is one. This should be true the first time this function is called and also any time
     *         the user can see this UI. It gives users information from their carrier about the
     *         check failing and how they can sign up for tethering if possible.
     * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller
     *         of the result of trying to tether.
     * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
     * @hide
     */
    @SystemApi
    public void startTethering(int type, boolean showProvisioningUi,
            final OnStartTetheringCallback callback, Handler handler) {
        ResultReceiver wrappedCallback = new ResultReceiver(handler) {
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                if (resultCode == TETHER_ERROR_NO_ERROR) {
                    callback.onTetheringStarted();
                } else {
                    callback.onTetheringFailed();
                }
            }
        };
        try {
            mService.startTethering(type, wrappedCallback, showProvisioningUi);
        } catch (RemoteException e) {
            Log.e(TAG, "Exception trying to start tethering.", e);
            wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
        }
    }

wifiConfiguration (API <= 29)

When API level < = 29, the invoked WiFi configuration API is required to enable Wi Fi hotspot. When Android version < = 7.0, also invoke setwifi enabled API in WiFi manager class; In Android Version 7.1-9.0, you need to use the startTethering API in ConnectivityManager class.

After Android version 10, Google recommends using WiFi network specifier Builder to create NetworkSpecifier and WiFi networksuggestion Builder creates WiFi networksuggestion But most Android phones still use Android version 9.0 and below.

Sample code reference https://juejin.cn/post/6844903936957087758

static WifiConfiguration createWifiConfig(String SSID, @WifiSecurityType int wifiCipherType, String password, boolean hiddenSSID) {

    WifiConfiguration wifiConfiguration = new WifiConfiguration();
    wifiConfiguration.SSID = convertToQuotedString(SSID);
    wifiConfiguration.hiddenSSID=hiddenSSID;//Whether to hide the hotspot true = hidden. If hidden, other devices need to add the network manually
    switch (wifiCipherType) {
        case WifiSecurityType.SECURITY_NONE:
            wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
            break;
        case WifiSecurityType.SECURITY_WEP:
            wifiConfiguration.allowedKeyManagement.set(KeyMgmt.NONE);
            wifiConfiguration.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
            wifiConfiguration.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
            if (!TextUtils.isEmpty(password)) {
                int length = password.length();
                // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
                if ((length == 10 || length == 26 || length == 58)
                    && password.matches("[0-9A-Fa-f]*")) {
                    wifiConfiguration.wepKeys[0] = password;
                } else {
                    wifiConfiguration.wepKeys[0] = '"' + password + '"';
                }
            }
            break;

        case WifiSecurityType.SECURITY_WPA_PSK:
            wifiConfiguration.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
            if (!TextUtils.isEmpty(password)) {
                if (password.matches("[0-9A-Fa-f]{64}")) {
                    wifiConfiguration.preSharedKey = password;
                } else {
                    wifiConfiguration.preSharedKey = '"' + password + '"';
                }
            }
            break;

        case WifiSecurityType.SECURITY_WPA_EAP:
            wifiConfiguration.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
            wifiConfiguration.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
            wifiConfiguration.enterpriseConfig = new WifiEnterpriseConfig();
            int eapMethod = 0;
            int phase2Method = 0;
            wifiConfiguration.enterpriseConfig.setEapMethod(eapMethod);
            wifiConfiguration.enterpriseConfig.setPhase2Method(phase2Method);
            if (!TextUtils.isEmpty(password)) {
                wifiConfiguration.enterpriseConfig.setPassword(password);
            }
            break;
        default:
            break;
    }
    return wifiConfiguration;
}

terpriseConfig = new WifiEnterpriseConfig();
            int eapMethod = 0;
            int phase2Method = 0;
            wifiConfiguration.enterpriseConfig.setEapMethod(eapMethod);
            wifiConfiguration.enterpriseConfig.setPhase2Method(phase2Method);
            if (!TextUtils.isEmpty(password)) {
                wifiConfiguration.enterpriseConfig.setPassword(password);
            }
            break;
        default:
            break;
    }
    return wifiConfiguration;
}

Topics: Android hotspot