From 0cf9aaf9cf2c3e37123bfd4d4ba3ea21eb98dd74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jc=20Mi=C3=B1arro?= Date: Fri, 27 Sep 2024 12:51:29 +0200 Subject: [PATCH 1/4] Add User-Agent with the App Data --- .../core/internal/module/ConnectionModule.kt | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModule.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModule.kt index d7ee523639..2b5b85b0e5 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModule.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModule.kt @@ -18,6 +18,7 @@ package io.getstream.video.android.core.internal.module import android.content.Context import android.net.ConnectivityManager +import android.os.Build import io.getstream.video.android.core.StreamVideo import io.getstream.video.android.core.api.SignalServerService import io.getstream.video.android.core.dispatchers.DispatcherProvider @@ -67,7 +68,7 @@ internal class ConnectionModule( private val authInterceptor: CoordinatorAuthInterceptor by lazy { CoordinatorAuthInterceptor(apiKey, userToken) } - private val headersInterceptor: HeadersInterceptor by lazy { HeadersInterceptor() } + private val headersInterceptor: HeadersInterceptor by lazy { HeadersInterceptor(context) } val okHttpClient: OkHttpClient by lazy { buildOkHttpClient() } val networkStateProvider: NetworkStateProvider by lazy { NetworkStateProvider( @@ -288,12 +289,57 @@ internal class CoordinatorAuthInterceptor( } } -internal class HeadersInterceptor : Interceptor { +internal class HeadersInterceptor( + context: Context, +) : Interceptor { + private val userAgent by lazy { buildUserAgent(context) } + override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() .newBuilder() + .addHeader("User-Agent", userAgent) .addHeader("X-Stream-Client", StreamVideo.buildSdkTrackingHeaders()) .build() return chain.proceed(request) } + + private fun buildUserAgent(context: Context): String { + with(context.packageManager) { + val versionName = runCatching { + getPackageInfo(context.packageName, 0).versionName + }.getOrNull() ?: "nameNotFound" + val versionCode = runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + getPackageInfo(context.packageName, 0).longVersionCode.toString() + } else { + getPackageInfo(context.packageName, 0).versionCode.toString() + } + }.getOrNull() ?: "versionCodeNotFound" + + val applicationInfo = context.applicationInfo + val stringId = applicationInfo.labelRes + val appName = + if (stringId == 0) { + applicationInfo.nonLocalizedLabel.toString() + } else { + context.getString(stringId) + } + + val manufacturer = Build.MANUFACTURER + val model = Build.MODEL + val version = Build.VERSION.SDK_INT + val versionRelease = Build.VERSION.RELEASE + + val installerName = runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + getInstallSourceInfo(context.packageName).installingPackageName + } else { + getInstallerPackageName(context.packageName) + } + }.getOrNull() ?: "StandAloneInstall" + + return "$appName / $versionName($versionCode); $installerName; ($manufacturer; " + + "$model; SDK $version; Android $versionRelease)" + } + } } From 94242b45b7916382e09ca4b2d1fb79be05fdae0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jc=20Mi=C3=B1arro?= Date: Tue, 15 Oct 2024 14:31:09 +0200 Subject: [PATCH 2/4] Sanitize User Agen Header --- .../core/internal/module/ConnectionModule.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModule.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModule.kt index 2b5b85b0e5..f2a27b0129 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModule.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/ConnectionModule.kt @@ -41,6 +41,7 @@ import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.converter.scalars.ScalarsConverterFactory import retrofit2.converter.wire.WireConverterFactory import java.io.IOException +import java.text.Normalizer import java.util.concurrent.TimeUnit /** @@ -338,8 +339,16 @@ internal class HeadersInterceptor( } }.getOrNull() ?: "StandAloneInstall" - return "$appName / $versionName($versionCode); $installerName; ($manufacturer; " + - "$model; SDK $version; Android $versionRelease)" + return ( + "$appName / $versionName($versionCode); $installerName; ($manufacturer; " + + "$model; SDK $version; Android $versionRelease)" + ) + .sanitize() } } + + private fun String.sanitize(): String { + return Normalizer.normalize(this, Normalizer.Form.NFD) + .replace("[^\\p{ASCII}]".toRegex(), "") + } } From 4a974f2f639782206c2d7fb7ac36f4754f0ffc77 Mon Sep 17 00:00:00 2001 From: Aleksandar Apostolov Date: Tue, 31 Dec 2024 14:27:34 +0100 Subject: [PATCH 3/4] Move to correct module --- .../module/CoordinatorConnectionModule.kt | 2 +- .../internal/module/HeadersInterceptor.kt | 57 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt index fdf5ba1fad..891b10441f 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt @@ -64,7 +64,7 @@ internal class CoordinatorConnectionModule( } // API - override val http: OkHttpClient = OkHttpClient.Builder().addInterceptor(HeadersInterceptor()) + override val http: OkHttpClient = OkHttpClient.Builder().addInterceptor(HeadersInterceptor(context)) .addInterceptor(authInterceptor).addInterceptor( HttpLoggingInterceptor().apply { level = loggingLevel.httpLoggingLevel.level diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt index 63c248e36d..a542773200 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt @@ -16,16 +16,71 @@ package io.getstream.video.android.core.internal.module +import android.content.Context +import android.os.Build import io.getstream.video.android.core.StreamVideo import okhttp3.Interceptor import okhttp3.Response +import java.text.Normalizer + +internal class HeadersInterceptor(context: Context) : Interceptor { + + private val userAgent = buildUserAgent(context) -internal class HeadersInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() .newBuilder() + .addHeader("User-Agent", userAgent) .addHeader("X-Stream-Client", StreamVideo.buildSdkTrackingHeaders()) .build() return chain.proceed(request) } + + private fun buildUserAgent(context: Context): String { + with(context.packageManager) { + val versionName = runCatching { + getPackageInfo(context.packageName, 0).versionName + }.getOrNull() ?: "nameNotFound" + val versionCode = runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + getPackageInfo(context.packageName, 0).longVersionCode.toString() + } else { + getPackageInfo(context.packageName, 0).versionCode.toString() + } + }.getOrNull() ?: "versionCodeNotFound" + + val applicationInfo = context.applicationInfo + val stringId = applicationInfo.labelRes + val appName = + if (stringId == 0) { + applicationInfo.nonLocalizedLabel.toString() + } else { + context.getString(stringId) + } + + val manufacturer = Build.MANUFACTURER + val model = Build.MODEL + val version = Build.VERSION.SDK_INT + val versionRelease = Build.VERSION.RELEASE + + val installerName = runCatching { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + getInstallSourceInfo(context.packageName).installingPackageName + } else { + getInstallerPackageName(context.packageName) + } + }.getOrNull() ?: "StandAloneInstall" + + return ( + "$appName / $versionName($versionCode); $installerName; ($manufacturer; " + + "$model; SDK $version; Android $versionRelease)" + ) + .sanitize() + } + } + + private fun String.sanitize(): String { + return Normalizer.normalize(this, Normalizer.Form.NFD) + .replace("[^\\p{ASCII}]".toRegex(), "") + } } From e00bde12741f3b995471709fe73174c99553a414 Mon Sep 17 00:00:00 2001 From: Aleksandar Apostolov Date: Tue, 31 Dec 2024 14:28:37 +0100 Subject: [PATCH 4/4] Spotless --- .../core/internal/module/CoordinatorConnectionModule.kt | 4 +++- .../android/core/internal/module/HeadersInterceptor.kt | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt index 891b10441f..37e2bb4291 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/CoordinatorConnectionModule.kt @@ -64,7 +64,9 @@ internal class CoordinatorConnectionModule( } // API - override val http: OkHttpClient = OkHttpClient.Builder().addInterceptor(HeadersInterceptor(context)) + override val http: OkHttpClient = OkHttpClient.Builder().addInterceptor( + HeadersInterceptor(context), + ) .addInterceptor(authInterceptor).addInterceptor( HttpLoggingInterceptor().apply { level = loggingLevel.httpLoggingLevel.level diff --git a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt index a542773200..8928dd7b24 100644 --- a/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt +++ b/stream-video-android-core/src/main/kotlin/io/getstream/video/android/core/internal/module/HeadersInterceptor.kt @@ -72,9 +72,9 @@ internal class HeadersInterceptor(context: Context) : Interceptor { }.getOrNull() ?: "StandAloneInstall" return ( - "$appName / $versionName($versionCode); $installerName; ($manufacturer; " + - "$model; SDK $version; Android $versionRelease)" - ) + "$appName / $versionName($versionCode); $installerName; ($manufacturer; " + + "$model; SDK $version; Android $versionRelease)" + ) .sanitize() } }