I have a mobile application that allows users to enable/disable WiFi on click of a button.
However I noticed today that my app is no longer able to change the WiFi status. It was working since before few weeks. I tried to debug it but the following method always returns false.
boolean result = wifiManager.setWifiEnabled(true);
I am testing it on Samsung Galaxy Note 9 and Android 10.
This API is no longer supported when targeting Android 10 or higher.
Starting with Build.VERSION_CODES#Q, applications are not allowed to enable/disable Wi-Fi. Compatibility Note: For applications targeting Build.VERSION_CODES.Q or above, this API will always fail and return false. If apps are targeting an older SDK (Build.VERSION_CODES.P or below), they can continue to use this API.
Instead, you should use the Settings.Panel API to present a system UI allowing users to enable or disable Wi-Fi.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startActivity(Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY))
}
I came to know the privacy changes for Android 10 and I'm quite clear that third-party apps won't be able to get IMEI now.
But one thing from documentation is creating confusion for me.
They state
If your app targets Android 9 (API level 28) or lower, the method returns null or placeholder data if the app has the READ_PHONE_STATE permission. Otherwise, a SecurityException occurs.
which means that on Android devices with API LEVEL 28 or lower, this method returns null or placeholder data, even if the app is having READ_PHONE_STATE permission. Right?
But I have tested this thing on my app targetting API LEVEL 28 and I am still able to get IMEI number with the following.
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
fragment.requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_CODE);
}
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String deviceId = telephonyManager.getImei();
Shouldn't it send me NULL or placeholder (garbage) data?
any idea about it? or am I misinterpreting it?
You're misinterpreting it. What documentation says, is:
If your application targets Android 9 (API level 28) or lower, then it will return null or placeholder data when running on Android 10 device.
Documentation describes here how it will behave on Android 10 in case you don't target Android 10. It's so applications don't suddenly break in strange ways despite not being updated. It has no effect on Android 9 devices (and on emulators that emulate Android 9 devices, which is probably what you're testing it with when targeting API level 28).
I'm trying to make a network usage monitor app, which shows mobile data usage history to the user. For this I'm using Usage access to get accurate data usage stats from NetworkStatsManager. But this no longer works in Android 10.
I'm using NetworkStatsManager.querySummaryForDevice which requires subscriber Id, which earlier I was able to obtain using TelephonyManager.getSubscriberId.
But the getSubscriberId is now not working in Android 10 as it requires READ_PRIVILEGED_PHONE_STATE which third-party apps cannot have.
Any ideas on how to make it work? I understand the restrictions for getting subscriber Id, but I don't really care about the subscriber Id as long as I get the mobile data usage, for which I have enough permissions.
Try passing null as subscriber ID in the querySummaryForDevice method .. That worked for me
When calling NetworkStatsManager resolve subscriberId as follows:
use null when running on Android versions 10+ (API Level >= 29) devices
on prior versions of Android (API Level < 29) you should still resolve subscriberId (using TelephonyManager)
Here is a sample code that should help:
public static String getSubscriberId() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return telephonyManager.getSubscriberId();
} else {
return null;
}
}
Worked for me on Android API Level 29 and Android API Level 26 devices.
There's no way at the moment to get the information you want unless your app is a profile or device owner app. You can just use the TrafficStats but you can't use a query and it resets on reboot.
I'm using altbeacon library to monitor and range beacons. I've read your requesting permission page and just want to know, if I target location permission for API 23+ (), will scan works on devices with API < 23? I don't have real device, so can't test it. Or is there any way to not request location permission with device with API below 23? Thanks for your answers
Restating the core question:
If you build an app that has a minSdkVersion < 23 but the targetSdkVersion >= 23, what happens when you try to scan for bluetooth beacons?
Short answer: it works.
Longer answer:
The user permissions request won't happen. with a minSdkVersion < 23, The compiler will stop you from including a line of code like requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
because it won't run on earlier Android versions. If you wrap it in an if statement like if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) it won't get executed. If you add annotations like #SuppressLint("NewApi") the app will crash when trying to execute the code.
The beacon scanning will just work, both in the foreground and the background, regardless of the fact that the user permissions have not been granted. Earlier Android versions can't request the permission from the user, so the app just behaves as if they have been granted.
On Android 23 and on, you need to check if the app has been granted a "dangerous" permission and if not ask the user.
The Android support library has helper functions for this.
See Requesting Permissions at Run Time.
The main functions are checkSelfPermission and requestPermissions.
In order not to have problems with older versions of Android you can use the following to check if you are on a device running Marshmallow or not:
public static boolean isMNC() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
This will return true if you are on a device running Marshmallow or newer, false otherwise. So, if this returns true, check for the permission, otherwise don't.
You should probably also use the annotation #SuppressLint("NewApi") on the function where you call the checkSelfPermission and requestPermissions.
After upgrading to Android version 6.0 Bluetooth Low Energy (BLE) scanning will only work if Location services are enabled on the device. See here for reference: Bluetooth Low Energy startScan on Android 6.0 does not find devices
Basically, you need to have the permission enabled for the app as well as on for the phone. Is this a bug? Is it possible to scan without location services actually enabled? I don't want to have to have location for all my apps.
EDIT
I failed to mention that I am using the startScan() method in BluetoothLeScanner provided in API 21. I am okay with the course and fine location permissions in the manifest that this method require. I just don't want the users of my app to have to enable location services on their device (GPS, etc.) to use my app.
Previously, the startScan() method would run and return results with the Location services disabled on the phone. On Marshmallow, however, the same application would "scan" but silently failed and returned no results when location services were not enabled on the phone and course/fine location permissions were still in the manifest.
No, this is not a bug.
This issue was brought up to Google where they responded saying that this was the intended behavior and they won't fix it. They directed developers to this site where it points out that location permission is now needed for hardware identifier access. It is now the developer's responsibility to make their users aware of the requirement.
In the issue, however, it doesn't address why Location services (GPS, etc.) are required and it doesn't seem like they are going to revisit the issue to explain this since it has been marked as the intended behavior.
To answer the second part of the question: Yes, it is possible to scan without enabling Location services. You can do a Bluetooth classic scan using BluetoothAdapter.getDefaultAdapter().startDiscovery() and that will work with Location services off. This will discover all Bluetooth devices, BLE and otherwise. However, BLE devices won't have a scan record that they would have had if they were seen as a result of startScan().
I solved this by setting targetSdkVersion to 22 in the Gradle file.
You must declare ACCESS_COARSE_LOCATION in the manifest but, BLE scanning will work even if the user denies this permission from App Settings.
This is just a hack to avoid requesting location permission. It's better to target the latest android versions.
Edit
This solution should no longer be used as Google Play will require that new apps target at least Android 8.0 (API level 26). Apps should request for location permission for BLE scanning.
What I found is that after Android 6 you must grant ACCESS_COARSE_LOCATION permission. But on some devices is also necessary your phone location service (GPS) to be switched on, so you can discover peripheral devices. I found that using Nexus 5x, with Android 7.0.
I also tried this on manifest but did not request permission, not sure why. Is you app prompting for Location permission on startup? If it's not, we need to request for permission on runtime.
Also you can check this to test if your app is working fine:
Open Settings > Apps > YourApplication > Permissions
and enable Location and then try to scan for results.
Location will be listed here only if you have provided ACCESS_COARSE_LOCATION on manifest.
You can use BluetoothAdapter.startDiscovery().
It will scan for both Bluetooth Smart and classic Bluetooth devices, but location services do not need to be enabled.
(You still need ACCESS_COARSE_LOCATION permissions on Android 6.)
You can call BluetoothDevice.getType on found devices to filter for Bluetooth Smart / Low Energy devices.
after you add ACCESS_COARSE_LOCATION to Manifest,
ask for permission on runtime:
public void checkPermission() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
} else {
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,}, 1);
}
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
} else {
checkPermission();
}
}
worked for me!
You can scan BLE devices without location access using CompanionDeviceManager (API26).
https://developer.android.com/reference/android/companion/CompanionDeviceManager.
Well, I have looked at my code written in Eclipse and I use there the startScan (API 21) function without declaring location stuff in manifest file. I still get the proper callback.
Have you tried running the code without the location declaration?
In the other hand - you can use the deprecated startLeScan (API 18) which does not require these permissions. However, in my opinion searching and reading desired characteristic in service is more complicated with API 18 methods.
From what I recently noticed on android 8.0, it is not required to turn on your GPS to do a BLE Scan, but you have to declare it in the manifest, but the user must allow the permission.
Android will prompt the user to allow location permission when you attempt to do a scan with startScan() method. Your scan will fail if the permission is not allowed.
Starting with API 31, you can use a new BLUETOOTH_SCAN permission instead of location permission.
If the app does not derive physical locations, you can add the android:usesPermissionFlags="neverForLocation" attribute to the BLUETOOTH_SCAN permission declaration:
<manifest ...>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
...
</manifest>
And then:
val requiredPermissions = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
} else {
arrayOf(Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN)
}
requestPermissions(requiredPermissions, 9999)
More: https://xizzhu.me/post/2021-10-05-android-12-bluetooth-permissions/