Ok so here is my situation:
I have my app installed. It is now targeting Android 12. I also have Kontakt.io Android sample app installed.
When I use Kontakt.io's sample app to scan for beacons, it works. Among other devices, I can see my beacons appear in the list and logs.
When I use the same exact code (from their sample app) in my app with exactly the same configuration and exactly the same permission scheme I am unable to scan my beacons. I can see in Logcat there are a lot of other devices that are being scanned and reported but none of my beacons are scanned.
Then - and here is the weird thing - while my app is in the background, I switch back to Kontakt.io sample app and initiate a new scan. The second I start that scan, MY app suddenly starts seeing my beacons and I can see them in the logs. If I shut down that Kontakt.io sample app scan, I can still see the beacons appear in my app's scan. If I kill both apps and try to scan again in my app alone, again, I cannot see my beacons any more.
I've been struggling with this for days now and it simply does not make any sense. I'll add here that if I do not target API 31 (Android 12) everything is perfect. As it's been for the past few years.
Please! Any ideas?
EDIT1: adding some source code
The code is identical to the one in Kontakt.io's sample app. It is a simple copy paste:
In an activity, on a button click I call checkPermissions()
:
private void checkPermissions() {
String[] requiredPermissions = Build.VERSION.SDK_INT < Build.VERSION_CODES.S
? new String[]{Manifest.permission.ACCESS_FINE_LOCATION}
: new String[]{ Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.ACCESS_FINE_LOCATION };
if(isAnyOfPermissionsNotGranted(requiredPermissions)) {
ActivityCompat.requestPermissions(getScreen().getHostingActivity(), requiredPermissions, 100);
} else {
setupProximityManager();
setupSpaces();
}
}
private void setupProximityManager() {
proximityManager = ProximityManagerFactory.create(this);
//Configure proximity manager basic options
proximityManager.configuration()
//Using ranging for continuous scanning or MONITORING for scanning with intervals
.scanPeriod(ScanPeriod.RANGING)
//Using BALANCED for best performance/battery ratio
.scanMode(ScanMode.BALANCED)
//OnDeviceUpdate callback will be received with 5 seconds interval
.deviceUpdateCallbackInterval(TimeUnit.SECONDS.toMillis(5));
//Setting up iBeacon and Eddystone listeners
proximityManager.setIBeaconListener(createIBeaconListener());
proximityManager.setEddystoneListener(createEddystoneListener());
}
private void setupSpaces() {
//Setting up single iBeacon region. Put your own desired values here.
IBeaconRegion region = new BeaconRegion.Builder().identifier("My Region") //Region identifier is mandatory.
.proximity(UUID.fromString("f7826da6-4fa2-4e98-8024-bc5b71e0893e")) //Default Kontakt.io proximity.
//Optional major and minor values
//.major(1)
//.minor(1)
.build();
proximityManager.spaces().iBeaconRegion(region)
.forceResolveRegions(Collections.singleton(UUID.fromString("f7826da6-4fa2-4e98-8024-bc5b71e0893e")));
//Setting up single Eddystone namespace. Put your own desired values here.
IEddystoneNamespace namespace = new EddystoneNamespace.Builder().identifier("My Namespace") //Namespace identifier is mandatory.
.namespace("f7826da64fa24e988024") //Default Kontakt.io namespace.
//Optional instance id value
//.instanceId("instanceId")
.build();
proximityManager.spaces().eddystoneNamespace(namespace).forceResolveNamespaces(Collections.singletonList("f7826da64fa24e988024"));
}
On a different button click I call:
private void startScanning() {
//Connect to scanning service and start scanning when ready
proximityManager.connect(new OnServiceReadyListener() {
@Override
public void onServiceReady() {
//Check if proximity manager is already scanning
if (proximityManager.isScanning()) {
Toast.makeText(((Application)Application.getContextFromApplicationClass()).getActivityContext(), "Already scanning", Toast.LENGTH_SHORT).show();
return;
}
proximityManager.startScanning();
// progressBar.setVisibility(View.VISIBLE);
Toast.makeText(((Application)Application.getContextFromApplicationClass()).getActivityContext(), "Scanning started", Toast.LENGTH_SHORT).show();
}
});
}
androidmanifest.xml contains the needed permissions (copied from the sample app):
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
I already have a foreground service in my app if it makes any difference.
I also declared as instructed another service that is used by the Kontakt SDK:
<service
android:name="com.kontakt.sdk.android.ble.service.ProximityService"
android:exported="false" />
All the runtime permissions are requested in the same manner they are requested in the Kontakt.io sample app and the actual permissionCheck passes.