Configure user agent for all network request (#677)

This commit is contained in:
Benoit Marty 2023-06-26 21:43:19 +02:00 committed by Benoit Marty
parent 469b54f204
commit f4b4e4d316
12 changed files with 183 additions and 2 deletions

View file

@ -34,6 +34,7 @@ dependencies {
anvil(projects.anvilcodegen)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.network)
implementation(projects.libraries.architecture)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.uiStrings)

View file

@ -35,6 +35,7 @@ import io.element.android.libraries.core.extensions.toOnOff
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.network.useragent.UserAgentProvider
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import okhttp3.Call
@ -64,6 +65,7 @@ class DefaultBugReporter @Inject constructor(
private val crashDataStore: CrashDataStore,
private val coroutineDispatchers: CoroutineDispatchers,
private val okHttpClient: Provider<OkHttpClient>,
private val userAgentProvider: UserAgentProvider,
/*
private val activeSessionHolder: ActiveSessionHolder,
private val versionProvider: VersionProvider,
@ -222,7 +224,7 @@ class DefaultBugReporter @Inject constructor(
val builder = BugReporterMultipartBody.Builder()
.addFormDataPart("text", text)
.addFormDataPart("app", rageShakeAppNameForReport(reportType))
// .addFormDataPart("user_agent", matrix.getUserAgent())
.addFormDataPart("user_agent", userAgentProvider.provide())
.addFormDataPart("user_id", userId)
.addFormDataPart("can_contact", canContact.toString())
.addFormDataPart("device_id", deviceId)

View file

@ -33,6 +33,7 @@ dependencies {
implementation(libs.matrix.sdk)
implementation(projects.libraries.di)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.network)
implementation(projects.services.toolbox.api)
api(projects.libraries.matrix.api)
implementation(libs.dagger)

View file

@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
import io.element.android.libraries.matrix.api.auth.OidcDetails
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.impl.RustMatrixClient
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.libraries.sessionstorage.api.SessionData
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.services.toolbox.api.systemclock.SystemClock
@ -56,6 +57,7 @@ class RustMatrixAuthenticationService @Inject constructor(
private val coroutineDispatchers: CoroutineDispatchers,
private val sessionStore: SessionStore,
private val clock: SystemClock,
private val userAgentProvider: UserAgentProvider,
) : MatrixAuthenticationService {
private val authService: RustAuthenticationService = RustAuthenticationService(
@ -84,6 +86,7 @@ class RustMatrixAuthenticationService @Inject constructor(
.basePath(baseDirectory.absolutePath)
.homeserverUrl(sessionData.homeserverUrl)
.username(sessionData.userId)
.userAgent(userAgentProvider.provide())
.use { it.build() }
client.restoreSession(sessionData.toSession())
createMatrixClient(client)

View file

@ -23,6 +23,7 @@ import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.network.interceptors.FormattedJsonHttpLogger
import io.element.android.libraries.network.interceptors.UserAgentInterceptor
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
@ -35,10 +36,12 @@ object NetworkModule {
@SingleIn(AppScope::class)
fun providesOkHttpClient(
buildMeta: BuildMeta,
userAgentInterceptor: UserAgentInterceptor,
): OkHttpClient = OkHttpClient.Builder().apply {
connectTimeout(30, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
writeTimeout(60, TimeUnit.SECONDS)
addInterceptor(userAgentInterceptor)
if (buildMeta.isDebuggable) addInterceptor(providesHttpLoggingInterceptor())
}.build()

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.network.headers
internal object HttpHeaders {
const val Authorization = "Authorization"
const val UserAgent = "User-Agent"
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.network.interceptors
import io.element.android.libraries.network.headers.HttpHeaders
import io.element.android.libraries.network.useragent.UserAgentProvider
import okhttp3.Interceptor
import okhttp3.Response
import javax.inject.Inject
class UserAgentInterceptor @Inject constructor(
private val userAgentProvider: UserAgentProvider,
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val newRequest = chain.request()
.newBuilder()
.header(HttpHeaders.UserAgent, userAgentProvider.provide())
.build()
return chain.proceed(newRequest)
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.network.useragent
import android.os.Build
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn
import javax.inject.Inject
@SingleIn(AppScope::class)
@ContributesBinding(AppScope::class)
class DefaultUserAgentProvider @Inject constructor(
private val buildMeta: BuildMeta,
) : UserAgentProvider {
private val userAgent: String by lazy { buildUserAgent() }
override fun provide(): String = userAgent
/**
* Create an user agent with the application version.
* Ex: Element X/1.5.0 (Xiaomi Mi 9T; Android 11; RKQ1.200826.002; Sdk 0.1.0)
*/
private fun buildUserAgent(): String {
val appName = buildMeta.applicationName
val appVersion = buildMeta.versionName
val deviceManufacturer = Build.MANUFACTURER
val deviceModel = Build.MODEL
val androidVersion = Build.VERSION.RELEASE
val deviceBuildId = Build.DISPLAY
val matrixSdkVersion = "TODO"
return buildString {
append(appName)
append("/")
append(appVersion)
append(" (")
append(deviceManufacturer)
append(" ")
append(deviceModel)
append("; ")
append("Android ")
append(androidVersion)
append("; ")
append(deviceBuildId)
append("; ")
append("Sdk ")
append(matrixSdkVersion)
append(")")
}
}
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.network.useragent
class SimpleUserAgentProvider(
private val userAgent: String = "User agent"
) : UserAgentProvider {
override fun provide(): String = userAgent
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.network.useragent
interface UserAgentProvider {
fun provide(): String
}

View file

@ -54,6 +54,7 @@ dependencies {
implementation(projects.libraries.designsystem)
implementation(projects.libraries.architecture)
implementation(projects.libraries.core)
implementation(projects.libraries.network)
implementation(projects.libraries.dateformatter.impl)
implementation(projects.libraries.eventformatter.impl)
implementation(projects.features.invitelist.impl)

View file

@ -29,6 +29,7 @@ import androidx.core.view.WindowCompat
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore
import io.element.android.services.toolbox.impl.systemclock.DefaultSystemClock
import kotlinx.coroutines.runBlocking
@ -45,7 +46,8 @@ class MainActivity : ComponentActivity() {
appCoroutineScope = Singleton.appScope,
coroutineDispatchers = Singleton.coroutineDispatchers,
sessionStore = InMemorySessionStore(),
clock = DefaultSystemClock()
clock = DefaultSystemClock(),
userAgentProvider = SimpleUserAgentProvider("MinimalSample")
)
}