With Android 4.3, Android implemented the idea of always-on WiFi where, even if you had Wi-Fi toggled off, the device and apps could still scan for WiFi networks to improve the location's accuracy. Along with using network triangulation, it's another way of getting your current position as quickly as possible without having to rely too much on GPS signals.
Android M is taking the idea further, adding Bluetooth scanning to the equation. Under the Location settings on M, you'll find a Scanning option in the menu, where both Wifi and Bluetooth scanning can be toggled on and off. When enabled, Bluetooth scanning will presumably look for BLE devices like beacons to get a quicker location fix.
Image resized. Click to view in full size
This may be very useful in the future inside malls, airports, and various indoor or underground locations where the reach and dispersion of Bluetooth beacons can outweigh a slow or impossible GPS signal lock. And the fact that it's always on, accessible whenever apps need a location fix, will make it even handier than if you had to remember to manually turn on Bluetooth.
Can anyone help in providing some insights or sample code for scanning for beacons with BLE without the main Bluetooth settings turned on?
I figured it out.
We have to write a system application and use the
BluetoothAdapter.enableBLE()
method.
This method is for special/system apps which use Bluetooth Low energy to scan for nearby devices which is mostly used for location accuracy.Even if the Bluetooth is turned off in the device settings.
Then we can use
BluetoothAdapter.LeScanCallback
callback to get the device details.
Sample:
for calling the method:
mBluetoothAdapter.enableBLE())
for callback:
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if( device == null ){
System.out.println("-------onLeScan "+device);
}
runOnUiThread(new Runnable() {
#Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
Thanks
Related
I am scanning for Bluetooth LE devices and running as a Peripheral (running Android 6.0 on a Moto G 2nd Gen)
The problem I am having is that sometimes (randomly it seems but often) it will not find any of my other peripheral devices, the other times it works fine.
I have a companion iOS device running similar code (both scanning for peripherals and acting as a peripheral), and when the Android scanning can't find the iOS device, my iOS finds the Android device acting as a peripheral just fine. So it seems only to be a problem with the scanning side of things.
It's not only just not finding my companion iOS device, but doesn't find any Bluetooth devices. When it works, it finds my companion iOS device as well as a bunch of other devices.
I have tried it with and without ScanFilters, and get the same issue.
I am building against SDK 26 with a minimum SDK of 23.
I am setting the permissions that are needed, as it sometimes works.
Relevant code below:
private void startScanning() {
mHandler = new Handler(mContext.getMainLooper());
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0)
.build();
mBluetoothLeScanner.startScan(null, settings, mScanCallback);
}
}, 1000);
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if( result == null || result.getDevice() == null )
return;
Log.e("myTest", "Found Device");
BluetoothDevice device = result.getDevice();
final String deviceAddress = device.getAddress();
List<ParcelUuid> parcel = result.getScanRecord().getServiceUuids();
if (parcel != null) {
String parcelUUID = parcel.toString().substring(1,37);
if (parcelUUID.equalsIgnoreCase(mContext.getString(R.string.service_uuid))) {
final BluetoothDevice bleDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(deviceAddress);
if (!seenPeripherals.contains(deviceAddress)) {
stopScanning();
mHandler = new Handler(mContext.getMainLooper());
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.e("AppToApp", "Trying to connect to device " + deviceAddress);
mGatt = bleDevice.connectGatt(mContext, false, mGattCallback);
}
}, 1000);
}
}
}
}
}
I face the same issue. This is because Google policy has been changed for Marshmallow API 23 and higher version, to use BLE user need to turn ON GPS. For Google Docs check this Permission # Runtime link. To fix it you have to enable "Location" in the settings of the phone as well as request location permission in the app at Runtime. Both need to be done for scanning to work properly.
To request the location permission put the following in a dialog or the likes:
myActivity.requestPermissions(new String[]{Manifest.permission.ACCESS_COURSE_LOCATION}, yourPermissionRequestCode);
and implement:
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] Results){
if(requestCode == yourPermissionRequestCode)
{
... //Do something based on Results
}
}
in myActivity handle whatever the user selects. You also need to do the following to turn on your device's location services:
Intent enableLocationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
myActivity.startActivityForResult(enableLocationIntent, yourServiceRequestCode);
You can check if the user turned on the location services by implementing:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if(requestCode == yourServiceRequestCode)
{
...//Do whatever you need to
}
}
in myActivity. You can also manually turn on location services by doing:
Enter phone settings -> Select "Apps" -> Select your app -> Select the "Permissions" option -> Switch the "Location" permission on.
Once the user has enabled the permission and started location services then you could start scanning for peripherals. I've noticed that if you are already scanning while you enable the permission/turn on the location service it will still not put anything in your onScanResults
This allow companies to see your location and direct you to where they want.
while SCANNING for device, Once you are connected to the BLE device you can Turn Off the location service on your phone and you will still stay connected to your peripheral device. However, once you connected to a firmware (peripheral) you cannot then connect to any new peripheral until you disconnect the connection to the paired device, and all other scanning devices(mobile) cannot see the peripheral device name in their scanned list (when user search for near by peripheral device) when the peripheral is already connected to any other mobile device, because a peripheral can be connect to only 1 device at a time.
For BLE basic sample you can check this Truiton Ble tutorial. This snippet will fix your Issue I think. Happy coding :)
It might just simply be the case that the Android phone has a crappy Bluetooth chip. Have you looked at the hci log? Or logcat?
Bluetooth 4.0 chips (which is in your moto g) have strict limitations that you can't be a central and a peripheral at the same time, even though scanning should be allowed all the time. Therefore I wouldn't make a product that depends on the peripheral feature in Android until BT 4.0 is phased out.
You should test with a newer phone that has at least a Bluetooth 4.1 chip.
This question is related to something I posted before. Check these links
https://stackoverflow.com/a/39084810/3997720, https://stackoverflow.com/a/39597845/3997720
You need two methods: one for older api and one for newest api.
As for SDK 23 and up, You have to request run time permissions. Ask for location permission.
If you're using the app for non-commercial purposes (meaning expecting less than 5K users), you can use P2Pkit.
It uses P2P wifi connection in addition to BLE and non-BLE bluetooth, on older versions of android, and actually gets great results on discovery of nearby phones.
Upsides are it works on IOS too, and it has a fairly simple interface.
Downside is over 5K users you have to pay..
I am developing an Android app that scans for BLE devices periodically, and use the beacon information for further processes. I need to catch the Beacon which is closest.
// Scan for bluetooth devices and parse results
private void scanLeDevice() {
// Stops scanning after a pre-defined scan period.
if(!mScanning){
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d(TAG, "stop scanning");
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
Log.v(TAG, "closing scan. beacon found:" + beaconFound);
broadcastLocalUpdate();
beaconFound = false;
stopSelf();
}
}, SCAN_PERIOD);
proximity=null;
totalDevices=0;
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
Log.d(TAG, "start scanning");
}
}
The beacons I use:
Emit an advertisement 3x each second
Emit over 3 different channels
I have over 5 in a room
On one of my test devices, the Sony Xperia z2, this works. I get a long list of devices with Beacon information and RSSI values. each Beacon is discovered multiple times each scan.
On my other test device, the Motorolla Moto G(2nd generation), this scanning behaves WILDLY UNPREDICTABLE. On most scans only 2 or 3 devices are discovered. Other occasions it discovers about 10 devices (includes duplicates). It does not matter whether these devices are close or far.
Is this a software issue or a hardware issue?
How can I improve the results of a BLE scan?
Your beacons are not advertising frequently enough, creating a very real chance that no transmission occurs while the receiver is monitoring on its frequency. Try for at least 10 transmissions a second, but preferably more if you have an external power source.
Of course it is not all in the transmitter - Android phones vary drastically in how much they receive to detect BLE advertisements, and may report drastically different signal levels under the same conditions.
You may want to collect results over several scans in a short period, and look at the aggregate data, not just that of a single scan.
The scan result is for each received adv. No matter what rssi.
The antenna is a shared resource on most phones.
Wifi, bt classic, ble. So the phone only listens for adv in windows. Only if the adv falls into this window will get callback. The phone must listen on 3 channels.
So try playing with your adv interval and adv package size. If you have many in the same room you should not use too fast adv interval as the adv channels will be filled and collisions will occur.
Try turning off wifi and retest.
Avoid having classic bt connection.
Put name in scanresponse if you can to keep adv package short.
I'm developing an app to keep scanning certain smart bluetooth LE tags to check when a tag is out of range.
I'm getting one signal from each tag per second when I'm running my code in foreground. But in background (like scanning in a service) this rate keeps decreasing down to 1 signal per 10 seconds!
I'm almost sure that the frequency of the smart tag is the same in both cases, but maybe the Android operating system slows down discovering bluetooth devices due to battery issues when the scan happens in the background.
I'm afraid that this will be not satisfying, especially when you need to detect a bag is being stolen or something critical that forces me to keep the rate at 1 signal per 3 seconds or so.
UPDATE:
For the code I used, first:
private BluetoothAdapter bluetoothLeAdapter;
this for starting scanning:
bluetoothLeAdapter.startLeScan(LeScanCallback);
and this is the callback:
private BluetoothAdapter.LeScanCallback LeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
Log.i("Found: ", device.getName() + " - " + device.getAddress());
}
};
Android 4.3 and 4.4 do not slow down discovery in the background. In the Android Beacon Library, I had to put in extra code to slow down scans to save battery when apps are in the background.
I cannot explain what you say you are seeing. Perhaps you should try the library mentioned above and see if you see the same behavior. It is open source, so you can look at the code, too. It scans the same way as in your snippets in your question.
I am using Kontakt beacon device & testing ble on nexus 5. When I tested iPad as a beacon device I am able to find out the services & characteristics with the help of the code given on android developer site. But now I want to do some actions depending on the distance of the mobile from the beacon device. So I tried to detect how far is the mobile from the beacon device like iOS CLBeacon.Proximity (CLProximityFar, CLProximityNear, CLProximityImmideate & CLProximityUnknown). I searched but not able to find out like this in android. So decided to do some actions depending on the rssi. But problem is whatever rssi I am getting first time i.e at the time of mobile detect beacon device is not changing even if I move near to beacon device or went far away from it.
If any one had idea how to get the changing rssi in the android then please share.
Thanks.
I'm not sure how you checked the never changed rssi as you described, via a existed APP or writing codes by yourself?
but from the code, it's quite straight forward, I put a core code in Android 5.0,
//get your device's BT adapter
mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
mLescanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder().setScanMode(scanSettings.SCAN_MODE_LOW_LATENCY).build();
mLescanner.startScan(this.mLeScanCallback);
And the callback is like this, and you can get the real time rssi in the function:
this.mLeScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
int realTimeRssiYouWant = result.getRssi();
}
}
Is it possible to connect with other Bluetooth LE device with out scanning.
I am working on app when bluetooth is ON and then automatically received the notification when I enter in any marketPlace that Beacon device(basically Bluetooth LE) is in your range. without my scanning Bluetooth Le. My Bluetooth is just ON. no scanning.
Because our requirements are that bluetooth doesn't scan just on, when ever new BLE is in range show alert or any notification.
I implement some scan method
startScan(){}
stopScan(){}
#Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {}
but i don't want that i directly want to get connection message.
Please help me in form of pieces of code and also with little bit explanation
Thanks
You can use BluetoothAdapter.
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(mac);
device.connectGatt(mContext, false, mGattCallback);
// TODO your code
If the bluetooth device is not around, in BluetoothGattCallback's onConnectionStateChange will report BluetoothGatt.STATE_DISCONNECTED.
BluetoothDevice's creator has packages scope. But the source code of BluetoothAdapter's getRemoteDevice is:
public BluetoothDevice getRemoteDevice(String address) {
return new BluetoothDevice(address);
}
Do you just want to discover devices that support a particular service? There is an overload startLeScan(UUID[], ...), where you can pass the UUID's of the services that you are interested in.
Otherwise, if you just want to connect to a device of a known BT address, you maybe able to create a Bluetooth device object with that address and call connectGatt() on it. Just an idea, not sure if this would work :-)
Creating a bluetooth device object with address is not possible!
BluetoothDevice's creator has packages scope. so you can not create BluetoothDevice.
And although BluetoothDevice implements Parcelable, it cannot be created from a file.