I am a beginner working on a BLE scanner using Google's sample code: https://github.com/googlesamples/android-BluetoothLeGatt
Currently, the code is not able to detect any devices. This post says that this is because of location services being disabled or not being explicitly asked for on runtime. I have already included the required permissions in my Manifest.
I have added a few lines of code, which I think should work.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setTitle(R.string.title_devices);
mHandler = new Handler();
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
return;
}
// Quick permission check
int permissionCheck = this.checkSelfPermission("Manifest.permission.ACCESS_FINE_LOCATION");
permissionCheck += this.checkSelfPermission("Manifest.permission.ACCESS_COARSE_LOCATION");
if (permissionCheck != 0) {
this.requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1001); //Any number
}
}
I think the quick permission check I have added should work, but the app still doesn't seem to be detecting any devices.
I have met the same case that you are facing. If you are using android 6.0 or greater than. You must request location permission at runtime. After get Bluetooth Adapter, let's insert a code line as below to request location permission. When your app run, a dialog will be showed to ask whether you agree to shared your location.
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1001);
Let response my answer if this is not correct answer for you question, I will delete immediately.
onscannerregistered status =133
This might be because of the below reason.
1)Location Permission not given in Appication.
2)When Connecting to the Devies stop the scan and then connect to devices.
3)Scanning should not happen when we are reading/writing Data to Devices.
Make sure the scan is stopped
4)If Frist scan is started should wait until the scan is finished.Then start the second scan.
5)Scanning multiple times without waiting for the previous scan to finish might result in couldn't find the scan call-back.
Try fast BLE Library it s more effective the Google BLE library available.
Related
I've created a sample Wear OS app, which should discover BLE devices, but my code requires Bluetooth permission. When I put these lines in manifest:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
corresponding permission is not displayed in settings/apps/permissions and every permission request does nothing. By the way, my BLE-devices (a speaker and a esp-32) is not shown in settings/Bluetooth also.
How can I grant Bluetooth permissions for my app or how can I connect BLE device to my watch?
upd:
I tried these:
if (checkSelfPermission(Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.BLUETOOTH), 1001)
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.BLUETOOTH_CONNECT), 1001)
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.BLUETOOTH_SCAN), 1001)
}
if (checkSelfPermission(Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.BLUETOOTH_ADMIN), 1001)
}
But dialogs windows still are not displayed
According documentation, you need particular permissions in based of the target Android API version.
If your app targets Android 11 (API level 30) or lower, declare the following permissions in your app's manifest file:
BLUETOOTH is necessary to perform any Bluetooth classic or BLE communication, such as requesting a connection, accepting a connection, and transferring data.
ACCESS_FINE_LOCATION is necessary because, on Android 11 and lower, a Bluetooth scan could potentially be used to gather information about the location of the user.
If your app targets Android 9 (API level 28) or lower, you can declare the ACCESS_COARSE_LOCATION permission instead of the ACCESS_FINE_LOCATION permission.
In order to perform a scan to discover BLE devices the app must require explicitaly to the user the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission also to be declared in the AndroidManifest.xml.
In my project (WearOS API version 28) I used this code in the onCreate function of MainActivity class
if (ContextCompat.checkSelfPermission(
this, Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this,new String[] { Manifest.permission.ACCESS_FINE_LOCATION },
1);
}
And I overrided the onRequestPermissionsResult function
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case 1:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Logger.d("MainActivity","Permission approved");
} else {
Logger.d("MainActivity","Error getting permission");
}
return;
}
}
This works for me, I hope would help you
there are some permissions like camera, bluetooth which need to be asked first and then manually provided. use this in the activity that loads first in your app.
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(arrayOf<String>(Manifest.permission.CAMERA), 1001);
} //ask camera permissions
make sure to do required changes.
I am developing an app that allows a user to connect to a specific wifi network using the google vision api to scan a QR code containing an ssid. To scan for wifi networks I need to request the user to grant the location permission. This all works fine if i go to the settings and manually enable the Location permission but when requesting the permission at run-time, the user taps enable then a second dialog pops up, Screen Overlay Detected which requires a context switch to enable the Screen Overlay permission. I am not drawing over other apps and when i manually enable the location permission there is no problems. So why is this permission dialog popping up?
As I am not sure why Android is asking for this permission i am not sure what code to post. i have included the request for the 3 required permission for the change network request.
final int permissionCheck = ContextCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA);
final int locationPermissionCheck = ContextCompat.checkSelfPermission(mActivity, Manifest.permission.ACCESS_COARSE_LOCATION);
final int wifiPermissionCheck = ContextCompat.checkSelfPermission(mActivity, Manifest.permission.CHANGE_WIFI_STATE);
final int granted = PackageManager.PERMISSION_GRANTED;
if ((permissionCheck != granted) || (locationPermissionCheck != granted) || (wifiPermissionCheck != granted)) {
ActivityCompat.requestPermissions(mActivity, new String[]{
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.CHANGE_WIFI_STATE
}, 0);
} else {
cameraSource.start(cameraView.getHolder());
}
Update
Turns out its not just the location permission that trips it up. Any permission request is experiencing the same behaviour.
It may be some libraries you are using in your that requires screen overlay permission. Can you check the libraries you are using in your and the permission they require.
I'm currently working on a little app to get started with the services that the Bluetooth Android API can provide.
Edit -> Answer:
It seems that the issue was due to the specific Nexus 5 devices. Seems like their bluetooth receiver doesn't work well. Solution below should work for other devices
Remark:
I’ve read the documentation here: http://developer.android.com/guide/topics/connectivity/bluetooth.html
as well as the following source code of this tutorial http://www.londatiga.net/it/programming/android/how-to-programmatically-scan-or-discover-android-bluetooth-device/ located on github under /lorensiuswlt/AndroBluetooth
I’ve finished almost all the features that interested me (such as check for adapter existence, enable/disable the blueooth, querying paired divices, set the adapter discoverable).
Issue:
Actually no device is found when i launch the .onDiscovery() method, even though devices are found from Settings/Bluetooth on my Nexus 5.
Here is how I handle it:
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
...
protected void onCreate(Bundle savedInstanceState) {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, filter);
}
The filter is working well as far as i could try, i.e ACTION_STATE_CHANGED (on bluetooth enabling) and the two ACTION_DISCOVERY_***.
The following method is then successfuly called:
public void onDiscovery(View view)
{
mBluetoothAdapter.startDiscovery();
}
And then i have my bluetooth receiver:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (state == BluetoothAdapter.STATE_ON) {
showToast("ACTION_STATE_CHANGED: STATE_ON");
}
}
else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
mDeviceList = new ArrayList<>();
showToast("ACTION_DISCOVERY_STARTED");
mProgressDlg.show();
}
else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action) && !bluetoothSwitchedOFF) {
mProgressDlg.dismiss();
showToast("ACTION_DISCOVERY_FINISHED");
Intent newIntent = new Intent(MainActivity.this, DeviceListActivity.class);
newIntent.putParcelableArrayListExtra("device.list", mDeviceList);
startActivity(newIntent);
}
else if (BluetoothDevice.ACTION_FOUND.equals(action)) {// When discovery finds a device
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mDeviceList.add(device);
showToast("Device found = " + device.getName());
}
}
};
I don't have any issue coming out the logcat and didn't notice any trouble during the test I did. The only problem is that no device is discovered at the end of the scan, when many discoverable ones are available arround.
I tried to not put too much code in order to not flood the topic. Ask me if you need more.
Thanks for reading me, and thanks in advance for you answers.
What version of Android are you running this on? If it is Android 6.x, I believe you need to add the ACCESS_FINE_LOCATION permission to your manifest. For example:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
I had a similar issue and this fixed it for me.
UPDATE: Adding documentation direct from Google on this:
To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions
UPDATE 2020: We recently updated our application to target SDK Version 29. In doing this, our application stopped being able to discover Bluetooth devices again. We have been using ACCESS_COARSE_LOCATION since this answer was originally written. Changing to ACCESS_FINE_LOCATION appears to fix the issue. I now recommend developers try ACCESS_FINE_LOCATION if targeting 29+. Answer updated to reflect this.
Bit late to the party but this may come in handy for other people.
You need to do two things
Add <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
or <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
to your AndroidManifest.xml
Make sure you're requesting the permission on runtime as well for Android 6.0 Devices by using something like this
int MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION = 1;
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
just before mBluetoothAdapter.startDiscovery();
If you don't do step 2 you won't be able to get any Hardware Identifier info on device with Android >= 6.0
UPDATE/EDIT:
Example with Android Version check and alert dialog as warning
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // Only ask for these permissions on runtime when running Android 6.0 or higher
switch (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.ACCESS_COARSE_LOCATION)) {
case PackageManager.PERMISSION_DENIED:
((TextView) new AlertDialog.Builder(this)
.setTitle("Runtime Permissions up ahead")
.setMessage(Html.fromHtml("<p>To find nearby bluetooth devices please click \"Allow\" on the runtime permissions popup.</p>" +
"<p>For more info see here.</p>"))
.setNeutralButton("Okay", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(DeviceListActivity.this,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
REQUEST_ACCESS_COARSE_LOCATION);
}
}
})
.show()
.findViewById(android.R.id.message))
.setMovementMethod(LinkMovementMethod.getInstance()); // Make the link clickable. Needs to be called after show(), in order to generate hyperlinks
break;
case PackageManager.PERMISSION_GRANTED:
break;
}
}
As with new android version things are changed a bit
Older android version like android 10 needs FINE_LOCATION where as newer android version like android 12 works with COARSE_LOCATION also. hence my advice is to ask for FINE_LOCATION permission which will work in both. don't ask COAR_LOCATION and FINE_LOCATION simultaneously otherwise it will not work with android 12 and greater if user select coarse location at app start up.
I have request the permission in android version 6.0 - Marshmallow,But it still return empty list when using getScanResults().
private boolean checkPermission() {
List<String> permissionsList = new ArrayList<String>();
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
}
if (permissionsList.size() > 0) {
ActivityCompat.requestPermissions((Activity) mContext, permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
return false;
}
return true;
}
After request permission, then in the onRequestPermissionsResult method,I have get the permission of ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION, But I still can not the scan result
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
if (permissions.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED ||
(permissions.length == 2 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
grantResults[1] == PackageManager.PERMISSION_GRANTED)){
List<ScanResult> scanResults = mWifi.getScanResults();
//list is still empty
}
else {
// Permission Denied
Toast.makeText(mContext, getString(R.string.permission_deny), Toast.LENGTH_LONG).show();
}
break;
}
}
Is this a bug of android M?
You still need to enable WIFI after you request the permission. So in short, you have to do this in sequence for scanning the perimeter:
Request the necessary permissions (ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, ACCESS_COARSE_LOCATION). Additionally, on MM you need to request this at run-time, as you stated.
Enable WIFI with WifiManager#setWifiEnabled(true);
You don't have to enable location access programatically that I know off. But read the note below.
You have to register a BrodcastReceiver for SCAN_RESULTS_AVAILABLE_ACTION. This is where you get the signal that the scans are ready. Doesn't matter if you register through AndroidManifest or dynamically at run-time, as long as it's done before the next step.
You have to WifiManager#startScan() in order to request exactly ONE update for network scans. If you want more, set up a timer/timertask (recommended) or reschedule when you receive the previous one (which might never come)
Only on the BroadcastReceiver onReceive will you be able to call WifiManager#getScanResults() with plausible results.
Note: On some phones (Moto X 2014), I noticed you need basic location enabled to get any results, which only the user (system UI) seems to be able to do trigger on/off. If the user has location completely off, I can't seem to get a non-empty result list, even though the system UI can. This is likely due to Marshmallow needs to have location for Bluetooth and WiFi scans in user apps, and a bad implementation by Motorola, or a defect already fixed in latest Marshmallow bug tracker but not in Motorola's latest OTA, because this doesn't happen in a Nexus 5 or a Galaxy S6.
on nexus 5, with M update, it appears to me I also need to have GPS location turned on to get this working.
Maybe it cause by android M's runtime permission management,you can set targetSdkVersion 19(less than 21 or 23) to try again. It works for me
In Android Marshmallow 6.0.1 go to Settings > Connections and hold down Location. There click on Location method and you will see three options:
High accuracy: uses GPS, Wi-Fi and mobile networks.
Battery saving: uses Wi-Fi and mobile networks.
Device only: uses GPS.
Select Battery saving in order to get Wi-Fi scan results without enabling GPS.
The permission is requested when needed at run-time. Note that it is NOT:
Settings -> Location
It is checking:
Settings -> Applications -> (YOUR APP) -> Permissions
That is where the more granular run-time permission must be set. It must be set for each application as of version 6.
I am developing an application with NFC and wifi direct. I get the MAC address using NFC and the Wifi Direct to transfer data. I call discoverpeers() and could get success. But there is no callback WIFI_P2P_PEERS_CHANGED_ACTION, the callback comes only when I go to settings and the select wifidirect.
This was discussed in the other question
Can I turn on WiFi-Direct from code? on Android API-14 (ICS)
"I'd like to add that WiFi direct on JB and above (at least on AOSP) is not active all the time - it only appears to be. If you look at listeners for WiFi direct, it turns itself off after some time. It turns itself back on if you open the wifi direct menu, however. You might have to have the host do a peer search or initialize itself in order to be able to be found. Likely a battery saving trick. I have also found that it's blocking, since as it accepts a connection, the entire system will lock up and fail to connect sometimes. (The system invitation) – Mgamerz "
Can anyone suggest the solution for the problem WIFI_P2P_PEERS_CHANGED_ACTION callback is not got and can get only when manually go to settings->wifi->tap on wifidirect
I used two devices Samsung galaxy nexus and nexus 7 both running on 4.2.2
There is no available API to enable wifiP2P but you can invoke method "enableP2p" from android settings 4.0.1
WifiP2pManager manager = (WifiP2pManager) getActivity().getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(getActivity(), getActivity().getMainLooper(), null);
try {
Method method1 = manager.getClass().getMethod("enableP2p", Channel.class);
method1.invoke(manager, channel);
//Toast.makeText(getActivity(), "method found",
// Toast.LENGTH_SHORT).show();
} catch (Exception e) {
//Toast.makeText(getActivity(), "method did not found",
// Toast.LENGTH_SHORT).show();
}
To disable wifiP2P use this method
Method method1 = manager.getClass().getMethod("disableP2p", Channel.class);
Not from code. The user has to. That's why the demo has the link to wifi settings in the action bar.
When you call manager.discoverPeers(channel, new WifiP2pManager.ActionListener()
define onFailure and look at the reasonCode. If it's 0, then either the Wifi or WiFi direct is off.
If you look at the WiFi Direct demo app, the WifiDirectBroadcast Reciever, this piece of code looks at whether p2p is enabled specifically
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
// UI update to indicate wifi p2p status.
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
// Wifi Direct mode is enabled
activity.setIsWifiP2pEnabled(true);
} else {
activity.setIsWifiP2pEnabled(false);
activity.resetData();
}
Then when discover peers is called it looks at the variable set by setIsWifiP2pEnabled
thanks user3093354. to continue with your solution, in order to disable the p2p you have to invoke:
Method method1 = manager.getClass().getMethod("disableP2p", Channel.class);
//Try this it may be help you
WifiManager wifiManager = (WifiManager)this.getSystemService(this.WIFI_SERVICE);
wifiManager.setWifiEnabled(true); //True - to enable WIFI connectivity .
//False -disable WIFI connectivity.
//add this permissions in Manifest file :
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
You can load the wifi driver from a command prompt with the desired concurrency level if you are rooted:
/system/bin/insmod /system/lib/modules/wlan.ko con_mode=3
These are the values:
typedef enum
{
VOS_STA_MODE=0,
VOS_STA_SAP_MODE=1,
VOS_P2P_CLIENT_MODE,
VOS_P2P_GO_MODE,
VOS_MONITOR_MODE,
VOS_FTM_MODE = 5,
VOS_IBSS_MODE,
VOS_P2P_DEVICE_MODE,
VOS_MAX_NO_OF_MODE
} tVOS_CON_MODE;
This is for an Atheros card.