584 lines
25 KiB
Kotlin
584 lines
25 KiB
Kotlin
package com.navi.alfred.utils
|
|
|
|
import androidx.annotation.WorkerThread
|
|
import com.google.gson.Gson
|
|
import com.google.gson.reflect.TypeToken
|
|
import com.navi.alfred.AlfredManager
|
|
import com.navi.alfred.db.model.AnalyticsEvent
|
|
import com.navi.alfred.db.model.ApiMetricHelper
|
|
import com.navi.alfred.db.model.FailureEvent
|
|
import com.navi.alfred.db.model.NegativeCase
|
|
import com.navi.alfred.dispatcher.AlfredDispatcher
|
|
import com.navi.alfred.model.AnalyticsRequest
|
|
import com.navi.alfred.model.BaseAttribute
|
|
import com.navi.alfred.model.DeviceAttributes
|
|
import com.navi.alfred.model.EventAttribute
|
|
import com.navi.alfred.model.EventMetricRequest
|
|
import com.navi.alfred.model.Failure
|
|
import com.navi.alfred.model.FailureAttributes
|
|
import com.navi.alfred.model.FailureRequest
|
|
import com.navi.alfred.model.MetricAttribute
|
|
import com.navi.alfred.model.SessionEventAttribute
|
|
import com.navi.alfred.model.SessionRequest
|
|
import com.navi.alfred.utils.AlfredConstants.API_ERROR
|
|
import com.navi.alfred.utils.AlfredConstants.DEFAULT_INGEST_METRIC_URL
|
|
import com.navi.alfred.utils.AlfredConstants.DEFAULT_SEND_CRASH_URL
|
|
import com.navi.alfred.utils.AlfredConstants.DEFAULT_SEND_EVENT_POST_URL
|
|
import com.navi.alfred.utils.AlfredConstants.DEFAULT_SEND_FAILURE_POST_URL
|
|
import com.navi.alfred.utils.AlfredConstants.DEFAULT_SEND_SESSION_POST_URL
|
|
import com.navi.alfred.utils.AlfredConstants.INGEST_CRASH_FAILURE
|
|
import com.navi.alfred.utils.AlfredConstants.INGEST_EVENT_FAILURE
|
|
import com.navi.alfred.utils.AlfredConstants.INGEST_METRIC_FAILURE
|
|
import com.navi.alfred.utils.AlfredConstants.INGEST_SESSION_FAILURE
|
|
import com.navi.alfred.utils.AlfredConstants.POST_METHOD
|
|
import com.navi.alfred.worker.AddEventTask
|
|
import com.navi.alfred.worker.AddFailureTask
|
|
import com.navi.alfred.worker.AddMetricTask
|
|
import com.navi.alfred.worker.AddNegativeCase
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.runBlocking
|
|
import kotlinx.coroutines.sync.withLock
|
|
import java.lang.reflect.Type
|
|
import java.util.UUID
|
|
import kotlin.concurrent.fixedRateTimer
|
|
|
|
internal suspend fun sendEventsToServer(
|
|
clientTs: Long? = null,
|
|
snapshotPerSecond: Int? = null,
|
|
workManagerFlow: Boolean? = false
|
|
): Boolean {
|
|
if (workManagerFlow == true || (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus())) {
|
|
try {
|
|
var baseAttributes = BaseAttribute()
|
|
if (workManagerFlow == true) {
|
|
baseAttributes = BaseAttribute(
|
|
clientTs = clientTs,
|
|
snapshotPerSecond = snapshotPerSecond
|
|
)
|
|
AlfredManager.analyticsDao = AlfredManager.alfredDataBase.analyticsDao()
|
|
}
|
|
AlfredManager.mutex.withLock {
|
|
val analyticsEvents =
|
|
AlfredManager.analyticsDao.fetchEvents(AlfredManager.config.getEventBatchSize())
|
|
if (analyticsEvents.isNotEmpty()) {
|
|
try {
|
|
val detailsList = analyticsEvents.map { it.details }
|
|
val listType: Type =
|
|
object : TypeToken<ArrayList<EventAttribute?>?>() {}.type
|
|
val events: ArrayList<EventAttribute> =
|
|
Gson().fromJson(detailsList.toString(), listType)
|
|
if (events.size > 0) {
|
|
val request = AnalyticsRequest(
|
|
baseAttribute = baseAttributes,
|
|
events = events
|
|
)
|
|
val response = AlfredManager.networkRepository.sendEvents(
|
|
AlfredManager.config.getPostUrl(),
|
|
AlfredManager.config.getApiKey(),
|
|
request
|
|
)
|
|
return if (
|
|
response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS) {
|
|
AlfredManager.analyticsDao.deleteEvents(analyticsEvents.map { it.eventId })
|
|
true
|
|
} else {
|
|
val zipNamesList: MutableList<String> = mutableListOf()
|
|
val eventIdList: MutableList<String> = mutableListOf()
|
|
events.forEach { event ->
|
|
if (!zipNamesList.contains(event.zipName)) {
|
|
zipNamesList.add(event.zipName ?: "")
|
|
}
|
|
eventIdList.add(event.eventId ?: "")
|
|
}
|
|
val failureEvent = buildFailureEvent(
|
|
errorType = API_ERROR,
|
|
requestUrl = DEFAULT_SEND_EVENT_POST_URL,
|
|
requestMethod = POST_METHOD,
|
|
zipName = zipNamesList,
|
|
errorStatusCode = response.code().toLong(),
|
|
errorMessage = response.message(),
|
|
errorName = INGEST_EVENT_FAILURE,
|
|
clientTs = request.baseAttribute?.clientTs,
|
|
eventIdList = eventIdList
|
|
)
|
|
AlfredDispatcher.addTaskToQueue(
|
|
AddFailureTask(
|
|
failureEvent,
|
|
context = AlfredManager.applicationContext
|
|
)
|
|
)
|
|
if (response.code() == AlfredConstants.CODE_API_BAD_REQUEST) {
|
|
AlfredManager.analyticsDao.deleteEvents(analyticsEvents.map { it.eventId })
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
} catch (e: Exception) {
|
|
return false
|
|
}
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
internal suspend fun sendNegativeCaseToServer(
|
|
clientTs: Long? = null,
|
|
snapshotPerSecond: Int? = null,
|
|
workManagerFlow: Boolean? = false
|
|
): Boolean {
|
|
if (workManagerFlow == true || (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus())) {
|
|
try {
|
|
var baseAttributes = BaseAttribute()
|
|
if (workManagerFlow == true) {
|
|
baseAttributes = BaseAttribute(
|
|
clientTs = clientTs,
|
|
snapshotPerSecond = snapshotPerSecond
|
|
)
|
|
AlfredManager.negativeCaseDao = AlfredManager.alfredDataBase.negativeCaseDao()
|
|
}
|
|
AlfredManager.mutex.withLock {
|
|
val negativeEvents =
|
|
AlfredManager.negativeCaseDao.fetchNegativeCase(AlfredManager.config.getEventBatchSize())
|
|
if (negativeEvents.isNotEmpty()) {
|
|
try {
|
|
val detailsList = negativeEvents.map { it.details }
|
|
val listType: Type =
|
|
object : TypeToken<ArrayList<EventAttribute?>?>() {}.type
|
|
val events: ArrayList<EventAttribute> =
|
|
Gson().fromJson(detailsList.toString(), listType)
|
|
if (events.size > 0) {
|
|
val request =
|
|
AnalyticsRequest(
|
|
baseAttribute = baseAttributes,
|
|
events = events
|
|
)
|
|
val response = AlfredManager.networkRepository.sendNegativeCase(
|
|
DEFAULT_SEND_CRASH_URL,
|
|
AlfredManager.config.getApiKey(),
|
|
request
|
|
)
|
|
return if (
|
|
response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS
|
|
) {
|
|
AlfredManager.negativeCaseDao.deleteNegativeCase(negativeEvents.map { it.negativeCaseId })
|
|
true
|
|
} else {
|
|
val failureEvent = buildFailureEvent(
|
|
errorType = API_ERROR,
|
|
requestUrl = DEFAULT_SEND_CRASH_URL,
|
|
requestMethod = POST_METHOD,
|
|
errorStatusCode = response.code().toLong(),
|
|
errorMessage = response.message(),
|
|
errorName = INGEST_CRASH_FAILURE
|
|
)
|
|
AlfredDispatcher.addTaskToQueue(
|
|
AddFailureTask(
|
|
failureEvent,
|
|
context = AlfredManager.applicationContext
|
|
)
|
|
)
|
|
if (response.code() == AlfredConstants.CODE_API_BAD_REQUEST) {
|
|
AlfredManager.negativeCaseDao.deleteNegativeCase(negativeEvents.map { it.negativeCaseId })
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
} catch (e: Exception) {
|
|
return false
|
|
}
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
internal suspend fun sendFailureEventsToServer(
|
|
workManagerFlow: Boolean? = false
|
|
): Boolean {
|
|
if (workManagerFlow == true || (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus())) {
|
|
try {
|
|
if (workManagerFlow == true) {
|
|
AlfredManager.failureEventDao = AlfredManager.alfredDataBase.failureEventDao()
|
|
}
|
|
AlfredManager.mutex.withLock {
|
|
val failureEvents =
|
|
AlfredManager.failureEventDao.fetchFailureEvents(AlfredManager.config.getFailureEventBatchSize())
|
|
if (failureEvents.isNotEmpty()) {
|
|
try {
|
|
val detailsList = failureEvents.map { it.details }
|
|
val listType: Type =
|
|
object : TypeToken<ArrayList<Failure?>?>() {}.type
|
|
val events: ArrayList<Failure> =
|
|
Gson().fromJson(detailsList.toString(), listType)
|
|
|
|
if (events.size > 0) {
|
|
val failureAttributes = FailureAttributes(sessionId = events[0].sessionId)
|
|
val request =
|
|
FailureRequest(
|
|
failureAttributes = failureAttributes,
|
|
events = events
|
|
)
|
|
val response = AlfredManager.networkRepository.sendFailure(
|
|
DEFAULT_SEND_FAILURE_POST_URL,
|
|
AlfredManager.config.getApiKey(),
|
|
request
|
|
)
|
|
return if (
|
|
(response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS) or
|
|
(response.code() == AlfredConstants.CODE_API_BAD_REQUEST)
|
|
) {
|
|
AlfredManager.failureEventDao.deleteFailureEvents(failureEvents.map { it.eventId })
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
} catch (e: Exception) {
|
|
return false
|
|
}
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
internal fun sendAlfredSessionEvent(
|
|
dumpFlow: Boolean = false,
|
|
index: Int? = null,
|
|
currentZipName: String? = null,
|
|
latestScreenshotTimestamp: Long? = null
|
|
) {
|
|
var request: SessionRequest? = null
|
|
if (dumpFlow) {
|
|
var clientTs: Long? = null
|
|
var sessionTimeStamp: Long? = null
|
|
var sessionId: String? = null
|
|
if (index != null) {
|
|
val zipFileDetail = AlfredManager.zipFileDetails[index]
|
|
clientTs = zipFileDetail.eventStartRecordingTime
|
|
sessionTimeStamp = zipFileDetail.sessionStartRecordingTime
|
|
sessionId = zipFileDetail.alfredSessionId
|
|
} else {
|
|
clientTs = AlfredManager.eventStartRecordingTimeForCrash
|
|
sessionTimeStamp = AlfredManager.sessionStartRecordingTimeForCrash
|
|
sessionId = AlfredManager.sessionIdForCrash
|
|
}
|
|
|
|
request =
|
|
SessionRequest(
|
|
base_attribute =
|
|
BaseAttribute(
|
|
sessionId = sessionId,
|
|
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
|
|
clientTs = clientTs,
|
|
sessionTimeStamp = sessionTimeStamp,
|
|
latestScreenshotTimestamp = latestScreenshotTimestamp
|
|
),
|
|
session_upload_event_attributes =
|
|
SessionEventAttribute(
|
|
eventId = currentZipName,
|
|
beginningDeviceAttributes = DeviceAttributes(),
|
|
endDeviceAttributes = DeviceAttributes()
|
|
)
|
|
)
|
|
} else {
|
|
request =
|
|
SessionRequest(
|
|
base_attribute =
|
|
BaseAttribute(
|
|
sessionId = AlfredManager.config.getAlfredSessionId(),
|
|
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
|
|
clientTs = AlfredManager.config.getEventStartRecordingTime(),
|
|
latestScreenshotTimestamp = latestScreenshotTimestamp,
|
|
sessionTimeStamp = AlfredManager.config.getSessionStartRecordingTime()
|
|
),
|
|
session_upload_event_attributes =
|
|
SessionEventAttribute(
|
|
eventId = currentZipName,
|
|
beginningDeviceAttributes =
|
|
DeviceAttributes(
|
|
battery = AlfredManager.config.batteryPercentageBeforeEventStart,
|
|
cpu = AlfredManager.config.cpuUsageBeforeEventStart,
|
|
memory = AlfredManager.config.memoryUsageBeforeEventStart,
|
|
storage = AlfredManager.config.storageUsageBeforeEventStart
|
|
),
|
|
endDeviceAttributes =
|
|
DeviceAttributes(
|
|
battery = AlfredManager.config.getBatteryPercentage(),
|
|
cpu = AlfredManager.config.getCpuUsage(),
|
|
memory = AlfredManager.config.getMemoryUsagePercentage(),
|
|
storage = AlfredManager.config.getStorageUsage()
|
|
)
|
|
)
|
|
)
|
|
}
|
|
AlfredManager.coroutineScope.launch {
|
|
val response = AlfredManager.networkRepository.sendSession(
|
|
DEFAULT_SEND_SESSION_POST_URL,
|
|
AlfredManager.config.getApiKey(),
|
|
request
|
|
)
|
|
if (response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS) {
|
|
if (!dumpFlow) {
|
|
AlfredManager.config.setEventStartRecordingTime(true)
|
|
handleDeviceAttributes()
|
|
} else {
|
|
index?.let { index -> AlfredManager.zipDetailsDao.deleteZipFileDetail(AlfredManager.zipFileDetails[index].id) }
|
|
if (AlfredManager.workFailureData.size > 0) {
|
|
AlfredManager.workFailureData.removeAt(0)
|
|
}
|
|
}
|
|
} else {
|
|
val failureEvent = buildFailureEvent(
|
|
errorType = API_ERROR,
|
|
requestUrl = DEFAULT_SEND_SESSION_POST_URL,
|
|
requestMethod = POST_METHOD,
|
|
zipName = request.session_upload_event_attributes.eventId?.let { listOf(it) },
|
|
errorStatusCode = response.code().toLong(),
|
|
errorMessage = response.message(),
|
|
errorName = INGEST_SESSION_FAILURE,
|
|
clientTs = request.base_attribute.clientTs
|
|
)
|
|
AlfredDispatcher.addTaskToQueue(
|
|
AddFailureTask(
|
|
failureEvent,
|
|
context = AlfredManager.applicationContext
|
|
)
|
|
)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
internal suspend fun sendIngestMetric(
|
|
clientTs: Long? = null,
|
|
snapshotPerSecond: Int? = null,
|
|
workManagerFlow: Boolean? = false
|
|
): Boolean {
|
|
if ((workManagerFlow == true) || (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus())) {
|
|
try {
|
|
var baseAttributes = BaseAttribute()
|
|
if (workManagerFlow == true) {
|
|
baseAttributes = BaseAttribute(
|
|
clientTs = clientTs,
|
|
snapshotPerSecond = snapshotPerSecond
|
|
)
|
|
AlfredManager.apiMetricDao = AlfredManager.alfredDataBase.apiMetricDao()
|
|
}
|
|
AlfredManager.mutex.withLock {
|
|
val metricEventList =
|
|
AlfredManager.apiMetricDao.fetchApiMetric(AlfredManager.config.getEventBatchSize())
|
|
if (metricEventList.isNotEmpty()) {
|
|
try {
|
|
val detailsList = metricEventList.map { it.metric }
|
|
val listType: Type =
|
|
object : TypeToken<ArrayList<MetricAttribute?>?>() {}.type
|
|
val events: ArrayList<MetricAttribute> =
|
|
Gson().fromJson(detailsList.toString(), listType)
|
|
if (events.size > 0) {
|
|
val request =
|
|
EventMetricRequest(
|
|
baseAttribute = baseAttributes,
|
|
metricsAttribute = events
|
|
)
|
|
val response =
|
|
AlfredManager.networkRepository.eventMetric(
|
|
DEFAULT_INGEST_METRIC_URL,
|
|
AlfredManager.config.getApiKey(),
|
|
metricRequestBody = request
|
|
)
|
|
return if (
|
|
response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS
|
|
) {
|
|
AlfredManager.apiMetricDao.deleteApiMetric(metricEventList.map { it.id })
|
|
true
|
|
} else {
|
|
val failureEvent = buildFailureEvent(
|
|
errorType = API_ERROR,
|
|
requestUrl = DEFAULT_INGEST_METRIC_URL,
|
|
requestMethod = POST_METHOD,
|
|
errorStatusCode = response.code().toLong(),
|
|
errorMessage = response.message(),
|
|
errorName = INGEST_METRIC_FAILURE
|
|
)
|
|
AlfredDispatcher.addTaskToQueue(
|
|
AddFailureTask(
|
|
failureEvent,
|
|
context = AlfredManager.applicationContext
|
|
)
|
|
)
|
|
if (response.code() == AlfredConstants.CODE_API_BAD_REQUEST) {
|
|
AlfredManager.apiMetricDao.deleteApiMetric(metricEventList.map { it.id })
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
return false
|
|
}
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
internal fun startSyncEvents() {
|
|
AlfredManager.timer =
|
|
fixedRateTimer(
|
|
AlfredConstants.TIMER_THREAD_NAME,
|
|
false,
|
|
AlfredConstants.DEFAULT_INITIAL_DELAY,
|
|
AlfredManager.config.getEventsDelayInMilliseconds()
|
|
) {
|
|
runBlocking {
|
|
AddMetricTask.resetEventCount()
|
|
sendIngestMetric()
|
|
AddEventTask.resetEventCount()
|
|
sendEventsToServer()
|
|
AddNegativeCase.resetEventCount()
|
|
sendNegativeCaseToServer()
|
|
AddFailureTask.resetEventCount()
|
|
sendFailureEventsToServer()
|
|
}
|
|
}
|
|
}
|
|
|
|
@WorkerThread
|
|
internal fun buildEvent(
|
|
eventName: String,
|
|
properties: HashMap<String, String>? = null,
|
|
screenName: String? = null,
|
|
moduleName: String? = null
|
|
): AnalyticsEvent {
|
|
val timeStamp = System.currentTimeMillis()
|
|
val eventData =
|
|
EventAttribute(
|
|
eventId = UUID.randomUUID().toString(),
|
|
eventName = eventName,
|
|
eventType = eventName,
|
|
eventTimestamp = timeStamp,
|
|
attributes = properties,
|
|
sessionId = AlfredManager.config.getAlfredSessionId(),
|
|
screenName = screenName,
|
|
moduleName = moduleName,
|
|
fragmentList = getFragmentList(),
|
|
zipName = AlfredManager.config.getAlfredEventId(),
|
|
screenShotTimeStamp = AlfredManager.config.getLatestScreenshotTimestamp()
|
|
)
|
|
return AnalyticsEvent(timeStamp, Gson().toJson(eventData))
|
|
}
|
|
|
|
@WorkerThread
|
|
internal fun buildNegativeCaseEvent(
|
|
eventName: String,
|
|
properties: HashMap<String, String>? = null,
|
|
screenName: String? = null,
|
|
moduleName: String? = null
|
|
): NegativeCase {
|
|
val timeStamp = System.currentTimeMillis()
|
|
val eventData =
|
|
EventAttribute(
|
|
eventId = UUID.randomUUID().toString(),
|
|
eventName = eventName,
|
|
eventType = eventName,
|
|
eventTimestamp = timeStamp,
|
|
attributes = properties,
|
|
sessionId = AlfredManager.config.getAlfredSessionId(),
|
|
screenName = screenName,
|
|
moduleName = moduleName,
|
|
fragmentList = getFragmentList(),
|
|
zipName = AlfredManager.config.getAlfredEventId()
|
|
)
|
|
return NegativeCase(timeStamp, Gson().toJson(eventData))
|
|
}
|
|
|
|
@WorkerThread
|
|
internal fun buildFailureEvent(
|
|
errorType: String,
|
|
requestUrl: String,
|
|
requestMethod: String,
|
|
zipName: List<String>? = null,
|
|
errorStatusCode: Long,
|
|
errorMessage: String? = "",
|
|
errorName: String? = null,
|
|
clientTs: Long? = null,
|
|
eventIdList: List<String>? = null
|
|
): FailureEvent {
|
|
val timeStamp = System.currentTimeMillis()
|
|
val eventData =
|
|
Failure(
|
|
errorType = errorType,
|
|
requestUrl = requestUrl,
|
|
requestMethod = requestMethod,
|
|
zipName = zipName,
|
|
errorStatusCode = errorStatusCode,
|
|
errorMessage = errorMessage,
|
|
errorName = errorName,
|
|
clientTs = clientTs,
|
|
eventIdList = eventIdList
|
|
)
|
|
return FailureEvent(timeStamp, Gson().toJson(eventData))
|
|
}
|
|
|
|
@WorkerThread
|
|
internal fun buildAppPerformanceEvent(
|
|
eventName: String,
|
|
eventType: String,
|
|
attribute: HashMap<String, Any>? = null
|
|
): ApiMetricHelper {
|
|
val timeStamp = System.currentTimeMillis()
|
|
val metricData =
|
|
MetricAttribute(
|
|
eventId = AlfredManager.config.getAlfredEventId(),
|
|
eventName = eventName,
|
|
eventType = eventType,
|
|
sessionId = AlfredManager.config.getAlfredSessionId(),
|
|
attributes = attribute,
|
|
screenName = AlfredManager.currentScreen.name,
|
|
moduleName = AlfredManager.currentModuleName,
|
|
fragmentList = getFragmentList()
|
|
)
|
|
return ApiMetricHelper(timeStamp, Gson().toJson(metricData))
|
|
}
|