From de733c6794aa18e2d8382e322662790cbb7dee7f Mon Sep 17 00:00:00 2001 From: mk17ru Date: Sun, 16 Apr 2023 15:15:02 +0300 Subject: [PATCH 1/2] add solution --- app/build.gradle | 5 + .../ok/android/itmohack2023/GlideActivity.kt | 2 + .../ok/android/itmohack2023/MainActivity.kt | 8 ++ .../ok/android/itmohack2023/OkHttpActivity.kt | 3 +- .../itmohack2023/logcat/LogcatHelper.kt | 111 ++++++++++++++++++ .../android/itmohack2023/logcat/LogcatInfo.kt | 5 + .../itmohack2023/logcat/LogcatParser.kt | 68 +++++++++++ .../itmohack2023/logcat/LogcatRequest.kt | 12 ++ .../itmohack2023/retrofit/RetrofitProvider.kt | 5 +- 9 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatHelper.kt create mode 100644 app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatInfo.kt create mode 100644 app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatParser.kt create mode 100644 app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatRequest.kt diff --git a/app/build.gradle b/app/build.gradle index 9311fb1..23b4e49 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation 'com.google.android.exoplayer:exoplayer:2.18.5' implementation 'com.facebook.fresco:fresco:3.0.0' implementation 'com.github.bumptech.glide:glide:4.15.1' + implementation 'com.github.bumptech.glide:okhttp3-integration:4.15.1' implementation 'com.squareup.picasso:picasso:2.8' implementation platform('com.google.firebase:firebase-bom:31.5.0') @@ -68,4 +69,8 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + implementation("io.ktor:ktor-client-content-negotiation:2.2.4") + implementation("io.ktor:ktor-serialization-kotlinx-json:2.2.4") + implementation("io.ktor:ktor-client-cio:2.2.4") + implementation("com.google.code.gson:gson:2.10.1") } \ No newline at end of file diff --git a/app/src/main/java/ru/ok/android/itmohack2023/GlideActivity.kt b/app/src/main/java/ru/ok/android/itmohack2023/GlideActivity.kt index a35c9c5..518c9b2 100644 --- a/app/src/main/java/ru/ok/android/itmohack2023/GlideActivity.kt +++ b/app/src/main/java/ru/ok/android/itmohack2023/GlideActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import com.bumptech.glide.Glide +import ru.ok.android.itmohack2023.logcat.LogcatHelper.Companion.createGlide class GlideActivity : AppCompatActivity() { private lateinit var cat1: ImageView @@ -13,6 +14,7 @@ class GlideActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_glide) + createGlide(this) cat1 = findViewById(R.id.cat_photo_1) cat2 = findViewById(R.id.cat_photo_2) diff --git a/app/src/main/java/ru/ok/android/itmohack2023/MainActivity.kt b/app/src/main/java/ru/ok/android/itmohack2023/MainActivity.kt index 2a4528e..3307a82 100644 --- a/app/src/main/java/ru/ok/android/itmohack2023/MainActivity.kt +++ b/app/src/main/java/ru/ok/android/itmohack2023/MainActivity.kt @@ -4,11 +4,19 @@ import android.content.Intent import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import ru.ok.android.itmohack2023.logcat.LogcatHelper class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + val logcatHelper = LogcatHelper(60 * 1000, this) + CoroutineScope(Dispatchers.Default).launch { + logcatHelper.publish() + } findViewById(R.id.url_connection).setOnClickListener { startActivity(Intent(this, UrlConnectionActivity::class.java)) } diff --git a/app/src/main/java/ru/ok/android/itmohack2023/OkHttpActivity.kt b/app/src/main/java/ru/ok/android/itmohack2023/OkHttpActivity.kt index cec982e..7fb251b 100644 --- a/app/src/main/java/ru/ok/android/itmohack2023/OkHttpActivity.kt +++ b/app/src/main/java/ru/ok/android/itmohack2023/OkHttpActivity.kt @@ -8,6 +8,7 @@ import androidx.appcompat.app.AppCompatActivity import okhttp3.OkHttpClient import okhttp3.Request import org.json.JSONArray +import ru.ok.android.itmohack2023.logcat.LogcatHelper import java.io.IOException class OkHttpActivity : AppCompatActivity() { @@ -43,6 +44,6 @@ class OkHttpActivity : AppCompatActivity() { val request: Request = Request.Builder() .url(url) .build() - OkHttpClient().newCall(request).execute().use { response -> return response.body?.string() } + LogcatHelper.okHttpClient.newCall(request).execute().use { response -> return response.body?.string() } } } \ No newline at end of file diff --git a/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatHelper.kt b/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatHelper.kt new file mode 100644 index 0000000..c69d067 --- /dev/null +++ b/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatHelper.kt @@ -0,0 +1,111 @@ +package ru.ok.android.itmohack2023.logcat + +import android.content.Context +import android.os.Build +import android.util.Log +import androidx.annotation.RequiresApi +import com.bumptech.glide.Glide +import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader +import com.bumptech.glide.load.model.GlideUrl +import com.google.gson.Gson +import com.squareup.picasso.OkHttp3Downloader +import com.squareup.picasso.Picasso +import io.ktor.client.HttpClient +import io.ktor.client.engine.cio.CIO +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.client.utils.EmptyContent.contentType +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.serialization.kotlinx.json.json +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import kotlinx.serialization.json.Json +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import java.io.BufferedReader +import java.io.InputStream +import java.io.InputStreamReader +import java.util.concurrent.TimeUnit + + +class LogcatHelper(private val timeout: Long, mainContext: Context) { + companion object { + private const val LOG_TAG = "LogcatHandler" + val okHttpClient: OkHttpClient = OkHttpClient().newBuilder() + .addInterceptor(HttpLoggingInterceptor { + Log.d(LOG_TAG, it) + } + .setLevel(HttpLoggingInterceptor.Level.BODY)) + .connectTimeout(5, TimeUnit.SECONDS) + .build() + + fun createGlide(context: Context) { + Glide.get(context).registry + .replace( + GlideUrl::class.java, + InputStream::class.java, OkHttpUrlLoader.Factory(okHttpClient) + ) + } + var processed = 0 + } + + val parser = LogcatParser() + + init { + Picasso.setSingletonInstance( + Picasso.Builder(mainContext) + .downloader(OkHttp3Downloader(okHttpClient)) + .build() + ) + + } + + @RequiresApi(Build.VERSION_CODES.N) + suspend fun publish() { + while (true) { + val process = withContext(Dispatchers.IO) { + Runtime.getRuntime().exec("logcat -d") + } + val lines = BufferedReader(InputStreamReader(process.inputStream)) + .readLines() + .filter { it.contains(LOG_TAG) } + val logs = lines + .drop(processed) + .map { parser.parse(it) } + .filterNotNull() + .filter { it.sender == LOG_TAG } + .groupBy { it.identification } + processed += lines.size + + val finalLogs = mutableListOf() + + for (log in logs) { + val request = parser.processAnswer(log.value) + if (request != null) { + finalLogs.add(request) + } + } + val client = HttpClient(CIO) { + expectSuccess = true + install(ContentNegotiation) { + json(Json { + ignoreUnknownKeys = true + }) + } + } + runBlocking { + + client.post("http://10.0.2.2:8080/addEvent") { + contentType(ContentType.Application.Json) + setBody(Gson().toJson(finalLogs)) + } + } + withContext(Dispatchers.IO) { + Thread.sleep(timeout) + } + } + } +} diff --git a/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatInfo.kt b/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatInfo.kt new file mode 100644 index 0000000..74921fc --- /dev/null +++ b/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatInfo.kt @@ -0,0 +1,5 @@ +package ru.ok.android.itmohack2023.logcat + +import java.time.LocalDateTime + +data class LogcatInfo(var time: LocalDateTime, var identification: String, var sender: String, var logBody: String) \ No newline at end of file diff --git a/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatParser.kt b/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatParser.kt new file mode 100644 index 0000000..e1ba3fa --- /dev/null +++ b/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatParser.kt @@ -0,0 +1,68 @@ +package ru.ok.android.itmohack2023.logcat + +import java.lang.RuntimeException +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +class LogcatParser { + private val timeRegex = Regex("\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3}") + private val identificationRegex = Regex("\\d+ \\d+") + private val messRegex = Regex("D|I|E|A|W") + private val senderRegex = Regex("\\w+") + private val typeRegex = Regex("GET|POST|DELETE") + + private fun process(match: MatchResult?): String { + if (match == null) { + throw RuntimeException("Log hasn't matched") + } + return match.value + } + + fun parse(input: String): LogcatInfo? { + try { + var log = input + val time = "2023-" + process(timeRegex.find(log)) + log = log.replaceFirst(timeRegex, "") + + val identification = process(identificationRegex.find(log)) + log = log.replaceFirst(identificationRegex, "") + + log = log.replaceFirst(messRegex, "").trim() + + val sender = process(senderRegex.find(log)) + log = log.replaceFirst(senderRegex, "") + val logBody = log.replaceFirst(":", "").trim() + + return LogcatInfo( + time = LocalDateTime.parse( + time, + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS") + ), + identification = identification, + sender = sender, + logBody = logBody + ) + } catch (e: Exception) { + return null + } + } + + fun processAnswer(logs: List) : LogcatRequest? { + try { + val start = logs.filter { it.logBody.startsWith("-->") }[0] + val response = logs.filter { it.logBody.startsWith("<--") }[0] + val finish = logs.filter { it.logBody.startsWith("<-- END HTTP") }[0] + val type = process(typeRegex.find(start.logBody)) + val bytes = process(Regex("\\d+").find(finish.logBody)).toLong() + return LogcatRequest(startTimestamp = start.time, + finishTimestamp = finish.time, + request = start.logBody.replaceFirst("--> ", ""), + response = response.logBody.replaceFirst("<-- ", ""), + bytesCount = bytes, + type = type + ) + } catch (e: Exception) { + return null + } + } +} diff --git a/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatRequest.kt b/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatRequest.kt new file mode 100644 index 0000000..8a97ab2 --- /dev/null +++ b/app/src/main/java/ru/ok/android/itmohack2023/logcat/LogcatRequest.kt @@ -0,0 +1,12 @@ +package ru.ok.android.itmohack2023.logcat + +import java.time.LocalDateTime + +data class LogcatRequest( + var request: String, + var response: String, + var type: String, + var startTimestamp: LocalDateTime, + var finishTimestamp: LocalDateTime, + var bytesCount: Long +) \ No newline at end of file diff --git a/app/src/main/java/ru/ok/android/itmohack2023/retrofit/RetrofitProvider.kt b/app/src/main/java/ru/ok/android/itmohack2023/retrofit/RetrofitProvider.kt index 44c0530..1e4a340 100644 --- a/app/src/main/java/ru/ok/android/itmohack2023/retrofit/RetrofitProvider.kt +++ b/app/src/main/java/ru/ok/android/itmohack2023/retrofit/RetrofitProvider.kt @@ -6,6 +6,7 @@ import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory +import ru.ok.android.itmohack2023.logcat.LogcatHelper.Companion.okHttpClient import java.util.concurrent.TimeUnit object RetrofitProvider { @@ -13,10 +14,6 @@ object RetrofitProvider { val retrofit: Retrofit by lazy { val loggingInterceptor = HttpLoggingInterceptor() loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY - val okHttpClient = OkHttpClient.Builder() - .addInterceptor(loggingInterceptor) - .connectTimeout(5, TimeUnit.SECONDS) - .build() val gson = GsonBuilder() .setPrettyPrinting() .create() From b01ad3b5f3a14d4afd79f7e13318c55b15fd65fd Mon Sep 17 00:00:00 2001 From: mk17ru Date: Sun, 16 Apr 2023 15:29:19 +0300 Subject: [PATCH 2/2] integrate --- app/build.gradle | 2 ++ .../ru/ok/android/itmohack2023/MainActivity.kt | 14 ++++++++++++++ local.properties | 9 ++++----- settings.gradle | 1 + 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 23b4e49..801c3d2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -73,4 +73,6 @@ dependencies { implementation("io.ktor:ktor-serialization-kotlinx-json:2.2.4") implementation("io.ktor:ktor-client-cio:2.2.4") implementation("com.google.code.gson:gson:2.10.1") + implementation("com.github.BetsonWick:NetworkMonitor:1.1.1") + } \ No newline at end of file diff --git a/app/src/main/java/ru/ok/android/itmohack2023/MainActivity.kt b/app/src/main/java/ru/ok/android/itmohack2023/MainActivity.kt index 3307a82..254b0b0 100644 --- a/app/src/main/java/ru/ok/android/itmohack2023/MainActivity.kt +++ b/app/src/main/java/ru/ok/android/itmohack2023/MainActivity.kt @@ -1,15 +1,23 @@ package ru.ok.android.itmohack2023 +import android.app.ActivityManager +import android.app.usage.NetworkStatsManager +import android.content.Context import android.content.Intent +import android.os.Build import android.os.Bundle import android.view.View +import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import ru.ok.android.itmohack2023.logcat.LogcatHelper +import ru.itmo.networkmonitor.NetworkMonitorTask + class MainActivity : AppCompatActivity() { + @RequiresApi(Build.VERSION_CODES.M) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) @@ -17,6 +25,12 @@ class MainActivity : AppCompatActivity() { CoroutineScope(Dispatchers.Default).launch { logcatHelper.publish() } + NetworkMonitorTask( + getSystemService(NETWORK_STATS_SERVICE) as NetworkStatsManager, getSystemService( + Context.ACTIVITY_SERVICE + ) as ActivityManager, + listOf("ru.ok") + ).start() findViewById(R.id.url_connection).setOnClickListener { startActivity(Intent(this, UrlConnectionActivity::class.java)) } diff --git a/local.properties b/local.properties index a5a1835..4af3c03 100644 --- a/local.properties +++ b/local.properties @@ -1,10 +1,9 @@ -## This file is automatically generated by Android Studio. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file should *NOT* be checked into Version Control Systems, +## This file must *NOT* be checked into Version Control Systems, # as it contains information specific to your local configuration. # # Location of the SDK. This is only used by Gradle. # For customization when using a Version Control System, please read the # header note. -sdk.dir=/Users/kirill.popov/Library/Android/sdk \ No newline at end of file +#Sun Apr 16 15:26:28 MSK 2023 +sdk.dir=/Users/mk17ru/Library/Android/sdk + diff --git a/settings.gradle b/settings.gradle index 005db70..2d2d4c0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,6 +10,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven { url 'https://jitpack.io' } } } rootProject.name = "ItmoHack2023"