1 // Copyright 2015-present 650 Industries. All rights reserved.
2 package host.exp.exponent.kernel.services
3 
4 import host.exp.exponent.di.NativeModuleDepsProvider
5 import host.exp.exponent.kernel.ExperienceKey
6 import host.exp.exponent.storage.ExponentSharedPreferences
7 import org.json.JSONException
8 import org.json.JSONObject
9 import java.util.*
10 import javax.inject.Inject
11 
12 private const val FIVE_MINUTES_MS = (5 * 60 * 1000).toLong()
13 private const val AUTO_RELOAD_BUFFER_BASE_MS = (5 * 1000).toLong()
14 
15 class ErrorRecoveryManager(private val experienceKey: ExperienceKey?) {
16   private var timeLastLoaded = 0L
17   private var didError = false
18 
19   @Inject
20   lateinit var exponentSharedPreferences: ExponentSharedPreferences
21 
markExperienceLoadednull22   fun markExperienceLoaded() {
23     timeLastLoaded = System.currentTimeMillis()
24     timeAnyExperienceLoaded = timeLastLoaded
25     markErrored(false)
26   }
27 
28   @JvmOverloads
markErrorednull29   fun markErrored(didError: Boolean = true) {
30     this.didError = didError
31     if (experienceKey != null) {
32       val metadata = exponentSharedPreferences.getExperienceMetadata(experienceKey) ?: JSONObject()
33       try {
34         metadata.put(ExponentSharedPreferences.EXPERIENCE_METADATA_LOADING_ERROR, didError)
35         exponentSharedPreferences.updateExperienceMetadata(experienceKey, metadata)
36       } catch (e: JSONException) {
37         e.printStackTrace()
38       }
39     }
40   }
41 
shouldReloadOnErrornull42   fun shouldReloadOnError(): Boolean {
43     val diff = System.currentTimeMillis() - timeLastLoaded
44     val reloadBuffer = reloadBuffer()
45     return diff >= reloadBuffer
46   }
47 
reloadBuffernull48   private fun reloadBuffer(): Long {
49     var interval = Math.min(
50       FIVE_MINUTES_MS,
51       (
52         AUTO_RELOAD_BUFFER_BASE_MS * Math.pow(
53           1.5,
54           reloadBufferDepth.toDouble()
55         )
56         ).toLong()
57     )
58     val timeSinceLastExperienceLoaded = System.currentTimeMillis() - timeAnyExperienceLoaded
59     if (timeSinceLastExperienceLoaded > interval * 2) {
60       reloadBufferDepth = 0
61       interval = AUTO_RELOAD_BUFFER_BASE_MS
62     }
63     return interval
64   }
65 
66   companion object {
67     private val experienceScopeKeyToManager: MutableMap<String, ErrorRecoveryManager> = HashMap()
68     private var timeAnyExperienceLoaded: Long = 0
69 
70     // This goes up when there are a bunch of errors in succession
71     private var reloadBufferDepth: Long = 0
72 
getInstancenull73     @JvmStatic fun getInstance(experienceKey: ExperienceKey): ErrorRecoveryManager {
74       if (!experienceScopeKeyToManager.containsKey(experienceKey.scopeKey)) {
75         experienceScopeKeyToManager[experienceKey.scopeKey] = ErrorRecoveryManager(experienceKey)
76       }
77       return experienceScopeKeyToManager[experienceKey.scopeKey]!!
78     }
79   }
80 
81   init {
82     NativeModuleDepsProvider.instance.inject(ErrorRecoveryManager::class.java, this)
83   }
84 }
85