Skip to content

CCCT-2196 - Avoid Stale location#3571

Open
Jignesh-dimagi wants to merge 1 commit intomasterfrom
ccct-2196-stale-location
Open

CCCT-2196 - Avoid Stale location#3571
Jignesh-dimagi wants to merge 1 commit intomasterfrom
ccct-2196-stale-location

Conversation

@Jignesh-dimagi
Copy link
Contributor

@Jignesh-dimagi Jignesh-dimagi commented Feb 26, 2026

CCCT-2196

Technical Summary

https://dimagi.atlassian.net/browse/CCCT-2196

The application is avoiding the stale location as per the proposal.

Also, a new preference file LocationPreferences has been created instead of using the existing one, as it was crowded and kept the location-specific methods in LocationPreferences.

Safety Assurance

Safety story

  • Tested the working of the location provider by trying to sign up for PersonalID. The location was received, and the logic, for the first time and later for different locations, worked. Not able to test the same repeated location scenario, as never got it.

Labels and Review

  • Do we need to enhance the manual QA test coverage ? If yes, the "QA Note" label is set correctly
  • Does the PR introduce any major changes worth communicating ? If yes, the "Release Note" label is set and a "Release Note" is specified in PR description.
  • Risk label is set correctly
  • The set of people pinged as reviewers is appropriate for the level of risk of the change

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

This change refactors location handling across multiple components by centralizing location processing logic. A new onLocationReceived function is introduced in CommCareLocationController to orchestrate location validation, first-location initialization, change detection, and drift logging. Multiple activity and controller classes that previously implemented inline location handling are updated to delegate to this centralized function. A new LocationPreferences utility is added to persist the last accepted location and its timestamp. The removed calls to logStaleLocationSaved across multiple files are replaced with more sophisticated drift detection and logging within the centralized handler.

Sequence Diagram(s)

sequenceDiagram
    participant Controller as LocationController<br/>(FusedOrProvider)
    participant Handler as CommCareLocationController
    participant Prefs as LocationPreferences
    participant Listener as CommCareLocationListener

    Controller->>Handler: onLocationReceived(newLocation, listener, lambda)
    alt isFirstLocation
        Handler->>Prefs: updateLastLocation(newLocation)
        Handler->>Listener: onLocationResult(newLocation)
        Handler->>Prefs: setLastAcceptedLocationTimestamp(now)
    else isDifferentLocation
        Handler->>Prefs: updateLastLocation(newLocation)
        Handler->>Listener: onLocationResult(newLocation)
        Handler->>Prefs: setLastAcceptedLocationTimestamp(now)
        Handler->>Handler: logStaleLocationIfGpsTimeDrifted()
    else SameLocation
        alt thresholdElapsed
            Handler->>Handler: discardLocation(error)
        else thresholdNotElapsed
            Handler->>Listener: onLocationResult(newLocation)
            Handler->>Prefs: setLastAcceptedLocationTimestamp(now)
        end
    end
    Handler->>Controller: lambda(location) → mCurrentLocation = it
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive PR description includes technical summary with ticket link and proposal reference, but lacks product description, automated test coverage details, and a complete QA plan. Add product description detailing user-facing effects, specify automated test coverage for stale location scenarios, and provide a comprehensive QA plan with specific test cases for repeated locations and stale location handling.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly identifies the ticket (CCCT-2196) and the main technical change (avoiding stale location), directly matching the changeset's core objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ccct-2196-stale-location

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/org/commcare/location/CommCareLocationController.kt (1)

55-60: Consider simplifying discardLocation logging.

Using Logger.exception() with a fabricated Exception for informational logging is unconventional. If this is purely for telemetry/logging purposes rather than indicating an actual error state, consider using a standard log method instead, or add a comment clarifying why exception-style logging is used here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/org/commcare/location/CommCareLocationController.kt` around lines 55
- 60, The discardLocation function currently calls Logger.exception with a
fabricated Exception which is misleading; replace this with a non-exception log
call (e.g., Logger.info or Logger.warn) and log the same message and details
(use GeoUtils.locationToString(location) and location.accuracy) or, if
exception-style telemetry is intentional, add a clarifying comment above
discardLocation explaining why an Exception is being created for telemetry;
update references in discardLocation and remove the fabricated Exception usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/org/commcare/location/CommCareLocationController.kt`:
- Around line 84-115: onLocationReceived performs a non-atomic read-modify-write
on LocationPreferences (reads lastLocationString/lastAcceptedTimestamp then
calls updateLastLocation/acceptLocation), which can race when multiple
controllers call it concurrently; protect this critical section by serializing
access (e.g., add a private lock object or make the method synchronized) around
the logic that reads LocationPreferences and calls
updateLastLocation/acceptLocation/discardLocation so the checks in
isFirstLocation/isDifferentLocation and subsequent updates execute atomically;
ensure the same lock is used by any other code paths that modify
LocationPreferences so state stays consistent.

