Check Bluetooth State on specific device on android - java

I'm trying to know at my application start up if a paired device is connect or not. In practice the main app give me an MAC address and I've got to respond if we're connected or not.
A BroadcastReceiver can't be a full option because if the user start the connection with the device before launching the app I'll be unable to know if I'm connect to the device or not.
I found this on the forum : How to programmatically tell if a Bluetooth device is connected?
There is no way to retrieve the list of connected devices at application startup. The Bluetooth API will only let you listen to connection changes.
but i think this isn't accurate anymore because with this code :
public class cServiceListener implements BluetoothProfile.ServiceListener {
private static final int[] states={ BluetoothProfile.STATE_DISCONNECTING,
BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING};
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
#Override
public void onServiceConnected(int profile, BluetoothProfile bluetoothProfile) {
//List<BluetoothDevice> Devices=bluetoothProfile.getDevicesMatchingConnectionStates(states);
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
Log.i("myTag","\n\n<==============profile connexion state =============> : "+ mBluetoothAdapter.getProfileConnectionState(1));
for (BluetoothDevice loop:devices){
Log.i("myTag","Nom du device :" +loop.getName()+" Etat du Device:"+bluetoothProfile.getConnectionState(loop)
+ " Type du device : "+ loop.getType() + " Classe du device : " + loop.getBluetoothClass().toString() );
}
}
#Override
public void onServiceDisconnected(int profile) {
}}
I'm able to tell if SOME devices are connected or not but not with all of them !!
I try the code with portable speakers and with others devices where the calls AND the sounds is take in charge and it work perfectly : if the device is connect it return 2 and if not it return 0.
But when i try it on my real objective (which is a car) it return 0 at anytime. The test car only take in charge calls (you can't play your music with Bluetooth connection, to old). So I think with more modern car it can work..
First i was thinking that car using BLE technology so I try with this code
List<BluetoothDevice> devices = bluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
Log.d("myTag","\n Taille de la liste : " + devices.size());
for(BluetoothDevice device : devices) {
if(device.getType() == BluetoothDevice.DEVICE_TYPE_LE) {
Log.d("myTag","\n BLE <===> Nom du device :" + device.getName());
}
Log.d("myTag","\n<====NONE BLE=====> // \n\n Nom du device :" + device.getName()+"\n\n");
}
But in fact this isn't the point, this don't work.
And it's weird because
mBluetoothAdapter.getProfileConnectionState(1)
returning the correct value when I'm connect to the car only.
I really don't know what to do know and why
bluetoothProfile.getConnectionState() returning the wrong value.
If someone have any idea, I'll be glad to know.

Finally I've achived the work. Bluetooth car are considere like headset so I focus on it :
public static void startListening() {
final BroadcastReceiver mBroadcastReceiver3 = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle info = intent.getExtras();
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceAddress = device.getAddress();
if (action.equals( BluetoothDevice.ACTION_ACL_CONNECTED)){
//do some stuff with connected device
}
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)){
// do things with disconnected
}
}
};}

Related

Scanning for Bluetooth LE devices when the phone is in doze mode, don't work. CallBack method dosn't called when doze mode

