Ble Scanning Pause When Device Screen Off in vivo and poco device


**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}]
0
Apr 22 at 6:38 AM
User AvatarAditya_Giri
#android#bluetooth-lowenergy

Accepted Answer


## 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

2. Acquire a PARTIAL_WAKE_LOCK

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.

3. Switch Scan Mode to LOW_POWER

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.

4. Register Screen-Off Receiver to Restart Scan

// 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.

5. OEM-Specific User Settings (Required for Vivo/Poco)

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 |

6. Use the AltBeacon Library

The android-beacon-library handles many OEM-specific BLE quirks internally and has workarounds baked in for these exact scenarios.

7. Reference dontkillmyapp.com

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.


Key Takeaway

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.

User Avatartej shah
Apr 22 at 10:15 AM
0