Skip to content

Commit

Permalink
Merge branch 'molly-6.29'
Browse files Browse the repository at this point in the history
  • Loading branch information
valldrac committed Aug 22, 2023
2 parents 9c9b660 + db7d8e1 commit 71b4d74
Show file tree
Hide file tree
Showing 6 changed files with 0 additions and 295 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@

import com.google.android.material.dialog.MaterialAlertDialogBuilder;

import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController;
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner;
import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSheetFragment;
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceExitActivity;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor;
import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics;
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabRepository;
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel;
import org.thoughtcrime.securesms.util.AppStartup;
Expand Down Expand Up @@ -136,10 +134,6 @@ protected void onResume() {
}

updateTabVisibility();

if (SlowNotificationHeuristics.shouldPromptUserForLogs()) {
DebugLogsPromptDialogFragment.show(this, getSupportFragmentManager());
}
}

@Override
Expand Down

This file was deleted.

This file was deleted.

18 changes: 0 additions & 18 deletions app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ public class UiHints extends SignalStoreValues {
private static final String HAS_SEEN_TEXT_FORMATTING_ALERT = "uihints.text_formatting.has_seen_alert";
private static final String HAS_NOT_SEEN_EDIT_MESSAGE_BETA_ALERT = "uihints.edit_message.has_not_seen_beta_alert";
private static final String HAS_SEEN_SAFETY_NUMBER_NUX = "uihints.has_seen_safety_number_nux";
private static final String DECLINED_NOTIFICATION_LOGS_PROMPT = "uihints.declined_notification_logs";
private static final String LAST_NOTIFICATION_LOGS_PROMPT_TIME = "uihints.last_notification_logs_prompt";

UiHints(@NonNull KeyValueStore store) {
super(store);
Expand Down Expand Up @@ -120,20 +118,4 @@ public boolean hasSeenSafetyNumberUpdateNux() {
public void markHasSeenSafetyNumberUpdateNux() {
putBoolean(HAS_SEEN_SAFETY_NUMBER_NUX, true);
}

public long getLastNotificationLogsPrompt() {
return getLong(LAST_NOTIFICATION_LOGS_PROMPT_TIME, 0);
}

public void setLastNotificationLogsPrompt(long timeMs) {
putLong(LAST_NOTIFICATION_LOGS_PROMPT_TIME, timeMs);
}

public void markDeclinedShareNotificationLogs() {
putBoolean(DECLINED_NOTIFICATION_LOGS_PROMPT, true);
}

public boolean hasDeclinedToShareNotificationLogs() {
return getBoolean(DECLINED_NOTIFICATION_LOGS_PROMPT, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,135 +18,6 @@ import java.util.concurrent.TimeUnit
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours

/**
* Heuristic for estimating if a user has been experiencing issues with delayed notifications.
*
* This uses local metrics based off of message latency, failed service starts, and queue drain
* timeouts.
*
* This will enable us to attempt to improve notifications for users who are experiencing these issues.
*/
object SlowNotificationHeuristics {

private val TAG = Log.tag(SlowNotificationHeuristics::class.java)

fun getConfiguration(): Configuration {
val json = FeatureFlags.delayedNotificationsPromptConfig()
return if (TextUtils.isEmpty(json)) {
getDefaultConfiguration()
} else {
try {
JsonUtils.fromJson(json, Configuration::class.java)
} catch (exception: Exception) {
getDefaultConfiguration()
}
}
}

private fun getDefaultConfiguration(): Configuration {
return Configuration(
minimumEventAgeMs = 3.days.inWholeMilliseconds,
minimumServiceEventCount = 10,
serviceStartFailurePercentage = 0.5f,
messageLatencyPercentage = 75,
messageLatencyThreshold = 6.hours.inWholeMilliseconds,
minimumMessageLatencyEvents = 50,
weeklyFailedQueueDrains = 5
)
}

@JvmStatic
fun shouldPromptUserForLogs(): Boolean {
if (!LocaleFeatureFlags.isDelayedNotificationPromptEnabled() || SignalStore.uiHints().hasDeclinedToShareNotificationLogs()) {
return false
}
if (System.currentTimeMillis() - SignalStore.uiHints().lastNotificationLogsPrompt < TimeUnit.DAYS.toMillis(7)) {
return false
}
val configuration = getConfiguration()

return isHavingDelayedNotifications(configuration)
}

fun isHavingDelayedNotifications(configuration: Configuration): Boolean {
val db = LocalMetricsDatabase.getInstance(ApplicationDependencies.getApplication())

val metrics = db.getMetrics()

val failedServiceStarts = hasRepeatedFailedServiceStarts(metrics, configuration.minimumEventAgeMs, configuration.minimumServiceEventCount, configuration.serviceStartFailurePercentage)
val failedQueueDrains = isFailingToDrainQueue(metrics, configuration.minimumEventAgeMs, configuration.weeklyFailedQueueDrains)
val longMessageLatency = hasLongMessageLatency(metrics, configuration.minimumEventAgeMs, configuration.messageLatencyPercentage, configuration.minimumMessageLatencyEvents, configuration.messageLatencyThreshold)

if (failedServiceStarts || failedQueueDrains || longMessageLatency) {
Log.w(TAG, "User seems to be having delayed notifications: failed-service-starts=$failedServiceStarts failedQueueDrains=$failedQueueDrains longMessageLatency=$longMessageLatency")
return true
}
return false
}

private fun hasRepeatedFailedServiceStarts(metrics: List<LocalMetricsDatabase.EventMetrics>, minimumEventAgeMs: Long, minimumEventCount: Int, failurePercentage: Float): Boolean {
if (!haveEnoughData(SignalLocalMetrics.FcmServiceStartSuccess.NAME, minimumEventAgeMs) && !haveEnoughData(SignalLocalMetrics.FcmServiceStartFailure.NAME, minimumEventAgeMs)) {
Log.d(TAG, "insufficient data for service starts")
return false
}

val successes = metrics.filter { it.name == SignalLocalMetrics.FcmServiceStartSuccess.NAME }
val failures = metrics.filter { it.name == SignalLocalMetrics.FcmServiceStartFailure.NAME }

if ((successes.size + failures.size) < minimumEventCount) {
Log.d(TAG, "insufficient service start events")
return false
}

if (failures.size / (failures.size + successes.size) >= failurePercentage) {
Log.w(TAG, "User often unable start FCM service. ${failures.size} failed : ${successes.size} successful")
return true
}
return false
}

private fun isFailingToDrainQueue(metrics: List<LocalMetricsDatabase.EventMetrics>, minimumEventAgeMs: Long, failureThreshold: Int): Boolean {
if (!haveEnoughData(SignalLocalMetrics.PushWebsocketFetch.SUCCESS_EVENT, minimumEventAgeMs) && !haveEnoughData(SignalLocalMetrics.PushWebsocketFetch.TIMEOUT_EVENT, minimumEventAgeMs)) {
Log.d(TAG, "insufficient data for failed queue drains")
return false
}
val failures = metrics.filter { it.name == SignalLocalMetrics.PushWebsocketFetch.TIMEOUT_EVENT }
if (failures.size < failureThreshold) {
return false
}
Log.w(TAG, "User has repeatedly failed to drain queue ${failures.size} events")
return true
}

private fun hasLongMessageLatency(metrics: List<LocalMetricsDatabase.EventMetrics>, minimumEventAgeMs: Long, percentage: Int, messageThreshold: Int, durationThreshold: Long): Boolean {
if (!haveEnoughData(SignalLocalMetrics.MessageLatency.NAME_HIGH, minimumEventAgeMs)) {
Log.d(TAG, "insufficient data for message latency")
return false
}
val eventCount = metrics.count { it.name == SignalLocalMetrics.MessageLatency.NAME_HIGH }
if (eventCount < messageThreshold) {
Log.d(TAG, "not enough messages for message latency")
return false
}
val db = LocalMetricsDatabase.getInstance(ApplicationDependencies.getApplication())
val averageLatency = db.eventPercent(SignalLocalMetrics.MessageLatency.NAME_HIGH, percentage.coerceAtMost(100).coerceAtLeast(0))

val longMessageLatency = averageLatency > durationThreshold
if (longMessageLatency) {
Log.w(TAG, "User has high average message latency of $averageLatency ms over $eventCount events")
}
return longMessageLatency
}

private fun haveEnoughData(eventName: String, minimumEventAgeMs: Long): Boolean {
val db = LocalMetricsDatabase.getInstance(ApplicationDependencies.getApplication())

val oldestEvent = db.getOldestMetricTime(eventName)

return !(oldestEvent == 0L || oldestEvent > System.currentTimeMillis() - minimumEventAgeMs)
}
}

data class Configuration(
val minimumEventAgeMs: Long,
val minimumServiceEventCount: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.thoughtcrime.securesms.jobs.RemoteConfigRefreshJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.messageprocessingalarm.MessageProcessReceiver;
import org.thoughtcrime.securesms.notifications.Configuration;
import org.whispersystems.signalservice.api.RemoteConfigResult;

import java.io.IOException;
Expand Down Expand Up @@ -595,13 +594,6 @@ public static boolean cdsCompatMode() {
}
}

public static String promptForDelayedNotificationLogs() {
return getString(PROMPT_FOR_NOTIFICATION_LOGS, "*");
}

public static String delayedNotificationsPromptConfig() {
return getString(PROMPT_FOR_NOTIFICATION_CONFIG, "");
}
/** Only for rendering debug info. */
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
return new TreeMap<>(REMOTE_VALUES);
Expand Down

0 comments on commit 71b4d74

Please sign in to comment.