I am trying to retrieve the MAC address of an Android device. This is ordinarily possible through the WiFiManager API if WiFi is on.
Is there any way to get the MAC address if WiFi is off and WiFi Direct is on?
WiFi AND WiFi Direct can't be on at same time on my phone.
Thanks
I had been searching for this during my project. My requirements were to uniquely identify devices in an adhoc P2p network formed with WiFi Direct. Each device should identify its friend device the next time when it comes into proximity. I needed my own WiFi (Direct) MAC and my friends' to create a Key for this friend zone creation.
My Research: The design is in such a way that there is an Unique Universal ID and a Local ID. Reason: Universal ID can only be used to connect to Infrastructure mode Networks. Local ID could be used for "ad-hoc" mode networks(device to device). In this ad-hoc mode, there are possibilities that a single device might simultaneosly belong to several ad-hoc groups.
Hence to support this concurrent operations, P2p devices support
Multiple MAC entities, possibly on different channels.
For each session, a persistent group MAY use a different channel and device
MAC for each session.
P2P devices use their global MAC address as Device ID during discovery and negotiation, and a temporary local MAC address for all frames within a group. Understood from here
However, there is NO straight forward way to obtain one's own WiFi P2p MAC address. Issue 53437: Android.
In this issue discussion, the project member from google has suggested this is possible and just that it hasn't been documented
Solution: Using intent filter WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION and the extra
from the intent WifiP2pManager.EXTRA_WIFI_P2P_DEVICE
This is how I have used it in my project:
#Override
public void onReceive(Context context, Intent intent) {
....
....
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
.equals(action)) {
WifiP2pDevice device = (WifiP2pDevice) intent
.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
String myMac = device.deviceAddress;
Log.d(TAG, "Device WiFi P2p MAC Address: " + myMac);
/* Saving WiFi P2p MAC in SharedPref */
sharedPref = context.getSharedPreferences(context.getString(R.string.sp_file_name), Context.MODE_PRIVATE);
String MY_MAC_ADDRESS = sharedPref.getString(context.getString(R.string.sp_field_my_mac), null);
if (MY_MAC_ADDRESS == null || MY_MAC_ADDRESS != myMac) {
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString(context.getString(R.string.sp_field_my_mac), myMac);
editor.commit();
}
Hope this helps someone!
The mac addresss of WiFi is different than that of WiFi Direct. Usually first 2 letters might be different. Be careful about that.
The mac address of WiFi is different than that of WiFi Direct.
You can get WiFi direct address using next code:
public String getWFDMacAddress(){
try {
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface ntwInterface : interfaces) {
if (ntwInterface.getName().equalsIgnoreCase("p2p0")) {
byte[] byteMac = ntwInterface.getHardwareAddress();
if (byteMac==null){
return null;
}
StringBuilder strBuilder = new StringBuilder();
for (int i=0; i<byteMac.length; i++) {
strBuilder.append(String.format("%02X:", byteMac[i]));
}
if (strBuilder.length()>0){
strBuilder.deleteCharAt(strBuilder.length()-1);
}
return strBuilder.toString();
}
}
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
return null;
}
The WiFi Direct mac address is going to be different. It's explained beautifully by #auselen here https://stackoverflow.com/a/14480530/3167704.
I just figured out a way to retrieve WiFi Direct mac address. It isn't pretty but gets the job done. Here's the code,
final WifiP2pManager p2pManager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);
final WifiP2pManager.Channel channel = p2pManager.initialize(this, getMainLooper(), null);
p2pManager.createGroup(channel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
p2pManager.requestGroupInfo(channel, new WifiP2pManager.GroupInfoListener() {
#Override
public void onGroupInfoAvailable(WifiP2pGroup wifiP2pGroup) {
Log.i("", wifiP2pGroup.getOwner().deviceAddress);
// Following removal necessary to not have the manager busy for other stuff, subsequently
p2pManager.removeGroup(channel, new WifiP2pManager.ActionListener() {
#Override
public void onSuccess() {
Log.i("", "Removed");
}
#Override
public void onFailure(int i) {
Log.i("", "Failed " + i);
}
});
}
});
}
#Override
public void onFailure(int i) {
Log.i("", String.valueOf(i));
}
});
Related
I saw that Android system has a bad behavior with Wifi roaming.
We have a Wifi centralized network with many AP with a signle SSID.
The Adroid Phones wont roams seamlessly.
An Android Phone tries to stay connected to an AP until the signal reaches zero even if there are others AP (with the same SSID) with a good signal!
When the signal is zero, finally it performs an assosiation to another AP (with a good signal). But with this behavior the phone loses all the TCP Connections!
For example:
the phone is connected in WiFi to AP1
the phone moves in the building and now hears two signals from AP1 and from AP2.
When the signal form AP2 is stronger than the signal from AP1, i want that the phone do a reassosiation (not an assosiation) to AP2.
The idea is:
Perform a WifiManager.startScan()
Get the results WifiManager.getScanResults()
Find the best AP in the results
Perform a reassosiation to the best AP
Repeat every 30 seconds.
I talk about reassosiation because i don't want that the phone loses the TCP Connections.
There is a way to do this ?
Thank you,
Salvo
You cannot do this as you describe. A client cannot determine the state of the TCP connection on it's own. Your network must also move the communication channel from one AP to another. This can be done with the right network controllers.
Also, you should look at IEEE 802.11k -
https://en.wikipedia.org/wiki/IEEE_802.11k-2008
Add below permissions;
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
Register for below intent;
private WifiBroadcastReceiver wifiBroadcastReceiver = new WifiBroadcastReceiver();
Then in routine;
registerReceiver(wifiBroadcastReceiver, new IntentFilter("android.net.wifi.SCAN_RESULTS"));
Use the below class to change the reassociation;
public class WifiBroadcastReceiver extends BroadcastReceiver {
private WiFiManager manager = null;//set the value in constructor
private WifiConfiguration connectedConfiguration = null;//set the value in constructor
private int connectedNetId;
private void updateConnectedConfiguration(String ssid) {
configs = manager.getConfiguredNetworks();
int nid = 0;
for (WifiConfiguration cnf : configs) {
if (cnf.SSID.substring(1, cnf.SSID.length() - 1).equals(ssid)) {
connectedConfiguration = cnf;
connectedNetId = nid;
}
nid++;
}
}
public void onReceive(Context c, Intent intent) {
List<ScanResult> results = manager.getScanResults();
WifiInfo info = manager.getConnectionInfo();
ScanResult stronger = null;
for (ScanResult scanResult : results) {
try {
if (scanResult.SSID.equals(info.getSSID())) {
if (stronger == null) {
if (WifiManager.compareSignalLevel(info.getRssi() + 5, scanResult.level) < 0) {
stronger = scanResult;
}
} else if (WifiManager.compareSignalLevel(stronger.level, scanResult.level) < 0) {
stronger = scanResult;
}
}
} catch (Exception e) {
}
}
if (stronger != null && !stronger.BSSID.equals(info.getBSSID())) {
updateConnectedConfiguration(info.getSSID());
if (connectedConfiguration != null) {
connectedConfiguration.BSSID = stronger.BSSID;
manager.updateNetwork(connectedConfiguration);
manager.saveConfiguration();
manager.enableNetwork(connectedNetId, true);
manager.reassociate();
info = manager.getConnectionInfo();
//showNotification("\nConnecting " + stronger.SSID, stronger.BSSID + " " + stronger.level + "dBm");
}
}
}
}
I have a function that connects to a device (a Bluetooth credit card machine in this case) that looks like the following:
private void pinPar(final String name, final String address) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
pinpadSelected = new PinpadObject(name, address, false);
BluetoothConnectionProvider bluetoothConnectionProvider = new BluetoothConnectionProvider(MainActivity.this, pinpadSelected);
bluetoothConnectionProvider.setDialogMessage("Connecting to pinpad");
bluetoothConnectionProvider.setWorkInBackground(false);
bluetoothConnectionProvider.setConnectionCallback(new StoneCallbackInterface() {
#Override
public void onSuccess() {
Toast.makeText(getApplicationContext(), "Pinpad connected", Toast.LENGTH_SHORT).show();
out.println("Connected to " + name + " at " + address);
}
#Override
public void onError() {
Toast.makeText(getApplicationContext(), "Connection failed", Toast.LENGTH_SHORT).show();
out.println("Failed connecting to "+ name + " at " + address);
}
}
);
bluetoothConnectionProvider.execute();
}
});
}
I was looking to make an analogous function, pinUnpar that would simply close that connection, but bluetoothConnectionProvider has no method close() or something of the kind. How can I achieve this?
Alrighty, I haven't dealt with bluetooth on android in a while but here goes.
There are many different ways of connecting devices using bluetooth, however one simple way I particularly like, because it doesn't require scanning for the device you wish to connect to, and doesn't require pairing either. It is as follows:
first off you need a common UUID that both your client and server know, since your server in this case is the bluetooth credit card machine, you need to find out what the UUID it uses for the connection is (Shouldn't be too hard, if it's not written in the manual of the machine, then you can detect it yourself using a laptop).
Client code :-
BluetoothAdapter adapter;
adapter= BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device;
device= adapter.getRemoteDevice(serverAddress); //address here would be the address value
//passed to your function
BluetoothSocket socket= device.createInsecureRfcommSocketToServiceRecord(uuid);
//here uuid is the UUID the device uses as mentioned perviously
socket.connect();
OutputStream ouput=socket.getOutputStream();
InputStream input=socket.getInputStream();
And just like that, you have a connection to your machine, which you can write anything and read anything on. I assume you're not programming the credit card machine, so I omitted the corresponding server code.
Since this code uses simple streams and sockets, it's very easy to close, just as it is easy to open.
Edit:-
This uses android API only for the BT connection, and PLEASE NOTE that this code uses an INSECURE rfcomm, which means it's vulnerable to MITMA and other such attacks. If you don't wish for that you can instead replace
device.createInsecureRfcommSocketToServiceRecord(uuid)
with
device.createRfcommSocketToServiceRecord(uuid);
I am trying to develop a small app where I can scan and connect to the WiFi hot-spots from scanned list of networks. But for both, Open and Secure networks I have written a password prompt and if the network is open (by knowing it beforehand) I do not enter password and keep the text blank and enter and then it connects. Can anyone tell how to identify programmatically open and secure wifi so that I won't ask for password for Open network and let it connect directly. (I won't be knowing which is Open and Which is secure network in future, so we need to identify open and secure network from SSID or something)
Here you have capabilities field which is used to identify the network type
WifiManager wifimanger = (WifiManager) getSystemService(Context.WIFI_SERVICE);
List<ScanResult> networkList = wifimanger.getScanResults();
if (networkList != null) {
for (ScanResult network : networkList) {
String capabilities = network.capabilities;
Log.w(TAG, network.SSID + " capabilities : " + capabilities);
if (capabilities.toUpperCase().contains("WEP")) {
// WEP Network
} else if (capabilities.toUpperCase().contains("WPA")
|| capabilities.toUpperCase().contains("WPA2")) {
// WPA or WPA2 Network
} else {
// Open Network
}
}
}
Use this filter function to differentiate.
private boolean isProtectedNetwork(String capability){
return (capability.contains("WPA") ||
capability.contains("WEP") ||
capability.contains("WPS")
);
}
So when you have hold of all the List. Just iterate through it and add them in two different lists. (One for open and one for secure networks).
Here is the code for that
private void filterScan(List<ScanResult> allScanResults){
List<ScanResult>openScans = new ArrayList<ScanResult>();
List<ScanResult>closeScans = new ArrayList<ScanResult>();
for(ScanResult result : allScanResults)
{
if(!isProtectedNetwork(result.capabilities))
{
openScans.add(result);
}
else {
closeScans.add(result);
}
}
}
Useful Resource:
You can find more related solutions on My Github Repository
Today I was looking for an answer to this same question, but the solution pointed out here and in other similar questions made me a little insecure. And if a new security mode is created in the future?
For now I prefer to do something like:
public WifiSecurityMode testAgainstSecurityModes(WifiSecurityMode... securities) {
for (WifiSecurityMode security : securities) {
if (this.capabilities.toUpperCase().contains(security.getName().toUpperCase())) {
return security;
}
}
return WifiSecurityMode.UNKNOWN;
}
public enum WifiSecurityMode {
WEP("WEP"), WPA("WPA"), WPA2("WPA2"),//....
UNKNOWN ("UNKNOWN");
private String name;
WifiSecurityMode (String name){
this.name = name;
}
public String getName() {
return name;
}
}
//to use it: (WifiInfo are just a class I create to encapsulate the values in a ScanResult object)
public List<WifiInfo> getOpenWifis() {
List<WifiInfo> open = new ArrayList<>();
for (WifiInfo w : wifiInfoList) {
if (w.testAgainstSecurityModes(WifiSecurityMode.WEP, WifiSecurityMode.WPA, WifiSecurityMode.WPA2).equals(WifiSecurityMode.UNKNOWN)) {
open.add(w);
}
}
return open;
}
After that, you do what you want with UNKNOWN return.
If a new security mode be created in the future, you will not need to change testAgainstSecurityModes method.
I can enumerate all wifi networks in range (using startScan + SCAN_RESULTS_AVAILABLE_ACTION + getScanResults) and get their SSID and BSSID values, but I can't figure out how to determine the security type of each network.
In my main object:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(scanReceiver, intentFilter);
((WifiManager)getSystemService(Context.WIFI_SERVICE)).startScan();
In my scanReceiver object:
public void onReceive(Context c, Intent intent) {
if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())){
mainObject.scanComplete();
}
}
And again in my main object:
public void scanComplete()
{
List<ScanResult> networkList = ((WifiManager)getSystemService.(Context.WIFI_SERVICE)).getScanResults();
for (ScanResult network : networkList)
{
<do stuff>
}
}
The code works insofar that scanComplete eventually gets called, and I can successfully enumerate all nearby wifi networks and get their SSID and BSSID, but I can't figure out how to determine their security type.
Is there a way to do this?
Thanks in advance.
I think you can find it in the source code of Settings.apk.
First you should call wifiManager.getConfiguredNetworks() or wifiManager.getScanResults(),
then use the two methods below: (find them in AccessPoint class "com.android.settings.wifi"):
static int getSecurity(WifiConfiguration config) {
if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
return SECURITY_PSK;
}
if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
return SECURITY_EAP;
}
return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
}
static int getSecurity(ScanResult result) {
if (result.capabilities.contains("WEP")) {
return SECURITY_WEP;
} else if (result.capabilities.contains("PSK")) {
return SECURITY_PSK;
} else if (result.capabilities.contains("EAP")) {
return SECURITY_EAP;
}
return SECURITY_NONE;
}
Hope this is helpful.
You need to parse the ScanResult's capabilities string in the scanComplete method. According to the Android developer documentation, :
ScanResult.capabilities describes the authentication, key management, and
encryption schemes supported by the access point.
You might be able to make use of -- or at the very least use as an example -- the static helper methods available in the AccessPointState class.
AccessPointState.getScanResultSecurity
AccessPointState.isEnterprise
Thanks very much,... you made my day...
I have something to add here. Without scanning for the networks, one can get the currently connected wifi configuration information (specially encryption and key management) as follows,
WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
List<ScanResult> networkList = wifi.getScanResults();
if (networkList != null) {
for (ScanResult network : networkList)
{
String Capabilities = network.capabilities;
Log.w (TAG, network.SSID + " capabilities : " + Capabilities);
}
}
The security type of each network is in the Capabilities column of the scan results.
So to get the security type for you, add this to your part of your code.
public void scanComplete()
{
List<ScanResult> networkList = ((WifiManager)getSystemService.(Context.WIFI_SERVICE)).getScanResults();
for (ScanResult network : networkList)
{
String Capabilities = network.capabilities;
//Then you could add some code to check for a specific security type.
if(Capabilities.contains("WPA"))
{
// We know there is WPA encryption
}
else if(Capabilities.contains("WEP"))
{
// We know there is WEP encryption
}
else
{
// Another type of security scheme, open wifi, captive portal, etc..
}
}
}
Anyways, here's some quick source code. I would fully recomment Ayj's answer though, as it is an exceptional response and more complete.
I'm currently working on an Android application that connects to an instrument via Bluetooth and need to write string commands and receive string responses back. Currently I have the connect/read/write working for TCP/IP over Wi-Fi and now trying to implement Bluetooth. But I am running into some roadblocks. I have been searching the web trying to find examples of something similar and haven't had any luck. I have been using the Android developer resource example: Bluetooth Chat as my main reference point.
My current code seems to work.. Then it throws a Service Discovery Failed exception at the point of the connection. I am using the DeviceListActivity class to do the discovery and selecting of the device I want to connect to. It returns anActivityResult and then my Bluetooth class waits for it to handle that and then does the connect to it. The code beneath is almost identical to the Bluetooth Chat App.
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(!m_BluetoothAdapter.isEnabled())
{
m_BluetoothAdapter.enable();
}
switch (requestCode) {
case REQUEST_CONNECT_DEVICE:
// When DeviceListActivity returns with a device to connect
if (resultCode == Activity.RESULT_OK) {
// Get the device MAC address
String address = data.getExtras()
.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// Get the BLuetoothDevice object
BluetoothDevice device = m_BluetoothAdapter.getRemoteDevice(address);
// Attempt to connect to the device
connect(device);
}
break;
case REQUEST_ENABLE_BT:
// When the request to enable Bluetooth returns
if (resultCode == Activity.RESULT_OK) {
// Bluetooth is now enabled, so set up a chat session
}
else {
// User did not enable Bluetooth or an error occured
Toast.makeText(this, "Bluetooth not enabled", Toast.LENGTH_SHORT).show();
finish();
}
}
}
This is my connect function:
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private void connect(BluetoothDevice device) {
m_Device = device;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
}
catch (IOException e) {
}
m_Socket = tmp;
m_BluetoothAdapter.cancelDiscovery();
try {
// This is a blocking call and will only return on a
// successful connection or an exception
m_Socket.connect();
}
catch (IOException e) {
try {
m_Socket.close();
}
catch (IOException e2) {
}
return;
}
}
Hopefully, whatever I am doing wrong is simple, but I'm afraid it's never that easy. This is my first time doing any Bluetooth development, and maybe I'm doing something blatantly wrong... But I'm not sure why I get the service discovery failed exception.
You can pair/find the device at all times manually on the phone... It does require a passcode, but I don't think that is the problem that I am having.
After three days I got it figured out thanks to some very helpful posts.
I had to replace:
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
with:
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
tmp = (BluetoothSocket) m.invoke(device, 1);
and voilĂ it works!
As of API 15 you can use the following method:
Try replacing your UUID with the return value of getUuids() method of BluetoothDevice class.
What worked for me was something like this:
UUID uuid = bluetoothDevice.getUuids()[0].getUuid();
BluetoothSocket socket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid);
The reason this works is that different devices support different UUIDs and by getting the UUIDs of the device using getUuids you are supporting all features and devices.
Another interesting new method (supported since API 14) is this: BluetoothHealth.getConnectionState. Haven't tried it but looks promising...
This was a suggested edit from an anonymous user attempting to reply to the accepted answer.
One big difference between your before and after code is the UUID you are passing. I found my answer here: http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createRfcommSocketToServiceRecord(java.util.UUID)
I had to replace:
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
with:
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
tmp = device.createRfcommSocketToServiceRecord(SPP_UUID);
and voila it works!
The original code is for a peer to peer android app. It makes no sense to use the app UUID when connecting to a simple serial bluetooth device. Thats why discovery fails.
So as it mentioned above, the point is that you need to use the UUID that the server is waiting for.
If you are connecting to a bluetooth device, such as a headset or mouse, you need to check which UUIDs the device is listening for. You can see the UUIDs like this.
UUID[] uuids = bluetoothDevice.getUuids();
And if you want to know what these UUIDs mean, see this.
This is a realy old one question but i found that using the createInsecureRfcommSocketToServiceRecord() instead of createRfcommSocketToServiceRecord() along with the getUuids() previously mentioned do the trick for me
UUID uuid = bluetoothDevice.getUuids()[0].getUuid();
BluetoothSocket socket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid);