I need to take SSID of connected WiFi and first i tried this:
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiInfo = wifiManager.connectionInfo
textField.text = wifiInfo.ssid
But wifiInfo.ssid returns <unknown ssid> when testing on emulator with API 30 or Xiaomi Redmi Note 8 Pro(Android 11), just as in emulator with API 27 wifiInfo.ssid returns correct SSID.
Then i tried this:
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build()
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities)
//wifiInfo = networkCapabilities.transportInfo as WifiInfo
Log.d("%%%", "onCapabilitiesChanged: ${networkCapabilities.transportInfo}")
}
}
connectivityManager.requestNetwork(request, networkCallback)
connectivityManager.registerNetworkCallback(request, networkCallback)
But networkCapabilities.transportInfo always null
EDIT:
Permissions in manifest file
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
Request location permission in OnCreate():
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
And it successfully granted.
SOLVE 1:
I just forget to on Geolocation on my phone)
It solves problem while i using first method taking wifiinfi by wifiManager.connectionInfo but when i use second one by networkCapabilities.transportInfo it still returns null, and since first method is Deprecated in java maybe it is not good to use it.
Then, I would be glad to have your comments about the second method.
Google assumes that knowing ssid means knowing location.
Go for fine location permission and you will see.
Related
Migrating from android 30 to the android 31 version, I am trying to connect to the paired Bluetooth device in my app.
But, I am facing an issue when I tried to get the paired device list.
val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
pairedDevices?.forEach { device ->
if(device.address.lowercase() == configHelper.bluetoothAddress.lowercase()){
bluetoothDevice = device
paymentTerminalName = device.name
paymentTerminalMAC = device.address // MAC address
UUIDFromPaymentTerminal = device.uuids[0].uuid // UUID specifies the service that the the server provides
}
}
if(bluetoothDevice == null){
//payment terminal is not paired yet
throw ex
}
socket = bluetoothDevice!!.createRfcommSocketToServiceRecord(UUIDFromPaymentTerminal)
//cancel discovery, otherwise it slows down the connection process
bluetoothAdapter!!.cancelDiscovery()
(socket!! as BluetoothSocket).connect()
I am getting the below error,
java.lang.SecurityException: Need android.permission.BLUETOOTH_CONNECT
permission for AttributionSource { uid = 10414, packageName =
com.test.app, attributionTag = null, token =
android.os.BinderProxy#8a4f87d, next = null }: AdapterService
getBondedDevices
I have added bluetooth permission to my manifest file,
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
Let me know where am I missing to get the paired device in kotlin 1.5.0
The BLUETOOTH_CONNECT & BLUETOOTH_SCAN permissions are runtime permissions, these permissions have to be asked during the runtime of your application.
The BLUETOOTH_SCAN permission should be asked before you scan for devices and the BLUETOOTH_CONNECT permission should be asked before you connect to a device.
An example of a permission requester for an single permission within the Fragments architecture:
val requestConnectPermission: ActivityResultLauncher<String> =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { permissionGranted ->
if (permissionGranted) {
// connect to device
}
}
An example of a requester for multiple permissions within the Fragments architecture:
val permissionRequester: ActivityResultLauncher<Array<String>> = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (permissions.values.all { it }) {
// connect to device
}
}
To handle a single permission request within an Composable is slightly different, you can do the following:
val permissionRequester =
rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
// connect to device
}
}
requestConnectPermission?.launch(Manifest.permission.BLUETOOTH_CONNECT)
And for multiple permissions at once:
val permissionRequester =
rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (permissions.values.all { it }) {
// connect to device
}
}
Before you should use such a permission launcher you should check if the user has already accepted the permissions. If not you can launch a request. An example to launch a single permission request:
if (ActivityCompat.checkSelfPermission(requireContext(), BLUETOOTH_CONNECT) == PERMISSION_GRANTED) {
// connect to device
} else {
permissionRequester.launch(BLUETOOTH_CONNECT)
}
And if you want to launch multiple permission requests at once you can do this:
if (ActivityCompat.checkSelfPermission(requireContext(), BLUETOOTH_CONNECT) == PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(requireContext(), BLUETOOTH_SCAN) == PERMISSION_GRANTED) {
// connect to device
} else {
permissionRequester.launch(arrayOf(BLUETOOTH_CONNECT, BLUETOOTH_SCAN))
}
More info about permissions can be found here.
Description
I am unable to get WIFI SSID using the onCapabilitiesChanged in the ConnectivityManager.NetworkCallback class in Android-12.
In Android-12, getConnectionInfo is deprecated. So, as the android document suggests I am trying to get the WifiInfo object using onCapabilitiesChanged.
Like this,
#Override
public void onCapabilitiesChanged(#NonNull Network network, #NonNull NetworkCapabilities networkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities);
WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
}
But, when I print the WifiInfo object. it will print SSID: <unknown ssid> and getHiddenSSID return true.
Unexpected behavior is,
Android-12 device is connected with one WIFI (Ex. ABC)
When I try to get WIFI SSID using getConnectionInfo. it return
SSID: ABC and getHiddenSSID : false
But, for the same network when I try to get WIFI SSID using
onCapabilitiesChanged, it returns SSID: <unknown ssid> and
getHiddenSSID : true
Note: Location permission is provided.
After spending few days, finally found why I am not getting SSID in onCapabilitiesChanged.
We need to pass FLAG_INCLUDE_LOCATION_INFO in the ConnectivityManager.NetworkCallback constructor while creating an object.
in the default constructor, location sensitive information is hidden due to that we are not able to get SSID in onCapabilitiesChanged. Once we create NetworkCallback object with a flag we will get SSID.
Link
Get SSID and BSSID API31 Xamarin C# Example
Required permissions: CHANGE_NETWORK_STATE, ACCESS_FINE_LOCATION
If API<31 TransportInfo will return Null
using Android.Content;
using Android.Net;
using Android.Net.Wifi;
protected override void OnStart()
{
base.OnStart();
NetworkRequest request = new NetworkRequest.Builder().AddTransportType(transportType: TransportType.Wifi).Build();
ConnectivityManager connectivityManager = Android.App.Application.Context.GetSystemService(Context.ConnectivityService) as ConnectivityManager;
NetworkCallbackFlags flagIncludeLocationInfo = NetworkCallbackFlags.IncludeLocationInfo;
NetworkCallback networkCallback = new NetworkCallback((int)flagIncludeLocationInfo);
connectivityManager.RequestNetwork(request, networkCallback);
}
private class NetworkCallback : ConnectivityManager.NetworkCallback
{
public NetworkCallback(int flags) : base(flags)
{
}
public override void OnCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities)
{
base.OnCapabilitiesChanged(network, networkCapabilities);
WifiInfo wifiInfo = (WifiInfo)networkCapabilities.TransportInfo;
if (wifiInfo != null)
{
string ssid = wifiInfo.SSID.Trim(new char[] {'"', '\"' });
string bssid = wifiInfo.BSSID;
}
}
}
Click Android API reference.ConnectivityManager.NetworkCallback(int)!
Replace from new NetworkCallback() to new NetworkCallback(ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO).
Example Code:
final NetworkRequest request =
new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
final ConnectivityManager connectivityManager =
context.getSystemService(ConnectivityManager.class);
final NetworkCallback networkCallback = new NetworkCallback(ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
...
#Override
void onAvailable(Network network) {}
#Override
void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
}
// etc.
};
connectivityManager.requestNetwork(request, networkCallback); // For request
connectivityManager.registerNetworkCallback(request, networkCallback); // For listen
Reference Link:
https://developer.android.com/reference/android/net/wifi/WifiManager#getConnectionInfo()
https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#NetworkCallback(int)
this method work for me in all android versions, but For Android 8.0 (Oreo) and later versions, you need to have location services (GPS) turned on to access the Wi-Fi SSID. Also, you need to request the ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions at runtime and declare them in the manifest file.
If you want to access the Wi-Fi SSID in the background, you can write a foreground service to handle that.
private fun checkWIFIName(context: Context) : Boolean {
val wifiManager = context.applicationContext.getSystemService(WIFI_SERVICE) as WifiManager
val wifiInfo = wifiManager.connectionInfo
println("SSID : ${wifiInfo.ssid}")
return wifiInfo.ssid.trim().equals("\"$SSID\"", true)
}
I try to get my currend Wifi SSID on Android 10.
Code from Android 9 or lower does not work anymore.
Is there any sample for Android 10.
Regards Hacki
Since API 26 (Android 8 Oreo) you need to obtain the user location permission in order to get the wifi name (SSID), which is why also in Android 9 (API 28), Android 10 (API 29) or Android 11 (API 30) and newer you may get <unknown name> as the SSID returned or 02:00:00:00:00:00.
For that, in AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
I will provide some demo code written in Kotlin.
For declaring your permission request success code, we store it in a companion object (Kotlin way for storing constants) inside the class we are testing from (MainActivity in this case) or you may define a class explicitly for constants, which is actually a common practice.
class MainActivity : AppCompatActivity() {
...
companion object {
const val PERMISSION_CODE_ACCEPTED = 1
const val PERMISSION_CODE_NOT_AVAILABLE = 0
}
...
}
In your testing activity:
when(requestLocationPermission()){
MainActivity.PERMISSION_CODE_ACCEPTED -> getWifiSSID()
}
For checking an requesting the permission:
fun requestLocationPermission(): Int {
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)) {
} else {
// request permission
ActivityCompat.requestPermissions(this,
arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION),
MainActivity.PERMISSION_CODE_ACCEPTED)
}
} else {
// already granted
return MainActivity.PERMISSION_CODE_ACCEPTED
}
// not available
return MainActivity.PERMISSION_CODE_NOT_AVAILABLE
}
For actually getting the SSID (wifi name):
fun getWifiSSID() {
val mWifiManager: WifiManager = (this.getApplicationContext().getSystemService(Context.WIFI_SERVICE) as WifiManager)!!
val info: WifiInfo = mWifiManager.getConnectionInfo()
if (info.getSupplicantState() === SupplicantState.COMPLETED) {
val ssid: String = info.getSSID()
Log.d("wifi name", ssid)
} else {
Log.d("wifi name", "could not obtain the wifi name")
}
}
Tested on the emulator on API 29 (Android 10).
2020-10-04 15:35:28.625 13013-13013/com.example.myapplication D/wifi name: "AndroidWifi"
Set his two permissions requests into your project manifest file:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"\>
Than you have to set Application permission manually by using Settings screen of Android.
I'am working on a RSSI scanner for Android. I want to archieve this via constantly scanning for BLE devices. So if the RSSI of a device should be figured out it needs to advertise via BLE. The measuring device start scanning in really short intervalls and as it finds the advertiser the RSSI can be read in the scan result.
That results in folowing code on the advertiser side:
fun beginAdvertise() {
val advertiser = BluetoothAdapter.getDefaultAdapter().bluetoothLeAdvertiser
val settings = AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.setConnectable(false)
.build()
val data = AdvertiseData.Builder()
.setIncludeDeviceName(true)
.build()
val advertisingCallback = object : AdvertiseCallback() {
override fun onStartFailure(errorCode: Int) {
Log.e("BLE", "Advertising onStartFailure: " + errorCode);
super.onStartFailure(errorCode)
}
}
advertiser.startAdvertising(settings, data, advertisingCallback);
}
The function for the scanning device gets called every 20ms (I tried much longer delays) and looks like this:
private val onBluetoothLeDeviceFound = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult?) {
result?.let {...}}
fun startScan() {
val scanner = mBluetoothAdapter?.bluetoothLeScanner
val scanSetting =
ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE).build()
scanner?.startScan(
null,
scanSetting,
onBluetoothLeDeviceFound
)
}
This code is working perfectly on my Pixel 2 devices. But if I try it on Pixel 1 (one on Android 9, the other on Android 10) devices the onBluetoothLeDeviceFound callback does not get called.
I have these permissions:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCTION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
I really don'T know what I should try out next. Thank you for your help!
The solution was simple, but time consuming. You dont just need to have the location permissions, but also have to enable location services! On the Pixel 2 devices, they were randomly turned on, so it worked there, but not on the Pixel 1.
I am checking all permisions from the Manifest with the code bellow. Now I added USE_BIOMETRIC permission, because I want to use fingerprint, but this permission is not granted and I don't know why.
I know that USE_BIOMETRIC is normal permission so it shouldn't be asked and should be granted when it's in the Manifest, but it's not.
Checking for other permissions is working and all are granted or asked for it, only this one is not.
I am testing app on 2 phones, emulated Google pixel with Android 8.0 and API 26 and physical Xiaomi Redmi 5 with Android 8.1 and API 27.
Both phones are using fingerprint to unlock screen.
Permisions in the manifest:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
Code that loads permissions from the manifest and check if is granted or not:
fun checkAndRequestPermissions(): Boolean{
var listPermissionsNeeded = ArrayList<String>()
val permissions = retrievePermissions(activity.baseContext)
permissions.forEach {
if(ContextCompat.checkSelfPermission(activity.baseContext, it) != PackageManager.PERMISSION_GRANTED){
listPermissionsNeeded.add(it)
Log.d("Missing permission", it)
}
}
if (!listPermissionsNeeded.isEmpty()) {
val array = arrayOfNulls<String>(listPermissionsNeeded.size)
listPermissionsNeeded.toArray(array)
ActivityCompat.requestPermissions(activity, array, ConstantsStorage.ACTIVITY_REQUEST_PERMISSIONS_CODE)
return false
}
return true
}
companion object {
/**
* Retrieves permissions listed in the manifest file
* #param context Context
* #return Returns String array of permissions
*/
fun retrievePermissions(context: Context): Array<String> {
try {
return context
.packageManager
.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS)
.requestedPermissions
} catch (e: PackageManager.NameNotFoundException) {
throw RuntimeException("This should have never happened.", e)
}
}
}
Thank you for your help
According to the new policy in Google Console, you couldn't use this permission on the Manifest. So you should Request and Check in Activity.
You can use BiometricManager to check it:
val biometricManager = BiometricManager.from(this)
when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
BiometricManager.BIOMETRIC_SUCCESS ->
Log.d("MY_APP_TAG", "App can authenticate using biometrics.")
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
Log.e("MY_APP_TAG", "No biometric features available on this device.")
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE ->
Log.e("MY_APP_TAG", "Biometric features are currently unavailable.")
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
// Prompts the user to create credentials that your app accepts.
val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {
putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
}
startActivityForResult(enrollIntent, REQUEST_CODE)
}
}
Note: It's very important to add suitable import
import androidx.biometric.BiometricManager;