Skip to content

Commit

Permalink
Do not fail service start if ACCESS_NETWORK_STATE permission missing
Browse files Browse the repository at this point in the history
  • Loading branch information
05nelsonm committed Aug 5, 2024
1 parent e61e859 commit 7977a0a
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

<uses-sdk tools:overrideLibrary="io.matthewnelson.kmp.tor.resource.tor" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<application>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
**/
package io.matthewnelson.kmp.tor.runtime.service

import android.content.Context
import android.os.Build
import androidx.test.core.app.ApplicationProvider
import io.matthewnelson.kmp.process.Blocking
import io.matthewnelson.kmp.tor.resource.tor.TorResources
import io.matthewnelson.kmp.tor.runtime.Action
Expand All @@ -26,13 +28,18 @@ import io.matthewnelson.kmp.tor.runtime.RuntimeEvent
import io.matthewnelson.kmp.tor.runtime.TorRuntime
import io.matthewnelson.kmp.tor.runtime.core.OnEvent
import io.matthewnelson.kmp.tor.runtime.core.UncaughtException
import io.matthewnelson.kmp.tor.runtime.service.internal.isPermissionGranted
import kotlin.coroutines.cancellation.CancellationException
import kotlin.test.*
import kotlin.time.Duration.Companion.milliseconds

class AndroidServiceFactoryTest {

private val config = TorServiceConfig.Builder {}
private val ctx = ApplicationProvider.getApplicationContext<Context>()

private val config = TorServiceConfig.Builder {
useNetworkStateObserver = true
}

private fun newEnvironment(dirName: String): TorRuntime.Environment {
return config.newEnvironment(
Expand Down Expand Up @@ -185,6 +192,39 @@ class AndroidServiceFactoryTest {
}
}

@Test
fun givenNetworkObserver_whenNoPermissions_thenStillStarts() {
if (Build.VERSION.SDK_INT < 21) {
println("Skipping...")
return
}

assertTrue(config.useNetworkStateObserver)
assertFalse(ctx.isPermissionGranted("android.permission.ACCESS_NETWORK_STATE"))
val env = newEnvironment("sf_permissions")

val exceptions = mutableListOf<IllegalStateException>()
val factory = TorRuntime.Builder(env) {
observerStatic(RuntimeEvent.ERROR) { t ->
if (t !is IllegalStateException) throw t
synchronized(exceptions) { exceptions.add(t) }
}
}

try {
factory.startDaemonSync().stopDaemonSync()

synchronized(exceptions) {
assertEquals(1, exceptions.size)
// Missing permissions exception
assertEquals(true, exceptions.first().message?.contains("ACCESS_NETWORK_STATE"))
}
} catch (t: Throwable) {
factory.enqueue(Action.StopDaemon, {}, {})
throw t
}
}

private fun List<Lifecycle.Event>.assertDoesNotContain(className: String, name: Lifecycle.Event.Name) {
var error: AssertionError? = null
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,24 +226,15 @@ internal class TorService internal constructor(): Service() {
})

if (conn.config.useNetworkStateObserver) {
val observer = try {
AndroidNetworkObserver.of(service, service.serviceJob)
try {
val observer = AndroidNetworkObserver.of(service, service.serviceJob)
service.networkObserver = observer
} catch (e: IllegalStateException) {
// Configured to be used, but permission was missing.
executables.add(Executable {
conn.binder.e(e)

val appContext = appContext.get()
appContext.unbindService(conn)
conn.binder.lce(Lifecycle.Event.OnUnbind(service))
appContext.stopService(Intent(appContext, TorService::class.java))
})
null
}

if (observer == null) return@withLock executables

service.networkObserver = observer
}

// TorServiceConfig is a singleton and is the same for all
Expand Down

0 comments on commit 7977a0a

Please sign in to comment.