From 007975e7dac6cb3f52d873bd5c00871f7e2cc224 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 21 Mar 2022 17:21:42 -0400 Subject: [PATCH] Track inconsistencies between new and old network availability for internal users. --- .../securesms/ApplicationContext.java | 2 + .../jobmanager/impl/NetworkConstraint.java | 2 + .../jobmanager/impl/NewNetworkConnectivity.kt | 133 ++++++++++++++++++ .../notifications/NotificationIds.java | 1 + 4 files changed, 138 insertions(+) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NewNetworkConnectivity.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index bc3bbb211e..29c1e16eba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -47,6 +47,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider; import org.thoughtcrime.securesms.emoji.EmojiSource; import org.thoughtcrime.securesms.emoji.JumboEmoji; import org.thoughtcrime.securesms.gcm.FcmJobService; +import org.thoughtcrime.securesms.jobmanager.impl.NewNetworkConnectivity; import org.thoughtcrime.securesms.jobs.CheckServiceReachabilityJob; import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob; import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob; @@ -203,6 +204,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr .addPostRender(() -> ApplicationDependencies.getJobManager().add(new FontDownloaderJob())) .addPostRender(CheckServiceReachabilityJob::enqueueIfNecessary) .addPostRender(GroupV2UpdateSelfProfileKeyJob::enqueueForGroupsIfNecessary) + .addPostRender(NewNetworkConnectivity::start) .execute(); Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java index 5851a1af04..de10a370c9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NetworkConstraint.java @@ -47,6 +47,8 @@ public class NetworkConstraint implements Constraint { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); + NewNetworkConnectivity.consistencyCheck(); + return activeNetworkInfo != null && activeNetworkInfo.isConnected(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NewNetworkConnectivity.kt b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NewNetworkConnectivity.kt new file mode 100644 index 0000000000..6afb4e973e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobmanager/impl/NewNetworkConnectivity.kt @@ -0,0 +1,133 @@ +package org.thoughtcrime.securesms.jobmanager.impl + +import android.app.Application +import android.app.PendingIntent +import android.content.Intent +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkInfo +import android.net.NetworkRequest +import android.os.Build +import android.os.Handler +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat +import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogActivity +import org.thoughtcrime.securesms.notifications.NotificationChannels +import org.thoughtcrime.securesms.notifications.NotificationIds +import org.thoughtcrime.securesms.util.FeatureFlags + +@Suppress("DEPRECATION") +object NewNetworkConnectivity { + + private val TAG: String = Log.tag(NewNetworkConnectivity::class.java) + private val context: Application = ApplicationDependencies.getApplication() + + private lateinit var connectivityManager: ConnectivityManager + private var retryCount = 0 + + private var callback: NetworkCallback? = null + private var handler: Handler? = null + private var hasInternet: Boolean? = null + private var previousHasInternet: Boolean? = null + + @JvmStatic + fun start() { + if (FeatureFlags.internalUser() && Build.VERSION.SDK_INT >= 26) { + connectivityManager = ContextCompat.getSystemService(context, ConnectivityManager::class.java)!! + + val thread = SignalExecutors.getAndStartHandlerThread("NewNetwork") + handler = Handler(thread.looper) + handler!!.post(this::requestNetwork) + } + } + + @RequiresApi(26) + private fun requestNetwork() { + if (retryCount > 5) { + Log.internal().w(TAG, "Unable to request network after $retryCount attempts") + } else { + callback?.let { connectivityManager.unregisterNetworkCallback(it) } + + Log.internal().i(TAG, "Requesting network attempt: $retryCount") + val callback = NetworkCallback() + connectivityManager.requestNetwork(NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build(), callback, handler!!) + this.callback = callback + } + } + + @JvmStatic + fun consistencyCheck() { + if (FeatureFlags.internalUser() && Build.VERSION.SDK_INT >= 26) { + handler?.post { + val oldActiveNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo + val oldIsMet: Boolean = isMet(oldActiveNetwork) + + if (hasInternet == null) { + Log.internal().w(TAG, "New has not been initialized yet, defaulting to false") + hasInternet = false + } + + when { + oldIsMet == hasInternet && previousHasInternet != hasInternet -> { + Log.internal().i(TAG, "Old and new are consistent: $hasInternet") + previousHasInternet = hasInternet + } + oldIsMet != hasInternet && previousHasInternet != hasInternet -> { + Log.internal().w(TAG, "Network inconsistent old: $oldIsMet new: $hasInternet", Throwable()) + Log.internal().w(TAG, "Old active network: $oldActiveNetwork") + + previousHasInternet = hasInternet + postInternalErrorNotification() + } + } + } ?: Log.internal().w(TAG, "New handler has not been initialized yet") + } + } + + private fun isMet(oldActiveNetwork: NetworkInfo?): Boolean { + return oldActiveNetwork != null && oldActiveNetwork.isConnected + } + + private fun postInternalErrorNotification() { + if (!FeatureFlags.internalUser()) return + + NotificationManagerCompat.from(context).notify( + NotificationIds.INTERNAL_NET_ERROR, + NotificationCompat.Builder(context, NotificationChannels.FAILURES) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle("Network check inconsistency") + .setContentText(context.getString(R.string.MessageDecryptionUtil_tap_to_send_a_debug_log)) + .setContentIntent(PendingIntent.getActivity(context, 0, Intent(context, SubmitDebugLogActivity::class.java), 0)) + .build() + ) + } + + @RequiresApi(26) + private class NetworkCallback : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + Log.internal().i(TAG, "onAvailable $network ${connectivityManager.getNetworkCapabilities(network)}") + retryCount = 0 + hasInternet = true + consistencyCheck() + } + + override fun onLost(network: Network) { + Log.internal().i(TAG, "onLost $network") + retryCount = 0 + hasInternet = false + consistencyCheck() + } + + override fun onUnavailable() { + retryCount++ + requestNetwork() + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java index ebec892283..9a0cb7d6ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/NotificationIds.java @@ -14,6 +14,7 @@ public final class NotificationIds { public static final int USER_NOTIFICATION_MIGRATION = 525600; public static final int DEVICE_TRANSFER = 625420; public static final int DONOR_BADGE_FAILURE = 630001; + public static final int INTERNAL_NET_ERROR = 404404; private NotificationIds() { }