@@ -10,6 +10,8 @@ import androidx.annotation.WorkerThread
10
10
import com.datadog.android.api.InternalLogger
11
11
import com.datadog.android.api.context.DatadogContext
12
12
import com.datadog.android.api.context.NetworkInfo
13
+ import com.datadog.android.api.feature.Feature
14
+ import com.datadog.android.api.feature.FeatureSdkCore
13
15
import com.datadog.android.api.storage.RawBatchEvent
14
16
import com.datadog.android.core.configuration.UploadSchedulerStrategy
15
17
import com.datadog.android.core.internal.ContextProvider
@@ -19,10 +21,14 @@ import com.datadog.android.core.internal.persistence.BatchId
19
21
import com.datadog.android.core.internal.persistence.Storage
20
22
import com.datadog.android.core.internal.system.SystemInfoProvider
21
23
import com.datadog.android.core.internal.utils.scheduleSafe
24
+ import com.datadog.android.internal.telemetry.UploadQualityBlockers
25
+ import com.datadog.android.internal.telemetry.UploadQualityCategories
26
+ import com.datadog.android.internal.telemetry.UploadQualityEvent
22
27
import java.util.concurrent.ScheduledThreadPoolExecutor
23
28
import java.util.concurrent.TimeUnit
24
29
25
30
internal class DataUploadRunnable (
31
+ featureSdkCore : FeatureSdkCore ,
26
32
private val featureName : String ,
27
33
private val threadPoolExecutor : ScheduledThreadPoolExecutor ,
28
34
private val storage : Storage ,
@@ -35,12 +41,15 @@ internal class DataUploadRunnable(
35
41
private val internalLogger : InternalLogger
36
42
) : UploadRunnable {
37
43
44
+ private val rumFeature = featureSdkCore.getFeature(Feature .RUM_FEATURE_NAME )
45
+
38
46
// region Runnable
39
47
40
48
@WorkerThread
41
49
override fun run () {
42
50
var uploadAttempts = 0
43
51
var lastBatchUploadStatus: UploadStatus ? = null
52
+
44
53
if (isNetworkAvailable() && isSystemReady()) {
45
54
val context = contextProvider.context
46
55
var batchConsumerAvailableAttempts = maxBatchesPerJob
@@ -55,6 +64,8 @@ internal class DataUploadRunnable(
55
64
)
56
65
}
57
66
67
+ logUploadQualityEvents()
68
+
58
69
val delayMs = uploadSchedulerStrategy.getMsDelayUntilNextUpload(
59
70
featureName,
60
71
uploadAttempts,
@@ -84,11 +95,46 @@ internal class DataUploadRunnable(
84
95
return uploadStatus
85
96
}
86
97
98
+ private fun logUploadQualityEvents () {
99
+ sendUploadQualityEvent(category = UploadQualityCategories .COUNT )
100
+
101
+ if (! isNetworkAvailable()) {
102
+ sendUploadQualityEvent(
103
+ category = UploadQualityCategories .BLOCKER ,
104
+ specificType = UploadQualityBlockers .OFFLINE .key
105
+ )
106
+ }
107
+
108
+ if (isLowPower()) {
109
+ sendUploadQualityEvent(
110
+ category = UploadQualityCategories .BLOCKER ,
111
+ specificType = UploadQualityBlockers .LOW_BATTERY .key
112
+ )
113
+ }
114
+
115
+ if (isPowerSaveMode()) {
116
+ sendUploadQualityEvent(
117
+ category = UploadQualityCategories .BLOCKER ,
118
+ specificType = UploadQualityBlockers .POWER_SAVE_MODE .key
119
+ )
120
+ }
121
+ }
122
+
87
123
private fun isNetworkAvailable (): Boolean {
88
124
val networkInfo = networkInfoProvider.getLatestNetworkInfo()
89
125
return networkInfo.connectivity != NetworkInfo .Connectivity .NETWORK_NOT_CONNECTED
90
126
}
91
127
128
+ private fun isPowerSaveMode (): Boolean {
129
+ val systemInfo = systemInfoProvider.getLatestSystemInfo()
130
+ return systemInfo.powerSaveMode
131
+ }
132
+
133
+ private fun isLowPower (): Boolean {
134
+ val systemInfo = systemInfoProvider.getLatestSystemInfo()
135
+ return systemInfo.batteryLevel <= LOW_BATTERY_THRESHOLD
136
+ }
137
+
92
138
private fun isSystemReady (): Boolean {
93
139
val systemInfo = systemInfoProvider.getLatestSystemInfo()
94
140
val hasEnoughPower = systemInfo.batteryFullOrCharging ||
@@ -116,6 +162,12 @@ internal class DataUploadRunnable(
116
162
batchMeta : ByteArray?
117
163
): UploadStatus {
118
164
val status = dataUploader.upload(context, batch, batchMeta, batchId)
165
+ if (status.code != HTTP_SUCCESS_CODE ) {
166
+ sendUploadQualityEvent(
167
+ category = UploadQualityCategories .FAILURE ,
168
+ specificType = status.code.toString()
169
+ )
170
+ }
119
171
val removalReason = if (status is UploadStatus .RequestCreationError ) {
120
172
RemovalReason .Invalid
121
173
} else {
@@ -125,9 +177,20 @@ internal class DataUploadRunnable(
125
177
return status
126
178
}
127
179
180
+ private fun sendUploadQualityEvent (category : UploadQualityCategories , specificType : String? = null) {
181
+ rumFeature?.sendEvent(
182
+ UploadQualityEvent (
183
+ track = featureName,
184
+ category = category,
185
+ specificType = specificType
186
+ )
187
+ )
188
+ }
189
+
128
190
// endregion
129
191
130
192
companion object {
131
193
internal const val LOW_BATTERY_THRESHOLD = 10
194
+ private const val HTTP_SUCCESS_CODE = 202
132
195
}
133
196
}
0 commit comments