在 Android API 级别 28 及以上连接时如何获取 SSID、频段等 Wifi AP 详细信息?
How to get Wifi AP details like SSID, Band, etc when connected in Android API level 28 and above?
我正在尝试获取 Wifi AP 详细信息。我正在使用以下代码
private val TAG = "[MainActivity]"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if(WifiManager.NETWORK_STATE_CHANGED_ACTION == intent?.action){
val networkInfo = intent.getParcelableExtra<NetworkInfo>(WifiManager.EXTRA_NETWORK_INFO)
if(ConnectivityManager.TYPE_WIFI == networkInfo.type){
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val info = wifiManager.connectionInfo
Log.d(TAG, "info = $info")
}
}
}
}
val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION)
applicationContext.registerReceiver(receiver, intentFilter)
}
NetworkInfo 和 ConnectivityManager.TYPE_WIFI 显示为已弃用。另外,我得到的 SSID 为空。
info = SSID: , BSSID: 02:00:00:00:00:00, MAC: 02:00:00:00:00:00, Supplicant state: COMPLETED
我使用的权限:
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
完成上述任务的最佳方法是什么?
您必须拥有位置权限才能获取 WiFi AC 详细信息(参见 Location Requirements for WiFi APIs)。
在您的清单中声明 ACCESS_FINE_LOCATION
不足以获取 WiFi 信息 - 用户必须授予您该权限(通过在运行时向用户请求权限或由用户手动打开权限在应用信息中)。
此外,在 Android 10 中,google 添加了 enforcement that prevents you from getting the permission in the background,因此请记住,只有当您的请求 ACCESS_FINE_LOCATION
才允许您获取 WiFi AC 详细信息应用程序在前台。
要在后台获取 WiFi AC 详细信息,您必须声明(并被授予)ACCESS_BACKGROUND_LOCATION
以及常规位置权限。
@Shlomi 回答的补充,
下面是运行时获取用户权限的示例
Perform a check before performing operation
if (!checkPermissions()) {
// App does not have permissions, ask for permissions.
requestPermissions()
} else {
// App has permissions
// Perform your operations here......
}
Code to check and ask permissions
Kotlin
/**
* Return the current state of the permissions needed.
*/
private fun checkPermissions(): Boolean {
val permissionState = ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION
)
return permissionState == PackageManager.PERMISSION_GRANTED
}
private fun requestPermissions() {
val shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
)
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(
TAG,
"Displaying permission rationale to provide additional context."
)
Snackbar.make(
binding.wifiConnectionFragment,
"Location permission is needed for core functionality",
Snackbar.LENGTH_INDEFINITE
)
.setAction("OK") { // Request permission
ActivityCompat.requestPermissions(
requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_PERMISSIONS_REQUEST_CODE
)
}
.show()
} else {
Log.i(TAG, "Requesting permission")
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(
requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_PERMISSIONS_REQUEST_CODE
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.isEmpty()) {
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
// Perform your operations
} else {
// Permission denied.
// Notify the user via a SnackBar that they have rejected a core permission for the
// app, which makes the Activity useless. In a real app, core permissions would
// typically be best requested during a welcome-screen flow.
// Additionally, it is important to remember that a permission might have been
// rejected without asking the user for permission (device policy or "Never ask
// again" prompts). Therefore, a user interface affordance is typically implemented
// when permissions are denied. Otherwise, your app could appear unresponsive to
// touches or interactions which have required permissions.
Snackbar.make(
binding.wifiConnectionFragment,
"Permission was denied, but is needed for core functionality.",
Snackbar.LENGTH_INDEFINITE
)
.setAction(
"Settings",
View.OnClickListener { // Build intent that displays the App settings screen.
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts(
"package",
BuildConfig.APPLICATION_ID, null
)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
})
.show()
}
}
}
我正在尝试获取 Wifi AP 详细信息。我正在使用以下代码
private val TAG = "[MainActivity]"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if(WifiManager.NETWORK_STATE_CHANGED_ACTION == intent?.action){
val networkInfo = intent.getParcelableExtra<NetworkInfo>(WifiManager.EXTRA_NETWORK_INFO)
if(ConnectivityManager.TYPE_WIFI == networkInfo.type){
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val info = wifiManager.connectionInfo
Log.d(TAG, "info = $info")
}
}
}
}
val intentFilter = IntentFilter()
intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION)
applicationContext.registerReceiver(receiver, intentFilter)
}
NetworkInfo 和 ConnectivityManager.TYPE_WIFI 显示为已弃用。另外,我得到的 SSID 为空。
info = SSID: , BSSID: 02:00:00:00:00:00, MAC: 02:00:00:00:00:00, Supplicant state: COMPLETED
我使用的权限:
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
完成上述任务的最佳方法是什么?
您必须拥有位置权限才能获取 WiFi AC 详细信息(参见 Location Requirements for WiFi APIs)。
在您的清单中声明 ACCESS_FINE_LOCATION
不足以获取 WiFi 信息 - 用户必须授予您该权限(通过在运行时向用户请求权限或由用户手动打开权限在应用信息中)。
此外,在 Android 10 中,google 添加了 enforcement that prevents you from getting the permission in the background,因此请记住,只有当您的请求 ACCESS_FINE_LOCATION
才允许您获取 WiFi AC 详细信息应用程序在前台。
要在后台获取 WiFi AC 详细信息,您必须声明(并被授予)ACCESS_BACKGROUND_LOCATION
以及常规位置权限。
@Shlomi 回答的补充,
下面是运行时获取用户权限的示例
Perform a check before performing operation
if (!checkPermissions()) {
// App does not have permissions, ask for permissions.
requestPermissions()
} else {
// App has permissions
// Perform your operations here......
}
Code to check and ask permissions
Kotlin
/**
* Return the current state of the permissions needed.
*/
private fun checkPermissions(): Boolean {
val permissionState = ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.ACCESS_FINE_LOCATION
)
return permissionState == PackageManager.PERMISSION_GRANTED
}
private fun requestPermissions() {
val shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(
requireActivity(),
Manifest.permission.ACCESS_FINE_LOCATION
)
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(
TAG,
"Displaying permission rationale to provide additional context."
)
Snackbar.make(
binding.wifiConnectionFragment,
"Location permission is needed for core functionality",
Snackbar.LENGTH_INDEFINITE
)
.setAction("OK") { // Request permission
ActivityCompat.requestPermissions(
requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_PERMISSIONS_REQUEST_CODE
)
}
.show()
} else {
Log.i(TAG, "Requesting permission")
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(
requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_PERMISSIONS_REQUEST_CODE
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.isEmpty()) {
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
// Perform your operations
} else {
// Permission denied.
// Notify the user via a SnackBar that they have rejected a core permission for the
// app, which makes the Activity useless. In a real app, core permissions would
// typically be best requested during a welcome-screen flow.
// Additionally, it is important to remember that a permission might have been
// rejected without asking the user for permission (device policy or "Never ask
// again" prompts). Therefore, a user interface affordance is typically implemented
// when permissions are denied. Otherwise, your app could appear unresponsive to
// touches or interactions which have required permissions.
Snackbar.make(
binding.wifiConnectionFragment,
"Permission was denied, but is needed for core functionality.",
Snackbar.LENGTH_INDEFINITE
)
.setAction(
"Settings",
View.OnClickListener { // Build intent that displays the App settings screen.
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts(
"package",
BuildConfig.APPLICATION_ID, null
)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
})
.show()
}
}
}