diff --git a/plugin.xml b/plugin.xml index c665eae..9202320 100644 --- a/plugin.xml +++ b/plugin.xml @@ -29,6 +29,7 @@ + diff --git a/src/android/wifiwizard2/WifiWizard2.java b/src/android/wifiwizard2/WifiWizard2.java index f3c57b8..9132aed 100644 --- a/src/android/wifiwizard2/WifiWizard2.java +++ b/src/android/wifiwizard2/WifiWizard2.java @@ -17,13 +17,15 @@ import org.apache.cordova.*; import java.util.List; -import java.util.concurrent.Future; +import java.util.concurrent.Future; import java.lang.InterruptedException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import android.Manifest; +import android.content.Context; import android.content.pm.PackageManager; import android.content.BroadcastReceiver; import android.content.Intent; @@ -36,6 +38,10 @@ import android.net.DhcpInfo; import android.net.wifi.WifiManager; +import android.net.wifi.rtt.RangingResult; +import android.net.wifi.rtt.WifiRttManager; +import android.net.wifi.rtt.RangingRequest; +import android.net.wifi.rtt.RangingResultCallback; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.ScanResult; @@ -46,12 +52,13 @@ import android.net.ConnectivityManager.NetworkCallback; import android.net.NetworkSpecifier; -import android.content.Context; import android.os.AsyncTask; import android.util.Log; import android.os.Build.VERSION; import android.os.PatternMatcher; +import androidx.core.app.ActivityCompat; + import java.net.URL; import java.net.InetAddress; import java.net.Inet4Address; @@ -80,6 +87,7 @@ public class WifiWizard2 extends CordovaPlugin { private static final String IS_WIFI_ENABLED = "isWifiEnabled"; private static final String SET_WIFI_ENABLED = "setWifiEnabled"; private static final String SCAN = "scan"; + private static final String SCAN_RTT = "scanWithRTT"; private static final String ENABLE_NETWORK = "enable"; private static final String DISABLE_NETWORK = "disable"; private static final String GET_SSID_NET_ID = "getSSIDNetworkID"; @@ -110,6 +118,7 @@ public class WifiWizard2 extends CordovaPlugin { private static boolean bssidRequested = false; private WifiManager wifiManager; + private WifiRttManager rttWifiManager; private CallbackContext callbackContext; private JSONArray passedData; @@ -154,12 +163,13 @@ private static boolean getHexKey(String s) { public void initialize(CordovaInterface cordova, CordovaWebView webView) { super.initialize(cordova, webView); this.wifiManager = (WifiManager) cordova.getActivity().getApplicationContext().getSystemService(Context.WIFI_SERVICE); + this.rttWifiManager = (WifiRttManager) cordova.getActivity().getApplicationContext().getSystemService(Context.WIFI_RTT_RANGING_SERVICE); this.connectivityManager = (ConnectivityManager) cordova.getActivity().getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); } @Override public boolean execute(String action, JSONArray data, CallbackContext callbackContext) - throws JSONException { + throws JSONException { this.callbackContext = callbackContext; this.passedData = data; @@ -196,7 +206,7 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo } // Return only IP address - if( action.equals( GET_WIFI_IP_ADDRESS ) ){ + if (action.equals(GET_WIFI_IP_ADDRESS)) { callbackContext.success(ip); return true; } @@ -240,6 +250,8 @@ public boolean execute(String action, JSONArray data, CallbackContext callbackCo this.reconnect(callbackContext); } else if (action.equals(SCAN)) { this.scan(callbackContext, data); + } else if (action.equals(SCAN_RTT)) { + this.scanWithRTT(callbackContext, data); } else if (action.equals(REMOVE_NETWORK)) { this.remove(callbackContext, data); } else if (action.equals(CONNECT_NETWORK)) { @@ -357,6 +369,151 @@ public void run() { return true; } + /** + * Scans for RTT Ranging data, must call scan first to get Wifi Scan Results + * + * @param callbackContext A Cordova callback context + * @param data JSONArray with [0] == JSONObject + * @return true + */ + private boolean scanWithRTT(final CallbackContext callbackContext, final JSONArray data) { + Log.v(TAG, "Entering Scan RTT"); + final Context context = cordova.getActivity().getApplicationContext(); + final ScanSyncContext syncContext = new ScanSyncContext(); + + synchronized (syncContext) { + if (syncContext.finished) { + Log.v(TAG, "In onReceive, already finished"); + return false; + } + + syncContext.finished = true; + } + + Log.v(TAG, "Checking for RTT support"); + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + callbackContext.error("ACCESS_FINE_LOCATION_FALSE"); + return false; + } + + final List wifiResults = wifiManager.getScanResults(); + JSONArray rttData = new JSONArray(); + final String[] error = {""}; + + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT) && API_VERSION > 27) { + Log.v(TAG, "RTT is supported"); + + if (rttWifiManager.isAvailable()) { + Log.v(TAG, "RTT is available"); + RangingRequest.Builder builder = new RangingRequest.Builder(); + + int maxPeers = RangingRequest.getMaxPeers(); + int peerCount = 0; + + for (ScanResult result : wifiResults) { + if (peerCount >= maxPeers) { + break; + } + + if (result.is80211mcResponder()) { + builder.addAccessPoint(result); + peerCount++; + } + } + + RangingRequest request = builder.build(); + + try { + rttWifiManager.startRanging(request, cordova.getThreadPool(), new RangingResultCallback() { + @Override + public void onRangingResults(List results) { + // Process RTT results + for (RangingResult result : results) { + final int status = result.getStatus(); + + if (status != STATUS_CODE_FAIL) { + JSONObject rttItem = new JSONObject(); + + try { + rttItem.put("status", result.getStatus()); + rttItem.put("macAddress", result.getMacAddress()); + rttItem.put("distanceMm", result.getDistanceMm()); + rttItem.put("distanceStdDevMm", result.getDistanceStdDevMm()); + rttItem.put("rssi", result.getRssi()); + rttData.put(rttItem); + } catch (JSONException e) { + error[0] = e.getMessage(); + } + } else { + Log.v(TAG, "Result Failed With Status: " + status); + } + } + + try { + JSONObject returnData = new JSONObject(); + returnData.put("rttData", rttData); + returnData.put("error", error[0]); + + callbackContext.success(returnData); + } catch (JSONException e) { + callbackContext.error("SCAN_WITH_RTT_FAILURE_1"); + } + } + + @Override + public void onRangingFailure(int code) { + // Handle failure + error[0] = "Ranging failed with code: " + code; + callbackContext.error(error[0]); + } + }); + } catch (Exception e) { + error[0] = "SecurityException: " + e.getMessage(); + callbackContext.error(error[0]); + } + } else { + Log.v(TAG, "RTT is not available"); + error[0] = "RTT_NOT_AVAILABLE"; + callbackContext.error("SCAN_WITH_RTT_FAILURE_2"); + } + } else { + Log.v(TAG, "RTT is not supported"); + error[0] = "RTT_NOT_SUPPORTED"; + callbackContext.error("SCAN_WITH_RTT_FAILURE_3"); + } + + Log.v(TAG, "Submitting timeout to threadpool"); + cordova.getThreadPool().submit(new Runnable() { + public void run() { + Log.v(TAG, "Entering timeout"); + final int FIFTEEN_SECONDS = 15000; + + try { + Thread.sleep(FIFTEEN_SECONDS); + } catch (InterruptedException e) { + Log.e(TAG, "Received InterruptedException e, " + e); + return; + // keep going into error + } + + Log.v(TAG, "Thread sleep done"); + + synchronized (syncContext) { + if (syncContext.finished) { + Log.v(TAG, "In timeout, already finished"); + return; + } + syncContext.finished = true; + } + + Log.v(TAG, "In timeout, error"); + callbackContext.error("TIMEOUT_WAITING_FOR_RTT"); + } + }); + + return true; + } + /** * This methods adds a network to the list of available WiFi networks. If the network already * exists, then it updates it. @@ -1911,4 +2068,4 @@ private static class AP { } } -} +} \ No newline at end of file diff --git a/src/ios/WifiWizard2.h b/src/ios/WifiWizard2.h index 3f2ab83..0080c69 100644 --- a/src/ios/WifiWizard2.h +++ b/src/ios/WifiWizard2.h @@ -6,6 +6,7 @@ - (void)iOSConnectNetwork:(CDVInvokedUrlCommand *)command; - (void)iOSConnectOpenNetwork:(CDVInvokedUrlCommand *)command; - (void)iOSDisconnectNetwork:(CDVInvokedUrlCommand *)command; +- (void)getWifiIP:(CDVInvokedUrlCommand *)command; - (void)getConnectedSSID:(CDVInvokedUrlCommand *)command; - (void)getConnectedBSSID:(CDVInvokedUrlCommand *)command; - (void)isWifiEnabled:(CDVInvokedUrlCommand *)command; @@ -26,4 +27,4 @@ - (void)canPingWifiRouter:(CDVInvokedUrlCommand *)command; - (void)canConnectToRouter:(CDVInvokedUrlCommand *)command; -@end +@end \ No newline at end of file diff --git a/src/ios/WifiWizard2.m b/src/ios/WifiWizard2.m index 675c571..8e86c4e 100644 --- a/src/ios/WifiWizard2.m +++ b/src/ios/WifiWizard2.m @@ -1,11 +1,51 @@ #import "WifiWizard2.h" #include +#include #import #import -#import +#import +#import @implementation WifiWizard2 +- (void)getWifiIP:(CDVInvokedUrlCommand*)command { + CDVPluginResult *pluginResult = nil; + + NSString *address = @"error"; + struct ifaddrs *interfaces = NULL; + struct ifaddrs *temp_addr = NULL; + int success = 0; + // retrieve the current interfaces - returns 0 on success + success = getifaddrs(&interfaces); + if (success == 0) { + // Loop through linked list of interfaces + temp_addr = interfaces; + while(temp_addr != NULL) { + if(temp_addr->ifa_addr->sa_family == AF_INET) { + // Check if interface is en0 which is the wifi connection on the iPhone + if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { + // Get NSString from C String + address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; + + } + + } + + temp_addr = temp_addr->ifa_next; + } + } + // Free memory + freeifaddrs(interfaces); + if (address) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:address]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Not available"]; + } + + [self.commandDelegate sendPluginResult:pluginResult + callbackId:command.callbackId]; +} + - (id)fetchSSIDInfo { // see http://stackoverflow.com/a/5198968/907720 NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces(); @@ -50,22 +90,31 @@ - (void)iOSConnectNetwork:(CDVInvokedUrlCommand*)command { passwordString = [options objectForKey:@"Password"]; if (@available(iOS 11.0, *)) { - if (ssidString && [ssidString length]) { - NEHotspotConfiguration *configuration = [[NEHotspotConfiguration - alloc] initWithSSID:ssidString - passphrase:passwordString - isWEP:(BOOL)false]; - - configuration.joinOnce = false; - + if (ssidString && [ssidString length]) { + NEHotspotConfiguration *configuration; + + + if (@available(iOS 13.0, *)) { + configuration = [[NEHotspotConfiguration + alloc] initWithSSIDPrefix:ssidString + passphrase:passwordString + isWEP:(BOOL)false]; + } else { + configuration = [[NEHotspotConfiguration + alloc] initWithSSID:ssidString + passphrase:passwordString + isWEP:(BOOL)false]; + } + configuration.joinOnce = false; + [[NEHotspotConfigurationManager sharedManager] applyConfiguration:configuration completionHandler:^(NSError * _Nullable error) { NSDictionary *r = [self fetchSSIDInfo]; NSString *ssid = [r objectForKey:(id)kCNNetworkInfoKeySSID]; //@"SSID" - if ([ssid isEqualToString:ssidString]){ - pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:ssidString]; + if ([ssid hasPrefix:ssidString]){ + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:ssid]; }else{ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description]; } @@ -73,7 +122,6 @@ - (void)iOSConnectNetwork:(CDVInvokedUrlCommand*)command { callbackId:command.callbackId]; }]; - } else { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"SSID Not provided"]; [self.commandDelegate sendPluginResult:pluginResult @@ -100,8 +148,16 @@ - (void)iOSConnectOpenNetwork:(CDVInvokedUrlCommand*)command { if (@available(iOS 11.0, *)) { if (ssidString && [ssidString length]) { - NEHotspotConfiguration *configuration = [[NEHotspotConfiguration - alloc] initWithSSID:ssidString]; + NEHotspotConfiguration *configuration; + + + if (@available(iOS 13.0, *)) { + configuration = [[NEHotspotConfiguration + alloc] initWithSSIDPrefix:ssidString]; + } else { + configuration = [[NEHotspotConfiguration + alloc] initWithSSID:ssidString]; + } configuration.joinOnce = false; @@ -111,8 +167,8 @@ - (void)iOSConnectOpenNetwork:(CDVInvokedUrlCommand*)command { NSString *ssid = [r objectForKey:(id)kCNNetworkInfoKeySSID]; //@"SSID" - if ([ssid isEqualToString:ssidString]){ - pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:ssidString]; + if ([ssid hasPrefix:ssidString]){ + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:ssid]; }else{ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description]; } @@ -161,9 +217,8 @@ - (void)iOSDisconnectNetwork:(CDVInvokedUrlCommand*)command { - (void)getConnectedSSID:(CDVInvokedUrlCommand*)command { CDVPluginResult *pluginResult = nil; - NSDictionary *r = [self fetchSSIDInfo]; - NSString *ssid = [r objectForKey:(id)kCNNetworkInfoKeySSID]; //@"SSID" + NSString *ssid = [self getWifiSsid]; //@"SSID" if (ssid && [ssid length]) { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:ssid]; @@ -329,5 +384,41 @@ - (void)canConnectToRouter:(CDVInvokedUrlCommand*)command { callbackId:command.callbackId]; } +- (NSString*) getWifiSsid { + if (@available(iOS 13.0, *)) { + CLLocationManager* manager = [[CLLocationManager alloc] init]; + CLAuthorizationStatus authStatus = [manager authorizationStatus]; + + if (authStatus == kCLAuthorizationStatusNotDetermined) { + [manager requestWhenInUseAuthorization]; + } else if (authStatus == kCLAuthorizationStatusDenied) { + NSLog(@"User has explicitly denied authorization for this application, or location services are disabled in Settings."); + return nil; + } + } + + NSString *wifiName = nil; + CFArrayRef wifiInterfaces = CNCopySupportedInterfaces(); + if (!wifiInterfaces) { + return nil; + } + + NSArray *interfaces = (__bridge NSArray *)wifiInterfaces; + for (NSString *interfaceName in interfaces) { + CFDictionaryRef dictRef = CNCopyCurrentNetworkInfo((__bridge CFStringRef)(interfaceName)); + + if (dictRef) { + NSDictionary *networkInfo = (__bridge NSDictionary *)dictRef; + NSLog(@"network info -> %@", networkInfo); + wifiName = [networkInfo objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID]; + CFRelease(dictRef); + } + } + + CFRelease(wifiInterfaces); + return wifiName; +} + + -@end +@end \ No newline at end of file diff --git a/www/WifiWizard2.js b/www/WifiWizard2.js index 2408a31..431ebdd 100644 --- a/www/WifiWizard2.js +++ b/www/WifiWizard2.js @@ -299,6 +299,16 @@ var WifiWizard2 = { }); }, + /** + * Ranges for Wi-Fi RTT Data if it is supported (must call startScan, just use scan()) + * @returns {Promise} + */ + scanWithRTT: function () { + return new Promise(function (resolve, reject) { + cordova.exec(resolve, reject, 'WifiWizard2', 'scanWithRTT', []); + }); + }, + /** * Check if WiFi is enabled * @returns {Promise}