---

Nitpick comments:
In `@app/src/org/commcare/location/CommCareLocationController.kt`:
- Around line 55-60: The discardLocation function currently calls
Logger.exception with a fabricated Exception which is misleading; replace this
with a non-exception log call (e.g., Logger.info or Logger.warn) and log the
same message and details (use GeoUtils.locationToString(location) and
location.accuracy) or, if exception-style telemetry is intentional, add a
clarifying comment above discardLocation explaining why an Exception is being
created for telemetry; update references in discardLocation and remove the
fabricated Exception usage.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 519effc and d62287f.

📒 Files selected for processing (6)
  • app/src/org/commcare/activities/GeoPointActivity.java
  • app/src/org/commcare/android/javarosa/PollSensorAction.java
  • app/src/org/commcare/location/CommCareFusedLocationController.kt
  • app/src/org/commcare/location/CommCareLocationController.kt
  • app/src/org/commcare/location/CommCareProviderLocationController.kt
  • app/src/org/commcare/preferences/LocationPreferences.kt
💤 Files with no reviewable changes (2)
  • app/src/org/commcare/activities/GeoPointActivity.java
  • app/src/org/commcare/android/javarosa/PollSensorAction.java

Comment on lines +35 to +40
private fun isFirstLocation(lastLocationString: String?): Boolean = lastLocationString.isNullOrEmpty()

private fun isDifferentLocation(
newLocation: Location,
lastLocationString: String,
): Boolean = GeoUtils.locationToString(newLocation) != lastLocationString
Copy link
Contributor

@conroy-ricketts conroy-ricketts Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two functions are only being used in one place currently. Unless we plan on reusing them in the future, can we convert these to local boolean variables instead?

Comment on lines +55 to 69
private fun discardLocation(location: Location) {
Logger.exception(
"Discarding stale repeated location",
Exception("Discarding stale repeated location ${GeoUtils.locationToString(location)} with accuracy ${location.accuracy}"),
)
}

fun getStaleLocationException(location: Location): Throwable =
private fun getStaleLocationException(
location: Location,
currentDeviceTime: Long,
): Throwable =
Exception(
"Stale location with accuracy ${location.accuracy}" +
" with time ${location.time}" + " and current device time ${System.currentTimeMillis()}",
" with time ${location.time}" + " and current device time $currentDeviceTime",
)
Copy link
Contributor

@conroy-ricketts conroy-ricketts Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar concern with these two functions as well. Guess this brings up a more general question - how modular do we want this class to be really? Understandably, all these functions make onLocationReceived() a bit easier to read, but there is a trade-off with having a lot of functions doing very simple tasks that could be reduced to single lines and/or local variables

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@conroy-ricketts Yeah, here more important part was code readability and also performing each task separately as defined here in proposal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool that sounds good to me if we want to put more emphasis on code readability 👍

): Boolean = GeoUtils.locationToString(newLocation) != lastLocationString

private fun updateLastLocation(location: Location) {
LocationPreferences.setLastAcceptedLocation(GeoUtils.locationToString(location))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to have the location timestamp as part of the location string we are saving here as well.

fun onLocationReceived(
newLocation: Location,
listener: CommCareLocationListener?,
setCurrentLocation: (Location) -> Unit,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think we can put the mCurrentLocation from the implementations in this class itself and get rid of the callback here.

Comment on lines +77 to +79
Logger.exception(
"Received a stale location",
getStaleLocationException(location, currentDeviceTime),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should log the actual drift in mins here as well to easily know more about the drift range if this happens.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-integration-tests Skip android tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants