Skip to content

Commit 58c28c1

Browse files
RUM-10378 Collect battery attributes
1 parent 4163546 commit 58c28c1

File tree

15 files changed

+921
-61
lines changed

15 files changed

+921
-61
lines changed

Datadog/Datadog.xcodeproj/project.pbxproj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,12 +850,18 @@
850850
86092FDE2DEDC6830075D63B /* AccessibilityValuesMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86092FDC2DEDC6830075D63B /* AccessibilityValuesMock.swift */; };
851851
862B0B272DEF2D8000F61E73 /* MockNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 862B0B262DEF2D8000F61E73 /* MockNotificationCenter.swift */; };
852852
862B0B282DEF2D8000F61E73 /* MockNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 862B0B262DEF2D8000F61E73 /* MockNotificationCenter.swift */; };
853+
8648D0782E01A389002F226B /* BrightnessStatusPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8648D0772E01A389002F226B /* BrightnessStatusPublisher.swift */; };
854+
8648D0792E01A389002F226B /* BrightnessStatusPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8648D0772E01A389002F226B /* BrightnessStatusPublisher.swift */; };
855+
8648D07B2E01ABB0002F226B /* BrightnessStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8648D07A2E01ABB0002F226B /* BrightnessStatus.swift */; };
856+
8648D07C2E01ABB0002F226B /* BrightnessStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8648D07A2E01ABB0002F226B /* BrightnessStatus.swift */; };
853857
864A70792DDF742A00AC0619 /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864A70782DDF742A00AC0619 /* Accessibility.swift */; };
854858
864A707A2DDF742A00AC0619 /* Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864A70782DDF742A00AC0619 /* Accessibility.swift */; };
855859
864A707C2DDF743900AC0619 /* AccessibilityReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864A707B2DDF743900AC0619 /* AccessibilityReader.swift */; };
856860
864A707D2DDF743900AC0619 /* AccessibilityReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864A707B2DDF743900AC0619 /* AccessibilityReader.swift */; };
857861
864A70802DE092AD00AC0619 /* AccessibilityReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864A707E2DE092AD00AC0619 /* AccessibilityReaderTests.swift */; };
858862
864A70812DE092AD00AC0619 /* AccessibilityReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 864A707E2DE092AD00AC0619 /* AccessibilityReaderTests.swift */; };
863+
866CA4A82E02EE0100E0CD03 /* BrightnessStatusPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 866CA4A72E02EE0100E0CD03 /* BrightnessStatusPublisherTests.swift */; };
864+
866CA4A92E02EE0100E0CD03 /* BrightnessStatusPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 866CA4A72E02EE0100E0CD03 /* BrightnessStatusPublisherTests.swift */; };
859865
960A0D3B2D6E2490004BB999 /* Reflector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 960A0D3A2D6E2490004BB999 /* Reflector.swift */; };
860866
960A0D3C2D6E2490004BB999 /* CustomDump.swift in Sources */ = {isa = PBXBuildFile; fileRef = 960A0D382D6E2490004BB999 /* CustomDump.swift */; };
861867
960A0D3D2D6E2490004BB999 /* ReflectionMirror.swift in Sources */ = {isa = PBXBuildFile; fileRef = 960A0D392D6E2490004BB999 /* ReflectionMirror.swift */; };
@@ -2954,9 +2960,12 @@
29542960
61FF9A4425AC5DEA001058CC /* ViewIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewIdentifier.swift; sourceTree = "<group>"; };
29552961
86092FDC2DEDC6830075D63B /* AccessibilityValuesMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityValuesMock.swift; sourceTree = "<group>"; };
29562962
862B0B262DEF2D8000F61E73 /* MockNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNotificationCenter.swift; sourceTree = "<group>"; };
2963+
8648D0772E01A389002F226B /* BrightnessStatusPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrightnessStatusPublisher.swift; sourceTree = "<group>"; };
2964+
8648D07A2E01ABB0002F226B /* BrightnessStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrightnessStatus.swift; sourceTree = "<group>"; };
29572965
864A70782DDF742A00AC0619 /* Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessibility.swift; sourceTree = "<group>"; };
29582966
864A707B2DDF743900AC0619 /* AccessibilityReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityReader.swift; sourceTree = "<group>"; };
29592967
864A707E2DE092AD00AC0619 /* AccessibilityReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityReaderTests.swift; sourceTree = "<group>"; };
2968+
866CA4A72E02EE0100E0CD03 /* BrightnessStatusPublisherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrightnessStatusPublisherTests.swift; sourceTree = "<group>"; };
29602969
960A0D382D6E2490004BB999 /* CustomDump.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDump.swift; sourceTree = "<group>"; };
29612970
960A0D392D6E2490004BB999 /* ReflectionMirror.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReflectionMirror.swift; sourceTree = "<group>"; };
29622971
960A0D3A2D6E2490004BB999 /* Reflector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reflector.swift; sourceTree = "<group>"; };
@@ -5438,6 +5447,7 @@
54385447
D26C49AE2886DC7B00802B2D /* ApplicationStatePublisherTests.swift */,
54395448
D2C7E3AA28F97DCF0023B2CC /* BatteryStatusPublisherTests.swift */,
54405449
D234613028B7712F00055D4C /* FeatureContextTests.swift */,
5450+
866CA4A72E02EE0100E0CD03 /* BrightnessStatusPublisherTests.swift */,
54415451
);
54425452
path = Context;
54435453
sourceTree = "<group>";
@@ -6232,6 +6242,7 @@
62326242
D23039BE298D5235001A1FA3 /* LaunchTime.swift */,
62336243
D2F8235229915E12003C7E99 /* DatadogSite.swift */,
62346244
6174D6122BFDF16C00EC7469 /* BundleType.swift */,
6245+
8648D07A2E01ABB0002F226B /* BrightnessStatus.swift */,
62356246
);
62366247
path = Context;
62376248
sourceTree = "<group>";
@@ -6793,6 +6804,7 @@
67936804
D2EFA867286DA85700F1FAA6 /* DatadogContextProvider.swift */,
67946805
D20605A2287464F40047275C /* ContextValuePublisher.swift */,
67956806
D20605A5287476230047275C /* ServerOffsetPublisher.swift */,
6807+
8648D0772E01A389002F226B /* BrightnessStatusPublisher.swift */,
67966808
D20605A82874C1CD0047275C /* NetworkConnectionInfoPublisher.swift */,
67976809
D20605B12874E1660047275C /* CarrierInfoPublisher.swift */,
67986810
D2A1EE31287DA51900D28DFB /* UserInfoPublisher.swift */,
@@ -8376,6 +8388,7 @@
83768388
D26C49BF288982DA00802B2D /* FeatureUpload.swift in Sources */,
83778389
A70A82652A935F210072F5DC /* BackgroundTaskCoordinator.swift in Sources */,
83788390
61D3E0D2277B23F1008BE766 /* KronosInternetAddress.swift in Sources */,
8391+
8648D0782E01A389002F226B /* BrightnessStatusPublisher.swift in Sources */,
83798392
D2553826288F0B1A00727FAD /* BatteryStatusPublisher.swift in Sources */,
83808393
61D3E0D5277B23F1008BE766 /* KronosNTPPacket.swift in Sources */,
83818394
6128F5712BA223D100D35B08 /* DataStore+TLV.swift in Sources */,
@@ -8466,6 +8479,7 @@
84668479
61133C582423990D00786299 /* FileWriterTests.swift in Sources */,
84678480
D22743DC29DEB8B4001A7EF9 /* VitalRefreshRateReaderTests.swift in Sources */,
84688481
617B954224BF4E7600E6F443 /* RUMMonitorConfigurationTests.swift in Sources */,
8482+
866CA4A92E02EE0100E0CD03 /* BrightnessStatusPublisherTests.swift in Sources */,
84698483
3C0D5DE22A543DC400446CF9 /* EventGeneratorTests.swift in Sources */,
84708484
6136CB4A2A69C29C00AC265D /* FilesOrchestrator+MetricsTests.swift in Sources */,
84718485
266BFA5E2D6F4E31003041A5 /* AccountInfoPublisherTests.swift in Sources */,
@@ -8978,6 +8992,7 @@
89788992
D2303A01298D5236001A1FA3 /* DateFormatting.swift in Sources */,
89798993
D23039F1298D5236001A1FA3 /* AnyDecodable.swift in Sources */,
89808994
6167E6E22B81207200C3CA2D /* DDCrashReport.swift in Sources */,
8995+
8648D07B2E01ABB0002F226B /* BrightnessStatus.swift in Sources */,
89818996
D2160CC529C0DED100FAA9A5 /* URLSessionTaskInterception.swift in Sources */,
89828997
6167E6FD2B81EC0400C3CA2D /* BacktraceReporter.swift in Sources */,
89838998
D23039DD298D5235001A1FA3 /* DD.swift in Sources */,
@@ -9751,6 +9766,7 @@
97519766
D2612F48290197C700509B7D /* LaunchTimePublisher.swift in Sources */,
97529767
A70A82662A935F210072F5DC /* BackgroundTaskCoordinator.swift in Sources */,
97539768
D2A1EE24287740B500D28DFB /* ApplicationStatePublisher.swift in Sources */,
9769+
8648D0792E01A389002F226B /* BrightnessStatusPublisher.swift in Sources */,
97549770
D2CB6E2927C50EAE00A62B57 /* KronosInternetAddress.swift in Sources */,
97559771
6128F5722BA223D100D35B08 /* DataStore+TLV.swift in Sources */,
97569772
D2CB6E2C27C50EAE00A62B57 /* KronosNTPPacket.swift in Sources */,
@@ -9861,6 +9877,7 @@
98619877
D2CB6F1A27C520D400A62B57 /* FileReaderTests.swift in Sources */,
98629878
266BFA5F2D6F4E31003041A5 /* AccountInfoPublisherTests.swift in Sources */,
98639879
D2777D9E29F6A75800FFBB40 /* TelemetryReceiverTests.swift in Sources */,
9880+
866CA4A82E02EE0100E0CD03 /* BrightnessStatusPublisherTests.swift in Sources */,
98649881
D2A1EE39287EEB7600D28DFB /* NetworkConnectionInfoPublisherTests.swift in Sources */,
98659882
D2CB6F1D27C520D400A62B57 /* DataUploaderTests.swift in Sources */,
98669883
D2A1EE35287EB8DB00D28DFB /* ServerOffsetPublisherTests.swift in Sources */,
@@ -10083,6 +10100,7 @@
1008310100
6167E6E32B81207200C3CA2D /* DDCrashReport.swift in Sources */,
1008410101
D2160CC629C0DED100FAA9A5 /* URLSessionTaskInterception.swift in Sources */,
1008510102
6167E6FE2B81EC0400C3CA2D /* BacktraceReporter.swift in Sources */,
10103+
8648D07C2E01ABB0002F226B /* BrightnessStatus.swift in Sources */,
1008610104
D2DA2372298D57AA00C6C7E6 /* DD.swift in Sources */,
1008710105
D2160C9B29C0DE5700FAA9A5 /* FirstPartyHosts.swift in Sources */,
1008810106
D2EBEE3029BA161100B15732 /* TracePropagationHeadersReader.swift in Sources */,
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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 2019-Present Datadog, Inc.
5+
*/
6+
7+
import Foundation
8+
import DatadogInternal
9+
10+
#if os(iOS)
11+
import UIKit
12+
13+
/// A publisher that publishes the screen brightness level from UIScreen.
14+
internal final class BrightnessStatusPublisher: ContextValuePublisher {
15+
/// The initial brightness level.
16+
let initialValue: BrightnessStatus?
17+
18+
/// The notification center to observe brightness changes.
19+
private let notificationCenter: NotificationCenter
20+
private let screen: UIScreen
21+
private var observers: [Any]? = nil
22+
23+
init(notificationCenter: NotificationCenter = .default, screen: UIScreen = .main) {
24+
self.notificationCenter = notificationCenter
25+
self.screen = screen
26+
self.initialValue = BrightnessStatus(level: Float(screen.brightness))
27+
}
28+
29+
/// Publishes the brightness level to the given receiver.
30+
///
31+
/// - Parameter receiver: The receiver to publish the brightness level to.
32+
func publish(to receiver: @escaping ContextValueReceiver<BrightnessStatus?>) {
33+
let block = { (notification: Notification) in
34+
receiver(BrightnessStatus(level: Float(self.screen.brightness)))
35+
}
36+
37+
observers = [
38+
notificationCenter.addObserver(
39+
forName: UIScreen.brightnessDidChangeNotification,
40+
object: nil,
41+
queue: .main,
42+
using: block
43+
)
44+
]
45+
46+
receiver(initialValue)
47+
}
48+
49+
func cancel() {
50+
observers?.forEach(notificationCenter.removeObserver)
51+
observers = nil
52+
}
53+
}
54+
55+
#endif

