在 Android API 级别 28 及更高级别连接时,如何获取 Wifi AP 详细信息,如 SSID、Band 等?

问题描述

我正在尝试获取 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 设为 null。

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 中,谷歌添加了 enforcement that prevents you from getting the permission in the background,因此请记住,仅当您的应用处于前台时,仅请求 ACCESS_FINE_LOCATION 才允许您获取 WiFi AC 详细信息。

要在后台获取 WiFi AC 详细信息,您必须声明(并被授予)ACCESS_BACKGROUND_LOCATION 以及常规位置权限。

,

@Shlomi 答案的补充,

这是在运行时从用户那里获取权限的示例

操作前先检查

if (!checkPermissions()) {
    // App does not have permissions,ask for permissions.
    requestPermissions()
} else {
    // App has permissions
    // Perform your operations here......
}

检查和询问权限的代码

科特林

/**
 * 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(),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()
        }
    }
}