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

Commit ec705dd9 authored by Guillaume Jacquart's avatar Guillaume Jacquart Committed by Guillaume Jacquart
Browse files

7530: POC whitelist app from fake location with content provider

parent 4456175a
Loading
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -45,6 +45,16 @@
        android:windowSoftInputMode="adjustResize"
        tools:replace="android:icon,android:label,android:theme"
        >

        <!-- TODO: which protection could we use ? -->
        <provider
            android:name=".FakeLocationContentProvider"
            android:authorities="foundation.e.advancedprivacy.fakelocations"
            android:enabled="true"
            android:exported="true"
            />


        <receiver
            android:name="foundation.e.advancedprivacy.common.BootCompletedReceiver"
            android:exported="true"
+69 −0
Original line number Diff line number Diff line
package foundation.e.advancedprivacy

import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.util.Log
import foundation.e.advancedprivacy.dummy.CityDataSource

class FakeLocationContentProvider : ContentProvider() {
    private val PARAM_UID = "uid"
    private val PARAM_LATITUDE = "latitude"
    private val PARAM_LONGITUDE = "longitude"

    private val fakeLocations = mapOf(
        "com.generalmagic.magicearth" to CityDataSource.citiesLocationsList[0],
        "foundation.e.privacymodules.fakelocationdemo" to CityDataSource.citiesLocationsList[1],
        "com.google.android.apps.maps" to CityDataSource.citiesLocationsList[2],
        "foundation.e.advancedprivacy" to CityDataSource.citiesLocationsList[3],
        "com.mirfatif.mylocation" to CityDataSource.citiesLocationsList[0]
    )

    override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
        Log.d("AP-FakeLocation", "Called FakeLoc ContentProvider, for $arg")
        // val appUid = extras?.getInt(PARAM_UID, -1)?.takeIf { it != -1 }

        return fakeLocations.get(arg)?.let { (lat, lon) ->
            Bundle().apply {
                putDouble(PARAM_LATITUDE, lat.toDouble())
                putDouble(PARAM_LONGITUDE, lon.toDouble())
            }
        }
    }

    override fun onCreate(): Boolean {
        return true
    }

    override fun query(
        uri: Uri,
        projection: Array<out String>?,
        selection: String?,
        selectionArgs: Array<out String>?,
        sortOrder: String?
    ): Cursor? {
        // Use call instead
        return null
    }

    override fun getType(uri: Uri): String? {
        return "text/plain"
    }

    override fun insert(p0: Uri, p1: ContentValues?): Uri? {
        // ReadOnly content provider
        return null
    }

    override fun delete(p0: Uri, p1: String?, p2: Array<out String>?): Int {
        // ReadOnly content provider
        return 0
    }

    override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int {
        // ReadOnly content provider
        return 0
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
  ~ along with this program.  If not, see <https://www.gnu.org/licenses/>.
  -->
<resources>
    <string name="app_name" translatable="false">Advanced Privacy</string>
    <string name="app_name" translatable="false">Advanced Privacy_POC</string>

    <!-- Commons -->
    <string name="empty" translatable="false"> </string>
+3 −2
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ android {
        allWarningsAsErrors = false
    }
    buildFeatures {
        dataBinding true
        viewBinding true
    }
    namespace 'foundation.e.privacymodules.fakelocationdemo'
}
@@ -59,12 +59,13 @@ dependencies {
    implementation project(':fakelocation')
    implementation project(':permissionsstandalone')


    implementation (
        libs.androidx.core.ktx,
        libs.androidx.appcompat,
        libs.google.material,
    )

    implementation("com.google.android.gms:play-services-location:21.0.0")
    testImplementation libs.junit

}
+121 −10
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.Looper
import android.os.Process.myUid
import android.util.Log
import android.view.View
@@ -32,13 +33,18 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
import foundation.e.advancedprivacy.domain.entities.AppOpModes
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
import foundation.e.advancedprivacy.domain.entities.ProfileType
import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule
import foundation.e.advancedprivacy.permissions.externalinterfaces.PermissionsPrivacyModuleImpl
import foundation.e.privacymodules.fakelocationdemo.aosp.CallerIdentity
import foundation.e.privacymodules.fakelocationdemo.aosp.LocationResult
import foundation.e.privacymodules.fakelocationdemo.databinding.ActivityMainBinding
import foundation.e.privacymodules.fakelocationdemo.microg.ClientIdentity

class MainActivity : AppCompatActivity() {
    companion object {
@@ -63,11 +69,12 @@ class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        actionBar?.setDisplayHomeAsUpEnabled(true)

        binding.view = this
        setOnClickListeners()
    }

