diff --git a/android/src/main/java/com/amplitude/android/plugins/AndroidNetworkConnectivityCheckerPlugin.kt b/android/src/main/java/com/amplitude/android/plugins/AndroidNetworkConnectivityCheckerPlugin.kt index f187669f..761c09d0 100644 --- a/android/src/main/java/com/amplitude/android/plugins/AndroidNetworkConnectivityCheckerPlugin.kt +++ b/android/src/main/java/com/amplitude/android/plugins/AndroidNetworkConnectivityCheckerPlugin.kt @@ -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() } diff --git a/android/src/main/java/com/amplitude/android/utilities/AndroidNetworkConnectivityChecker.kt b/android/src/main/java/com/amplitude/android/utilities/AndroidNetworkConnectivityChecker.kt index 275f2e42..41bafa76 100644 --- a/android/src/main/java/com/amplitude/android/utilities/AndroidNetworkConnectivityChecker.kt +++ b/android/src/main/java/com/amplitude/android/utilities/AndroidNetworkConnectivityChecker.kt @@ -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 } } diff --git a/android/src/main/java/com/amplitude/android/utilities/AndroidNetworkListener.kt b/android/src/main/java/com/amplitude/android/utilities/AndroidNetworkListener.kt index 336602cf..5fcb9aed 100644 --- a/android/src/main/java/com/amplitude/android/utilities/AndroidNetworkListener.kt +++ b/android/src/main/java/com/amplitude/android/utilities/AndroidNetworkListener.kt @@ -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 @@ -28,10 +28,20 @@ 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}") } } @@ -39,7 +49,8 @@ class AndroidNetworkListener(private val context: Context) { // 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) @@ -56,7 +67,10 @@ class AndroidNetworkListener(private val context: Context) { } } - connectivityManager.registerNetworkCallback(networkRequest, networkCallbackForHigherApiLevels!!) + connectivityManager.registerNetworkCallback( + networkRequest, + networkCallbackForHigherApiLevels!! + ) } private fun setupBroadcastReceiver() { @@ -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 @@ -88,8 +103,13 @@ 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) } } @@ -97,6 +117,14 @@ class AndroidNetworkListener(private val context: Context) { // 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}") } } }