Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: changes to hopefully improve startup and ANRs [WPB-6048] #2611

Merged
merged 2 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ dependencies {
implementation(libs.androidx.splashscreen)
implementation(libs.androidx.exifInterface)
implementation(libs.androidx.biometric)
implementation(libs.androidx.startup)

implementation(libs.ktx.dateTime)
implementation(libs.material)
Expand Down
2 changes: 1 addition & 1 deletion app/src/dev/kotlin/com/wire/android/util/DataDogLogger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ object DataDogLogger : LogWriter() {

private val logger = Logger.Builder()
.setNetworkInfoEnabled(true)
.setLogcatLogsEnabled(true)
.setLogcatLogsEnabled(false) // we already use platformLogWriter() along with DataDogLogger, don't need duplicates in LogCat
.setDatadogLogsEnabled(true)
.setBundleWithTraceEnabled(true)
.setLoggerName("DATADOG")
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ object DataDogLogger : LogWriter() {

private val logger = Logger.Builder()
.setNetworkInfoEnabled(true)
.setLogcatLogsEnabled(true)
.setLogcatLogsEnabled(false) // we already use platformLogWriter() along with DataDogLogger, don't need duplicates in LogCat
.setDatadogLogsEnabled(true)
.setBundleWithTraceEnabled(true)
.setLoggerName("DATADOG")
.build()
Expand Down
39 changes: 17 additions & 22 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -229,17 +229,34 @@
</intent-filter>
</activity>

<!--
We make a custom Firebase init, because the app selects the Firebase project at a runtime,
so we need to remove default FirebaseInitProvider and create our custom FirebaseInitializer.
-->
<provider
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:authorities="${applicationId}.firebaseinitprovider"
tools:node="remove" />

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">

<!--
We create custom WorkManager Configuration.Provider and initialize on-demand,
so we need to remove default WorkManagerInitializer.
-->
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />

<meta-data
android:name="com.wire.android.initializer.FirebaseInitializer"
android:value="androidx.startup" />

</provider>

<provider
Expand Down Expand Up @@ -301,28 +318,6 @@
android:exported="false"
android:foregroundServiceType="phoneCall" />

<!-- If you want to disable android.startup completely. -->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />

<provider
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:authorities="${applicationId}.firebaseinitprovider"
tools:node="remove" />
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- If you are using androidx.startup to initialize other components -->
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>

</application>

</manifest>
85 changes: 39 additions & 46 deletions app/src/main/kotlin/com/wire/android/WireApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,19 @@
import android.os.StrictMode
import androidx.work.Configuration
import co.touchlab.kermit.platformLogWriter
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.wire.android.datastore.GlobalDataStore
import com.wire.android.di.ApplicationScope
import com.wire.android.di.KaliumCoreLogic
import com.wire.android.util.DataDogLogger
import com.wire.android.util.LogFileWriter
import com.wire.android.util.extension.isGoogleServicesAvailable
import com.wire.android.util.getGitBuildId
import com.wire.android.util.lifecycle.ConnectionPolicyManager
import com.wire.android.workmanager.WireWorkerFactory
import com.wire.kalium.logger.KaliumLogLevel
import com.wire.kalium.logger.KaliumLogger
import com.wire.kalium.logic.CoreLogger
import com.wire.kalium.logic.CoreLogic
import dagger.Lazy
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
Expand All @@ -50,29 +48,29 @@

@Inject
@KaliumCoreLogic
lateinit var coreLogic: CoreLogic
lateinit var coreLogic: Lazy<CoreLogic>

@Inject
lateinit var logFileWriter: LogFileWriter
lateinit var logFileWriter: Lazy<LogFileWriter>

@Inject
lateinit var connectionPolicyManager: ConnectionPolicyManager
lateinit var connectionPolicyManager: Lazy<ConnectionPolicyManager>

@Inject
lateinit var wireWorkerFactory: WireWorkerFactory
lateinit var wireWorkerFactory: Lazy<WireWorkerFactory>

@Inject
lateinit var globalObserversManager: GlobalObserversManager
lateinit var globalObserversManager: Lazy<GlobalObserversManager>

@Inject
lateinit var globalDataStore: GlobalDataStore
lateinit var globalDataStore: Lazy<GlobalDataStore>

@Inject
@ApplicationScope
lateinit var globalAppScope: CoroutineScope
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setWorkerFactory(wireWorkerFactory)
.setWorkerFactory(wireWorkerFactory.get())
.build()
}

Expand All @@ -81,23 +79,19 @@

enableStrictMode()

if (this.isGoogleServicesAvailable()) {
val firebaseOptions = FirebaseOptions.Builder()
.setApplicationId(BuildConfig.FIREBASE_APP_ID)
.setGcmSenderId(BuildConfig.FIREBASE_PUSH_SENDER_ID)
.setApiKey(BuildConfig.GOOGLE_API_KEY)
.setProjectId(BuildConfig.FCM_PROJECT_ID)
.build()
FirebaseApp.initializeApp(this, firebaseOptions)
}
globalAppScope.launch {
initializeApplicationLoggingFrameworks()

Check warning on line 83 in app/src/main/kotlin/com/wire/android/WireApplication.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/WireApplication.kt#L83

Added line #L83 was not covered by tests

initializeApplicationLoggingFrameworks()
connectionPolicyManager.startObservingAppLifecycle()
appLogger.i("$TAG app lifecycle")
connectionPolicyManager.get().startObservingAppLifecycle()

Check warning on line 86 in app/src/main/kotlin/com/wire/android/WireApplication.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/WireApplication.kt#L85-L86

Added lines #L85 - L86 were not covered by tests

// TODO: Can be handled in one of Sync steps
coreLogic.updateApiVersionsScheduler.schedulePeriodicApiVersionUpdate()
appLogger.i("$TAG api version update")

Check warning on line 88 in app/src/main/kotlin/com/wire/android/WireApplication.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/WireApplication.kt#L88

Added line #L88 was not covered by tests
// TODO: Can be handled in one of Sync steps
coreLogic.get().updateApiVersionsScheduler.schedulePeriodicApiVersionUpdate()

Check warning on line 90 in app/src/main/kotlin/com/wire/android/WireApplication.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/WireApplication.kt#L90

Added line #L90 was not covered by tests

globalObserversManager.observe()
appLogger.i("$TAG global observers")
globalObserversManager.get().observe()
}

Check warning on line 94 in app/src/main/kotlin/com/wire/android/WireApplication.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/WireApplication.kt#L92-L94

Added lines #L92 - L94 were not covered by tests
}

private fun enableStrictMode() {
Expand All @@ -121,29 +115,27 @@
}
}

