@@ -76,11 +76,17 @@ class ProxyVpnService : VpnService(), IProtectSocket {
7676
7777 if (intent.action == START_VPN_ACTION ) {
7878 val proxyConfig = intent.getParcelableExtra<ProxyConfig >(PROXY_CONFIG_EXTRA )
79- startVpn(proxyConfig)
8079
81- // If the system briefly kills us for some reason (memory, the user, whatever) whilst
82- // running the VPN, it should redeliver the VPN setup intent ASAP.
83- return Service .START_REDELIVER_INTENT
80+ val vpnStarted = startVpn(proxyConfig)
81+
82+ if (vpnStarted) {
83+ // If the system briefly kills us for some reason (memory, the user, whatever) whilst
84+ // running the VPN, it should redeliver the VPN setup intent ASAP.
85+ return Service .START_REDELIVER_INTENT
86+ } else {
87+ // We failed to start somehow - cleanup
88+ stopVpn()
89+ }
8490 } else if (intent.action == STOP_VPN_ACTION ) {
8591 stopVpn()
8692 }
@@ -132,7 +138,7 @@ class ProxyVpnService : VpnService(), IProtectSocket {
132138
133139 }
134140
135- private fun startVpn (proxyConfig : ProxyConfig ) {
141+ private fun startVpn (proxyConfig : ProxyConfig ): Boolean {
136142 this .proxyConfig = proxyConfig
137143 val packages = packageManager.getInstalledApplications(PackageManager .GET_META_DATA )
138144
@@ -143,67 +149,76 @@ class ProxyVpnService : VpnService(), IProtectSocket {
143149 name -> name.startsWith(" com.genymotion" )
144150 }
145151
146- if (vpnInterface == null ) {
147- app !! .pauseEvents() // Try not to send events while the VPN is active, it's unnecessary noise
148- app!! .trackEvent( " VPN" , " vpn-started " )
149- vpnInterface = Builder ( )
150- .addAddress( VPN_IP_ADDRESS , 32 )
151- .addRoute( ALL_ROUTES , 0 )
152- . apply {
153- if ( Build . VERSION . SDK_INT >= Build . VERSION_CODES . Q ) {
154- // Where possible, we want to explicitly set the proxy in addition to
155- // manually redirecting traffic. This is useful because it captures HTTP sent
156- // to non-default ports. We still need to do both though, as not all clients
157- // will use the proxy settings.
158- setHttpProxy( ProxyInfo .buildDirectProxy(proxyConfig.ip, proxyConfig.port))
159- }
152+ if (this . vpnInterface != null ) return false // The VPN is already running, somehow? Do nothing
153+
154+ app!! .pauseEvents() // Try not to send events while the VPN is active, it's unnecessary noise
155+ app !! .trackEvent( " VPN " , " vpn-started " )
156+ val vpnInterface = Builder ( )
157+ .addAddress( VPN_IP_ADDRESS , 32 )
158+ .addRoute( ALL_ROUTES , 0 )
159+ . apply {
160+ if ( Build . VERSION . SDK_INT >= Build . VERSION_CODES . Q ) {
161+ // Where possible, we want to explicitly set the proxy in addition to
162+ // manually redirecting traffic. This is useful because it captures HTTP sent
163+ // to non-default ports. We still need to do both though, as not all clients
164+ // will use the proxy settings.
165+ setHttpProxy( ProxyInfo .buildDirectProxy(proxyConfig.ip, proxyConfig.port))
160166 }
167+ }
168+
169+ .setMtu(MAX_PACKET_LEN ) // Limit the packet size to the buffer used by ProxyVpnRunnable
170+ .setBlocking(true ) // We use a blocking loop to read in ProxyVpnRunnable
161171
162- .setMtu(MAX_PACKET_LEN ) // Limit the packet size to the buffer used by ProxyVpnRunnable
163- .setBlocking(true ) // We use a blocking loop to read in ProxyVpnRunnable
164-
165- .apply {
166- // We exclude ourselves from interception, so we can still make network requests
167- // separately, primarily because otherwise pinging with isReachable is recursive.
168- val httpToolkitPackage = packageName
169-
170- // For some reason, with Genymotion the whole device crashes if we intercept
171- // blindly, but intercepting every single application explicitly is fine.
172- if (isGenymotion) {
173- packageNames.forEach { name ->
174- if (name != httpToolkitPackage) addAllowedApplication(name)
175- }
176- } else {
177- addDisallowedApplication(httpToolkitPackage)
172+ .apply {
173+ // We exclude ourselves from interception, so we can still make network requests
174+ // separately, primarily because otherwise pinging with isReachable is recursive.
175+ val httpToolkitPackage = packageName
176+
177+ // For some reason, with Genymotion the whole device crashes if we intercept
178+ // blindly, but intercepting every single application explicitly is fine.
179+ if (isGenymotion) {
180+ packageNames.forEach { name ->
181+ if (name != httpToolkitPackage) addAllowedApplication(name)
178182 }
183+ } else {
184+ addDisallowedApplication(httpToolkitPackage)
179185 }
180- .setSession(getString(R .string.app_name))
181- .establish()
182-
183- app.lastProxy = proxyConfig
184- showServiceNotification()
185- localBroadcastManager!! .sendBroadcast(
186- Intent (VPN_STARTED_BROADCAST ).apply {
187- putExtra(PROXY_CONFIG_EXTRA , proxyConfig)
188- }
189- )
186+ }
187+ .setSession(getString(R .string.app_name))
188+ .establish()
190189
191- SocketProtector .getInstance().setProtector(this )
192-
193- vpnRunnable = ProxyVpnRunnable (
194- vpnInterface!! ,
195- proxyConfig.ip,
196- proxyConfig.port,
197- intArrayOf(
198- 80 , // HTTP
199- 443 , // HTTPS
200- 8000 , 8001 , 8080 , 8888 , 9000 // Common local dev ports
201- )
190+ // establish() returns null if we no longer have permissions to establish the VPN somehow
191+ // In that case, we give up. The UI
192+ if (vpnInterface == null ) {
193+ return false
194+ } else {
195+ this .vpnInterface = vpnInterface
196+ }
197+
198+ app.lastProxy = proxyConfig
199+ showServiceNotification()
200+ localBroadcastManager!! .sendBroadcast(
201+ Intent (VPN_STARTED_BROADCAST ).apply {
202+ putExtra(PROXY_CONFIG_EXTRA , proxyConfig)
203+ }
204+ )
205+
206+ SocketProtector .getInstance().setProtector(this )
207+
208+ vpnRunnable = ProxyVpnRunnable (
209+ vpnInterface,
210+ proxyConfig.ip,
211+ proxyConfig.port,
212+ intArrayOf(
213+ 80 , // HTTP
214+ 443 , // HTTPS
215+ 8000 , 8001 , 8080 , 8888 , 9000 // Common local dev ports
202216 )
203- Thread (vpnRunnable, " Vpn thread" ).start()
217+ )
218+ Thread (vpnRunnable, " Vpn thread" ).start()
204219
205- app.vpnShouldBeRunning = true
206- }
220+ app.vpnShouldBeRunning = true
221+ return true
207222 }
208223
209224 private fun stopVpn () {
0 commit comments