**I'm working on project where i need to start ble scanning for sos message coming from Beacon device.
This scanning must remain active after app killed for listening sos message.
For this i am using foreground service with connectedType where i called below method.**
<service android:name=".ui.service.BeaconForegroundService" android:foregroundServiceType="connectedDevice" android:exported="false"/>
I am using pending intent approach.
fun startScanBluetoothDevice(pendingIntent: PendingIntent) {
MyLogger.w(msg = "Scanning started !")
val builder = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
scanner = bluetoothAdapter?.bluetoothLeScanner
scanner?.let {
it.startScan(getScanFilter(), builder.build(), pendingIntent)
}
}
And pending intent below way
val intent = Intent(context, BleScanReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
And listening result in broadcast receiver.
@AndroidEntryPoint
class BleScanReceiver : BroadcastReceiver() {
@Inject lateinit var bleManager: BleManager
override fun onReceive(context: Context, intent: Intent) {
MyLogger.d(msg = intent.toString())
}
}
Note:
- All batery optimizaiton disable in device i check.
- I have all neccessary permission and bluetooth on.
- I follow android docs.
** I check with motorlola device where it work but it not work in poco or vivo device**
Means scanning feel pause.
In log message it feel it pause and resume when screen on.
When screen off :
D Result Come : ScanResult{device=XX:XX:XX:XX:C8:98, scanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=[0000fff1-0000-1000-8000-00805f9b34fb], mServiceSolicitationUuids=[], mManufacturerSpecificData={}, mServiceData={00000000-0000-1000-8000-00805f9b34fb=[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -59, 97]}, mTxPowerLevel=-2147483648, mDeviceName=null, mTDSData=null, mTransportDiscoveryData=null], rssi=-47, timestampNanos=154178666809128, eventType=27, primaryPhy=1, secondaryPhy=0, advertisingSid=255, txPower=127, periodicAdvertisingInterval=0}
2026-04-22 12:00:50.682 14072-14072 VRI[MainActivity] com.uffizio.androidbledemo D visibilityChanged oldVisibility=true newVisibility=false
2026-04-22 12:00:50.772 14072-14072 ViewRootImpl com.uffizio.androidbledemo D AppSizeAfterRelayout, size: Point(720, 1612), rotation: ROTATION_0
2026-04-22 12:00:50.775 14072-14072 BLASTBufferQueue com.uffizio.androidbledemo D [VRI[MainActivity]#3](f:0,a:4) destructor()
2026-04-22 12:00:50.775 14072-14072 BufferQueueConsumer com.uffizio.androidbledemo D [VRI[MainActivity]#3(BLAST Consumer)3](id:36f800000003,api:0,p:-1,c:14072) disconnect
no scanning result come.
When screen on:
D Intent { flg=0x10 cmp=com.uffizio.androidbledemo/.ble_new.broadcast.BleScanReceiver (has extras) }
2026-04-22 12:02:19.097 14072-14072 AppFlow com.uffizio.androidbledemo D Bundle[mParcelledData.dataSize=440]
Intent { flg=0x10 cmp=com.uffizio.androidbledemo/.ble_new.broadcast.BleScanReceiver (has extras) }
2026-04-22 12:02:19.097 14072-14072 AppFlow com.uffizio.androidbledemo D type : 1
2026-04-22 12:02:19.097 14072-14072 AppFlow com.uffizio.androidbledemo D [ScanResult{device=XX:XX:XX:XX:C8:98, scanRecord=ScanRecord [mAdvertiseFlags=6, mServiceUuids=[0000ffe1-0000-1000-8000-00805f9b34fb], mServiceSolicitationUuids=[], mManufacturerSpecificData={}, mServiceData={0000ffe1-0000-1000-8000-00805f9b34fb=[-95, 8, 97, -20, 65, -40, 105, -56, -104, 80, 76, 85, 83]}, mTxPowerLevel=-2147483648, mDeviceName=null, mTDSData=null, mTransportDiscoveryData=null], rssi=-43, timestampNanos=154272727111903, eventType=27, primaryPhy=1, secondaryPhy=0, advertisingSid=255, txPower=127, periodicAdvertisingInterval=0}]
## Why This Happens
**Vivo (FuntouchOS/OriginOS) and Poco/Xiaomi (MIUI/HyperOS)** implement **proprietary aggressive power management layers** that are separate from, and in addition to, standard Android battery optimization. These ROMs throttle or completely pause BLE scanning when the screen turns off — even if battery optimization is disabled for the app.
The standard Android "disable battery optimization" (`REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`) only controls AOSP-level Doze mode. It does **not** disable OEM-level proprietary kill systems.
---
## Suggested Fixes
### 1. Use Both Approaches Simultaneously
The `PendingIntent` approach alone is unreliable on these OEMs. Inside the foreground service, also use the **callback-based** `startScan()`:
```kotlin
scanner?.startScan(getScanFilter(), settings, scanCallback) // callback inside service
scanner?.startScan(getScanFilter(), settings, pendingIntent) // PendingIntent as backup
Inside the foreground service, hold a wake lock to prevent CPU sleep:
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "app:BLEScanWakeLock") wakeLock.acquire()
Release it when scanning stops.
SCAN_MODE_LOW_LATENCY ironically triggers more aggressive throttling on some OEMs. Try:
builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
Or use SCAN_MODE_BALANCED as a middle ground.
// In foreground service val filter = IntentFilter().apply { addAction(Intent.ACTION_SCREEN_OFF) addAction(Intent.ACTION_SCREEN_ON) } registerReceiver(screenReceiver, filter)
Restart the scan inside onReceive when ACTION_SCREEN_OFF fires.
These OEMs require the user to manually configure additional permissions:
| Device | Path | |--------|------| | Vivo | Settings → Battery → Background app management → Allow your app | | Vivo | Settings → Battery → High background power consumption → Allow | | Poco/MIUI | Settings → Apps → Manage apps → Your app → Battery Saver → No restrictions | | Poco/MIUI | Settings → Battery & performance → Autostart → Enable your app |
The android-beacon-library handles many OEM-specific BLE quirks internally and has workarounds baked in for these exact scenarios.
dontkillmyapp.com documents per-OEM APIs and deep-link intents you can use to open the correct settings screen directly from your app, guiding users to the right place programmatically.
This is fundamentally an OEM firmware restriction, not a bug in the code. The code shown in the question is correct per Android docs — it just works on stock Android (Motorola) but is blocked by Vivo/Xiaomi's proprietary power systems. A combination of wake lock + callback-based scan inside the service + user guidance to OEM battery settings is the most reliable mitigation.