feat(track): record which devices are paired, and report it on upload

The app knew about bonded devices only in passing - syncPairedDevices marked
them tracked and threw the bond state away - so nothing downstream could tell
a paired device from one merely seen. Adds devices.paired (schema 17), set
from ACTION_BOND_STATE_CHANGED and from the bonded list at startup, and sends
it as a `paired` column on every uploaded row.

The flag is its own column rather than inferred from is_tracked, which answers
a different question: a device can be tracked without ever having been paired.

syncPairedDevices clears the flag before re-asserting it from getBondedDevices.
A device unpaired while the service was dead never broadcast BOND_NONE, so the
bonded list is the only reliable answer at startup, and without the clear such
a device would stay marked paired forever.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
n0tst3v3
2026-08-19 18:03:11 -06:00
co-authored by Claude Opus 5
parent 07504555b6
commit 60feffc256
4 changed files with 45 additions and 9 deletions
@@ -11,7 +11,7 @@ import java.util.Locale;
public class TrackerDatabase extends SQLiteOpenHelper {
private static final String DB_NAME = "bluetooth_tracker.db";
private static final int DB_VERSION = 16;
private static final int DB_VERSION = 17;
public TrackerDatabase(Context context) {
super(context, DB_NAME, null, DB_VERSION);
@@ -47,7 +47,7 @@ public class TrackerDatabase extends SQLiteOpenHelper {
"reason TEXT,updated_at INTEGER DEFAULT 0,photo_uri TEXT,icon_preset TEXT DEFAULT 'bag'," +
"gps_polling_rate INTEGER DEFAULT 900000,pin_color TEXT DEFAULT '#E53935'," +
"tracking_priority INTEGER DEFAULT 15,photo_path TEXT,device_gps_interval INTEGER DEFAULT 15," +
"is_tracked INTEGER DEFAULT 0,custom_name TEXT,broadcast_name TEXT,vendor_name TEXT,tx_power_1m INTEGER DEFAULT -59,path_loss_exponent REAL DEFAULT 2.7,last_rssi_at INTEGER DEFAULT 0,device_category TEXT,category_manual INTEGER DEFAULT 0,vendor_manual INTEGER DEFAULT 0,map_enabled INTEGER DEFAULT 0,live_enabled INTEGER DEFAULT 0,location_locked INTEGER DEFAULT 0,show_trail INTEGER DEFAULT 1,track_broadcast INTEGER DEFAULT 0)");
"is_tracked INTEGER DEFAULT 0,custom_name TEXT,broadcast_name TEXT,vendor_name TEXT,tx_power_1m INTEGER DEFAULT -59,path_loss_exponent REAL DEFAULT 2.7,last_rssi_at INTEGER DEFAULT 0,device_category TEXT,category_manual INTEGER DEFAULT 0,vendor_manual INTEGER DEFAULT 0,map_enabled INTEGER DEFAULT 0,live_enabled INTEGER DEFAULT 0,location_locked INTEGER DEFAULT 0,show_trail INTEGER DEFAULT 1,track_broadcast INTEGER DEFAULT 0,paired INTEGER DEFAULT 0)");
createSupportingTables(db);
seedVendors(db);
}
@@ -103,6 +103,8 @@ public class TrackerDatabase extends SQLiteOpenHelper {
if (oldVersion < 15) seedVendors(db);
if (oldVersion < 16)
addColumn(db, "ALTER TABLE location_history ADD COLUMN event TEXT");
if (oldVersion < 17)
addColumn(db, "ALTER TABLE devices ADD COLUMN paired INTEGER DEFAULT 0");
}
private void addColumn(SQLiteDatabase db, String sql) {
@@ -331,6 +333,24 @@ public class TrackerDatabase extends SQLiteOpenHelper {
getWritableDatabase().update("devices", values, "address=?", new String[] { address });
}
/**
* Records whether this device is bonded to the phone.
*
* Kept as its own column rather than inferred from is_tracked: a device can
* be tracked without ever having been paired, and the two answer different
* questions.
*/
/** Clears the flag on every device, before the bonded list re-asserts it. */
public synchronized void clearPaired() {
ContentValues values = new ContentValues(); values.put("paired", 0);
getWritableDatabase().update("devices", values, null, null);
}
public synchronized void setPaired(String address, boolean paired) {
ContentValues values = new ContentValues(); values.put("paired", paired ? 1 : 0);
getWritableDatabase().update("devices", values, "address=?", new String[] { address });
}
public synchronized void setMapEnabled(String address, boolean enabled) {
ContentValues values = new ContentValues(); values.put("map_enabled", enabled ? 1 : 0);
getWritableDatabase().update("devices", values, "address=?", new String[] { address });
@@ -517,6 +537,7 @@ public class TrackerDatabase extends SQLiteOpenHelper {
r.locationLocked = integer(c, "location_locked");
r.showTrail = integer(c, "show_trail");
r.trackBroadcast = integer(c, "track_broadcast");
r.paired = integer(c, "paired");
r.connected = integer(c, "connected");
r.connectedAt = number(c, "connected_at");
r.disconnectedAt = number(c, "disconnected_at");