Urvi today
Urvi is Platform Commons' offline-first Android app for natural-farming field staff. It serves one programme: Andhra Pradesh's natural-farming programme run by RySS (APCNF, tenant ap.gov). Staff register farmers, record land, livestock, crops, models, harvests and plans, and supervisors endorse the data, all of it working offline for days.
- Features
- 40 live features in 8 groups
- Screens
- 75 (21 activities, 54 fragments), 28 ViewModels
- Local tables
- 71 live tables in 5 stores (55 in agri_db, schema v62)
- Endpoints
- 111 live endpoints
- Services
- 12 on the gateway host, plus S3 and Firebase
- Set-aside code
- 234 unused items, about 6,500 lines
- Size
- 91,553 Kotlin lines, 9 test methods
What Urvi is
Field staff register farmers (each farmer is an "IE"), record their land, livestock, crops, monthly crop progress, ATM and kitchen-garden models, harvests, annual plans and surveys. Supervisors review and endorse that data at VO, SHG and farmer level. The app works for days without a network and syncs when it can.
| Fact | Value |
|---|---|
| Platform | Android only (minSdk 26, targetSdk 36), no iOS code |
| Version | 2.2.13 (versionCode 126) |
| Age | Code from about 2018 (Mindtree IGX era); git history from 2022, 1,013 commits |
| Size | 91,553 Kotlin LOC in :app and :data, 170 layouts, 21 activities, 54 fragments |
| Languages | English, Telugu (48 strings missing), Tamil (416 strings missing, 40%) |
| Backend | About 12 services and about 40 report datasets, called directly |
| Team | About 5-6 core Android developers, all Kotlin |
| Local storage | 4 Room databases (agri_db at schema v62 with 57 migrations) plus the Bridge sync queue |
| Networking | 5 HTTP stacks, 3 JSON libraries |
| Background work | 23 WorkManager workers (15 live), 41 sync response handlers (33 live) |
| Tests | 9 @Test methods in 6 files; effectively no coverage of business rules, sync or migrations |
| Dead code | About 6,500-7,000 lines; see Set-aside code |
| Coupling | 149 files import the proprietary Bridge libraries |
Who uses it
| Role | How the app recognises it | What they mainly do |
|---|---|---|
| Cadre (CRP, lead, etc.) | Default ap.gov user | Picks one Village → Habitation → VO, manages its farmers, logs daily activities |
| Unit In-charge / FMT L1 | Role code contains unit-incharge or fmt_l1 | Gets all assigned VOs, endorses VO/SHG/farmer data, builds compact blocks, runs VO/SHG surveys |
| HAN (add-on) | Role code contains role.ap.gov.han | Read-only view of farmer data |
| FCCC (add-on) | Role code contains role.ap.gov.fccc | Extra geographic VO filter on endorsements |
No usage numbers (active users, farmers, devices) exist in the code. The farmer download pages at 250 rows by default, which suggests VOs of a few hundred farmers; ask the programme for real counts before sizing the backend.
A field worker's day
A cadre opens Urvi in a village with weak signal. The app skips login (the account is already on the phone) and opens the farmer list for their VO. They search for a farmer by mobile number, open the farmer hub and see that the profile is complete and land is authenticated, so most tiles are open. They enter this month's Crop Actual: the app has already copied last month's crop blocks, and they edit one crop, give an edit reason and add a photo. For a new ATM plot they walk the boundary with GPS motion capture, which drops a point every 5 m. Back home in the evening they record their day's activities in the Activity Tracker, which only lets them submit after 18:00. When the phone finds a network, a background job drains the queued changes. If the server rejects one (for example, the crop area now exceeds the land), a red badge appears on the sync-error icon the next morning.
Module map
| Module | What it does | Key tables | More |
|---|---|---|---|
| Startup and login | Language pick, Bridge login, role and tenant detection, work-node pick | prefs, worknode* | App startup |
| Data download | Pages farmers, roles, land, portfolios, ATM and attachments onto the device; resumable | sync_status, ie_flat_entity | Data download |
| Home, menu, settings | Farmer list or VO tabs, sync badge, force sync, backup, language switch | prefs, sync_errors | Home |
| Device binding and remote actions | One device per user (optional), remote logout or wipe | device_registration_prefs | Device registration |
| Farmer list and profile (IE) | Search, filter, add and edit farmers, category and status | ie_flat_entity, ie (+6 child tables), legacy ie | Farmer profile |
| Farmer hub | Gate rules that decide which tiles a farmer can use | reads many | Farmer hub |
| Land and lease | Land parcels, authentication, leased-in land, disable | farmerassets, legacy ie_asset | Land assets |
| Geofencing | Walk, tap or KML polygon capture for land, crop tile, ATM | farmerassets, PLI, farming_model + files | Geofencing |
| Livestock and inventory | Animal counts; crop stock in quintals | farmerassets, inventoryproduct, ie_inventory | Livestock |
| Crop Actual (portfolio) and harvest | Monthly crop blocks per land, practices, PMDS, month-on-month copy, harvest | portfolio*, harvest_logs | Crop Actual |
| ATM and ATM harvest | Small year-round income plot model | farming_model | ATM model |
| 365 DKG | Monthly kitchen-garden yes/no | farming_model | 365 DKG |
| KAP / AAP 2025-26 | Annual action plan with ceilings | ie_flat_entity.annualActionPlanData2526 | KAP 2025-26 |
| Endorsements and comments | VO → SHG → farmer review, flags, abstracts | endorsement_entity, endorsement_comment_entity | Endorsement |
| Compact Block | Groups geofenced S2S tiles of several farmers, at least 5 acres | compact_block, compact_block_member | Compact Block |
| Surveys, certification, activity tracker | Built on the internal assessment library | library DB | Activity Tracker |
| Analytics and notifications | MIS dashboard, comment and push notifications | prefs, notifications | Analytics |
How it is put together today
- Two Gradle modules.
:app(org.platformcommons.urvi) holds screens, its own Room DB and repositories;:data(org.mindtree.app.agricultor.data) holds the main DB, a 3,395-line god repository and the sync code. The boundary is blurred. - Six on-device stores.
agri_db(Room v62, 64 tables), legacyie_register.db(Room v20, a denormalised copy kept in step by "bridge" mappers),error_log_db,sync_ledger_db, the Bridge library'sSYNC_QUEUE_DB, and the assessment library's DB. None is encrypted. - Two-level write queue. A screen saves a row and adds a
sync_ledgerentry with dependencies. A 15-minute WorkManager job turns ready entries into BridgeOperations inSYNC_QUEUE_DB, which an Android SyncAdapter in a separate process drains. About 40 response handlers write server ids back. - Reads go to report datasets. About 40 named datasets on two report engines, with parameters and SQL-like
criteria=strings built on the phone. - Five HTTP stacks, three JSON libraries. Bridge CoreAPI on Apache HttpClient, three Retrofit clients, plus account-lib's own Retrofit; Gson, Moshi and Jackson.
- Four in-house Bridge libraries (core-api, offline-core, offline-android, account-lib) carry login, HTTP and the queue. Login stores the password in AccountManager and uses a session token as
X-SESSIONID. - Server-driven switches. Feature flags, season, paging and retry settings come from the
CLASS.PORTFOLIO_APP_CONFIGref data, read by string code across a 1,970-lineAppUtils. - Firebase on the side. Crashlytics, Remote Config, FCM topics, and Firestore/Storage used as a sync debug log and image fallback.
Top findings
| # | Finding | Why it matters | Read more |
|---|---|---|---|
| 1 | Release keystore and its passwords, Nexus credentials and the app key are committed or shipped in the APK | Anyone with repo or APK access can sign or impersonate | /security/ |
| 2 | Full sync operations, including session headers and farmer PII, go to Firestore on errors and the whole queue goes to sync-service every 6 hours | Personal data (Aadhaar, phone) leaves the device unredacted | /security/ |
| 3 | Aadhaar and phone numbers sit unencrypted in Room; the password is stored in plaintext in AccountManager; support exports zip the whole DB | Device loss or a shared log exposes farmers | /security/ |
| 4 | Down-sync can overwrite unsynced work: ATM download inserts the unfiltered list, the VO filter wipes the whole farmer table, REPLACE inserts reset device-only flags | Silent loss of field data | /sync/ |
| 5 | No conflict detection, effectively unlimited retries, handler exceptions silently drop operations, and two processes can drain the queue at once | Data can vanish or be applied twice with no trace | /sync/ |
| 6 | The app calls about 12 services and 40 datasets directly; one data setup costs roughly 300 calls for 1,500 farmers and Home fires about 15 calls on every open | Slow, fragile on 2G/3G, and every backend rename needs an app release | /api/ |
| 7 | Two databases hold the same farmers, land and crops in different shapes, with no foreign keys and few indexes; each new season is a copy-pasted screen, entity and handler | Drift between copies, slow queries, a release per season | /database/ |
| 8 | About 6,500-7,000 lines are dead, 8% of lines are duplicated, and there are only 9 @Test methods | Rules are hard to find and nothing protects a refactor | /tech-debt/, /stale/ |
How to read this site
| Section | Pages | Use it to |
|---|---|---|
| Today | Overview, Features, Roles and access, Key flows | Learn what the app does now, screen by screen, rule by rule |
| Data | Database, APIs and services, Sync and background, Sibling libraries | Find which table, endpoint or worker holds or moves a piece of data |
| Debt | Tech debt, Security, Set-aside code | See what is broken, risky or dead |
| Tomorrow | Improvements, Compose vs Flutter, App Service (BFF), Roadmap | Plan the rewrite |
Every factual page describes live code only. Unused classes, tables, endpoints and workers are listed once, on /stale/, and nowhere else. File paths are repo-relative. Secret values found in the code are never shown, only where they are.