Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Catch network connectivity exceptions #221

Merged
merged 5 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ class AndroidNetworkConnectivityCheckerPlugin : Plugin {
amplitude.configuration.offline = true
}
}
networkListener = AndroidNetworkListener((amplitude.configuration as Configuration).context)
networkListener = AndroidNetworkListener(
(amplitude.configuration as Configuration).context,
amplitude.logger
)
networkListener.setNetworkChangeCallback(networkChangeHandler)
networkListener.startListening()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,33 @@ class AndroidNetworkConnectivityChecker(private val context: Context, private va
return true
}

val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE)
if (cm is ConnectivityManager) {
if (isMarshmallowAndAbove) {
val network = cm.activeNetwork ?: return false
val capabilities = cm.getNetworkCapabilities(network) ?: return false

return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
try {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE)
if (cm is ConnectivityManager) {
if (isMarshmallowAndAbove) {
val network = cm.activeNetwork ?: return false
val capabilities = cm.getNetworkCapabilities(network) ?: return false

return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
} else {
@SuppressLint("MissingPermission")
val networkInfo = cm.activeNetworkInfo
return networkInfo != null && networkInfo.isConnectedOrConnecting
}
} else {
@SuppressLint("MissingPermission")
val networkInfo = cm.activeNetworkInfo
return networkInfo != null && networkInfo.isConnectedOrConnecting
logger.debug("Service is not an instance of ConnectivityManager. Offline mode is not supported")
return true
}
} else {
logger.debug("Service is not an instance of ConnectivityManager. Offline mode is not supported")
} catch (throwable: Throwable) {
// We've seen issues where we see exceptions being thrown by connectivity manager
// which crashes an app. Its safe to ignore these exceptions since we try our best
// to mark a device as offline
// Github Issues:
// https://github.com/amplitude/Amplitude-Kotlin/issues/220
// https://github.com/amplitude/Amplitude-Kotlin/issues/197
logger.warn("Error checking network connectivity: ${throwable.message}")
logger.warn(throwable.stackTraceToString())
return true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import java.lang.IllegalArgumentException
import com.amplitude.common.Logger

class AndroidNetworkListener(private val context: Context) {
class AndroidNetworkListener(private val context: Context, private val logger: Logger) {
private var networkCallback: NetworkChangeCallback? = null
private var networkCallbackForLowerApiLevels: BroadcastReceiver? = null
private var networkCallbackForHigherApiLevels: ConnectivityManager.NetworkCallback? = null
Expand All @@ -28,18 +28,29 @@ class AndroidNetworkListener(private val context: Context) {
}

fun startListening() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setupNetworkCallback()
} else {
setupBroadcastReceiver()
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setupNetworkCallback()
} else {
setupBroadcastReceiver()
}
} catch (throwable: Throwable) {
// We've seen issues where we see exceptions being thrown by connectivity manager
// which crashes an app. Its safe to ignore these exceptions since we try our best
// to mark a device as offline
// Github Issues:
// https://github.com/amplitude/Amplitude-Kotlin/issues/220
// https://github.com/amplitude/Amplitude-Kotlin/issues/197
logger.warn("Error starting network listener: ${throwable.message}")
}
}

@SuppressLint("NewApi", "MissingPermission")
// startListening() checks API level
// ACCESS_NETWORK_STATE permission should be added manually by users to enable this feature
private fun setupNetworkCallback() {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkRequest =
NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
Expand All @@ -56,7 +67,10 @@ class AndroidNetworkListener(private val context: Context) {
}
}

connectivityManager.registerNetworkCallback(networkRequest, networkCallbackForHigherApiLevels!!)
connectivityManager.registerNetworkCallback(
networkRequest,
networkCallbackForHigherApiLevels!!
)
}

private fun setupBroadcastReceiver() {
Expand All @@ -68,7 +82,8 @@ class AndroidNetworkListener(private val context: Context) {
intent: Intent,
) {
if (ConnectivityManager.CONNECTIVITY_ACTION == intent.action) {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetworkInfo
val isConnected = activeNetwork?.isConnectedOrConnecting == true

Expand All @@ -88,15 +103,28 @@ class AndroidNetworkListener(private val context: Context) {
fun stopListening() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
networkCallbackForHigherApiLevels?.let { connectivityManager.unregisterNetworkCallback(it) }
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
networkCallbackForHigherApiLevels?.let {
connectivityManager.unregisterNetworkCallback(
it
)
}
} else {
networkCallbackForLowerApiLevels?.let { context.unregisterReceiver(it) }
}
} catch (e: IllegalArgumentException) {
// callback was already unregistered.
} catch (e: IllegalStateException) {
// shutdown process is in progress and certain operations are not allowed.
} catch (throwable: Throwable) {
// We've seen issues where we see exceptions being thrown by connectivity manager
// which crashes an app. Its safe to ignore these exceptions since we try our best
// to mark a device as offline
// Github Issues:
// https://github.com/amplitude/Amplitude-Kotlin/issues/220
// https://github.com/amplitude/Amplitude-Kotlin/issues/197
logger.warn("Error stopping network listener: ${throwable.message}")
}
}
}