    override fun onResume() {
@@ -75,15 +82,28 @@ class MainActivity : AppCompatActivity() {
        updateData("")
    }

    private fun setOnClickListeners() {
        binding.permissionsButton.setOnClickListener(::onClickPermission)

        binding.trueLocationButton.setOnClickListener(::onClickReset)
        binding.parisButton.setOnClickListener(::onClickParis)
        binding.londonButton.setOnClickListener(::onClickLondon)
        binding.amsterdamButton.setOnClickListener(::onClickAmsterdam)
        binding.toggleListenLocation.setOnClickListener(::onClickToggleListenLocation)
        binding.fakeLocationConfigButton.setOnClickListener { onClickFakeLocationConfig() }
        binding.toggleListenGmsLocation.setOnClickListener { onClickToggleListenGMSLocation(binding.toggleListenGmsLocation.isChecked) }
    }

    private fun updateData(mockedLocation: String) {
        binding.granted = permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED
        binding.permissionsButton.isChecked =
            permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED

        binding.mockedLocation = mockedLocation
        binding.mockedLocation.text = mockedLocation
    }

    private val listener = object : LocationListener {
        override fun onLocationChanged(location: Location) {
            binding.currentLocation = "lat: ${location.latitude} - lon: ${location.longitude}"
            binding.currentLocation.text = "lat: ${location.latitude} - lon: ${location.longitude}"
        }

        @Deprecated("Deprecated since API 29, never called.")
@@ -92,11 +112,36 @@ class MainActivity : AppCompatActivity() {
        }

        override fun onProviderEnabled(provider: String) {
            binding.providerInfo = "onProdivderEnabled: $provider"
            binding.providerInfo.text = "onProdivderEnabled: $provider"
        }

        override fun onProviderDisabled(provider: String) {
            binding.providerInfo = "onProdivderDisabled: $provider"
            binding.providerInfo.text = "onProdivderDisabled: $provider"
        }
    }

    private val fusedLocationClient
        get() = LocationServices.getFusedLocationProviderClient(this)

    val locationRequest = LocationRequest.Builder(
        Priority.PRIORITY_HIGH_ACCURACY,
        // 1,5s is a good value for the UX while setting specific location.
        1500L
    ).build()

    private var localListener = com.google.android.gms.location.LocationListener { location ->
        binding.currentGmsLocation.text = "lat: ${location.latitude} - lon: ${location.longitude}"
    }

    fun onClickToggleListenGMSLocation(isChecked: Boolean) {
        if (isChecked) {
            fusedLocationClient.requestLocationUpdates(
                locationRequest,
                localListener,
                Looper.getMainLooper()
            )
        } else {
            fusedLocationClient.removeLocationUpdates(localListener)
        }
    }

@@ -110,7 +155,7 @@ class MainActivity : AppCompatActivity() {
        }

        locationManager.removeUpdates(listener)
        binding.currentLocation = "no listening"
        binding.currentLocation.text = "no listening"
    }

    private fun startLocationUpdates() {
@@ -124,7 +169,7 @@ class MainActivity : AppCompatActivity() {
                1f,
                listener
            )
            binding.currentLocation = "listening started"
            binding.currentLocation.text = "listening started"
        } catch (se: SecurityException) {
            Log.e(TAG, "Missing permission", se)
        }
@@ -214,4 +259,70 @@ class MainActivity : AppCompatActivity() {
    fun onClickAmsterdam(view: View?) {
        setFakeLocation(52.3547498, 4.8339211)
    }

    private fun testFakeLocationResolverMicroG(): List<Location> {
        return foundation.e.privacymodules.fakelocationdemo.microg.FakeLocationResolver.fakeLocations(
            this,
            ClientIdentity(packageName, myUid()),
            listOf(
                Location("gps").apply {
                    latitude = 1.0
                    longitude = 2.0
                }
            )
        )
    }

    private fun testFakeLocationsResolverAOSP(): List<Location> {
        val baseLocation = LocationResult(
            arrayListOf(
                Location("gps").apply {
                    latitude = 1.0
                    longitude = 2.0
                }
            )
        )

        val identity = CallerIdentity(myUid(), packageName)

        val result = foundation.e.privacymodules.fakelocationdemo.aosp.FakeLocationResolver
            .fakeLocations(this, baseLocation, identity)

        return result.asList()
    }

    private fun testFakeLocationResolverAOSP(): Location {
        val baseLocation = Location("gps").apply {
            latitude = 1.0
            longitude = 2.0
        }

        val identity = CallerIdentity(myUid(), packageName)

        val result = foundation.e.privacymodules.fakelocationdemo.aosp.FakeLocationResolver
            .fakeLocation(this, baseLocation, identity)

        return result
    }

    private fun testHasFakeLocationAOSP(): Boolean {
        val identity = CallerIdentity(myUid(), packageName)

        val result = foundation.e.privacymodules.fakelocationdemo.aosp.FakeLocationResolver
            .hasFakeLocation(this, identity)

        return result
    }

    private fun onClickFakeLocationConfig() {
        Log.d("DebugFakeLoc", "onClickFakeLocationConfig")
        val test = "microg: ${testFakeLocationResolverMicroG()}" +
            "\nAOSP: ${testFakeLocationsResolverAOSP()}" +
            "\nAOSP one: ${testFakeLocationResolverAOSP()}" +
            "\nAOSP has: ${testHasFakeLocationAOSP()}"

        binding.currentLocation.text = test

        Log.d("DebugFakeLoc", test)
    }
}
Loading