private fun initializeApplicationLoggingFrameworks() {
globalAppScope.launch {
// 1. Datadog should be initialized first
ExternalLoggerManager.initDatadogLogger(applicationContext, globalDataStore)
// 2. Initialize our internal logging framework
val isLoggingEnabled = globalDataStore.isLoggingEnabled().first()
val config = if (isLoggingEnabled) {
KaliumLogger.Config.DEFAULT.apply {
setLogLevel(KaliumLogLevel.VERBOSE)
setLogWriterList(listOf(DataDogLogger, platformLogWriter()))
}
} else {
KaliumLogger.Config.disabled()
private suspend fun initializeApplicationLoggingFrameworks() {
// 1. Datadog should be initialized first
ExternalLoggerManager.initDatadogLogger(applicationContext, globalDataStore.get())
// 2. Initialize our internal logging framework
val isLoggingEnabled = globalDataStore.get().isLoggingEnabled().first()
val config = if (isLoggingEnabled) {
KaliumLogger.Config.DEFAULT.apply {
setLogLevel(KaliumLogLevel.VERBOSE)
setLogWriterList(listOf(DataDogLogger, platformLogWriter()))
}
// 2. Initialize our internal logging framework
AppLogger.init(config)
CoreLogger.init(config)
// 3. Initialize our internal FILE logging framework
logFileWriter.start()
// 4. Everything ready, now we can log device info
appLogger.i("Logger enabled")
logDeviceInformation()
} else {
KaliumLogger.Config.disabled()
}
// 2. Initialize our internal logging framework
AppLogger.init(config)
CoreLogger.init(config)
// 3. Initialize our internal FILE logging framework
logFileWriter.get().start()
// 4. Everything ready, now we can log device info
appLogger.i("Logger enabled")
logDeviceInformation()
}

private fun logDeviceInformation() {
Expand All @@ -169,7 +161,7 @@
override fun onLowMemory() {
super.onLowMemory()
appLogger.w("onLowMemory called - Stopping logging, buckling the seatbelt and hoping for the best!")
logFileWriter.stop()
logFileWriter.get().stop()
}

private companion object {
Expand All @@ -190,5 +182,6 @@
values().firstOrNull { it.level == value } ?: TRIM_MEMORY_UNKNOWN
}
}
private const val TAG = "WireApplication"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Wire
* Copyright (C) 2024 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.initializer

import android.content.Context
import androidx.startup.Initializer
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.wire.android.BuildConfig
import com.wire.android.util.extension.isGoogleServicesAvailable

class FirebaseInitializer : Initializer<Unit> {

Check warning on line 27 in app/src/main/kotlin/com/wire/android/initializer/FirebaseInitializer.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/initializer/FirebaseInitializer.kt#L27

Added line #L27 was not covered by tests
override fun create(context: Context) {
if (context.isGoogleServicesAvailable()) {
val firebaseOptions = FirebaseOptions.Builder()
.setApplicationId(BuildConfig.FIREBASE_APP_ID)
.setGcmSenderId(BuildConfig.FIREBASE_PUSH_SENDER_ID)
.setApiKey(BuildConfig.GOOGLE_API_KEY)
.setProjectId(BuildConfig.FCM_PROJECT_ID)
.build()
FirebaseApp.initializeApp(context, firebaseOptions)

Check warning on line 36 in app/src/main/kotlin/com/wire/android/initializer/FirebaseInitializer.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/initializer/FirebaseInitializer.kt#L30-L36

Added lines #L30 - L36 were not covered by tests
}
}
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList() // no dependencies on other libraries

Check warning on line 39 in app/src/main/kotlin/com/wire/android/initializer/FirebaseInitializer.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/initializer/FirebaseInitializer.kt#L38-L39

Added lines #L38 - L39 were not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Wire
* Copyright (C) 2024 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.initializer

import android.content.Context
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent

@EntryPoint
@InstallIn(SingletonComponent::class)
interface InitializerEntryPoint {

companion object {
// a helper method to resolve the InitializerEntryPoint from the context
fun resolve(context: Context): InitializerEntryPoint {
val appContext = context.applicationContext ?: throw IllegalStateException()
return EntryPointAccessors.fromApplication(appContext, InitializerEntryPoint::class.java)

Check warning on line 34 in app/src/main/kotlin/com/wire/android/initializer/InitializerEntryPoint.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/initializer/InitializerEntryPoint.kt#L34

Added line #L34 was not covered by tests
}
}
}
Loading
Loading