@@ -83,7 +83,7 @@ open class MockCentralManagerImpl(
8383
8484 // Simulation methods
8585 private var peripheralSpecs = mutableListOf<PeripheralSpec <String >>()
86- private val mockAdvertiser = MockBluetoothLeAdvertiser <String >(scope, environment )
86+ private val mockAdvertiser = MockBluetoothLeAdvertiser <String >(scope)
8787
8888 override fun simulatePowerOn () = simulateStateChange(Manager .State .POWERED_ON )
8989
@@ -93,14 +93,31 @@ open class MockCentralManagerImpl(
9393 require(peripheralSpecs.isEmpty()) {
9494 " Peripherals have already been added to the simulation"
9595 }
96+ // Validate the MAC addresses.
9697 peripherals.forEach {
97- // Validate the MAC address.
98- require(it.identifier.matches(Regex (" ([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})" ))) {
99- " Invalid MAC address: ${it.identifier} "
98+ require(checkBluetoothAddress(it.identifier)) {
99+ " ${it.identifier} + is not a valid Bluetooth address"
100100 }
101101 }
102+ // Add known peripherals to the managed list. They will be available for connection without scanning.
103+ // This list includes bonded devices as well.
104+ peripherals
105+ .filter { it.isKnown }
106+ .forEach {
107+ managedPeripherals.put(
108+ key = it.identifier,
109+ value = Peripheral (
110+ scope = scope,
111+ impl = MockExecutor (
112+ peripheralSpec = it,
113+ name = it.name,
114+ environment = environment,
115+ advertisements = mockAdvertiser.events,
116+ )
117+ )
118+ )
119+ }
102120 peripheralSpecs.addAll(peripherals)
103- managedPeripherals
104121 mockAdvertiser.simulateAdvertising(peripherals)
105122 }
106123
@@ -131,8 +148,6 @@ open class MockCentralManagerImpl(
131148 }
132149
133150 // Implementation
134- private val _advertisingEvents = MutableSharedFlow <ScanResult >()
135-
136151 private val _state = MutableStateFlow (
137152 when {
138153 ! environment.isBluetoothSupported -> Manager .State .UNSUPPORTED
@@ -160,7 +175,9 @@ open class MockCentralManagerImpl(
160175 ensureOpen()
161176
162177 return ids.map { id ->
163- require(checkBluetoothAddress(id)) { " $id + is not a valid Bluetooth address" }
178+ require(checkBluetoothAddress(id)) {
179+ " $id + is not a valid Bluetooth address"
180+ }
164181 peripheral(id) {
165182 Peripheral (
166183 scope = scope,
@@ -172,7 +189,9 @@ open class MockCentralManagerImpl(
172189 identifier = id,
173190 proximity = Proximity .OUT_OF_RANGE
174191 ),
175- name = null
192+ name = null ,
193+ environment = environment,
194+ advertisements = mockAdvertiser.events,
176195 )
177196 )
178197 }
@@ -195,19 +214,25 @@ open class MockCentralManagerImpl(
195214 // but were not scanned yet.
196215 val managedBondedPeripherals = managedPeripherals.values
197216 .filter { it.hasBondInformation }
198- val otherBondedPeripherals = peripheralSpecs
199- .filter { it.isBonded }
200- .filter { it.identifier !in managedPeripherals.keys }
201- .map { peripheralSpec ->
202- peripheral(peripheralSpec.identifier) {
203- Peripheral (
204- scope = scope,
205- impl = MockExecutor (peripheralSpec, peripheralSpec.name)
206- )
207- }
208- }
217+ // val otherBondedPeripherals = peripheralSpecs
218+ // .filter { it.isBonded }
219+ // .filter { it.identifier !in managedPeripherals.keys }
220+ // .map { peripheralSpec ->
221+ // peripheral(peripheralSpec.identifier) {
222+ // Peripheral(
223+ // scope = scope,
224+ // impl = MockExecutor(
225+ // peripheralSpec = peripheralSpec,
226+ // name = peripheralSpec.name,
227+ // advertisements = mockAdvertiser.events,
228+ // )
229+ // )
230+ // }
231+ // }
209232 // TODO any order?
210- return managedBondedPeripherals + otherBondedPeripherals
233+ // TODO NOTE all known (including bonded) peripherals were added to managed already in simulatePeripherals
234+ // TODO a peripheral may change MAC address, how about that?
235+ return managedBondedPeripherals // + otherBondedPeripherals
211236 }
212237
213238 override fun scan (
@@ -235,15 +260,32 @@ open class MockCentralManagerImpl(
235260 val reportResult = environment.scanner().getOrThrow()
236261
237262 // Emit all scan results until the timeout.
238- withTimeoutOrNull<Nothing >(timeout) {
263+ withTimeoutOrNull(timeout) {
264+ // Keep IDs of all scanned peripherals to this scan.
265+ // This is used for handing passive scan.
266+ val cache = mutableSetOf<String >()
267+
239268 mockAdvertiser.events.collect { result ->
240269 // Some (most?) Android devices do not report scan error using `onScanFailed`
241270 // callback, but instead don't return any results.
242- // Re
243271 if (! reportResult) {
244272 return @collect
245273 }
246274
275+ // Some phones send only one Scan Request to connectable devices per scan.
276+ // Such devices are only reported once. Non-connectable devices, which only
277+ // send Advertising Data, are reported continuously.
278+ // TODO Modify to support passive scan on Android 16+
279+ if (environment.issueOnlyOneActiveScan) {
280+ if (result.isConnectable) {
281+ if (cache.contains(result.peripheralSpec.identifier)) {
282+ return @collect
283+ } else {
284+ cache.add(result.peripheralSpec.identifier)
285+ }
286+ }
287+ }
288+
247289 // Starting from Android 6 Location permission and Location service are required
248290 // to scan for BLE devices. Since Android 12, apps can set a `neverForLocation`
249291 // flag to claim that they won't estimate user's location from scan results.
@@ -283,7 +325,12 @@ open class MockCentralManagerImpl(
283325 peripheral(peripheralSpec.identifier) {
284326 Peripheral (
285327 scope = scope,
286- impl = MockExecutor (peripheralSpec, name)
328+ impl = MockExecutor (
329+ peripheralSpec = peripheralSpec,
330+ name = name,
331+ environment = environment,
332+ advertisements = mockAdvertiser.events,
333+ )
287334 )
288335 }
289336 }
0 commit comments