summaryrefslogtreecommitdiff
path: root/fakelocation/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'fakelocation/src/main')
-rw-r--r--fakelocation/src/main/AndroidManifest.xml32
-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
3 files changed, 261 insertions, 0 deletions
diff --git a/fakelocation/src/main/AndroidManifest.xml b/fakelocation/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5077c24
--- /dev/null
+++ b/fakelocation/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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/>.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="foundation.e.privacymodules.fakelocation"
+ >
+
+ <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
+ tools:ignore="MockLocation,ProtectedPermissions" />
+
+ <application>
+ <service android:name="foundation.e.privacymodules.fakelocation.FakeLocationService"
+ android:enabled="true" />
+ </application>
+</manifest> \ No newline at end of file
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
+ }
+}