Skip to content

Commit a390640

Browse files
committed
RUM-11120: Change polling time provider
1 parent 9f737d7 commit a390640

File tree

6 files changed

+66
-98
lines changed

6 files changed

+66
-98
lines changed

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/battery/BatteryInfo.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66

77
package com.datadog.android.rum.internal.domain.battery
88

9+
import androidx.annotation.FloatRange
10+
911
/**
1012
* Provides information about the battery state.
1113
*
1214
* @property batteryLevel the current battery charge level, expressed as a float from 0.0f (empty) to 1.0f (full).
1315
* @property lowPowerMode a boolean indicating whether the device is currently in Low Power Mode.
1416
*/
1517
internal data class BatteryInfo(
16-
val batteryLevel: Float? = null,
18+
@FloatRange(0.0, 1.0) val batteryLevel: Float? = null,
1719
val lowPowerMode: Boolean? = null
1820
) {
1921
fun toMap(): Map<String, Any> = buildMap {

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProvider.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ internal class DefaultBatteryInfoProvider(
2828
private val batteryManager: BatteryManager? = applicationContext.getSystemService(
2929
BATTERY_SERVICE
3030
) as? BatteryManager,
31-
private val batteryLevelPollInterval: Int = BATTERY_POLL_INTERVAL_MS
31+
private val batteryLevelPollInterval: Int = BATTERY_POLL_INTERVAL_MS,
32+
private val systemClockWrapper: SystemClockWrapper = SystemClockWrapper() // this wrapper is needed for unit tests
3233
) : InfoProvider {
3334

3435
@Volatile
3536
private var currentState = BatteryInfo()
3637

37-
private var lastTimeBatteryLevelChecked = AtomicLong(System.currentTimeMillis())
38+
private var lastTimeBatteryLevelChecked = AtomicLong(systemClockWrapper.elapsedRealTime())
3839

3940
private val powerSaveModeReceiver = object : BroadcastReceiver() {
4041
override fun onReceive(context: Context, intent: Intent) {
@@ -57,8 +58,8 @@ internal class DefaultBatteryInfoProvider(
5758
// while we could register a receiver for battery level,
5859
// it fires far too often (multiple times per second)
5960
// so it seems better to only poll battery charge state once in a period of time
60-
val now = System.currentTimeMillis()
61-
if (now >= lastTimeBatteryLevelChecked.get() + batteryLevelPollInterval) {
61+
val now = systemClockWrapper.elapsedRealTime()
62+
if (now - batteryLevelPollInterval >= lastTimeBatteryLevelChecked.get()) {
6263
lastTimeBatteryLevelChecked.set(now)
6364

6465
getBatteryLevel()?.let {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.rum.internal.domain.battery
8+
9+
import android.os.SystemClock
10+
11+
internal class SystemClockWrapper {
12+
fun elapsedRealTime(): Long {
13+
return SystemClock.elapsedRealtime()
14+
}
15+
}

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/display/DefaultDisplayInfoProvider.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ internal class DefaultDisplayInfoProvider(
5050
buildInitialState()
5151
}
5252

53-
@Synchronized
5453
override fun getState(): Map<String, Any> {
5554
return currentState.toMap()
5655
}

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/display/DisplayInfo.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
package com.datadog.android.rum.internal.domain.display
88

9+
/**
10+
* Provides information about the display state.
11+
*
12+
* @property screenBrightness The current screen brightness,
13+
* normalized as a float between 0.0 (darkest) and 1.0 (brightest).
14+
*/
915
internal data class DisplayInfo(
1016
val screenBrightness: Number? = null
1117
) {

features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt

Lines changed: 37 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,23 @@ internal class DefaultBatteryInfoProviderTest {
5757
@Mock
5858
lateinit var mockPowerManager: PowerManager
5959

60+
@Mock
61+
lateinit var mockSystemClockWrapper: SystemClockWrapper
62+
6063
@Mock
6164
lateinit var mockBatteryManager: BatteryManager
6265

66+
private val testSuiteStartTime = System.currentTimeMillis()
67+
68+
private val shortPollingInterval = 200
69+
6370
@BeforeEach
6471
fun setup() {
6572
whenever(mockApplicationContext.contentResolver) doReturn mockContentResolver
73+
whenever(mockSystemClockWrapper.elapsedRealTime()) doReturn testSuiteStartTime
74+
whenever(mockPowerManager.isPowerSaveMode) doReturn false
75+
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 50
76+
initializeBatteryManager()
6677
}
6778

6879
// region getBatteryState
@@ -75,13 +86,7 @@ internal class DefaultBatteryInfoProviderTest {
7586
// Given
7687
whenever(mockPowerManager.isPowerSaveMode) doReturn fakeLowPowerMode
7788
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn fakeBatteryLevel
78-
79-
// Create provider (initialization happens in constructor)
80-
testedProvider = DefaultBatteryInfoProvider(
81-
applicationContext = mockApplicationContext,
82-
powerManager = mockPowerManager,
83-
batteryManager = mockBatteryManager
84-
)
89+
initializeBatteryManager()
8590

8691
// When
8792
val batteryInfo = BatteryInfo.fromMap(testedProvider.getState())
@@ -96,11 +101,7 @@ internal class DefaultBatteryInfoProviderTest {
96101
@Test
97102
fun `M return battery info with nulls W getBatteryState() { services unavailable }`() {
98103
// Given
99-
testedProvider = DefaultBatteryInfoProvider(
100-
applicationContext = mockApplicationContext,
101-
powerManager = null,
102-
batteryManager = null
103-
)
104+
initializeBatteryManager(null, null)
104105

105106
// When
106107
val batteryInfo = BatteryInfo.fromMap(testedProvider.getState())
@@ -118,17 +119,6 @@ internal class DefaultBatteryInfoProviderTest {
118119
@SuppressLint("UnspecifiedRegisterReceiverFlag")
119120
@Test
120121
fun `M register broadcast receiver W constructor { initialization }`() {
121-
// Given
122-
whenever(mockPowerManager.isPowerSaveMode) doReturn false
123-
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 50
124-
125-
// When - provider is created (initialization happens in constructor)
126-
testedProvider = DefaultBatteryInfoProvider(
127-
applicationContext = mockApplicationContext,
128-
powerManager = mockPowerManager,
129-
batteryManager = mockBatteryManager
130-
)
131-
132122
// Then
133123
val receiverCaptor = argumentCaptor<BroadcastReceiver>()
134124
val filterCaptor = argumentCaptor<IntentFilter>()
@@ -142,15 +132,6 @@ internal class DefaultBatteryInfoProviderTest {
142132
@SuppressLint("UnspecifiedRegisterReceiverFlag")
143133
@Test
144134
fun `M unregister receiver W cleanup() { after initialization }`() {
145-
// Given - create provider (initialization happens in constructor)
146-
whenever(mockPowerManager.isPowerSaveMode) doReturn false
147-
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 50
148-
testedProvider = DefaultBatteryInfoProvider(
149-
applicationContext = mockApplicationContext,
150-
powerManager = mockPowerManager,
151-
batteryManager = mockBatteryManager
152-
)
153-
154135
// When
155136
testedProvider.cleanup()
156137

@@ -162,15 +143,6 @@ internal class DefaultBatteryInfoProviderTest {
162143

163144
@Test
164145
fun `M retain initial state W cleanup() then getState() { no re-initialization }`() {
165-
// Given - create provider
166-
whenever(mockPowerManager.isPowerSaveMode) doReturn false
167-
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 50
168-
testedProvider = DefaultBatteryInfoProvider(
169-
applicationContext = mockApplicationContext,
170-
powerManager = mockPowerManager,
171-
batteryManager = mockBatteryManager
172-
)
173-
174146
// When - cleanup (no re-initialization happens)
175147
testedProvider.cleanup()
176148
val batteryInfo = BatteryInfo.fromMap(testedProvider.getState())
@@ -184,48 +156,20 @@ internal class DefaultBatteryInfoProviderTest {
184156

185157
// region Polling Tests
186158

187-
@Test
188-
fun `M respect polling interval W getState() { multiple rapid calls }`() {
189-
// Given
190-
val shortInterval = 500 // 500ms for testing
191-
whenever(mockPowerManager.isPowerSaveMode) doReturn false
192-
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 50
193-
194-
testedProvider = DefaultBatteryInfoProvider(
195-
applicationContext = mockApplicationContext,
196-
powerManager = mockPowerManager,
197-
batteryManager = mockBatteryManager,
198-
batteryLevelPollInterval = shortInterval
199-
)
200-
201-
// When - rapid calls within polling interval
202-
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 75
203-
testedProvider.getState()
204-
205-
// Then - should still have initial battery level (50) from constructor, not 75
206-
val batteryInfo = BatteryInfo.fromMap(testedProvider.getState())
207-
assertThat(batteryInfo.batteryLevel).isEqualTo(0.5f) // Still 50, not 75
208-
}
209-
210159
@Test
211160
fun `M update battery level W getState() { after polling interval }`() {
212-
// Given
213-
val shortInterval = 50 // 50ms for testing
214-
whenever(mockPowerManager.isPowerSaveMode) doReturn false
215-
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 50
216-
217-
testedProvider = DefaultBatteryInfoProvider(
218-
applicationContext = mockApplicationContext,
219-
powerManager = mockPowerManager,
220-
batteryManager = mockBatteryManager,
221-
batteryLevelPollInterval = shortInterval
222-
)
223-
224161
// When
225-
Thread.sleep(shortInterval + 10L)
226162
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 75
227-
val batteryInfo = BatteryInfo.fromMap(testedProvider.getState())
228163

164+
// nothing changes because we are within polling interval
165+
assertThat(BatteryInfo.fromMap(testedProvider.getState()).batteryLevel).isEqualTo(0.5f)
166+
whenever(mockSystemClockWrapper.elapsedRealTime()) doReturn testSuiteStartTime + shortPollingInterval / 2
167+
assertThat(BatteryInfo.fromMap(testedProvider.getState()).batteryLevel).isEqualTo(0.5f)
168+
169+
// Then
170+
// after polling interval level should change
171+
whenever(mockSystemClockWrapper.elapsedRealTime()) doReturn testSuiteStartTime + shortPollingInterval
172+
val batteryInfo = BatteryInfo.fromMap(testedProvider.getState())
229173
assertThat(batteryInfo.batteryLevel).isEqualTo(0.8f) // Now 75
230174
}
231175

@@ -239,13 +183,7 @@ internal class DefaultBatteryInfoProviderTest {
239183
// Given
240184
whenever(mockPowerManager.isPowerSaveMode) doReturn false
241185
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn Integer.MIN_VALUE
242-
243-
// Create provider (initialization happens in constructor)
244-
testedProvider = DefaultBatteryInfoProvider(
245-
applicationContext = mockApplicationContext,
246-
powerManager = mockPowerManager,
247-
batteryManager = mockBatteryManager
248-
)
186+
initializeBatteryManager()
249187

250188
// When
251189
val batteryInfo = BatteryInfo.fromMap(testedProvider.getState())
@@ -261,13 +199,7 @@ internal class DefaultBatteryInfoProviderTest {
261199
// Given
262200
whenever(mockPowerManager.isPowerSaveMode) doReturn false
263201
whenever(mockBatteryManager.getIntProperty(BATTERY_PROPERTY_CAPACITY)) doReturn 0
264-
265-
// Create provider (initialization happens in constructor)
266-
testedProvider = DefaultBatteryInfoProvider(
267-
applicationContext = mockApplicationContext,
268-
powerManager = mockPowerManager,
269-
batteryManager = mockBatteryManager
270-
)
202+
initializeBatteryManager()
271203

272204
// When
273205
val batteryInfo = BatteryInfo.fromMap(testedProvider.getState())
@@ -278,4 +210,17 @@ internal class DefaultBatteryInfoProviderTest {
278210
}
279211

280212
// endregion
213+
214+
private fun initializeBatteryManager(
215+
powerManager: PowerManager? = mockPowerManager,
216+
batteryManager: BatteryManager? = mockBatteryManager
217+
) {
218+
testedProvider = DefaultBatteryInfoProvider(
219+
applicationContext = mockApplicationContext,
220+
batteryLevelPollInterval = shortPollingInterval,
221+
powerManager = powerManager,
222+
batteryManager = batteryManager,
223+
systemClockWrapper = mockSystemClockWrapper
224+
)
225+
}
281226
}

0 commit comments

Comments
 (0)