Loading app/src/main/AndroidManifest.xml +10 −0 Original line number Diff line number Diff line Loading @@ -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" Loading app/src/main/java/foundation/e/advancedprivacy/FakeLocationContentProvider.kt 0 → 100644 +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 } } app/src/main/res/values/strings.xml +1 −1 Original line number Diff line number Diff line Loading @@ -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> Loading fakelocation/fakelocationdemo/build.gradle +3 −2 Original line number Diff line number Diff line Loading @@ -49,7 +49,7 @@ android { allWarningsAsErrors = false } buildFeatures { dataBinding true viewBinding true } namespace 'foundation.e.privacymodules.fakelocationdemo' } Loading @@ -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 } fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt +121 −10 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 { Loading @@ -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() { Loading @@ -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.") Loading @@ -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) } } Loading @@ -110,7 +155,7 @@ class MainActivity : AppCompatActivity() { } locationManager.removeUpdates(listener) binding.currentLocation = "no listening" binding.currentLocation.text = "no listening" } private fun startLocationUpdates() { Loading @@ -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) } Loading Loading @@ -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
app/src/main/AndroidManifest.xml +10 −0 Original line number Diff line number Diff line Loading @@ -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" Loading
app/src/main/java/foundation/e/advancedprivacy/FakeLocationContentProvider.kt 0 → 100644 +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 } }
app/src/main/res/values/strings.xml +1 −1 Original line number Diff line number Diff line Loading @@ -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> Loading
fakelocation/fakelocationdemo/build.gradle +3 −2 Original line number Diff line number Diff line Loading @@ -49,7 +49,7 @@ android { allWarningsAsErrors = false } buildFeatures { dataBinding true viewBinding true } namespace 'foundation.e.privacymodules.fakelocationdemo' } Loading @@ -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 }
fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt +121 −10 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 { Loading @@ -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() { Loading @@ -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.") Loading @@ -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) } } Loading @@ -110,7 +155,7 @@ class MainActivity : AppCompatActivity() { } locationManager.removeUpdates(listener) binding.currentLocation = "no listening" binding.currentLocation.text = "no listening" } private fun startLocationUpdates() { Loading @@ -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) } Loading Loading @@ -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) } }