fuzzyworld

haunt / commit

Add NEARBY_WIFI_DEVICES permission for Android 13+ RTT ranging

246276e6cc13981e23bee0068091bf028c79de20 · Pavel Safronov · 2026-05-12 22:31:19 -0700 · parents ee0a854834e1

Add NEARBY_WIFI_DEVICES permission for Android 13+ RTT ranging

Without this, WifiManager.scanResults silently returns no RTT-capable APs
on API 33+ devices when the app derives location from WiFi.

Declared without usesPermissionFlags="neverForLocation" and
ACCESS_FINE_LOCATION kept unrestricted (both required when the app uses
WiFi for location, per developer.android.com/develop/connectivity/wifi/wifi-rtt).

Requested at runtime alongside ACCESS_FINE_LOCATION via
RequestMultiplePermissions; NEARBY_WIFI_DEVICES only added to the request
on API 33+ (TIRAMISU) where it exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
 app/src/main/AndroidManifest.xml                   |  2 ++
 .../main/java/net/fuzzyworld/haunt/MainActivity.kt | 22 ++++++++++++++--------
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ad9605e..beff27d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <!-- Required for RTT ranging on Android 13+ when deriving location from WiFi -->
+    <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.INTERNET" />
diff --git a/app/src/main/java/net/fuzzyworld/haunt/MainActivity.kt b/app/src/main/java/net/fuzzyworld/haunt/MainActivity.kt
index f1e81b3..404eec9 100644
--- a/app/src/main/java/net/fuzzyworld/haunt/MainActivity.kt
+++ b/app/src/main/java/net/fuzzyworld/haunt/MainActivity.kt
@@ -2,6 +2,7 @@ package net.fuzzyworld.haunt
 
 import android.Manifest
 import android.content.pm.PackageManager
+import android.os.Build
 import android.os.Bundle
 import android.widget.EditText
 import androidx.activity.result.contract.ActivityResultContracts
@@ -29,10 +30,12 @@ class MainActivity : AppCompatActivity() {
         onRecolorYou = { color -> vm.setYouColor(color) },
     )
 
-    private val requestPermission = registerForActivityResult(
-        ActivityResultContracts.RequestPermission()
-    ) { granted ->
-        if (!granted) binding.readingsText.text = "Location permission required for RTT ranging"
+    private val requestPermissions = registerForActivityResult(
+        ActivityResultContracts.RequestMultiplePermissions()
+    ) { grants ->
+        if (grants.values.any { !it }) {
+            binding.readingsText.text = "Location/nearby-wifi permission required for RTT ranging"
+        }
     }
 
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -45,11 +48,14 @@ class MainActivity : AppCompatActivity() {
 
         binding.addPointButton.setOnClickListener { showAddPointDialog() }
 
-        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
-            != PackageManager.PERMISSION_GRANTED
-        ) {
-            requestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
+        val needed = buildList {
+            if (ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.ACCESS_FINE_LOCATION)
+                != PackageManager.PERMISSION_GRANTED) add(Manifest.permission.ACCESS_FINE_LOCATION)
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
+                ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.NEARBY_WIFI_DEVICES)
+                != PackageManager.PERMISSION_GRANTED) add(Manifest.permission.NEARBY_WIFI_DEVICES)
         }
+        if (needed.isNotEmpty()) requestPermissions.launch(needed.toTypedArray())
 
         lifecycleScope.launch {
             vm.readings.collectLatest { readings ->