@josuelmm/cordova-background-geolocation 4.2.2 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +11 -0
- package/CHANGELOG.md +213 -0
- package/HISTORY.md +73 -0
- package/README.md +45 -74
- package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +24 -0
- package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +61 -1
- package/android/common/src/main/AndroidManifest.xml +1 -1
- package/android/common/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +6 -3
- package/android/common/src/main/java/com/marianhello/bgloc/Config.java +65 -1
- package/android/common/src/main/java/com/marianhello/bgloc/PostLocationTask.java +1 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java +94 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java +205 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/LocationTemplateFactory.java +6 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +5 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +32 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationContract.java +12 -2
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java +33 -2
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +15 -1
- package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +23 -8
- package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +246 -21
- package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +5 -2
- package/android/common/src/main/java/com/marianhello/bgloc/sync/BatchManager.java +46 -13
- package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +23 -1
- package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +111 -5
- package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +20 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.h +2 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.m +16 -2
- package/ios/common/BackgroundGeolocation/MAURConfigurationContract.h +3 -0
- package/ios/common/BackgroundGeolocation/MAURConfigurationContract.m +3 -1
- package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +15 -1
- package/ios/common/BackgroundGeolocation/MAURLocation.h +12 -0
- package/ios/common/BackgroundGeolocation/MAURLocation.m +33 -4
- package/ios/common/BackgroundGeolocation/MAURLocationContract.h +4 -0
- package/ios/common/BackgroundGeolocation/MAURLocationContract.m +5 -1
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +9 -0
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +59 -1
- package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.m +54 -4
- package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.h +12 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.m +125 -5
- package/package.json +36 -1
- package/plugin.xml +3 -2
- package/www/BackgroundGeolocation.d.ts +114 -3
- package/www/BackgroundGeolocation.js +11 -4
- package/CLAUDE.md +0 -56
- package/android/CDVBackgroundGeolocation/src/test/java/com/marianhello/ConfigMapperTest.java +0 -220
- package/android/common/src/androidTest/java/com/marianhello/bgloc/BackgroundGeolocationFacadeTest.java +0 -45
- package/android/common/src/androidTest/java/com/marianhello/bgloc/BatchManagerTest.java +0 -570
- package/android/common/src/androidTest/java/com/marianhello/bgloc/ConfigTest.java +0 -76
- package/android/common/src/androidTest/java/com/marianhello/bgloc/ContentProviderLocationDAOTest.java +0 -437
- package/android/common/src/androidTest/java/com/marianhello/bgloc/DBLogReaderTest.java +0 -95
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationContentProviderTest.java +0 -159
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceProxyTest.java +0 -161
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceTest.java +0 -247
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteConfigurationDAOTest.java +0 -200
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOTest.java +0 -457
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOThreadTest.java +0 -96
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteOpenHelperTest.java +0 -225
- package/android/common/src/androidTest/java/com/marianhello/bgloc/TestPluginDelegate.java +0 -46
- package/android/common/src/androidTest/java/com/marianhello/bgloc/TestResourceResolver.java +0 -14
- package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/MockLocationProvider.java +0 -50
- package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/TestLocationProviderFactory.java +0 -17
- package/android/common/src/androidTest/java/com/marianhello/bgloc/sqlite/SQLiteOpenHelper10.java +0 -92
- package/android/common/src/androidTest/java/com/marianhello/bgloc/test/LocationProviderTestCase.java +0 -107
- package/android/common/src/androidTest/java/com/marianhello/bgloc/test/TestConstants.java +0 -5
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ArrayListLocationTemplateTest.java +0 -82
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/BackgroundLocationTest.java +0 -128
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ConfigTest.java +0 -191
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/DBLogReaderTest.java +0 -37
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HashMapLocationTemplateTest.java +0 -216
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HttpPostServiceTest.java +0 -223
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/LocationTemplateFactoryTest.java +0 -50
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/PostLocationTaskTest.java +0 -180
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/TestHelper.java +0 -16
- package/ios/common/BackgroundGeolocationTests/Info.plist +0 -24
- package/ios/common/BackgroundGeolocationTests/MAURBackgroundLocationTest.m +0 -185
- package/ios/common/BackgroundGeolocationTests/MAURConfigTest.m +0 -161
- package/ios/common/BackgroundGeolocationTests/MAURGeolocationOpenHelperTest.m +0 -102
- package/ios/common/BackgroundGeolocationTests/MAURLocationTest.m +0 -216
- package/ios/common/BackgroundGeolocationTests/MAURLocationUploaderTest.m +0 -55
- package/ios/common/BackgroundGeolocationTests/MAURLogReaderTest.m +0 -43
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteConfigurationDAOTest.m +0 -102
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteHelperTest.m +0 -41
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOTests.m +0 -240
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOThreadTest.m +0 -84
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteOpenHelperTest.m +0 -144
- package/ios/common/scripts/xcode-refactor.js +0 -184
package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java
CHANGED
|
@@ -300,7 +300,10 @@ public class SQLiteLocationDAO implements LocationDAO {
|
|
|
300
300
|
.append(LocationEntry.COLUMN_NAME_LOCATION_PROVIDER).append("= ?,")
|
|
301
301
|
.append(LocationEntry.COLUMN_NAME_BATCH_START_MILLIS).append("= ?,")
|
|
302
302
|
.append(LocationEntry.COLUMN_NAME_STATUS).append("= ?,")
|
|
303
|
-
.append(LocationEntry.COLUMN_NAME_MOCK_FLAGS).append("=
|
|
303
|
+
.append(LocationEntry.COLUMN_NAME_MOCK_FLAGS).append("= ?,")
|
|
304
|
+
.append(LocationEntry.COLUMN_NAME_EVENTS_JSON).append("= ?,")
|
|
305
|
+
.append(LocationEntry.COLUMN_NAME_BATTERY_LEVEL).append("= ?,")
|
|
306
|
+
.append(LocationEntry.COLUMN_NAME_IS_CHARGING).append("= ?")
|
|
304
307
|
.append(" WHERE ").append(LocationEntry._ID)
|
|
305
308
|
.append("= ?")
|
|
306
309
|
.toString();
|
|
@@ -325,6 +328,9 @@ public class SQLiteLocationDAO implements LocationDAO {
|
|
|
325
328
|
location.getBatchStartMillis(),
|
|
326
329
|
location.getStatus(),
|
|
327
330
|
location.getMockFlags(),
|
|
331
|
+
location.hasDrivingEvents() ? location.getDrivingEvents().toString() : null,
|
|
332
|
+
location.getBatteryLevel(),
|
|
333
|
+
location.isCharging() != null ? (location.isCharging() ? 1 : 0) : null,
|
|
328
334
|
locationId
|
|
329
335
|
});
|
|
330
336
|
|
|
@@ -459,6 +465,18 @@ public class SQLiteLocationDAO implements LocationDAO {
|
|
|
459
465
|
l.setStatus(c.getInt(c.getColumnIndex(LocationEntry.COLUMN_NAME_STATUS)));
|
|
460
466
|
l.setLocationId(c.getLong(c.getColumnIndex(LocationEntry._ID)));
|
|
461
467
|
l.setMockFlags(c.getInt((c.getColumnIndex(LocationEntry.COLUMN_NAME_MOCK_FLAGS))));
|
|
468
|
+
// v4.5: events / battery / charging
|
|
469
|
+
int idxEv = c.getColumnIndex(LocationEntry.COLUMN_NAME_EVENTS_JSON);
|
|
470
|
+
if (idxEv >= 0 && !c.isNull(idxEv)) {
|
|
471
|
+
String s = c.getString(idxEv);
|
|
472
|
+
if (s != null && !s.isEmpty()) {
|
|
473
|
+
try { l.setDrivingEvents(new org.json.JSONArray(s)); } catch (org.json.JSONException ignored) {}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
int idxBat = c.getColumnIndex(LocationEntry.COLUMN_NAME_BATTERY_LEVEL);
|
|
477
|
+
if (idxBat >= 0 && !c.isNull(idxBat)) l.setBatteryLevel(c.getInt(idxBat));
|
|
478
|
+
int idxChg = c.getColumnIndex(LocationEntry.COLUMN_NAME_IS_CHARGING);
|
|
479
|
+
if (idxChg >= 0 && !c.isNull(idxChg)) l.setCharging(c.getInt(idxChg) == 1);
|
|
462
480
|
|
|
463
481
|
return l;
|
|
464
482
|
}
|
|
@@ -485,6 +503,16 @@ public class SQLiteLocationDAO implements LocationDAO {
|
|
|
485
503
|
values.put(LocationEntry.COLUMN_NAME_STATUS, l.getStatus());
|
|
486
504
|
values.put(LocationEntry.COLUMN_NAME_BATCH_START_MILLIS, l.getBatchStartMillis());
|
|
487
505
|
values.put(LocationEntry.COLUMN_NAME_MOCK_FLAGS, l.getMockFlags());
|
|
506
|
+
// v4.5.1: always write — NULL when absent — to clear stale values on maxRows recycle.
|
|
507
|
+
if (l.hasDrivingEvents()) {
|
|
508
|
+
values.put(LocationEntry.COLUMN_NAME_EVENTS_JSON, l.getDrivingEvents().toString());
|
|
509
|
+
} else {
|
|
510
|
+
values.putNull(LocationEntry.COLUMN_NAME_EVENTS_JSON);
|
|
511
|
+
}
|
|
512
|
+
if (l.getBatteryLevel() != null) values.put(LocationEntry.COLUMN_NAME_BATTERY_LEVEL, l.getBatteryLevel());
|
|
513
|
+
else values.putNull(LocationEntry.COLUMN_NAME_BATTERY_LEVEL);
|
|
514
|
+
if (l.isCharging() != null) values.put(LocationEntry.COLUMN_NAME_IS_CHARGING, l.isCharging() ? 1 : 0);
|
|
515
|
+
else values.putNull(LocationEntry.COLUMN_NAME_IS_CHARGING);
|
|
488
516
|
|
|
489
517
|
return values;
|
|
490
518
|
}
|
|
@@ -511,7 +539,10 @@ public class SQLiteLocationDAO implements LocationDAO {
|
|
|
511
539
|
LocationEntry.COLUMN_NAME_LOCATION_PROVIDER,
|
|
512
540
|
LocationEntry.COLUMN_NAME_STATUS,
|
|
513
541
|
LocationEntry.COLUMN_NAME_BATCH_START_MILLIS,
|
|
514
|
-
LocationEntry.COLUMN_NAME_MOCK_FLAGS
|
|
542
|
+
LocationEntry.COLUMN_NAME_MOCK_FLAGS,
|
|
543
|
+
LocationEntry.COLUMN_NAME_EVENTS_JSON,
|
|
544
|
+
LocationEntry.COLUMN_NAME_BATTERY_LEVEL,
|
|
545
|
+
LocationEntry.COLUMN_NAME_IS_CHARGING
|
|
515
546
|
};
|
|
516
547
|
|
|
517
548
|
return columns;
|
package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java
CHANGED
|
@@ -22,7 +22,7 @@ import static com.marianhello.bgloc.data.sqlite.SQLiteLocationContract.LocationE
|
|
|
22
22
|
public class SQLiteOpenHelper extends android.database.sqlite.SQLiteOpenHelper {
|
|
23
23
|
private static final String TAG = SQLiteOpenHelper.class.getName();
|
|
24
24
|
public static final String SQLITE_DATABASE_NAME = "cordova_bg_geolocation.db";
|
|
25
|
-
public static final int DATABASE_VERSION =
|
|
25
|
+
public static final int DATABASE_VERSION = 22;
|
|
26
26
|
|
|
27
27
|
public static final String TEXT_TYPE = " TEXT";
|
|
28
28
|
public static final String INTEGER_TYPE = " INTEGER";
|
|
@@ -143,6 +143,20 @@ public class SQLiteOpenHelper extends android.database.sqlite.SQLiteOpenHelper {
|
|
|
143
143
|
case 19:
|
|
144
144
|
alterSql.add(SessionEntry.SQL_CREATE_SESSION_TABLE);
|
|
145
145
|
alterSql.add(SessionEntry.SQL_CREATE_SESSION_TABLE_TIME_IDX);
|
|
146
|
+
case 20:
|
|
147
|
+
// v4.4.1: store the full Config as a single JSON blob so future-added fields
|
|
148
|
+
// do not require a per-field schema bump.
|
|
149
|
+
alterSql.add("ALTER TABLE " + ConfigurationEntry.TABLE_NAME +
|
|
150
|
+
" ADD COLUMN " + ConfigurationEntry.COLUMN_NAME_CONFIG_JSON + TEXT_TYPE);
|
|
151
|
+
case 21:
|
|
152
|
+
// v4.5.0: persist driving events / battery / isCharging on each location so
|
|
153
|
+
// they survive the sync queue (POST failure → SQLite → background sync).
|
|
154
|
+
alterSql.add("ALTER TABLE " + LocationEntry.TABLE_NAME +
|
|
155
|
+
" ADD COLUMN " + LocationEntry.COLUMN_NAME_EVENTS_JSON + TEXT_TYPE);
|
|
156
|
+
alterSql.add("ALTER TABLE " + LocationEntry.TABLE_NAME +
|
|
157
|
+
" ADD COLUMN " + LocationEntry.COLUMN_NAME_BATTERY_LEVEL + INTEGER_TYPE);
|
|
158
|
+
alterSql.add("ALTER TABLE " + LocationEntry.TABLE_NAME +
|
|
159
|
+
" ADD COLUMN " + LocationEntry.COLUMN_NAME_IS_CHARGING + INTEGER_TYPE);
|
|
146
160
|
|
|
147
161
|
break; // DO NOT FORGET TO MOVE DOWN BREAK ON DB UPGRADE!!!
|
|
148
162
|
default:
|
|
@@ -33,9 +33,10 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
|
|
|
33
33
|
private static final String SINGLE_LOCATION_UPDATE_ACTION = P_NAME + ".SINGLE_LOCATION_UPDATE_ACTION";
|
|
34
34
|
private static final String STATIONARY_LOCATION_MONITOR_ACTION = P_NAME + ".STATIONARY_LOCATION_MONITOR_ACTION";
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
private static final long
|
|
38
|
-
private static final long
|
|
36
|
+
// v4.5.1: defaults — overridable per-config via config.stationaryTimeout / stationaryPollInterval / stationaryPollFast
|
|
37
|
+
private static final long DEFAULT_STATIONARY_TIMEOUT = 5 * 1000 * 60;
|
|
38
|
+
private static final long DEFAULT_STATIONARY_LOCATION_POLLING_INTERVAL_LAZY = 3 * 1000 * 60;
|
|
39
|
+
private static final long DEFAULT_STATIONARY_LOCATION_POLLING_INTERVAL_AGGRESSIVE = 1 * 1000 * 60;
|
|
39
40
|
private static final int MAX_STATIONARY_ACQUISITION_ATTEMPTS = 5;
|
|
40
41
|
private static final int MAX_SPEED_ACQUISITION_ATTEMPTS = 3;
|
|
41
42
|
|
|
@@ -50,6 +51,20 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
|
|
|
50
51
|
private PendingIntent stationaryAlarmPI;
|
|
51
52
|
private PendingIntent stationaryLocationPollingPI;
|
|
52
53
|
private long stationaryLocationPollingInterval;
|
|
54
|
+
|
|
55
|
+
/** v4.5.1: read overrides from {@link com.marianhello.bgloc.Config}; fall back to defaults. */
|
|
56
|
+
private long getStationaryTimeout() {
|
|
57
|
+
Integer v = mConfig != null ? mConfig.getStationaryTimeout() : null;
|
|
58
|
+
return v != null ? v.longValue() : DEFAULT_STATIONARY_TIMEOUT;
|
|
59
|
+
}
|
|
60
|
+
private long getStationaryPollLazy() {
|
|
61
|
+
Integer v = mConfig != null ? mConfig.getStationaryPollInterval() : null;
|
|
62
|
+
return v != null ? v.longValue() : DEFAULT_STATIONARY_LOCATION_POLLING_INTERVAL_LAZY;
|
|
63
|
+
}
|
|
64
|
+
private long getStationaryPollFast() {
|
|
65
|
+
Integer v = mConfig != null ? mConfig.getStationaryPollFast() : null;
|
|
66
|
+
return v != null ? v.longValue() : DEFAULT_STATIONARY_LOCATION_POLLING_INTERVAL_AGGRESSIVE;
|
|
67
|
+
}
|
|
53
68
|
private PendingIntent stationaryRegionPI;
|
|
54
69
|
private PendingIntent singleUpdatePI;
|
|
55
70
|
private Integer scaledDistanceFilter;
|
|
@@ -367,7 +382,7 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
|
|
|
367
382
|
public void resetStationaryAlarm() {
|
|
368
383
|
if (alarmManager == null || stationaryAlarmPI == null) return;
|
|
369
384
|
alarmManager.cancel(stationaryAlarmPI);
|
|
370
|
-
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() +
|
|
385
|
+
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + getStationaryTimeout(), stationaryAlarmPI);
|
|
371
386
|
}
|
|
372
387
|
|
|
373
388
|
private Integer calculateDistanceFilter(Float speed) {
|
|
@@ -401,7 +416,7 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
|
|
|
401
416
|
|
|
402
417
|
this.stationaryRadius = proximityRadius;
|
|
403
418
|
|
|
404
|
-
startPollingStationaryLocation(
|
|
419
|
+
startPollingStationaryLocation(getStationaryPollLazy());
|
|
405
420
|
} catch (SecurityException e) {
|
|
406
421
|
logger.error("Security exception: {}", e.getMessage());
|
|
407
422
|
this.handleSecurityException(e);
|
|
@@ -464,9 +479,9 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
|
|
|
464
479
|
if (distance > stationaryRadius) {
|
|
465
480
|
onExitStationaryRegion(location);
|
|
466
481
|
} else if (distance > 0) {
|
|
467
|
-
startPollingStationaryLocation(
|
|
468
|
-
} else if (stationaryLocationPollingInterval !=
|
|
469
|
-
startPollingStationaryLocation(
|
|
482
|
+
startPollingStationaryLocation(getStationaryPollFast());
|
|
483
|
+
} else if (stationaryLocationPollingInterval != getStationaryPollLazy()) {
|
|
484
|
+
startPollingStationaryLocation(getStationaryPollLazy());
|
|
470
485
|
}
|
|
471
486
|
}
|
|
472
487
|
|
|
@@ -36,6 +36,7 @@ import android.os.Message;
|
|
|
36
36
|
import android.os.PowerManager;
|
|
37
37
|
import android.os.Process;
|
|
38
38
|
import androidx.annotation.Nullable;
|
|
39
|
+
import androidx.core.content.ContextCompat;
|
|
39
40
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
40
41
|
|
|
41
42
|
import com.marianhello.bgloc.Config;
|
|
@@ -179,6 +180,13 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
179
180
|
private com.marianhello.bgloc.sensor.SensorFusionDetector mSensorFusion;
|
|
180
181
|
/** v4.2 Phase 8: cached tripActive state so hot-reload can re-inject it. */
|
|
181
182
|
private volatile boolean mDrivingTripActive = false;
|
|
183
|
+
/** v4.3: events fired without a simultaneous fix (providerChange, sensor crash, phone usage,
|
|
184
|
+
* manual SOS) buffered here and flushed onto the next location's `events` array.
|
|
185
|
+
* v4.4.1: capped at PENDING_DRIVING_EVENTS_MAX entries (oldest evicted) and entries older
|
|
186
|
+
* than PENDING_DRIVING_EVENTS_TTL_MS are dropped at flush time. */
|
|
187
|
+
private final org.json.JSONArray mPendingDrivingEvents = new org.json.JSONArray();
|
|
188
|
+
private static final int PENDING_DRIVING_EVENTS_MAX = 20;
|
|
189
|
+
private static final long PENDING_DRIVING_EVENTS_TTL_MS = 60_000L;
|
|
182
190
|
private static final long WATCHDOG_INTERVAL_MS = 60_000L;
|
|
183
191
|
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
|
|
184
192
|
private final Runnable mWatchdogRunnable = new Runnable() {
|
|
@@ -188,12 +196,23 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
188
196
|
if (!Boolean.TRUE.equals(mConfig.getEnableWatchdog())) return;
|
|
189
197
|
long now = System.currentTimeMillis();
|
|
190
198
|
if (mLastLocationTime > 0 && (now - mLastLocationTime) > WATCHDOG_INTERVAL_MS) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
// v4.5.1: when drivingEvents is enabled, treat "no fixes" while NOT tripActive as
|
|
200
|
+
// intentional stationary → don't restart (saves battery). When drivingEvents is
|
|
201
|
+
// disabled (the plugin has no notion of "trip"), keep the legacy behaviour of
|
|
202
|
+
// restarting on every stale window.
|
|
203
|
+
Config.DrivingEventsOptions de = mConfig.getDrivingEvents();
|
|
204
|
+
boolean drivingEnabled = de != null && de.enabled;
|
|
205
|
+
boolean shouldRestart = !drivingEnabled || mDrivingTripActive;
|
|
206
|
+
if (shouldRestart) {
|
|
207
|
+
logger.info("Location watchdog: no update in {}s, restarting provider", WATCHDOG_INTERVAL_MS / 1000);
|
|
208
|
+
try {
|
|
209
|
+
mProvider.onStop();
|
|
210
|
+
mProvider.onStart();
|
|
211
|
+
} catch (Exception e) {
|
|
212
|
+
logger.warn("Watchdog restart failed", e);
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
logger.debug("Location watchdog: stationary (no active trip); skipping restart");
|
|
197
216
|
}
|
|
198
217
|
}
|
|
199
218
|
mMainHandler.postDelayed(this, WATCHDOG_INTERVAL_MS);
|
|
@@ -476,9 +495,12 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
476
495
|
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
|
|
477
496
|
}
|
|
478
497
|
}
|
|
479
|
-
|
|
498
|
+
// v4.5.1: only hold a permanent CPU wake lock when wakeLockMode == 'always'.
|
|
499
|
+
// Default 'posting' acquires only briefly during onLocation/post; 'none' never.
|
|
500
|
+
String wlMode = mConfig.getWakeLockMode() != null ? mConfig.getWakeLockMode() : "posting";
|
|
501
|
+
if ("always".equals(wlMode) && mWakeLock != null && !mWakeLock.isHeld()) {
|
|
480
502
|
mWakeLock.acquire();
|
|
481
|
-
logger.debug("Wake lock acquired");
|
|
503
|
+
logger.debug("Wake lock acquired (mode=always)");
|
|
482
504
|
}
|
|
483
505
|
|
|
484
506
|
if (Boolean.TRUE.equals(mConfig.getEnableWatchdog())) {
|
|
@@ -536,18 +558,21 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
536
558
|
mDrivingDetector = new com.marianhello.bgloc.driving.DrivingEventsDetector(
|
|
537
559
|
new com.marianhello.bgloc.driving.DrivingEventsDetector.Listener() {
|
|
538
560
|
@Override public void onMoving(BackgroundLocation l) {
|
|
561
|
+
attachDrivingEvent(l, "moving", null);
|
|
539
562
|
Bundle b = new Bundle();
|
|
540
563
|
b.putInt("action", MSG_ON_MOVING);
|
|
541
564
|
if (l != null) b.putParcelable("payload", l);
|
|
542
565
|
broadcastMessage(b);
|
|
543
566
|
}
|
|
544
567
|
@Override public void onStopped(BackgroundLocation l) {
|
|
568
|
+
attachDrivingEvent(l, "stopped", null);
|
|
545
569
|
Bundle b = new Bundle();
|
|
546
570
|
b.putInt("action", MSG_ON_STOPPED);
|
|
547
571
|
if (l != null) b.putParcelable("payload", l);
|
|
548
572
|
broadcastMessage(b);
|
|
549
573
|
}
|
|
550
574
|
@Override public void onTripStart(BackgroundLocation l) {
|
|
575
|
+
attachDrivingEvent(l, "tripStart", null);
|
|
551
576
|
Bundle b = new Bundle();
|
|
552
577
|
b.putInt("action", MSG_ON_TRIP_START);
|
|
553
578
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -556,6 +581,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
556
581
|
if (mSensorFusion != null) mSensorFusion.setTripActive(true);
|
|
557
582
|
}
|
|
558
583
|
@Override public void onTripEnd(BackgroundLocation l, double distance, long durationMs) {
|
|
584
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
585
|
+
try { extra.put("distance", distance); extra.put("durationMs", durationMs); } catch (org.json.JSONException ignored) {}
|
|
586
|
+
attachDrivingEvent(l, "tripEnd", extra);
|
|
559
587
|
Bundle b = new Bundle();
|
|
560
588
|
b.putInt("action", MSG_ON_TRIP_END);
|
|
561
589
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -566,6 +594,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
566
594
|
if (mSensorFusion != null) mSensorFusion.setTripActive(false);
|
|
567
595
|
}
|
|
568
596
|
@Override public void onSpeeding(BackgroundLocation l, double speedKmh, double limitKmh) {
|
|
597
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
598
|
+
try { extra.put("speedKmh", speedKmh); extra.put("limitKmh", limitKmh); } catch (org.json.JSONException ignored) {}
|
|
599
|
+
attachDrivingEvent(l, "speeding", extra);
|
|
569
600
|
Bundle b = new Bundle();
|
|
570
601
|
b.putInt("action", MSG_ON_SPEEDING);
|
|
571
602
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -574,12 +605,19 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
574
605
|
broadcastMessage(b);
|
|
575
606
|
}
|
|
576
607
|
@Override public void onProviderChange(String provider) {
|
|
608
|
+
// No location associated; buffer for next fix.
|
|
609
|
+
org.json.JSONObject ev = new org.json.JSONObject();
|
|
610
|
+
try { ev.put("type", "providerChange"); ev.put("provider", provider != null ? provider : ""); ev.put("time", System.currentTimeMillis()); } catch (org.json.JSONException ignored) {}
|
|
611
|
+
enqueuePendingDrivingEvent(ev);
|
|
577
612
|
Bundle b = new Bundle();
|
|
578
613
|
b.putInt("action", MSG_ON_PROVIDER_CHANGE);
|
|
579
614
|
b.putString("provider", provider != null ? provider : "");
|
|
580
615
|
broadcastMessage(b);
|
|
581
616
|
}
|
|
582
617
|
@Override public void onHardBrake(BackgroundLocation l, double decelMps2) {
|
|
618
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
619
|
+
try { extra.put("value", decelMps2); } catch (org.json.JSONException ignored) {}
|
|
620
|
+
attachDrivingEvent(l, "hardBrake", extra);
|
|
583
621
|
Bundle b = new Bundle();
|
|
584
622
|
b.putInt("action", MSG_ON_HARD_BRAKE);
|
|
585
623
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -587,6 +625,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
587
625
|
broadcastMessage(b);
|
|
588
626
|
}
|
|
589
627
|
@Override public void onRapidAcceleration(BackgroundLocation l, double accelMps2) {
|
|
628
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
629
|
+
try { extra.put("value", accelMps2); } catch (org.json.JSONException ignored) {}
|
|
630
|
+
attachDrivingEvent(l, "rapidAcceleration", extra);
|
|
590
631
|
Bundle b = new Bundle();
|
|
591
632
|
b.putInt("action", MSG_ON_RAPID_ACCELERATION);
|
|
592
633
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -594,6 +635,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
594
635
|
broadcastMessage(b);
|
|
595
636
|
}
|
|
596
637
|
@Override public void onSharpTurn(BackgroundLocation l, double degPerSec) {
|
|
638
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
639
|
+
try { extra.put("value", degPerSec); } catch (org.json.JSONException ignored) {}
|
|
640
|
+
attachDrivingEvent(l, "sharpTurn", extra);
|
|
597
641
|
Bundle b = new Bundle();
|
|
598
642
|
b.putInt("action", MSG_ON_SHARP_TURN);
|
|
599
643
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -601,6 +645,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
601
645
|
broadcastMessage(b);
|
|
602
646
|
}
|
|
603
647
|
@Override public void onPossibleCrash(BackgroundLocation l, double velocityDropKmh) {
|
|
648
|
+
org.json.JSONObject extra = new org.json.JSONObject();
|
|
649
|
+
try { extra.put("value", velocityDropKmh); extra.put("source", "gps"); } catch (org.json.JSONException ignored) {}
|
|
650
|
+
attachDrivingEvent(l, "possibleCrash", extra);
|
|
604
651
|
Bundle b = new Bundle();
|
|
605
652
|
b.putInt("action", MSG_ON_POSSIBLE_CRASH);
|
|
606
653
|
if (l != null) b.putParcelable("payload", l);
|
|
@@ -638,6 +685,15 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
638
685
|
com.marianhello.bgloc.sensor.SensorFusionDetector.Listener l =
|
|
639
686
|
new com.marianhello.bgloc.sensor.SensorFusionDetector.Listener() {
|
|
640
687
|
@Override public void onSensorCrash(BackgroundLocation lastLocation, double impactG) {
|
|
688
|
+
// Buffer for next fix (sensor events fire async to GPS).
|
|
689
|
+
try {
|
|
690
|
+
org.json.JSONObject ev = new org.json.JSONObject();
|
|
691
|
+
ev.put("type", "possibleCrash");
|
|
692
|
+
ev.put("value", impactG);
|
|
693
|
+
ev.put("source", "sensor");
|
|
694
|
+
ev.put("time", System.currentTimeMillis());
|
|
695
|
+
enqueuePendingDrivingEvent(ev);
|
|
696
|
+
} catch (org.json.JSONException ignored) {}
|
|
641
697
|
Bundle b = new Bundle();
|
|
642
698
|
b.putInt("action", MSG_ON_POSSIBLE_CRASH);
|
|
643
699
|
if (lastLocation != null) b.putParcelable("payload", lastLocation);
|
|
@@ -646,6 +702,12 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
646
702
|
broadcastMessage(b);
|
|
647
703
|
}
|
|
648
704
|
@Override public void onPhoneUsageWhileDriving(BackgroundLocation lastLocation) {
|
|
705
|
+
try {
|
|
706
|
+
org.json.JSONObject ev = new org.json.JSONObject();
|
|
707
|
+
ev.put("type", "phoneUsageWhileDriving");
|
|
708
|
+
ev.put("time", System.currentTimeMillis());
|
|
709
|
+
enqueuePendingDrivingEvent(ev);
|
|
710
|
+
} catch (org.json.JSONException ignored) {}
|
|
649
711
|
Bundle b = new Bundle();
|
|
650
712
|
b.putInt("action", MSG_ON_PHONE_USAGE_WHILE_DRIVING);
|
|
651
713
|
if (lastLocation != null) b.putParcelable("payload", lastLocation);
|
|
@@ -673,6 +735,89 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
673
735
|
if (sIsRunning) mSensorFusion.start();
|
|
674
736
|
}
|
|
675
737
|
|
|
738
|
+
/** v4.3 — append a {type, time, ...extra} entry to the location's events array. */
|
|
739
|
+
private void attachDrivingEvent(BackgroundLocation loc, String type, org.json.JSONObject extra) {
|
|
740
|
+
if (loc == null || type == null) return;
|
|
741
|
+
try {
|
|
742
|
+
org.json.JSONObject ev = new org.json.JSONObject();
|
|
743
|
+
ev.put("type", type);
|
|
744
|
+
ev.put("time", System.currentTimeMillis());
|
|
745
|
+
if (extra != null) {
|
|
746
|
+
java.util.Iterator<String> keys = extra.keys();
|
|
747
|
+
while (keys.hasNext()) {
|
|
748
|
+
String k = keys.next();
|
|
749
|
+
ev.put(k, extra.opt(k));
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
loc.addDrivingEvent(ev);
|
|
753
|
+
} catch (org.json.JSONException ignored) {}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/** v4.5.1 — acquire a short, time-bounded wake lock when wakeLockMode is 'posting'. */
|
|
757
|
+
private void acquireWakeLockForPosting() {
|
|
758
|
+
if (mWakeLock == null || mConfig == null) return;
|
|
759
|
+
String mode = mConfig.getWakeLockMode() != null ? mConfig.getWakeLockMode() : "posting";
|
|
760
|
+
if (!"posting".equals(mode)) return;
|
|
761
|
+
try {
|
|
762
|
+
// Bounded: SQLite write + HTTP POST should finish well within 30 s.
|
|
763
|
+
if (!mWakeLock.isHeld()) mWakeLock.acquire(30_000L);
|
|
764
|
+
} catch (Throwable ignored) { /* best-effort */ }
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/** v4.4 — read current device battery via sticky broadcast and stamp it onto the location.
|
|
768
|
+
* No permission required. Sticky broadcast returns instantly without blocking.
|
|
769
|
+
* v4.4.1: route through the application context to bypass our own registerReceiver()
|
|
770
|
+
* override (which forces RECEIVER_NOT_EXPORTED + handler — incompatible with sticky-only reads). */
|
|
771
|
+
private void attachBatterySnapshot(BackgroundLocation loc) {
|
|
772
|
+
if (loc == null) return;
|
|
773
|
+
try {
|
|
774
|
+
android.content.IntentFilter filter = new android.content.IntentFilter(android.content.Intent.ACTION_BATTERY_CHANGED);
|
|
775
|
+
android.content.Intent batteryStatus = getApplicationContext().registerReceiver(null, filter);
|
|
776
|
+
if (batteryStatus == null) return;
|
|
777
|
+
int level = batteryStatus.getIntExtra(android.os.BatteryManager.EXTRA_LEVEL, -1);
|
|
778
|
+
int scale = batteryStatus.getIntExtra(android.os.BatteryManager.EXTRA_SCALE, -1);
|
|
779
|
+
if (level >= 0 && scale > 0) {
|
|
780
|
+
loc.setBatteryLevel((int) Math.round(level * 100.0 / scale));
|
|
781
|
+
}
|
|
782
|
+
int status = batteryStatus.getIntExtra(android.os.BatteryManager.EXTRA_STATUS, -1);
|
|
783
|
+
boolean charging = (status == android.os.BatteryManager.BATTERY_STATUS_CHARGING
|
|
784
|
+
|| status == android.os.BatteryManager.BATTERY_STATUS_FULL);
|
|
785
|
+
loc.setCharging(charging);
|
|
786
|
+
} catch (Throwable ignored) { /* best-effort; never fail the fix */ }
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/** v4.3 — drain pending events (those fired without a simultaneous fix) onto this location.
|
|
790
|
+
* v4.4.1: drop entries older than PENDING_DRIVING_EVENTS_TTL_MS so we don't anexar an event
|
|
791
|
+
* whose context (location, speed, etc.) is no longer relevant. */
|
|
792
|
+
private void flushPendingDrivingEvents(BackgroundLocation loc) {
|
|
793
|
+
if (loc == null) return;
|
|
794
|
+
long now = System.currentTimeMillis();
|
|
795
|
+
synchronized (mPendingDrivingEvents) {
|
|
796
|
+
int n = mPendingDrivingEvents.length();
|
|
797
|
+
if (n == 0) return;
|
|
798
|
+
for (int i = 0; i < n; i++) {
|
|
799
|
+
org.json.JSONObject ev = mPendingDrivingEvents.optJSONObject(i);
|
|
800
|
+
if (ev == null) continue;
|
|
801
|
+
long t = ev.optLong("time", now);
|
|
802
|
+
if (now - t <= PENDING_DRIVING_EVENTS_TTL_MS) {
|
|
803
|
+
loc.addDrivingEvent(ev);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
for (int i = n - 1; i >= 0; i--) mPendingDrivingEvents.remove(i);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/** v4.4.1 — append to pending events with cap (oldest evicted). */
|
|
811
|
+
private void enqueuePendingDrivingEvent(org.json.JSONObject ev) {
|
|
812
|
+
if (ev == null) return;
|
|
813
|
+
synchronized (mPendingDrivingEvents) {
|
|
814
|
+
while (mPendingDrivingEvents.length() >= PENDING_DRIVING_EVENTS_MAX) {
|
|
815
|
+
mPendingDrivingEvents.remove(0);
|
|
816
|
+
}
|
|
817
|
+
mPendingDrivingEvents.put(ev);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
676
821
|
@Override
|
|
677
822
|
public synchronized void startForegroundService() {
|
|
678
823
|
start();
|
|
@@ -725,8 +870,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
725
870
|
* Required before starting a location foreground service on API 34+.
|
|
726
871
|
*/
|
|
727
872
|
private boolean hasLocationPermission() {
|
|
728
|
-
|
|
729
|
-
|
|
873
|
+
// v4.5.1 — ContextCompat handles API < 23 (permissions granted at install time).
|
|
874
|
+
return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
875
|
+
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|
730
876
|
}
|
|
731
877
|
|
|
732
878
|
/**
|
|
@@ -791,16 +937,32 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
791
937
|
mProvider.onCommand(LocationProvider.CMD_SWITCH_MODE,
|
|
792
938
|
LocationProvider.FOREGROUND_MODE);
|
|
793
939
|
}
|
|
794
|
-
// Android 14+ (API 34): type
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
940
|
+
// Android 14+ (API 34): type is required. Android 12-13 (API 31-33): type accepted (preferred).
|
|
941
|
+
// FOREGROUND_SERVICE_TYPE_LOCATION = 0x00000008. Resolve from merged manifest first;
|
|
942
|
+
// if reflection fails or manifest merge missed the attribute, fall back to LOCATION
|
|
943
|
+
// hardcoded so the FGS still promotes (otherwise: no notification, no background tracking).
|
|
944
|
+
try {
|
|
945
|
+
if (Build.VERSION.SDK_INT >= 30) {
|
|
946
|
+
int type = getManifestForegroundServiceType();
|
|
947
|
+
if (type == 0) {
|
|
948
|
+
// Defensive fallback: every consumer of this plugin requires location FGS.
|
|
949
|
+
// Logging at warn so the failure is visible without breaking the service.
|
|
950
|
+
logger.warn("Manifest foregroundServiceType unreadable; defaulting to LOCATION (0x8). "
|
|
951
|
+
+ "Verify merged AndroidManifest has foregroundServiceType=\"location\"." );
|
|
952
|
+
type = 0x00000008; // ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
|
|
953
|
+
}
|
|
954
|
+
super.startForeground(NOTIFICATION_ID, notification, type);
|
|
955
|
+
} else {
|
|
956
|
+
super.startForeground(NOTIFICATION_ID, notification);
|
|
957
|
+
}
|
|
958
|
+
} catch (Throwable t) {
|
|
959
|
+
logger.error("startForeground threw {}; retrying without type", t.getMessage());
|
|
960
|
+
try {
|
|
961
|
+
super.startForeground(NOTIFICATION_ID, notification);
|
|
962
|
+
} catch (Throwable t2) {
|
|
963
|
+
logger.error("startForeground retry failed: {}", t2.getMessage());
|
|
799
964
|
return;
|
|
800
965
|
}
|
|
801
|
-
super.startForeground(NOTIFICATION_ID, notification, type);
|
|
802
|
-
} else {
|
|
803
|
-
super.startForeground(NOTIFICATION_ID, notification);
|
|
804
966
|
}
|
|
805
967
|
mIsInForeground = true;
|
|
806
968
|
scheduleNotificationUpdater();
|
|
@@ -955,6 +1117,26 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
955
1117
|
if (!equalsDrivingEvents(prevDe, newDe)) {
|
|
956
1118
|
configureDrivingDetector();
|
|
957
1119
|
}
|
|
1120
|
+
// v4.5.1 — hot-reload wakeLockMode: when transitioning between always /
|
|
1121
|
+
// posting / none, the existing permanent lock (if any) must be released,
|
|
1122
|
+
// or a new permanent lock acquired. Without this, switching mode at runtime
|
|
1123
|
+
// either leaked CPU or left the service running without the requested lock.
|
|
1124
|
+
String prevWl = currentConfig.getWakeLockMode() != null ? currentConfig.getWakeLockMode() : "posting";
|
|
1125
|
+
String newWl = mConfig.getWakeLockMode() != null ? mConfig.getWakeLockMode() : "posting";
|
|
1126
|
+
if (!prevWl.equals(newWl) && mWakeLock != null) {
|
|
1127
|
+
if ("always".equals(newWl)) {
|
|
1128
|
+
if (!mWakeLock.isHeld()) {
|
|
1129
|
+
try { mWakeLock.acquire(); logger.debug("Wake lock acquired (hot-reload → always)"); }
|
|
1130
|
+
catch (Throwable t) { logger.warn("Wake lock acquire failed", t); }
|
|
1131
|
+
}
|
|
1132
|
+
} else {
|
|
1133
|
+
// 'posting' or 'none' — release any permanent lock; per-fix lock continues to work via acquireWakeLockForPosting().
|
|
1134
|
+
if (mWakeLock.isHeld()) {
|
|
1135
|
+
try { mWakeLock.release(); logger.debug("Wake lock released (hot-reload → {})", newWl); }
|
|
1136
|
+
catch (Throwable t) { logger.warn("Wake lock release failed", t); }
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
958
1140
|
}
|
|
959
1141
|
}
|
|
960
1142
|
});
|
|
@@ -1022,15 +1204,19 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
1022
1204
|
|
|
1023
1205
|
@Override
|
|
1024
1206
|
public void onLocation(BackgroundLocation location) {
|
|
1207
|
+
// v4.5.1: in 'posting' wake-lock mode, hold the CPU briefly so SQLite writes + HTTP
|
|
1208
|
+
// POST finish before the system returns to deep sleep. 30s ceiling — plenty for a fix.
|
|
1209
|
+
acquireWakeLockForPosting();
|
|
1025
1210
|
mLastLocationTime = System.currentTimeMillis();
|
|
1026
1211
|
mLastReceivedLocation = location;
|
|
1027
1212
|
sLastReceivedLocation = location;
|
|
1028
1213
|
|
|
1029
|
-
// v4.0 Phase 6: feed the driver-insights state machine
|
|
1214
|
+
// v4.0 Phase 6: feed the driver-insights state machine on the *raw* location so speed/bearing
|
|
1215
|
+
// come straight from the sensors. Listener attaches events to this same instance.
|
|
1030
1216
|
if (mDrivingDetector != null) {
|
|
1031
1217
|
mDrivingDetector.onLocation(location);
|
|
1032
1218
|
}
|
|
1033
|
-
// v4.2 Phase 8: keep sensor pipeline aware of the latest fix.
|
|
1219
|
+
// v4.2 Phase 8: keep sensor pipeline aware of the latest raw fix.
|
|
1034
1220
|
if (mSensorFusion != null) {
|
|
1035
1221
|
mSensorFusion.setLastLocation(location);
|
|
1036
1222
|
}
|
|
@@ -1056,12 +1242,38 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
1056
1242
|
}
|
|
1057
1243
|
logger.debug("New location {}", location.toString());
|
|
1058
1244
|
|
|
1245
|
+
// v4.5.1 — events were attached to the RAW location above (so detector heuristics see real
|
|
1246
|
+
// speed/bearing). If transformLocation() returns a NEW instance, we'd lose those events
|
|
1247
|
+
// and the battery snapshot. Solution: copy them across to the transformed instance below.
|
|
1248
|
+
org.json.JSONArray rawEvents = location.getDrivingEvents();
|
|
1249
|
+
Integer rawBatteryLevel = null;
|
|
1250
|
+
Boolean rawIsCharging = null;
|
|
1251
|
+
|
|
1059
1252
|
location = transformLocation(location);
|
|
1060
1253
|
if (location == null) {
|
|
1061
1254
|
logger.debug("Skipping location as requested by the locationTransform");
|
|
1062
1255
|
return;
|
|
1063
1256
|
}
|
|
1064
1257
|
|
|
1258
|
+
// Re-attach events to the transformed location if the transform produced a new instance.
|
|
1259
|
+
if (rawEvents != null && rawEvents.length() > 0 && location.getDrivingEvents() != rawEvents) {
|
|
1260
|
+
try {
|
|
1261
|
+
org.json.JSONArray copy = new org.json.JSONArray(rawEvents.toString());
|
|
1262
|
+
for (int i = 0; i < copy.length(); i++) {
|
|
1263
|
+
org.json.JSONObject ev = copy.optJSONObject(i);
|
|
1264
|
+
if (ev != null) location.addDrivingEvent(ev);
|
|
1265
|
+
}
|
|
1266
|
+
} catch (org.json.JSONException ignored) {}
|
|
1267
|
+
}
|
|
1268
|
+
// v4.3: drain pending events (providerChange/sensor crash/phone usage) onto the post-transform
|
|
1269
|
+
// instance so they always reach the backend.
|
|
1270
|
+
flushPendingDrivingEvents(location);
|
|
1271
|
+
// v4.4: stamp device battery snapshot onto the *transformed* location so it survives any
|
|
1272
|
+
// user-supplied locationTransform that creates a new instance.
|
|
1273
|
+
if (mConfig == null || !Boolean.FALSE.equals(mConfig.getIncludeBattery())) {
|
|
1274
|
+
attachBatterySnapshot(location);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1065
1277
|
Bundle bundle = new Bundle();
|
|
1066
1278
|
bundle.putInt("action", MSG_ON_LOCATION);
|
|
1067
1279
|
bundle.putParcelable("payload", location);
|
|
@@ -1091,6 +1303,11 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
1091
1303
|
logger.debug("Skipping location as requested by the locationTransform");
|
|
1092
1304
|
return;
|
|
1093
1305
|
}
|
|
1306
|
+
// v4.5.1 — same enrichment as regular fixes: drain pending events and stamp battery.
|
|
1307
|
+
flushPendingDrivingEvents(location);
|
|
1308
|
+
if (mConfig == null || !Boolean.FALSE.equals(mConfig.getIncludeBattery())) {
|
|
1309
|
+
attachBatterySnapshot(location);
|
|
1310
|
+
}
|
|
1094
1311
|
|
|
1095
1312
|
Bundle bundle = new Bundle();
|
|
1096
1313
|
bundle.putInt("action", MSG_ON_STATIONARY);
|
|
@@ -1190,7 +1407,15 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
|
|
|
1190
1407
|
|
|
1191
1408
|
@Override
|
|
1192
1409
|
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
|
|
1193
|
-
|
|
1410
|
+
// v4.5.1 — RECEIVER_NOT_EXPORTED flag is required on Android 13+ (API 33) for non-system
|
|
1411
|
+
// broadcasts and the 5-arg overload exists only from API 26. Guard for older OSs.
|
|
1412
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
1413
|
+
return super.registerReceiver(receiver, filter, null, mServiceHandler, Context.RECEIVER_NOT_EXPORTED);
|
|
1414
|
+
}
|
|
1415
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
1416
|
+
return super.registerReceiver(receiver, filter, null, mServiceHandler);
|
|
1417
|
+
}
|
|
1418
|
+
return super.registerReceiver(receiver, filter);
|
|
1194
1419
|
}
|
|
1195
1420
|
|
|
1196
1421
|
@Override
|
package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java
CHANGED
|
@@ -7,6 +7,8 @@ import android.content.pm.PackageManager;
|
|
|
7
7
|
import android.os.Build;
|
|
8
8
|
import android.util.Log;
|
|
9
9
|
|
|
10
|
+
import androidx.core.content.ContextCompat;
|
|
11
|
+
|
|
10
12
|
import com.marianhello.bgloc.Config;
|
|
11
13
|
|
|
12
14
|
public class LocationServiceProxy implements LocationService, LocationServiceInfo {
|
|
@@ -100,8 +102,9 @@ public class LocationServiceProxy implements LocationService, LocationServiceInf
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
private boolean hasLocationPermission() {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
// v4.5.1 — ContextCompat handles API < 23 safely.
|
|
106
|
+
return ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
107
|
+
|| ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
@Override
|