summaryrefslogtreecommitdiff
path: root/fakelocation/src/main/java/foundation/e
diff options
context:
space:
mode:
Diffstat (limited to 'fakelocation/src/main/java/foundation/e')
-rw-r--r--fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt117
-rw-r--r--fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt112
2 files changed, 229 insertions, 0 deletions
diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt
new file mode 100644
index 0000000..43a4545
--- /dev/null
+++ b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 E FOUNDATION
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacymodules.fakelocation
+
+import android.content.Context
+import android.content.Context.LOCATION_SERVICE
+import android.location.Criteria
+import android.location.Location
+import android.location.LocationManager
+import android.os.Build
+import android.os.SystemClock
+import android.util.Log
+
+/**
+ * Implementation of the functionality of fake location.
+ * All of them are available for normal application, so just one version is enough.
+ *
+ * @param context an Android context, to retrieve system services for example.
+ */
+class FakeLocationModule(protected val context: Context) {
+
+ /**
+ * List of all the Location provider that will be mocked.
+ */
+ private val providers = listOf(LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER)
+
+ /**
+ * Handy accessor to the locationManager service.
+ * We avoid getting it on module initialization to wait for the context to be ready.
+ */
+ private val locationManager: LocationManager get() =
+ context.getSystemService(LOCATION_SERVICE) as LocationManager
+
+ /**
+ * @see IFakeLocationModule.startFakeLocation
+ */
+ @Synchronized
+ fun startFakeLocation() {
+ providers.forEach { provider ->
+ try {
+ locationManager.removeTestProvider(provider)
+ } catch(e: Exception) {
+ Log.d("FakeLocationModule", "Test provider $provider already removed.")
+ }
+
+ locationManager.addTestProvider(
+ provider,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ true,
+ Criteria.POWER_LOW, Criteria.ACCURACY_FINE)
+ locationManager.setTestProviderEnabled(provider, true)
+ }
+ }
+
+ fun setFakeLocation(latitude: Double, longitude: Double) {
+ context.startService(FakeLocationService.buildFakeLocationIntent(context, latitude, longitude))
+ }
+
+ internal fun setTestProviderLocation(latitude: Double, longitude: Double) {
+ providers.forEach { provider ->
+ val location = Location(provider)
+ location.latitude = latitude
+ location.longitude = longitude
+
+ // Set default value for all the other required fields.
+ location.altitude = 3.0
+ location.time = System.currentTimeMillis()
+ location.speed = 0.01f
+ location.bearing = 1f
+ location.accuracy = 3f
+ location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ location.bearingAccuracyDegrees = 0.1f
+ location.verticalAccuracyMeters = 0.1f
+ location.speedAccuracyMetersPerSecond = 0.01f
+ }
+
+ locationManager.setTestProviderLocation(provider, location)
+ }
+ }
+
+ /**
+ * @see IFakeLocationModule.stopFakeLocation
+ */
+ fun stopFakeLocation() {
+ context.stopService(FakeLocationService.buildStopIntent(context))
+ providers.forEach { provider ->
+ try {
+ locationManager.setTestProviderEnabled(provider, false)
+ locationManager.removeTestProvider(provider)
+ } catch (e: Exception) {
+ Log.d("FakeLocationModule", "Test provider $provider already removed.")
+ }
+ }
+ }
+}
diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt
new file mode 100644
index 0000000..1337ddd
--- /dev/null
+++ b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 E FOUNDATION
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacymodules.fakelocation
+
+
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.os.CountDownTimer
+import android.os.IBinder
+import android.util.Log
+
+class FakeLocationService: Service() {
+
+ enum class Actions {
+ START_FAKE_LOCATION
+ }
+
+ companion object {
+ private const val PERIOD_LOCATION_UPDATE = 1000L
+ private const val PERIOD_UPDATES_SERIE = 2 * 60 * 1000L
+
+ private const val PARAM_LATITUDE = "PARAM_LATITUDE"
+ private const val PARAM_LONGITUDE = "PARAM_LONGITUDE"
+
+ fun buildFakeLocationIntent(context: Context, latitude: Double, longitude: Double): Intent {
+ return Intent(context, FakeLocationService::class.java).apply {
+ action = Actions.START_FAKE_LOCATION.name
+ putExtra(PARAM_LATITUDE, latitude)
+ putExtra(PARAM_LONGITUDE, longitude)
+ }
+ }
+
+ fun buildStopIntent(context: Context) = Intent(context, FakeLocationService::class.java)
+ }
+
+ private lateinit var fakeLocationModule: FakeLocationModule
+
+ private var countDownTimer: CountDownTimer? = null
+
+ private var fakeLocation: Pair<Double, Double>? = null
+
+ override fun onCreate() {
+ super.onCreate()
+ fakeLocationModule = FakeLocationModule(applicationContext)
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ intent?.let {
+ when (it.action?.let { str -> Actions.valueOf(str) }) {
+ Actions.START_FAKE_LOCATION -> {
+
+ fakeLocation = Pair(
+ it.getDoubleExtra(PARAM_LATITUDE, 0.0),
+ it.getDoubleExtra(PARAM_LONGITUDE, 0.0)
+ )
+ initTimer()
+ }
+ else -> {}
+ }
+ }
+
+ return START_STICKY
+ }
+
+ override fun onDestroy() {
+ countDownTimer?.cancel()
+ super.onDestroy()
+ }
+
+
+ private fun initTimer() {
+ countDownTimer?.cancel()
+ countDownTimer = object: CountDownTimer(PERIOD_UPDATES_SERIE, PERIOD_LOCATION_UPDATE) {
+ override fun onTick(millisUntilFinished: Long) {
+ fakeLocation?.let {
+ try {
+ fakeLocationModule.setTestProviderLocation(
+ it.first,
+ it.second
+ )
+ } catch (e: Exception) {
+ Log.d("FakeLocationService", "setting fake location", e)
+ }
+ }
+ }
+
+ override fun onFinish() {
+ initTimer()
+ }
+ }.start()
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return null
+ }
+}