diff --git a/native/android/app/src/main/java/app/comm/android/CommCoreJSIModulePackage.java b/native/android/app/src/main/java/app/comm/android/CommCoreJSIModulePackage.java deleted file mode 100644 --- a/native/android/app/src/main/java/app/comm/android/CommCoreJSIModulePackage.java +++ /dev/null @@ -1,44 +0,0 @@ -package app.comm.android; - -import app.comm.android.fbjni.CommHybrid; -import app.comm.android.fbjni.CommMMKV; -import app.comm.android.fbjni.CommSecureStore; -import app.comm.android.fbjni.DatabaseInitializer; -import app.comm.android.fbjni.GlobalDBSingleton; -import com.facebook.react.bridge.JSIModulePackage; -import com.facebook.react.bridge.JSIModuleSpec; -import com.facebook.react.bridge.JavaScriptContextHolder; -import com.facebook.react.bridge.ReactApplicationContext; -import expo.modules.securestore.SecureStoreModule; -import java.io.File; -import java.util.Collections; -import java.util.List; -import java.util.function.Supplier; - -public class CommCoreJSIModulePackage implements JSIModulePackage { - - @Override - public List getJSIModules( - ReactApplicationContext reactApplicationContext, - JavaScriptContextHolder jsContext) { - Supplier secureStoreModuleSupplier = - ExpoUtils.createExpoSecureStoreSupplier(reactApplicationContext); - CommSecureStore.getInstance().initialize(secureStoreModuleSupplier); - CommHybrid.initHybrid(reactApplicationContext); - - // We issue a useless set on CommSecureStore here to force it to initialize - // prior to scheduling initializeDatabaseManager on the DB thread below. - // This avoids a race condition where the DB thread and main thread both - // attempt to initialize CommSecureStore at the same time, which can cause - // a crash loop as described in ENG-7696 and ENG-8069. - CommSecureStore.set("comm.secure_store_initialization_complete", "1"); - - File sqliteFile = reactApplicationContext.getDatabasePath("comm.sqlite"); - GlobalDBSingleton.scheduleOrRun(() -> { - DatabaseInitializer.initializeDatabaseManager(sqliteFile.getPath()); - }); - CommMMKV.initialize(); - - return Collections.emptyList(); - } -} diff --git a/native/android/app/src/main/java/app/comm/android/MainApplication.kt b/native/android/app/src/main/java/app/comm/android/MainApplication.kt --- a/native/android/app/src/main/java/app/comm/android/MainApplication.kt +++ b/native/android/app/src/main/java/app/comm/android/MainApplication.kt @@ -8,6 +8,7 @@ import android.content.res.Configuration import android.database.CursorWindow import android.os.Build +import app.comm.android.comminitializermodule.CommInitializerPackage import app.comm.android.commservices.CommServicesPackage import app.comm.android.notifications.CommAndroidNotificationsPackage import com.facebook.react.PackageList @@ -36,6 +37,7 @@ packages.add(KeyboardInputPackage(this.application)) packages.add(CommAndroidNotificationsPackage()) packages.add(CommServicesPackage()) + packages.add(CommInitializerPackage()) return packages } diff --git a/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerModule.kt b/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerModule.kt new file mode 100644 --- /dev/null +++ b/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerModule.kt @@ -0,0 +1,38 @@ +package app.comm.android.comminitializermodule + +import app.comm.android.fbjni.CommHybrid +import app.comm.android.fbjni.CommMMKV +import app.comm.android.fbjni.CommSecureStore +import app.comm.android.fbjni.DatabaseInitializer +import app.comm.android.fbjni.GlobalDBSingleton +import app.comm.android.securestore.SecureStoreModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.module.annotations.ReactModule + +@ReactModule(name = CommInitializerModuleSpec.NAME) +class CommInitializerModule(reactContext: ReactApplicationContext?) : + CommInitializerModuleSpec(reactContext) { + @ReactMethod(isBlockingSynchronousMethod = true) + override fun initializeComm(): Boolean { + CommSecureStore.getInstance().initialize({ + SecureStoreModule(reactApplicationContext) + }) + CommHybrid.initHybrid(reactApplicationContext) + + // We issue a useless set on CommSecureStore here to force it to initialize + // prior to scheduling initializeDatabaseManager on the DB thread below. + // This avoids a race condition where the DB thread and main thread both + // attempt to initialize CommSecureStore at the same time, which can cause + // a crash loop as described in ENG-7696 and ENG-8069. + CommSecureStore.set("comm.secure_store_initialization_complete", "1") + + val sqliteFile = reactApplicationContext.getDatabasePath("comm.sqlite") + GlobalDBSingleton.scheduleOrRun { + DatabaseInitializer.initializeDatabaseManager(sqliteFile.path) + } + CommMMKV.initialize() + + return true + } +} \ No newline at end of file diff --git a/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerModuleSpec.kt b/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerModuleSpec.kt new file mode 100644 --- /dev/null +++ b/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerModuleSpec.kt @@ -0,0 +1,24 @@ +package app.comm.android.comminitializermodule + +import com.facebook.proguard.annotations.DoNotStrip +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import com.facebook.react.turbomodule.core.interfaces.TurboModule +import javax.annotation.Nonnull + +abstract class CommInitializerModuleSpec(reactContext: ReactApplicationContext?) : + ReactContextBaseJavaModule(reactContext), TurboModule { + @Nonnull + override fun getName(): String { + return NAME + } + + @ReactMethod(isBlockingSynchronousMethod = true) + @DoNotStrip + abstract fun initializeComm(): Boolean + + companion object { + const val NAME: String = "CommInitializerModule" + } +} \ No newline at end of file diff --git a/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerPackage.kt b/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerPackage.kt new file mode 100644 --- /dev/null +++ b/native/android/app/src/main/java/app/comm/android/comminitializermodule/CommInitializerPackage.kt @@ -0,0 +1,50 @@ +package app.comm.android.comminitializermodule + +import com.facebook.react.BaseReactPackage +import com.facebook.react.ReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.module.annotations.ReactModuleList +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.react.module.model.ReactModuleInfoProvider +import java.util.Objects + +@ReactModuleList( + nativeModules = [CommInitializerModule::class] +) +class CommInitializerPackage : BaseReactPackage(), ReactPackage { + override fun getModule( + name: String, reactContext: ReactApplicationContext + ): NativeModule? { + return when (name) { + CommInitializerModuleSpec.NAME -> CommInitializerModule(reactContext) + else -> null + } + } + + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { + val moduleList: Array> = arrayOf(CommInitializerModule::class.java) + + val reactModuleInfoMap: MutableMap = HashMap() + for (moduleClass in moduleList) { + val reactModule = + Objects.requireNonNull( + moduleClass.getAnnotation( + ReactModule::class.java + ) + ) + + reactModuleInfoMap[reactModule.name] = ReactModuleInfo( + reactModule.name, + moduleClass.name, + reactModule.canOverrideExistingModule, + reactModule.needsEagerInit, + reactModule.isCxxModule, + true + ) + } + + return ReactModuleInfoProvider { reactModuleInfoMap } + } +} \ No newline at end of file