From c7bd7a238236e9cf90f1da2a0c1a7a4eb7a7c8de Mon Sep 17 00:00:00 2001 From: Marten Rebane Date: Thu, 14 May 2026 14:09:57 +0300 Subject: [PATCH] Add new User-Agent value --- .../FileOpeningRepositoryImplTest.kt | 2 +- .../domain/service/siva/SivaServiceTest.kt | 2 +- .../viewmodel/DiagnosticsViewModelTest.kt | 2 +- .../viewmodel/FileOpeningViewModelTest.kt | 2 +- .../viewmodel/MobileIdViewModelTest.kt | 2 +- .../viewmodel/RecentDocumentsViewModelTest.kt | 2 +- .../DigiDoc/viewmodel/SigningViewModelTest.kt | 2 +- .../DigiDoc/viewmodel/SmartIdViewModelTest.kt | 2 +- .../shared/SharedContainerViewModelTest.kt | 2 +- .../shared/SharedSettingsViewModelTest.kt | 2 +- .../kotlin/ee/ria/DigiDoc/di/AppModules.kt | 7 - .../ee/ria/DigiDoc/viewmodel/NFCViewModel.kt | 2 +- config-lib/build.gradle.kts | 5 + .../DigiDoc/configuration/di/ConfigModules.kt | 5 +- .../service/CentralConfigurationService.kt | 7 +- .../CentralConfigurationServiceImplTest.kt | 27 +- .../DigiDoc/cryptolib/CryptoContainerTest.kt | 2 +- .../domain/service/IdCardServiceImplTest.kt | 2 +- .../domain/service/IdCardServiceImpl.kt | 2 +- .../libdigidoclib/SignedContainerTest.kt | 2 +- .../libdigidoclib/init/Initialization.kt | 3 +- .../ria/DigiDoc/mobileId/MobileSignService.kt | 2 +- .../network/utils/UserAgentUtilTest.kt | 122 ------- .../network/mid/rest/ServiceGenerator.kt | 3 + .../network/sid/rest/ServiceGenerator.kt | 3 + .../DigiDoc/network/utils/UserAgentUtil.kt | 118 ++++--- .../network/utils/UserAgentUtilTest.kt | 303 ++++++++++++++++++ .../ria/DigiDoc/smartId/SmartSignService.kt | 2 +- 28 files changed, 436 insertions(+), 201 deletions(-) rename config-lib/src/{androidTest => test}/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt (85%) delete mode 100644 networking-lib/src/androidTest/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtilTest.kt create mode 100644 networking-lib/src/test/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtilTest.kt diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/repository/FileOpeningRepositoryImplTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/repository/FileOpeningRepositoryImplTest.kt index 3b88deec2..66bd01d71 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/repository/FileOpeningRepositoryImplTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/repository/FileOpeningRepositoryImplTest.kt @@ -89,7 +89,7 @@ class FileOpeningRepositoryImplTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/siva/SivaServiceTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/siva/SivaServiceTest.kt index 6693bcefe..756dba119 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/siva/SivaServiceTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/siva/SivaServiceTest.kt @@ -98,7 +98,7 @@ class SivaServiceTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt index 875e014da..be5b51a75 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt @@ -143,7 +143,7 @@ class DiagnosticsViewModelTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/FileOpeningViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/FileOpeningViewModelTest.kt index cbc40e98d..14f5c31db 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/FileOpeningViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/FileOpeningViewModelTest.kt @@ -143,7 +143,7 @@ class FileOpeningViewModelTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt index 3d569edb0..551b5b33c 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt @@ -162,7 +162,7 @@ class MobileIdViewModelTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt index fe2bf0607..21d7ac61e 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt @@ -103,7 +103,7 @@ class RecentDocumentsViewModelTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModelTest.kt index d0f4b09a9..b5284063d 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SigningViewModelTest.kt @@ -115,7 +115,7 @@ class SigningViewModelTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt index 6114ea1f8..fe3e0a8b1 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt @@ -169,7 +169,7 @@ class SmartIdViewModelTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedContainerViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedContainerViewModelTest.kt index 91d33aa64..50c7b028e 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedContainerViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedContainerViewModelTest.kt @@ -102,7 +102,7 @@ class SharedContainerViewModelTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModelTest.kt index f2183ebdf..693e4ec92 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModelTest.kt @@ -97,7 +97,7 @@ class SharedSettingsViewModelTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/di/AppModules.kt b/app/src/main/kotlin/ee/ria/DigiDoc/di/AppModules.kt index 7c57593e8..f35a08593 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/di/AppModules.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/di/AppModules.kt @@ -58,7 +58,6 @@ import ee.ria.DigiDoc.libdigidoclib.domain.model.ContainerWrapper import ee.ria.DigiDoc.libdigidoclib.init.Initialization import ee.ria.DigiDoc.manager.ActivityManager import ee.ria.DigiDoc.manager.ActivityManagerImpl -import ee.ria.DigiDoc.network.utils.UserAgentUtil import ee.ria.DigiDoc.root.RootChecker import ee.ria.DigiDoc.root.RootCheckerImpl import ee.ria.DigiDoc.smartcardreader.nfc.NfcSmartCardReaderManager @@ -134,12 +133,6 @@ class AppModules { @Provides fun provideBuildVersionProvider(): BuildVersionProvider = BuildVersionProviderImpl() - @Provides - fun provideUserAgent( - @ApplicationContext context: Context, - buildVersionProvider: BuildVersionProvider, - ): String = UserAgentUtil.getUserAgent(context, buildVersionProvider) - @Provides @Singleton fun provideActivityManager(): ActivityManager = ActivityManagerImpl() diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/NFCViewModel.kt b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/NFCViewModel.kt index ca4f9f520..cb1318529 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/NFCViewModel.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/NFCViewModel.kt @@ -242,7 +242,7 @@ class NFCViewModel val signer = ExternalSigner(signerCert) signer.setProfile(SIGNATURE_PROFILE_TS) - signer.setUserAgent(UserAgentUtil.getUserAgent(context, SendDiagnostics.NFC)) + signer.setUserAgent(UserAgentUtil.getAppInfo(context, SendDiagnostics.NFC)) val dataToSignBytes = containerWrapper.prepareSignature(signer, container, signerCert, roleData) diff --git a/config-lib/build.gradle.kts b/config-lib/build.gradle.kts index 4a970fdc0..92b26145d 100644 --- a/config-lib/build.gradle.kts +++ b/config-lib/build.gradle.kts @@ -61,7 +61,12 @@ dependencies { implementation(libs.androidx.hilt) testImplementation(libs.junit) + testImplementation(libs.mockito.core) testImplementation(libs.mockito.kotlin) + testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.okhttp3) + testImplementation(libs.okhttp3.tls) + testImplementation(libs.okhttp3.mockwebserver) androidTestImplementation(libs.junit) androidTestImplementation(libs.byte.buddy) diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/di/ConfigModules.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/di/ConfigModules.kt index 0a1a070ee..2a399a0f2 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/di/ConfigModules.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/di/ConfigModules.kt @@ -82,10 +82,7 @@ class ConfigModules { ): CentralConfigurationRepository = CentralConfigurationRepositoryImpl(centralConfigurationService) @Provides - fun provideCentralConfigurationService( - userAgent: String, - configurationProperty: ConfigurationProperty, - ): CentralConfigurationService = CentralConfigurationServiceImpl(userAgent, configurationProperty) + fun provideCentralConfigurationService(impl: CentralConfigurationServiceImpl): CentralConfigurationService = impl @Singleton @Provides diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationService.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationService.kt index 1927ab00f..2f965cb19 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationService.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationService.kt @@ -21,6 +21,8 @@ package ee.ria.DigiDoc.configuration.service +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext import ee.ria.DigiDoc.configuration.ConfigurationProperty import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepository import ee.ria.DigiDoc.network.configuration.interceptors.NetworkInterceptor @@ -29,6 +31,7 @@ import ee.ria.DigiDoc.network.proxy.ManualProxy import ee.ria.DigiDoc.network.proxy.ProxyConfig import ee.ria.DigiDoc.network.proxy.ProxySetting import ee.ria.DigiDoc.network.utils.ProxyUtil +import ee.ria.DigiDoc.network.utils.UserAgentUtil import okhttp3.Authenticator import okhttp3.ConnectionPool import okhttp3.OkHttpClient @@ -70,7 +73,7 @@ interface CentralConfigurationService { open class CentralConfigurationServiceImpl @Inject constructor( - private val userAgent: String, + @param:ApplicationContext private val context: Context, private val configurationProperty: ConfigurationProperty, ) : CentralConfigurationService { private val defaultTimeout = 5L @@ -132,7 +135,7 @@ open class CentralConfigurationServiceImpl HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }, - ).addInterceptor(UserAgentInterceptor(userAgent)) + ).addInterceptor(UserAgentInterceptor(UserAgentUtil.getUserAgent(context))) .addInterceptor(NetworkInterceptor()) .hostnameVerifier(OkHostnameVerifier) .connectTimeout(defaultTimeout, TimeUnit.SECONDS) diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt similarity index 85% rename from config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt rename to config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt index f35ae4f45..35f05149f 100644 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt +++ b/config-lib/src/test/kotlin/ee/ria/DigiDoc/configuration/service/CentralConfigurationServiceImplTest.kt @@ -21,6 +21,9 @@ package ee.ria.DigiDoc.configuration.service +import android.content.Context +import android.content.pm.PackageInfo +import android.content.pm.PackageManager import ee.ria.DigiDoc.configuration.ConfigurationProperty import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepository import ee.ria.DigiDoc.network.proxy.ManualProxy @@ -41,6 +44,10 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` @ExperimentalCoroutinesApi class CentralConfigurationServiceImplTest { @@ -52,15 +59,21 @@ class CentralConfigurationServiceImplTest { private lateinit var expectedBaseUrl: String + private lateinit var context: Context private lateinit var property: ConfigurationProperty - private val userAgent = "test-agent" - @Before fun setup() { + val packageManager = mock(PackageManager::class.java) + context = mock(Context::class.java) + `when`(context.packageManager).thenReturn(packageManager) + `when`(context.packageName).thenReturn("test") + val packageInfo = mock(PackageInfo::class.java) + `when`(packageInfo.longVersionCode).thenReturn(0L) + `when`(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo) + Dispatchers.setMain(testDispatcher) - // Create a self-signed certificate for localhost val localhostCert = HeldCertificate .Builder() @@ -84,12 +97,12 @@ class CentralConfigurationServiceImplTest { OkHttpClient .Builder() .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager) - .hostnameVerifier { _, _ -> true } // Accept "localhost" for testing + .hostnameVerifier { _, _ -> true } .build() mockWebServer = MockWebServer() mockWebServer.useHttps(serverCertificates.sslSocketFactory(), false) - mockWebServer.start(0) // Automatically chooses a free port + mockWebServer.start(0) expectedBaseUrl = mockWebServer.url("/").toString().removeSuffix("/") @@ -99,7 +112,7 @@ class CentralConfigurationServiceImplTest { ) service = - object : CentralConfigurationServiceImpl(userAgent, property) { + object : CentralConfigurationServiceImpl(context, property) { override fun constructHttpClient( defaultTimeout: Long, proxySetting: ProxySetting?, @@ -158,7 +171,7 @@ class CentralConfigurationServiceImplTest { @Test fun centralConfigurationServiceImpl_constructHttpClient_createsClientWithProxy() { - val service = CentralConfigurationServiceImpl(userAgent, property) + val service = CentralConfigurationServiceImpl(context, property) val proxySetting = ProxySetting.MANUAL_PROXY val manualProxy = ManualProxy("localhost", 8888, "user", "pass") diff --git a/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt b/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt index f8cbbfae7..59b2e4f3c 100644 --- a/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt +++ b/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt @@ -162,7 +162,7 @@ class CryptoContainerTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/id-card-lib/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImplTest.kt b/id-card-lib/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImplTest.kt index ac467626d..748df08a4 100644 --- a/id-card-lib/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImplTest.kt +++ b/id-card-lib/src/androidTest/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImplTest.kt @@ -126,7 +126,7 @@ class IdCardServiceImplTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/id-card-lib/src/main/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImpl.kt b/id-card-lib/src/main/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImpl.kt index 734c785fd..f0eb33179 100644 --- a/id-card-lib/src/main/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImpl.kt +++ b/id-card-lib/src/main/kotlin/ee/ria/DigiDoc/domain/service/IdCardServiceImpl.kt @@ -126,7 +126,7 @@ class IdCardServiceImpl val signer = ExternalSigner(signCertificateData) signer.setProfile(SIGNATURE_PROFILE_TS) - signer.setUserAgent(UserAgentUtil.getUserAgent(context, SendDiagnostics.Devices)) + signer.setUserAgent(UserAgentUtil.getAppInfo(context, SendDiagnostics.Devices)) dataToSign = containerWrapper.prepareSignature( diff --git a/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/SignedContainerTest.kt b/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/SignedContainerTest.kt index d11bd12a2..3561366f0 100644 --- a/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/SignedContainerTest.kt +++ b/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/SignedContainerTest.kt @@ -84,7 +84,7 @@ class SignedContainerTest { ConfigurationLoaderImpl( Gson(), CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl("Tests", ConfigurationProperty()), + CentralConfigurationServiceImpl(context, ConfigurationProperty()), ), ConfigurationProperty(), ConfigurationPropertiesImpl(), diff --git a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/init/Initialization.kt b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/init/Initialization.kt index d1b645168..858e6982b 100644 --- a/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/init/Initialization.kt +++ b/libdigidoc-lib/src/main/kotlin/ee/ria/DigiDoc/libdigidoclib/init/Initialization.kt @@ -128,7 +128,8 @@ class Initialization context, isLoggingEnabled, ) - digidoc.initializeLib(UserAgentUtil.getUserAgent(context), path) + digidoc.initializeLib(UserAgentUtil.getAppInfo(context), path) + UserAgentUtil.setLibdigidocppVersion(digidoc.version()) isInitialized = true } diff --git a/mobile-id-lib/src/main/kotlin/ee/ria/DigiDoc/mobileId/MobileSignService.kt b/mobile-id-lib/src/main/kotlin/ee/ria/DigiDoc/mobileId/MobileSignService.kt index 1aa7dc46b..2f3aa9df8 100644 --- a/mobile-id-lib/src/main/kotlin/ee/ria/DigiDoc/mobileId/MobileSignService.kt +++ b/mobile-id-lib/src/main/kotlin/ee/ria/DigiDoc/mobileId/MobileSignService.kt @@ -341,7 +341,7 @@ class MobileSignServiceImpl val signerCert = getCertificate(response.cert) val signer = ExternalSigner(signerCert) signer.setProfile(SIGNATURE_PROFILE_TS) - signer.setUserAgent(UserAgentUtil.getUserAgent(context)) + signer.setUserAgent(UserAgentUtil.getAppInfo(context)) val dataToSignBytes = Base64.getEncoder().encode( diff --git a/networking-lib/src/androidTest/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtilTest.kt b/networking-lib/src/androidTest/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtilTest.kt deleted file mode 100644 index ab8faa34f..000000000 --- a/networking-lib/src/androidTest/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtilTest.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName") - -package ee.ria.DigiDoc.network.utils - -import android.content.Context -import android.content.pm.PackageInfo -import android.content.pm.PackageManager -import android.hardware.usb.UsbDevice -import android.hardware.usb.UsbManager -import android.os.Build -import ee.ria.DigiDoc.common.BuildVersionProvider -import ee.ria.DigiDoc.network.utils.UserAgentUtil.getUserAgent -import junit.framework.TestCase.assertEquals -import org.junit.Before -import org.junit.Test -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mockito.mock -import org.mockito.Mockito.spy -import org.mockito.Mockito.`when` - -class UserAgentUtilTest { - private lateinit var context: Context - private lateinit var usbManager: UsbManager - private lateinit var usbDevice: UsbDevice - private lateinit var packageManager: PackageManager - private lateinit var packageInfo: PackageInfo - private lateinit var buildVersionProvider: BuildVersionProvider - - @Before - fun setUp() { - context = spy(Context::class.java) - usbManager = mock(UsbManager::class.java) - usbDevice = mock(UsbDevice::class.java) - packageManager = mock(PackageManager::class.java) - - packageInfo = PackageInfo() - packageInfo.versionName = "3.0.0" - packageInfo.longVersionCode = 1234L - - buildVersionProvider = mock(BuildVersionProvider::class.java) - } - - @Test - fun getUserAgent_success() { - val devices: HashMap = HashMap() - devices.put("device1", usbDevice) - `when`(context.packageManager).thenReturn(packageManager) - `when`(context.packageName).thenReturn("DigiDoc") - `when`(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo) - `when`(context.getSystemService(Context.USB_SERVICE)).thenReturn(usbManager) - `when`(usbManager.getDeviceList()).thenReturn(devices) - `when`(usbDevice.productName).thenReturn("Smart Card Reader 1") - `when`(buildVersionProvider.getSDKInt()).thenReturn(32) - - val userAgentString = getUserAgent(context, buildVersionProvider) - - assertEquals( - userAgentString, - "riadigidoc/3.0.0.1234 (Android ${Build.VERSION.RELEASE}) Lang: en", - ) - } - - @Test - fun getUserAgent_successShouldIncludeDevicesTrue() { - val devices: HashMap = HashMap() - devices.put("device1", usbDevice) - `when`(context.packageManager).thenReturn(packageManager) - `when`(context.packageName).thenReturn("DigiDoc") - `when`(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo) - `when`(context.getSystemService(Context.USB_SERVICE)).thenReturn(usbManager) - `when`(usbManager.getDeviceList()).thenReturn(devices) - `when`(usbDevice.productName).thenReturn("Smart Card Reader 1") - `when`(buildVersionProvider.getSDKInt()).thenReturn(32) - - val userAgentString = getUserAgent(context, SendDiagnostics.Devices, buildVersionProvider) - - assertEquals( - userAgentString, - "riadigidoc/3.0.0.1234 (Android ${Build.VERSION.RELEASE}) Lang: en Devices: Smart Card Reader 1", - ) - } - - @Test - fun getUserAgent_successNFCTrue() { - val devices: HashMap = HashMap() - devices.put("device1", usbDevice) - `when`(context.packageManager).thenReturn(packageManager) - `when`(context.packageName).thenReturn("DigiDoc") - `when`(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo) - `when`(context.getSystemService(Context.USB_SERVICE)).thenReturn(usbManager) - `when`(usbManager.getDeviceList()).thenReturn(devices) - `when`(usbDevice.productName).thenReturn("Smart Card Reader 1") - `when`(buildVersionProvider.getSDKInt()).thenReturn(32) - - val userAgentString = getUserAgent(context, SendDiagnostics.NFC, buildVersionProvider) - - assertEquals( - userAgentString, - "riadigidoc/3.0.0.1234 (Android ${Build.VERSION.RELEASE}) Lang: en NFC: true", - ) - } -} diff --git a/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/mid/rest/ServiceGenerator.kt b/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/mid/rest/ServiceGenerator.kt index 793e7f869..9bfa119ef 100644 --- a/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/mid/rest/ServiceGenerator.kt +++ b/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/mid/rest/ServiceGenerator.kt @@ -25,10 +25,12 @@ import android.content.Context import com.takisoft.preferencex.BuildConfig import ee.ria.DigiDoc.common.Constant.PEM_BEGIN_CERT import ee.ria.DigiDoc.common.Constant.PEM_END_CERT +import ee.ria.DigiDoc.network.configuration.interceptors.UserAgentInterceptor import ee.ria.DigiDoc.network.proxy.ManualProxy import ee.ria.DigiDoc.network.proxy.ProxyConfig import ee.ria.DigiDoc.network.proxy.ProxySetting import ee.ria.DigiDoc.network.utils.ProxyUtil +import ee.ria.DigiDoc.network.utils.UserAgentUtil import ee.ria.DigiDoc.network.utils.isLoggingEnabled import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog @@ -128,6 +130,7 @@ class ServiceGeneratorImpl : ServiceGenerator { .writeTimeout(120, TimeUnit.SECONDS) .pingInterval(3, TimeUnit.SECONDS) .certificatePinner(trustedCertificates(midSignServiceUrl, certBundle)) + .addInterceptor(UserAgentInterceptor(UserAgentUtil.getUserAgent(context))) addLoggingInterceptor(httpClientBuilder, context) if (sslContext != null) { try { diff --git a/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/sid/rest/ServiceGenerator.kt b/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/sid/rest/ServiceGenerator.kt index d19b9bf3e..d686320c5 100644 --- a/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/sid/rest/ServiceGenerator.kt +++ b/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/sid/rest/ServiceGenerator.kt @@ -23,10 +23,12 @@ package ee.ria.DigiDoc.network.sid.rest import android.content.Context import com.takisoft.preferencex.BuildConfig +import ee.ria.DigiDoc.network.configuration.interceptors.UserAgentInterceptor import ee.ria.DigiDoc.network.proxy.ManualProxy import ee.ria.DigiDoc.network.proxy.ProxyConfig import ee.ria.DigiDoc.network.proxy.ProxySetting import ee.ria.DigiDoc.network.utils.ProxyUtil +import ee.ria.DigiDoc.network.utils.UserAgentUtil import ee.ria.DigiDoc.network.utils.isLoggingEnabled import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog @@ -115,6 +117,7 @@ class ServiceGeneratorImpl : ServiceGenerator { .writeTimeout(120, TimeUnit.SECONDS) .pingInterval(3, TimeUnit.SECONDS) .certificatePinner(trustedCertificates(sidSignServiceUrl, certBundle)) + .addInterceptor(UserAgentInterceptor(UserAgentUtil.getUserAgent(context))) .cache(null) addLoggingInterceptor(httpClientBuilder, context) return httpClientBuilder.build() diff --git a/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtil.kt b/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtil.kt index e1170fbe3..68b2e0ebf 100644 --- a/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtil.kt +++ b/networking-lib/src/main/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtil.kt @@ -21,19 +21,16 @@ package ee.ria.DigiDoc.network.utils -import android.annotation.SuppressLint import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.hardware.usb.UsbDevice import android.hardware.usb.UsbManager import android.os.Build -import android.text.TextUtils import ee.ria.DigiDoc.common.BuildVersionProvider import ee.ria.DigiDoc.common.BuildVersionProviderImpl import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog import java.util.Locale -import java.util.Objects import java.util.stream.Collectors enum class SendDiagnostics { @@ -45,39 +42,87 @@ enum class SendDiagnostics { object UserAgentUtil { private val LOG_TAG = javaClass.simpleName private val deviceNameFilters = listOf("Smart", "Reader", "Card") + private var libdigidocppVersion: String? = null - fun getUserAgent( - context: Context?, + fun setLibdigidocppVersion(version: String) { + libdigidocppVersion = version + } + + fun resetLibdigidocppVersion() { + libdigidocppVersion = null + } + + fun getAppInfo( + context: Context, + sendDiagnostics: SendDiagnostics = SendDiagnostics.None, buildVersionProvider: BuildVersionProvider = BuildVersionProviderImpl(), - ): String = getUserAgent(context, SendDiagnostics.None, buildVersionProvider) + ): String { + val sb = StringBuilder() + + sb.append("riadigidoc/").append(getAppVersion(context, buildVersionProvider)) + + sb.append(" (schema=1") + sb.append("; os=Android ").append(Build.VERSION.RELEASE) + sb.append("; lang=").append(Locale.getDefault().language) + + val deviceType = if (isTablet(context)) "tablet" else "mobile" + val deviceModel = + Build.MODEL + ?.lowercase() + ?.replace(" ", "-") + .orEmpty() + sb.append("; devicetype=$deviceType/$deviceModel") + + if (sendDiagnostics == SendDiagnostics.Devices) { + val deviceNames = getConnectedUsbDevices(context).map { it.productName ?: it.deviceName } + if (deviceNames.isNotEmpty()) { + sb.append("; devices=").append(deviceNames.joinToString(", ")) + } + } + + if (sendDiagnostics == SendDiagnostics.NFC) { + sb.append("; nfc=true") + } + + sb.append(")") + + return sb.toString() + } fun getUserAgent( - context: Context?, - sendDiagnostics: SendDiagnostics, + context: Context, + sendDiagnostics: SendDiagnostics = SendDiagnostics.None, buildVersionProvider: BuildVersionProvider = BuildVersionProviderImpl(), ): String { - val deviceProductNames = ArrayList() - val initializingMessage = StringBuilder() - if (context != null) { - for (device in getConnectedUsbDevices(context)) { - deviceProductNames.add(device.productName) - } - initializingMessage.append("riadigidoc/").append(getAppVersion(context, buildVersionProvider)) - initializingMessage.append(" (Android ").append(Build.VERSION.RELEASE).append(")") - initializingMessage.append(" Lang: ").append(Locale.getDefault().language) - - if (sendDiagnostics == SendDiagnostics.Devices && deviceProductNames.isNotEmpty()) { - initializingMessage - .append(" Devices: ") - .append(TextUtils.join(", ", deviceProductNames)) - } - if (sendDiagnostics == SendDiagnostics.NFC) { - initializingMessage.append(" NFC: true") - } + val sb = StringBuilder() + + libdigidocppVersion?.let { version -> + val arch = Build.SUPPORTED_ABIS?.firstOrNull()?.let { normalizeArch(it) } ?: "unknown" + sb.append("LIB libdigidocpp/").append(version) + sb.append(" (").append(arch).append(") ") } - return initializingMessage.toString() + + sb.append("APP ").append(getAppInfo(context, sendDiagnostics, buildVersionProvider)) + + return sb.toString() } + private fun isTablet(context: Context): Boolean = + try { + context.resources.configuration.smallestScreenWidthDp >= 600 + } catch (_: Exception) { + false + } + + private fun normalizeArch(abi: String): String = + when { + abi.startsWith("arm64") -> "arm64" + abi.startsWith("armeabi") -> "arm" + abi.startsWith("x86_64") -> "x86_64" + abi.startsWith("x86") -> "x86" + else -> abi + } + private fun getConnectedUsbDevices(context: Context): List { val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager val devices = usbManager.getDeviceList() @@ -90,11 +135,7 @@ object UserAgentUtil { deviceNameFilters .stream() .anyMatch { charSequence: String -> - Objects - .requireNonNull(value.productName) - .contains( - charSequence, - ) + value.productName?.contains(charSequence) == true } || deviceNameFilters .stream() @@ -115,20 +156,15 @@ object UserAgentUtil { private fun getAppVersion( context: Context, buildVersionProvider: BuildVersionProvider, - ): StringBuilder { - val versionName = StringBuilder() + ): String = try { - versionName - .append(getPackageInfo(context, buildVersionProvider).versionName) - .append(".") - .append(getPackageInfo(context, buildVersionProvider).longVersionCode) + val info = getPackageInfo(context, buildVersionProvider) + "${info.versionName}.${info.longVersionCode}" } catch (e: PackageManager.NameNotFoundException) { errorLog(LOG_TAG, "Failed getting app version from package info", e) + "" } - return versionName - } - @SuppressLint("NewApi") @Throws(PackageManager.NameNotFoundException::class) private fun getPackageInfo( context: Context, diff --git a/networking-lib/src/test/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtilTest.kt b/networking-lib/src/test/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtilTest.kt new file mode 100644 index 000000000..51766a7e4 --- /dev/null +++ b/networking-lib/src/test/kotlin/ee/ria/DigiDoc/network/utils/UserAgentUtilTest.kt @@ -0,0 +1,303 @@ +/* + * Copyright 2017 - 2026 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.network.utils + +import android.content.Context +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.res.Configuration +import android.content.res.Resources +import android.hardware.usb.UsbDevice +import android.hardware.usb.UsbManager +import android.os.Build +import ee.ria.DigiDoc.common.BuildVersionProvider +import ee.ria.DigiDoc.network.utils.UserAgentUtil.getAppInfo +import ee.ria.DigiDoc.network.utils.UserAgentUtil.getUserAgent +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` + +class UserAgentUtilTest { + private lateinit var context: Context + private lateinit var usbManager: UsbManager + private lateinit var usbDevice: UsbDevice + private lateinit var packageManager: PackageManager + private lateinit var packageInfo: PackageInfo + private lateinit var buildVersionProvider: BuildVersionProvider + + private val expectedDeviceModel = + Build.MODEL + ?.lowercase() + ?.replace(" ", "-") + .orEmpty() + + @Before + fun setUp() { + UserAgentUtil.resetLibdigidocppVersion() + context = mock(Context::class.java) + usbManager = mock(UsbManager::class.java) + usbDevice = mock(UsbDevice::class.java) + packageManager = mock(PackageManager::class.java) + + packageInfo = mock(PackageInfo::class.java) + packageInfo.versionName = "1.2.3" + `when`(packageInfo.longVersionCode).thenReturn(1234L) + + buildVersionProvider = mock(BuildVersionProvider::class.java) + } + + @Test + fun userAgentUtil_getUserAgent_returnBaseStringWithNoDiagnostics() { + mockPackageManagerHandling() + mockUsbManagerHandling() + + val result = getUserAgent(context, buildVersionProvider = buildVersionProvider) + + assertEquals( + "APP riadigidoc/1.2.3.1234 (schema=1; os=Android ${Build.VERSION.RELEASE}; lang=en; devicetype=mobile/$expectedDeviceModel)", + result, + ) + } + + @Test + fun userAgentUtil_getUserAgent_notIncludeDevicesWithNoDiagnostics() { + mockPackageManagerHandling() + mockUsbManagerHandling() + + val result = getUserAgent(context, SendDiagnostics.None, buildVersionProvider) + + assertFalse(result.contains("devices=")) + } + + @Test + fun userAgentUtil_getUserAgent_includeLibPrefixWhenLibVersionProvided() { + mockPackageManagerHandling() + mockUsbManagerHandling() + UserAgentUtil.setLibdigidocppVersion("4.5.6.40") + + val expectedArch = + Build.SUPPORTED_ABIS?.firstOrNull()?.let { abi -> + when { + abi.startsWith("arm64") -> "arm64" + abi.startsWith("armeabi") -> "arm" + abi.startsWith("x86_64") -> "x86_64" + abi.startsWith("x86") -> "x86" + else -> abi + } + } ?: "unknown" + + val result = getUserAgent(context, buildVersionProvider = buildVersionProvider) + + assertEquals( + "LIB libdigidocpp/4.5.6.40 ($expectedArch) APP riadigidoc/1.2.3.1234 (schema=1; os=Android ${Build.VERSION.RELEASE}; lang=en; devicetype=mobile/$expectedDeviceModel)", + result, + ) + } + + @Test + fun userAgentUtil_getUserAgent_includeTabletDeviceTypeForWideScreen() { + mockPackageManagerHandling() + mockUsbManagerHandling() + + val configuration = Configuration() + configuration.smallestScreenWidthDp = 700 + val resources = mock(Resources::class.java) + `when`(resources.configuration).thenReturn(configuration) + `when`(context.resources).thenReturn(resources) + + val result = getUserAgent(context, buildVersionProvider = buildVersionProvider) + + assertTrue(result.contains("devicetype=tablet/")) + } + + @Test + fun userAgentUtil_getUserAgent_returnEmptyAppVersionOnPackageNotFound() { + `when`(context.packageManager).thenReturn(packageManager) + `when`(context.packageName).thenReturn("App") + `when`(packageManager.getPackageInfo(anyString(), anyInt())) + .thenThrow(PackageManager.NameNotFoundException()) + mockUsbManagerHandling() + `when`(buildVersionProvider.getSDKInt()).thenReturn(Build.VERSION.SDK_INT) + + val result = getUserAgent(context, buildVersionProvider = buildVersionProvider) + + assertTrue(result.contains("APP riadigidoc/ ")) + } + + @Test + fun userAgentUtil_getUserAgent_includeConnectedDeviceInDevicesMode() { + val devices = HashMap() + devices["device1"] = usbDevice + mockPackageManagerHandling() + mockUsbManagerHandling(devices) + `when`(usbDevice.productName).thenReturn("Smart Card Reader 1") + + val result = getUserAgent(context, SendDiagnostics.Devices, buildVersionProvider) + + assertEquals( + "APP riadigidoc/1.2.3.1234 (schema=1; os=Android ${Build.VERSION.RELEASE}; lang=en; devicetype=mobile/$expectedDeviceModel; devices=Smart Card Reader 1)", + result, + ) + } + + @Test + fun userAgentUtil_getUserAgent_notIncludeDevicesWhenListEmpty() { + mockPackageManagerHandling() + mockUsbManagerHandling() + + val result = getUserAgent(context, SendDiagnostics.Devices, buildVersionProvider) + + assertFalse(result.contains("devices=")) + } + + @Test + fun userAgentUtil_getUserAgent_notIncludeNonMatchingDevice() { + val devices = HashMap() + devices["device1"] = usbDevice + mockPackageManagerHandling() + mockUsbManagerHandling(devices) + `when`(usbDevice.productName).thenReturn("Keyboard") + `when`(usbDevice.deviceName).thenReturn("/dev/bus/usb/001/001") + + val result = getUserAgent(context, SendDiagnostics.Devices, buildVersionProvider) + + assertFalse(result.contains("devices=")) + } + + @Test + fun userAgentUtil_getUserAgent_includeDeviceMatchedByDeviceName() { + val devices = HashMap() + devices["device1"] = usbDevice + mockPackageManagerHandling() + mockUsbManagerHandling(devices) + `when`(usbDevice.deviceName).thenReturn("/dev/bus/usb/001/Card-Reader") + + val result = getUserAgent(context, SendDiagnostics.Devices, buildVersionProvider) + + assertTrue(result.contains("devices=")) + } + + @Test + fun userAgentUtil_getUserAgent_includeAllMatchingDevices() { + val usbDevice2 = mock(UsbDevice::class.java) + val devices = LinkedHashMap() + devices["device1"] = usbDevice + devices["device2"] = usbDevice2 + mockPackageManagerHandling() + mockUsbManagerHandling(devices) + `when`(usbDevice.productName).thenReturn("Some Card Reader") + `when`(usbDevice.deviceName).thenReturn("/dev/bus/usb/001/001") + `when`(usbDevice2.productName).thenReturn("Smart Card Reader 2") + `when`(usbDevice2.deviceName).thenReturn("/dev/bus/usb/001/002") + + val result = getUserAgent(context, SendDiagnostics.Devices, buildVersionProvider) + + assertTrue(result.contains("Some Card Reader")) + assertTrue(result.contains("Smart Card Reader 2")) + } + + @Test + fun userAgentUtil_getUserAgent_includeNfcTrueInNfcMode() { + mockPackageManagerHandling() + mockUsbManagerHandling() + + val result = getUserAgent(context, SendDiagnostics.NFC, buildVersionProvider) + + assertEquals( + "APP riadigidoc/1.2.3.1234 (schema=1; os=Android ${Build.VERSION.RELEASE}; lang=en; devicetype=mobile/$expectedDeviceModel; nfc=true)", + result, + ) + } + + @Test + fun userAgentUtil_getAppInfo_returnBaseStringWithoutAppPrefix() { + mockPackageManagerHandling() + mockUsbManagerHandling() + + val result = getAppInfo(context, buildVersionProvider = buildVersionProvider) + + assertEquals( + "riadigidoc/1.2.3.1234 (schema=1; os=Android ${Build.VERSION.RELEASE}; lang=en; devicetype=mobile/$expectedDeviceModel)", + result, + ) + } + + @Test + fun userAgentUtil_getAppInfo_neverIncludesLibPrefixEvenWhenVersionSet() { + mockPackageManagerHandling() + mockUsbManagerHandling() + UserAgentUtil.setLibdigidocppVersion("4.5.6.40") + + val result = getAppInfo(context, buildVersionProvider = buildVersionProvider) + + // "LIB" and "APP" prefixes must not appear — libdigidocpp adds it itself + assertFalse(result.contains("LIB ")) + assertFalse(result.startsWith("APP ")) + } + + @Test + fun userAgentUtil_getAppInfo_includeDevicesInDevicesMode() { + val devices = HashMap() + devices["device1"] = usbDevice + mockPackageManagerHandling() + mockUsbManagerHandling(devices) + `when`(usbDevice.productName).thenReturn("Smart Card Reader 1") + + val result = getAppInfo(context, SendDiagnostics.Devices, buildVersionProvider) + + assertEquals( + "riadigidoc/1.2.3.1234 (schema=1; os=Android ${Build.VERSION.RELEASE}; lang=en; devicetype=mobile/$expectedDeviceModel; devices=Smart Card Reader 1)", + result, + ) + } + + @Test + fun userAgentUtil_getAppInfo_includeNfcTrueInNfcMode() { + mockPackageManagerHandling() + mockUsbManagerHandling() + + val result = getAppInfo(context, SendDiagnostics.NFC, buildVersionProvider) + + assertEquals( + "riadigidoc/1.2.3.1234 (schema=1; os=Android ${Build.VERSION.RELEASE}; lang=en; devicetype=mobile/$expectedDeviceModel; nfc=true)", + result, + ) + } + + private fun mockPackageManagerHandling(sdkInt: Int = Build.VERSION.SDK_INT) { + `when`(context.packageManager).thenReturn(packageManager) + `when`(context.packageName).thenReturn("App") + `when`(packageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo) + `when`(buildVersionProvider.getSDKInt()).thenReturn(sdkInt) + } + + private fun mockUsbManagerHandling(devices: HashMap = hashMapOf()) { + `when`(context.getSystemService(Context.USB_SERVICE)).thenReturn(usbManager) + `when`(usbManager.deviceList).thenReturn(devices) + } +} diff --git a/smart-id-lib/src/main/kotlin/ee/ria/DigiDoc/smartId/SmartSignService.kt b/smart-id-lib/src/main/kotlin/ee/ria/DigiDoc/smartId/SmartSignService.kt index ac497b48f..e6eb90da5 100644 --- a/smart-id-lib/src/main/kotlin/ee/ria/DigiDoc/smartId/SmartSignService.kt +++ b/smart-id-lib/src/main/kotlin/ee/ria/DigiDoc/smartId/SmartSignService.kt @@ -251,7 +251,7 @@ class SmartSignServiceImpl val signerCert = getCertificate(sessionStatusResponse.cert?.value) val signer = ExternalSigner(signerCert) signer.setProfile(SIGNATURE_PROFILE_TS) - signer.setUserAgent(UserAgentUtil.getUserAgent(context)) + signer.setUserAgent(UserAgentUtil.getAppInfo(context)) val dataToSignBytes = Base64.getEncoder().encode(