1
1
package tech.httptoolkit.android
2
2
3
3
import android.app.Application
4
- import android.content.Context
4
+ import android.content.*
5
+ import android.os.Build
5
6
import android.util.Log
6
7
import com.android.installreferrer.api.InstallReferrerClient
7
8
import com.android.installreferrer.api.InstallReferrerClient.InstallReferrerResponse
@@ -12,7 +13,8 @@ import com.google.android.gms.analytics.HitBuilders
12
13
import com.google.android.gms.analytics.Tracker
13
14
import io.sentry.Sentry
14
15
import io.sentry.android.AndroidSentryClientFactory
15
- import kotlinx.coroutines.*
16
+ import kotlinx.coroutines.Dispatchers
17
+ import kotlinx.coroutines.withContext
16
18
import net.swiftzer.semver.SemVer
17
19
import okhttp3.OkHttpClient
18
20
import okhttp3.Request
@@ -22,14 +24,51 @@ import java.util.concurrent.atomic.AtomicBoolean
22
24
import kotlin.coroutines.resume
23
25
import kotlin.coroutines.suspendCoroutine
24
26
27
+ private const val VPN_START_TIME_PREF = " vpn-start-time"
28
+ private const val APP_CRASHED_PREF = " app-crashed"
29
+ private const val FIRST_RUN_PREF = " is-first-run"
30
+
31
+ private val isProbablyEmulator =
32
+ Build .FINGERPRINT .startsWith(" generic" )
33
+ || Build .FINGERPRINT .startsWith(" unknown" )
34
+ || Build .MODEL .contains(" google_sdk" )
35
+ || Build .MODEL .contains(" Emulator" )
36
+ || Build .MODEL .contains(" Android SDK built for x86" )
37
+ || Build .BOARD == " QC_Reference_Phone"
38
+ || Build .MANUFACTURER .contains(" Genymotion" )
39
+ || Build .HOST .startsWith(" Build" )
40
+ || (Build .BRAND .startsWith(" generic" ) && Build .DEVICE .startsWith(" generic" ))
41
+ || Build .PRODUCT == " google_sdk"
42
+
43
+ private val bootTime = (System .currentTimeMillis() - android.os.SystemClock .elapsedRealtime())
25
44
26
45
class HttpToolkitApplication : Application () {
27
46
28
47
private var analytics: GoogleAnalytics ? = null
29
48
private var ga: Tracker ? = null
30
49
50
+ private lateinit var prefs: SharedPreferences
51
+ private var vpnWasKilled: Boolean = false
52
+
53
+ var vpnShouldBeRunning: Boolean
54
+ get() {
55
+ return prefs.getLong(VPN_START_TIME_PREF , - 1 ) > bootTime
56
+ }
57
+ set(value) {
58
+ if (value) {
59
+ prefs.edit().putLong(VPN_START_TIME_PREF , System .currentTimeMillis()).apply ()
60
+ } else {
61
+ prefs.edit().putLong(VPN_START_TIME_PREF , - 1 ).apply ()
62
+ }
63
+ }
64
+
31
65
override fun onCreate () {
32
66
super .onCreate()
67
+ prefs = getSharedPreferences(" tech.httptoolkit.android" , MODE_PRIVATE )
68
+
69
+ Thread .setDefaultUncaughtExceptionHandler { _, _ ->
70
+ prefs.edit().putBoolean(APP_CRASHED_PREF , true ).apply ()
71
+ }
33
72
34
73
if (BuildConfig .SENTRY_DSN != null ) {
35
74
Sentry .init (BuildConfig .SENTRY_DSN , AndroidSentryClientFactory (this ))
@@ -41,18 +80,35 @@ class HttpToolkitApplication : Application() {
41
80
resumeEvents() // Resume events on app startup, in case they were paused and we crashed
42
81
}
43
82
83
+ // Check if we've been recreated unexpectedly, with no crashes in the meantime:
84
+ val appCrashed = prefs.getBoolean(APP_CRASHED_PREF , false )
85
+ prefs.edit().putBoolean(APP_CRASHED_PREF , false ).apply ()
86
+
87
+ vpnWasKilled = vpnShouldBeRunning && ! isVpnActive() && ! appCrashed && ! isProbablyEmulator
88
+ if (vpnWasKilled) {
89
+ Sentry .capture(" VPN killed in the background" )
90
+ // The UI will show an alert next time the MainActivity is created.
91
+ }
92
+
44
93
Log .i(TAG , " App created" )
45
94
}
46
95
96
+ /* *
97
+ * Check whether the VPN was killed as a sleeping background process, and then
98
+ * reset that state so that future checks (until it's next killed) return false
99
+ */
100
+ fun popVpnKilledState (): Boolean {
101
+ return vpnWasKilled
102
+ .also { this .vpnWasKilled = false }
103
+ }
104
+
47
105
/* *
48
106
* Grab any first run params, drop them for future usage, and return them.
49
107
* This will return first-run params at most once (per install).
50
108
*/
51
109
suspend fun popFirstRunParams (): String? {
52
- val prefs = getSharedPreferences(" tech.httptoolkit.android" , MODE_PRIVATE )
53
-
54
- val isFirstRun = prefs.getBoolean(" is-first-run" , true )
55
- prefs.edit().putBoolean(" is-first-run" , false ).apply ()
110
+ val isFirstRun = prefs.getBoolean(FIRST_RUN_PREF , true )
111
+ prefs.edit().putBoolean(FIRST_RUN_PREF , false ).apply ()
56
112
57
113
val installTime = packageManager.getPackageInfo(packageName, 0 ).firstInstallTime
58
114
val now = System .currentTimeMillis()
@@ -96,7 +152,6 @@ class HttpToolkitApplication : Application() {
96
152
}
97
153
})
98
154
}
99
-
100
155
}
101
156
102
157
var lastProxy: ProxyConfig ?
0 commit comments