Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e74201e3 authored by Guillaume Jacquart's avatar Guillaume Jacquart
Browse files

Embed fake location module

parent 48333f65
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ PrivacyCentral needs to be installed as system app and whitelisting in order to
        ```bash
        adb push privapp-permissions-foundation.e.eprivacymoduledemo.xml /system/etc/permissions/
        ```
1. Allow the fake location service to run in background. Add     <allow-in-power-save package="foundation.e.privacymoduledemo" /> in the file /system/etc/permissions/platform.xml . 

1. Reboot the device
    ```shell
    adb reboot
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ android {
        minSdkVersion 24
        targetSdkVersion 29
        versionCode 3
        versionName "0.3.2"
        versionName "0.4.1"
        consumerProguardFiles "consumer-rules.pro"
    }

+99 −0
Original line number Diff line number Diff line
package foundation.e.privacymodules.location

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): IFakeLocationModule {

    /**
     * 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
     */
    override 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)
        }
    }

    override 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
     */
    override 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.")
            }
        }
    }
}
+94 −0
Original line number Diff line number Diff line
package foundation.e.privacymodules.location


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 (Actions.valueOf(it.action)) {
                Actions.START_FAKE_LOCATION -> {

                    fakeLocation = Pair(
                        it.getDoubleExtra(PARAM_LATITUDE, 0.0),
                        it.getDoubleExtra(PARAM_LONGITUDE, 0.0)
                    )
                    initTimer()
                }
            }
        }

        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
    }
}