I am developing an Android app (the app runs on Android 6): I want the app to send a notification to the user when it is near a BLE device (a device that I have at home). So I continuously scan, I scan through a service (which is running in the background). It works well when the phone screen is on; but, when the screen turns off, a few seconds later the application can no longer find the BLE (the scan is still running, but there is no callback.
if (enable) {
if (mScanning) return;
// Stops scanning after a pre-defined scan period.
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (!mScanning) return;
try {
mScanning = false;
mBluetoothLeScanner.stopScan(mScanCallback);
Log.i(TAG_LOG, "Stop scanning after pre-defined scan periode");
} catch (Exception e){Log.e(TAG_LOG,"mBluetoothLeScanner.stopScan Exception:=>"+e.getMessage());}
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
Log.i(TAG_LOG, "Start scanning ....");
}
private ScanCallback mScanCallback = new ScanCallback() {
//When a BLE advertisement has been found
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.i(TAG_LOG, "Name: "+result.getDevice().getName()+". Adresse: "+result.getDevice().getAddress()+". Rssi: "+result.getRssi());
//scanDevices(false);
if(result.getDevice().getName() != null && result.getDevice().getName().toString().equals(deviceName)){
mDeviceAdress = result.getDevice().getAddress();
mDevice = mBluetoothAdapter.getRemoteDevice(mDeviceAdress);
Log.i(TAG_LOG, "Device found");
scanDevices(false);
}
}
You can't make this work. Scanning is a very expensive operation that Android won't allow in the background. Instead, make an attempt to connect to the device. I had success doing this in a WorkManager job, running every 15 minutes. Battery drain was negligible and it was pretty reliable. Note that a connection state 0x85 usually represents the device being out of range, and 0x80 means a different device is already connected to it (or the phone is already connected to too many different devices). Full error list is at https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/master/stack/include/gatt_api.h#27

I want to know the existing of the device by bluetooth getRemoteDevice function or other function

I`m sorry about not good English skill.
I want to know that the device exists which has the mac address.
I used the getRemoteDevice method from BluetoothAdapter.
But the document said,
'A BluetoothDevice will always be returned for a valid hardware address, even if this adapter has never seen that device.'
So I couldn`t know the existing of the device.
How can I know the exists of the device?
Below code is my previous code. But I can`t know how to change the null checking.
public void connectSavedDevice() {
String result = pref.getString("first_device", "0");
if(result.equals("0")) {
Log.d(TAG,"not saved device");
return;
}
BluetoothDevice device = ble_adapter_.getRemoteDevice(result);
if(device == null) {
editor.clear();
editor.commit();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
startScan();
}
return;
}
GattClientCallback gatt_client_cb= new GattClientCallback();
ble_gatt_= device.connectGatt( context, false, gatt_client_cb );
}

Discovering all services on the network using jmDNS on Android

I'm using the jmdns.jar from this project https://github.com/twitwi/AndroidDnssdDemo in my Android project.
I'm currently trying to find all services on my network. I can't use Android NSD, so please avoid suggesting it as a solution.
protected Object doInBackground(Object[] params) {
try {
WifiManager wifi = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE);
final InetAddress deviceIpAddress = InetAddress.getByName(Formatter.formatIpAddress(wifi.getConnectionInfo().getIpAddress()));
multicastLock = wifi.createMulticastLock(getClass().getName());
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
jmDNS = JmDNS.create(deviceIpAddress, "Android Device Discovery");
jmDNS.addServiceListener("_http._tcp.local.", new ServiceListener() {//_services._dns-sd._udp _http._tcp.local. _workstation._tcp.local.
#Override
public void serviceAdded(ServiceEvent serviceEvent) {
jmDNS.requestServiceInfo("", "", 1000);
}
#Override
public void serviceRemoved(ServiceEvent serviceEvent) {
}
#Override
public void serviceResolved(ServiceEvent serviceEvent) {
System.out.println(serviceEvent.getInfo().getHostAddress());
System.out.println(serviceEvent.getInfo().getName());
}
});
}catch(IOException e) {
Log.e(TAG, e.getMessage(), e);
}
}
The above code gives me the address and name of a printer on my network. That's great. What I would like is a TYPE that will catch all the services being broadcasted on my network. Android NSD had a _services._dns-sd._udpthat could be used for the type of service and find all services on the network. This doesn't work with jmDNS. I can't find anything in the limited documentation about this.
Do I need to go through and add all the service types myself? That is not a very clean solution.
I have the proper perms in my AndroidManifest.
Try adding the dot at the end, my jmdns app crashed without it..
Also try adding the .local. by yourself with the service type.

How does shareit discovers nearby devices android programmatically [duplicate]

This question already has answers here:
How the "SHAREit" android application works technically?
(2 answers)
Closed 1 year ago.
I'm want to create a file sharing app like shareit but I'm really confused about how shareit discovers the nearby devices.
When you click receive button shareit creates a hotspot at the receiver side and the sender without connecting to the hotspot shows the receiver name. How is that possible?
If shareit uses Wi-Fi direct then what's the point of creating hotspot?
And to use Network Service Discovery (NSD) both server and client should be on same network so I think shareit is using something else
If anyone can explain this concept of shareit it will be very helpful.
I finally found the answer! SHAREit uses WiFi SSID to identify the nearby app users.
The SSID Consist of two partslike this. BAHD-bXViYQ WHERE 'B' Stands for ANDROID DEVICE and the AHD for the user icon. the second part is the user name encoded in Base64. In this example my name muba.
I hope this answer helps save some time.
Well I have found how to enable the hotspot from one sharing app and find the list of available wifi enabled by that sharing app into another one. just like shareIt receiver enables the wifi hotspot and sender discovers the list of available receivers.
First of all You have to scan all available wifi network using WifiManager
public void startScan(){
mWifiManager.startScan();
mScanResultList = mWifiManager.getScanResults();
mWifiConfigurations = mWifiManager.getConfiguredNetworks();
}
now pass this mScanResultList to a method which finds the network according to your requirement.
public static List<ScanResult> filterWithNoPassword(List<ScanResult> scanResultList){
if(scanResultList == null || scanResultList.size() == 0){
return scanResultList;
}
List<ScanResult> resultList = new ArrayList<>();
for(ScanResult scanResult : scanResultList){
if(scanResult.capabilities != null && scanResult.capabilities.equals(NO_PASSWORD) || scanResult.capabilities != null && scanResult.capabilities.equals(NO_PASSWORD_WPS)){
resultList.add(scanResult);
}
}
return resultList;
}
Now pass the resultList to arraylist adapter to show the network in list. Inside adapter's convertView method just pass that dataList to scanner to get ssid and mac address of the network
#Override
public View convertView(int position, View convertView) {
ScanResultHolder viewHolder = null;
if(convertView == null){
convertView = View.inflate(getContext(), R.layout.item_wifi_scan_result, null);
viewHolder = new ScanResultHolder();
viewHolder.iv_device = (ImageView) convertView.findViewById(R.id.iv_device);
viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
viewHolder.tv_mac = (TextView) convertView.findViewById(R.id.tv_mac);
convertView.setTag(viewHolder);
}else{
viewHolder = (ScanResultHolder) convertView.getTag();
}
ScanResult scanResult = getDataList().get(position);
if(scanResult != null){
viewHolder.tv_name.setText(scanResult.SSID);
viewHolder.tv_mac.setText(scanResult.BSSID);
}
return convertView;
}
This is the code to enable the hotspot
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 WiFi is on, turn it off
if(isApOn(context)) {
wifimanager.setWifiEnabled(false);
// if ap is on and then disable ap
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;
}

Prevent Android phone from connecting to WiFi network unless my app approves it?

I want to develop an app that can prevent connection to a WiFi network unless I approve it. I want to be able to query the MAC address of the access point and compare that to a list of known addresses corresponding to SSIDs. The goal of the app is to protect users from accidentally connecting to malicious access points, such as the types that can be produced with pineapple devices.
I'm not clear from my research how I would achieve this goal. Questions such as How to be notified on wifi network status change? explain how to detect the connection has happened, but for my use case that's already too late.
Neither ConnectivityManager nor WifiManager seem to offer methods for adding listeners that could interrupt a connection in progress.
Some thoughts I've had for a solution:
Install myself as a proxy and make the decision as to whether to allow data through. However, this doesn't seem to be an option based on Do Android proxy settings apply to all apps on the device? (hint: the answer is "No").
Replace the existing WiFi manager with something of my own creation. However, I've really struggled to find any information in the Android developer guides regarding replacing system components. Consequently, I'm not sure this is possible on non-rooted phones.
Store the network passwords within my app and set the passwords in the WiFi manager to nonsense values. Then capture a broadcast message that warns of a failed connection (presumably something like WifiManager.WPS_AUTH_FAILURE) and selectively decide to reconnect back to that network. Might be a possible (if ugly) solution, but can I set the password back to a nonsense value while the network is still connected, to ensure we don't quietly connect to another SSID of the same name? I'm not sure. It occurs to me that pineapple devices would probably accept any password, thus rendering this approach void.
Find some way to prevent Android automatically connecting to known networks (i.e. networks that have been used before or have a password stored with them). Then I could manage all connections/disconnections from my app. I can't see how to do this manually on my phone, however, so I'm doubtful this is possible programmatically.
Can anyone suggest an approach that would work on a non-rooted phone?
You can't implement a very robust system without rooting the device. Here's the closest you can get, I think:
Use getConfiguredNetworks() to fetch a list of networks currently configured on the user's device
For each WifiConfiguration in the list, set the public field BSSID to the desired "safe" MAC address
Call saveConfiguration() to persist the changes
Alternatively for step (2.), you could call disableNetwork() for each configured network, and selectively enabled them based on the BSSID. Note that MAC addresses can still be spoofed fairly easily.
you can listen to connectivity change of wifi and act on that events to enable disable wifi
private ConnectivityManager connectionManager;
boolean previousConnectivityStatus;
private WifiManager wifiManager;
/* Register Connectivity Receiver */
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
context.registerReceiver(networkBroadcastReceiver, intentFilter);
/* Register Wifi State Listener */
IntentFilter wifiStateIntentFilter = new IntentFilter();
wifiStateIntentFilter.addAction("android.net.wifi.WIFI_STATE_CHANGED");
context.registerReceiver(wifiStateReceiver, wifiStateIntentFilter);
connectionManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
private BroadcastReceiver wifiStateReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
Utility.traceM("NetworkController.wifiStateReceiver.new BroadcastReceiver() {...}::onReceive");
int extraWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
switch (extraWifiState)
{
case WifiManager.WIFI_STATE_DISABLED:
{
Utility.trace("Broadcast Wifi State Disabled");
if(isWifiStateEventsEnabled)
{
EventBus.getDefault().post(new NetworkEvent(NetworkEventType.WIFI_DISABLED));
}
break;
}
case WifiManager.WIFI_STATE_ENABLED:
{
Utility.trace("Broadcast Wifi State Enabled");
if(isWifiStateEventsEnabled)
{
EventBus.getDefault().post(new NetworkEvent(NetworkEventType.WIFI_ENABLED));
}
break;
}
}
}
};
private BroadcastReceiver networkBroadcastReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
Utility.traceM("NetworkController.networkBroadcastReceiver.new BroadcastReceiver() {...}::onReceive");
boolean connectivityStatus = isInternetConnectivityAvailable();
if (previousConnectivityStatus != connectivityStatus)
{
if (connectivityStatus)
{
previousConnectivityStatus = true;
Utility.trace("Broadcast Internet Available");
EventBus.getDefault().post(new NetworkEvent(NetworkEventType.INTERNET_CONNECTED));
}
else
{
previousConnectivityStatus = false;
Utility.trace("Broadcast Internet Disconnected");
EventBus.getDefault().post(new NetworkEvent(NetworkEventType.INTERNET_DISCONNECTED));
}
}
}
};
as you know when connecting to the Wifi the sifi manager app displays a hint message under the Wifi name that is connecting,
like connecting, authenticating, obtaining IP ... etc
so i tried to search how can detect those stages of connecting to a Wifi network
i came to an answer showing how is this done,
it was done using the a receiver to SUPPLICANT_STATE_CHANGED_ACTION
and i tried to implement it adding the code to just disconnect ... and that was success as the Wifi never got connected, the icon did not appear on the notification bar
and the logs keep repeating the steps, though some how it say's connected (at logs) but nothing actually appears on the device itself, so maybe it got connected for like (10 MS)
anyhow , below is the code i used:
public class MyNetworkMonitor extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// EXTRA_BSSID
// SUPPLICANT_STATE_CHANGED_ACTION
// EXTRA_NEW_STATE
// Log.i("YAZAN", intent.getAction() + " " +
// intent.getStringExtra(WifiManager.EXTRA_BSSID));
// Log.i("YAZAN", intent.getAction() + " "
// +intent.getStringExtra(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
// Log.i("YAZAN", intent.getAction() + " "
// +intent.getStringExtra(WifiManager.EXTRA_NEW_STATE));
//Log.i("YAZAN", intent.getAction() + " " + intent.getStringExtra(WifiManager.EXTRA_BSSID));
String action = intent.getAction();
if(action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)){
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Log.d("YAZAN", ">>>>SUPPLICANT_STATE_CHANGED_ACTION<<<<<<");
SupplicantState supl_state=((SupplicantState)intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE));
switch(supl_state){
case ASSOCIATED:Log.i("YAZAN", "ASSOCIATED");
break;
case ASSOCIATING:
Log.i("YAZAN", "ASSOCIATING");
wifi.disconnect();
Log.i("YAZAN", "disconnect()");
break;
case AUTHENTICATING:Log.i("YAZAN", "Authenticating...");
wifi.disconnect();
Log.i("YAZAN", "disconnect()");
break;
case COMPLETED:Log.i("YAZAN", "Connected");
break;
case DISCONNECTED:Log.i("YAZAN", "Disconnected");
break;
case DORMANT:Log.i("YAZAN", "DORMANT");
wifi.disconnect();
Log.i("YAZAN", "disconnect()");
break;
case FOUR_WAY_HANDSHAKE:Log.i("YAZAN", "FOUR_WAY_HANDSHAKE");
wifi.disconnect();
Log.i("YAZAN", "disconnect()");
break;
case GROUP_HANDSHAKE:Log.i("YAZAN", "GROUP_HANDSHAKE");
wifi.disconnect();
Log.i("YAZAN", "disconnect()");
break;
case INACTIVE:Log.i("YAZAN", "INACTIVE");
break;
case INTERFACE_DISABLED:Log.i("YAZAN", "INTERFACE_DISABLED");
break;
case INVALID:Log.i("YAZAN", "INVALID");
break;
case SCANNING:Log.i("YAZAN", "SCANNING");
break;
case UNINITIALIZED:Log.i("YAZAN", "UNINITIALIZED");
break;
default:Log.i("YAZAN", "Unknown");
break;
}
int supl_error=intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
if(supl_error==WifiManager.ERROR_AUTHENTICATING){
Log.i("YAZAN", "ERROR_AUTHENTICATING!");
}
}//if
}// onReceive()
where ever you find a wifi.disconnect(); thats how i interrupted the connection.
what remains here, is to get the network name or mac address to allow or disallow the process to complete
Permissions:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
adding the broadcast receiver:
<receiver android:name=".MyNetworkMonitor" >
<intent-filter>
<action android:name="android.net.wifi.supplicant.STATE_CHANGE" />
<action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
thanks

Categories