Skip to content

fix: don't report ANR while the app is in the background #796

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

Merged
merged 1 commit into from
Jun 14, 2022
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

- Include build ID in an event release info ([#795](https://github.com/getsentry/sentry-unity/pull/795))

### Fixes

- Don't report Aplication-Not-Responding while the app is in the background ([#796](https://github.com/getsentry/sentry-unity/pull/796))

## 0.18.0

### Features
Expand Down
20 changes: 16 additions & 4 deletions src/Sentry.Unity/Integrations/AnrIntegration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ internal abstract class AnrWatchDog
protected readonly IDiagnosticLogger? Logger;
protected readonly SentryMonoBehaviour MonoBehaviour;
internal event EventHandler<ApplicationNotResponding> OnApplicationNotResponding = delegate { };
protected bool Paused { get; private set; } = false;

internal AnrWatchDog(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviour, int detectionTimeoutMilliseconds = 5000)
{
Expand All @@ -56,6 +57,9 @@ internal AnrWatchDog(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviou
DetectionTimeoutMs = detectionTimeoutMilliseconds;
SleepIntervalMs = Math.Max(1, DetectionTimeoutMs / 5);

MonoBehaviour.ApplicationPausing += () => Paused = true;
MonoBehaviour.ApplicationResuming += () => Paused = false;

// Stop when the app is being shut down.
MonoBehaviour.Application.Quitting += () => Stop();
}
Expand All @@ -64,9 +68,13 @@ internal AnrWatchDog(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviou

protected void Report()
{
var message = $"Application not responding for at least {DetectionTimeoutMs} ms.";
Logger?.LogInfo("Detected an ANR event: {0}", message);
OnApplicationNotResponding?.Invoke(this, new ApplicationNotResponding(message));
// Don't report events while in the background.
if (!Paused)
{
var message = $"Application not responding for at least {DetectionTimeoutMs} ms.";
Logger?.LogInfo("Detected an ANR event: {0}", message);
OnApplicationNotResponding?.Invoke(this, new ApplicationNotResponding(message));
}
}
}

Expand Down Expand Up @@ -129,7 +137,11 @@ private void Run()
_ticksSinceUiUpdate++;
Thread.Sleep(SleepIntervalMs);

if (_ticksSinceUiUpdate >= reportThreshold && !_reported)
if (Paused)
{
_ticksSinceUiUpdate = 0;
}
else if (_ticksSinceUiUpdate >= reportThreshold && !_reported)
{
Report();
_reported = true;
Expand Down
44 changes: 18 additions & 26 deletions src/Sentry.Unity/SentryMonoBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ internal IApplication Application
set => _application = value;
}

internal void UpdatePauseStatus(bool paused)
{
if (paused && _isRunning)
{
_isRunning = false;
ApplicationPausing?.Invoke();
}
else if (!paused && !_isRunning)
{
_isRunning = true;
ApplicationResuming?.Invoke();
}
}

/// <summary>
/// To receive Leaving/Resuming events on Android.
/// <remarks>
Expand All @@ -75,20 +89,9 @@ internal IApplication Application
/// </summary>
internal void OnApplicationPause(bool pauseStatus)
{
if (Application.Platform != RuntimePlatform.Android)
{
return;
}

if (pauseStatus && _isRunning)
{
_isRunning = false;
ApplicationPausing?.Invoke();
}
else if (!pauseStatus && !_isRunning)
if (Application.Platform == RuntimePlatform.Android)
{
_isRunning = true;
ApplicationResuming?.Invoke();
UpdatePauseStatus(pauseStatus);
}
}

Expand All @@ -99,20 +102,9 @@ internal void OnApplicationPause(bool pauseStatus)
internal void OnApplicationFocus(bool hasFocus)
{
// To avoid event duplication on Android since the pause event will be handled via OnApplicationPause
if (Application.Platform == RuntimePlatform.Android)
{
return;
}

if (hasFocus && !_isRunning)
{
_isRunning = true;
ApplicationResuming?.Invoke();
}
else if (!hasFocus && _isRunning)
if (Application.Platform != RuntimePlatform.Android)
{
_isRunning = false;
ApplicationPausing?.Invoke();
UpdatePauseStatus(!hasFocus);
}
}

Expand Down
29 changes: 29 additions & 0 deletions test/Sentry.Unity.Tests/AnrDetectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,34 @@ public void DoesntReportShortlyStuckUI(bool multiThreaded)

Assert.IsNull(arn);
}

[UnityTest]
public IEnumerator DoesntReportWhilePaused([ValueSource(nameof(MultiThreadingTestValues))] bool multiThreaded)
{
ApplicationNotResponding? arn = null;
_sut = CreateWatchDog(multiThreaded);
_sut.OnApplicationNotResponding += (_, e) => arn = e;

// mark the app as paused
_monoBehaviour.UpdatePauseStatus(true);

// Thread.Sleep blocks the UI thread
Thread.Sleep(Timeout * 2);

// We need to let the single-threaded watchdog populate `arn` after UI became responsive again.
if (!multiThreaded)
{
var watch = Stopwatch.StartNew();
while (watch.ElapsedMilliseconds < Timeout && arn is null)
{
yield return null;
}
}

// mark as resumed
_monoBehaviour.UpdatePauseStatus(false);

Assert.IsNull(arn);
}
}
}