DatadogCore/Sources/Core/DatadogCore.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,10 @@ extension DatadogContextProvider {
525525
subscribe(\.isLowPowerModeEnabled, to: LowPowerModePublisher(notificationCenter: notificationCenter, processInfo: processInfo))
526526
#endif
527527

528+
#if os(iOS)
529+
subscribe(\.brightnessStatus, to: BrightnessStatusPublisher(notificationCenter: notificationCenter))
530+
#endif
531+
528532
#if os(iOS) || os(tvOS)
529533
DispatchQueue.main.async {
530534
// must be call on the main thread to read `UIApplication.State`
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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 2019-Present Datadog, Inc.
5+
*/
6+
7+
#if os(iOS)
8+
9+
import XCTest
10+
@testable import TestUtilities
11+
@testable import DatadogCore
12+
13+
final class BrightnessStatusPublisherTests: XCTestCase {
14+
private let notificationCenter = MockNotificationCenter()
15+
16+
func testInitialValue() throws {
17+
// Given
18+
let publisher = BrightnessStatusPublisher(notificationCenter: notificationCenter)
19+
20+
// Then
21+
XCTAssertNotNil(publisher.initialValue)
22+
XCTAssertEqual(publisher.initialValue?.level, Float(UIScreen.main.brightness))
23+
}
24+
25+
func testMultipleBrightnessChanges() throws {
26+
let expectation1 = self.expectation(description: "first brightness change")
27+
let expectation2 = self.expectation(description: "second brightness change")
28+
29+
// Given
30+
let mockScreen = UIScreenMock(brightness: 0.2)
31+
let publisher = BrightnessStatusPublisher(notificationCenter: notificationCenter, screen: mockScreen)
32+
var receivedValues: [Float] = []
33+
34+
publisher.publish { status in
35+
if let level = status?.level {
36+
receivedValues.append(level)
37+
38+
switch receivedValues.count {
39+
case 1:
40+
expectation1.fulfill()
41+
case 2:
42+
expectation2.fulfill()
43+
default:
44+
break
45+
}
46+
}
47+
}
48+
49+
// When
50+
mockScreen.brightness = 0.5
51+
notificationCenter.postFakeNotification(name: UIScreen.brightnessDidChangeNotification)
52+
mockScreen.brightness = 0.8
53+
notificationCenter.postFakeNotification(name: UIScreen.brightnessDidChangeNotification)
54+
55+
wait(for: [expectation1, expectation2], timeout: 0.1)
56+
57+
// Then
58+
XCTAssertEqual(receivedValues.count, 3)
59+
XCTAssertEqual(receivedValues[0], 0.2)
60+
XCTAssertEqual(receivedValues[1], 0.5)
61+
XCTAssertEqual(receivedValues[2], 0.8)
62+
}
63+
}
64+
65+
#endif
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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 2019-Present Datadog, Inc.
5+
*/
6+
7+
import Foundation
8+
9+
/// Describe the battery state for mobile devices.
10+
public struct BrightnessStatus: Codable, Equatable {
11+
/// The brightness level
12+
public let level: Float
13+
14+
public init(level: Float) {
15+
self.level = level
16+
}
17+
}

DatadogInternal/Sources/Context/DatadogContext.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ public struct DatadogContext {
107107
/// This value can be `nil` of the current device battery interface is not available.
108108
public var batteryStatus: BatteryStatus?
109109

110+
/// The current brightness status.
111+
public var brightnessStatus: BrightnessStatus?
112+
110113
/// `true` if the Low Power Mode is enabled.
111114
public var isLowPowerModeEnabled = false
112115

@@ -141,6 +144,7 @@ public struct DatadogContext {
141144
networkConnectionInfo: NetworkConnectionInfo? = nil,
142145
carrierInfo: CarrierInfo? = nil,
143146
batteryStatus: BatteryStatus? = nil,
147+
brightnessStatus: BrightnessStatus? = nil,
144148
isLowPowerModeEnabled: Bool = false,
145149
additionalContext: [String: AdditionalContext] = [:]
146150
) {
@@ -170,6 +174,7 @@ public struct DatadogContext {
170174
self.networkConnectionInfo = networkConnectionInfo
171175
self.carrierInfo = carrierInfo
172176
self.batteryStatus = batteryStatus
177+
self.brightnessStatus = brightnessStatus
173178
self.isLowPowerModeEnabled = isLowPowerModeEnabled
174179
self.additionalContext = additionalContext
175180
}

DatadogInternal/Sources/Models/CrashReporting/CrashContext.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@ public struct CrashContext: Codable, Equatable {
6565
/// not support telephony services.
6666
public let carrierInfo: CarrierInfo?
6767

68+
/// The current mobile device battery status.
69+
///
70+
/// This value can be `nil` of the current device battery interface is not available.
71+
public var batteryStatus: BatteryStatus?
72+
73+
/// The current brightness status.
74+
public var brightnessStatus: BrightnessStatus?
75+
76+
/// `true` if the Low Power Mode is enabled.
77+
public var isLowPowerModeEnabled = false
78+
6879
/// The last _"Is app in foreground?"_ information from crashed app process.
6980
public let lastIsAppInForeground: Bool
7081

@@ -96,6 +107,9 @@ public struct CrashContext: Codable, Equatable {
96107
accountInfo: AccountInfo?,
97108
networkConnectionInfo: NetworkConnectionInfo?,
98109
carrierInfo: CarrierInfo?,
110+
batteryStatus: BatteryStatus?,
111+
brightnessStatus: BrightnessStatus?,
112+
isLowPowerModeEnabled: Bool,
99113
lastIsAppInForeground: Bool,
100114
appLaunchDate: Date?,
101115
lastRUMViewEvent: RUMViewEvent?,
@@ -116,6 +130,9 @@ public struct CrashContext: Codable, Equatable {
116130
self.accountInfo = accountInfo
117131
self.networkConnectionInfo = networkConnectionInfo
118132
self.carrierInfo = carrierInfo
133+
self.batteryStatus = batteryStatus
134+
self.brightnessStatus = brightnessStatus
135+
self.isLowPowerModeEnabled = isLowPowerModeEnabled
119136
self.lastIsAppInForeground = lastIsAppInForeground
120137
self.appLaunchDate = appLaunchDate
121138
self.lastRUMViewEvent = lastRUMViewEvent

0 commit comments

Comments
 (0)