diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6be3dbc00a2a24b8a15f38d80863c4d18629955c..efda280494c7298ca47ecdff60f8dd5738406979 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -45,6 +45,16 @@
android:windowSoftInputMode="adjustResize"
tools:replace="android:icon,android:label,android:theme"
>
+
+
+
+
+
+ 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?,
+ selection: String?,
+ selectionArgs: Array?,
+ 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?): Int {
+ // ReadOnly content provider
+ return 0
+ }
+
+ override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array?): Int {
+ // ReadOnly content provider
+ return 0
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bbf4744ec2a5be5ecd9c46c9289911c87b96f0de..026a7516eaf5f1cc979e345df8159181861fbee9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -17,7 +17,7 @@
~ along with this program. If not, see .
-->
- Advanced Privacy
+ Advanced Privacy_POC
diff --git a/fakelocation/fakelocationdemo/build.gradle b/fakelocation/fakelocationdemo/build.gradle
index 49b4c3a3bc238b212a78cad7ffd122e53ae9971e..8e109bf5514aee86a6004832c78b9318dde51235 100644
--- a/fakelocation/fakelocationdemo/build.gradle
+++ b/fakelocation/fakelocationdemo/build.gradle
@@ -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
}
diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt
index 78543e61263356509fd83c13793d433bc358f753..62df5518865c78a1a4ff48e3b047ee6dce28670b 100644
--- a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt
+++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt
@@ -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 {
+ 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 {
+ 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)
+ }
}
diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/CallerIdentity.java b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/CallerIdentity.java
new file mode 100644
index 0000000000000000000000000000000000000000..b29fc91045d67b88ae97630a041d411fecce999a
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/CallerIdentity.java
@@ -0,0 +1,25 @@
+package foundation.e.privacymodules.fakelocationdemo.aosp;
+
+//import android.location.util.identity.CallerIdentity;
+
+
+public class CallerIdentity {
+
+ private int mUid;
+
+ private String mPackageName;
+
+ public CallerIdentity(int uid, String packageName) {
+ mUid = uid;
+ mPackageName = packageName;
+
+ }
+ public int getUid() {
+ return mUid;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+}
diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/FakeLocationResolver.java b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/FakeLocationResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..b80d9cef0d2ebbf88cac2149c130b8e6b5b7fbb1
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/FakeLocationResolver.java
@@ -0,0 +1,114 @@
+package foundation.e.privacymodules.fakelocationdemo.aosp;
+
+import android.content.Context;
+import android.location.Location;
+//import android.location.LocationResult;
+//import android.location.util.identity.CallerIdentity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class FakeLocationResolver {
+ private static final String FAKE_LOCATIONS_URI = "content://foundation.e.advancedprivacy.fakelocations";
+
+ private static final String PARAM_UID = "uid";
+ private static final String PARAM_LATITUDE = "latitude";
+ private static final String PARAM_LONGITUDE = "longitude";
+
+ public static LocationResult fakeLocations(Context context, LocationResult baseLocations, CallerIdentity identity) {
+ int uid = identity.getUid();
+ String packageName = identity.getPackageName();
+
+ if (context == null || packageName == null || uid < 0) {
+ Log.w("AP-FakeLocation", "FakeLocationResolver::fakeLocations invalid parameters");
+ return baseLocations;
+ }
+ FakeLocation latLon = getFakeLocation(context, packageName, uid);
+ if (latLon == null) return baseLocations;
+
+ return LocationResult.wrap(overrideLatLons( baseLocations.asList(), latLon));
+ }
+
+ public static Location fakeLocation(Context context, Location baseLocation, CallerIdentity identity) {
+ int uid = identity.getUid();
+ String packageName = identity.getPackageName();
+
+ if (context == null || packageName == null || uid < 0) {
+ Log.w("AP-FakeLocation", "FakeLocationResolver::fakeLocation invalid parameters");
+ return baseLocation;
+ }
+ FakeLocation latLon = getFakeLocation(context, packageName, uid);
+ if (latLon == null) return baseLocation;
+
+ return overrideLatLons( baseLocation, latLon);
+ }
+
+ public static boolean hasFakeLocation(Context context, CallerIdentity identity) {
+ int uid = identity.getUid();
+ String packageName = identity.getPackageName();
+
+ if (context == null || packageName == null || uid < 0) {
+ Log.w("AP-FakeLocation", "FakeLocationResolver::fakeLocation invalid parameters");
+ return false;
+ }
+ FakeLocation latLon = getFakeLocation(context, packageName, uid);
+ if (latLon == null) return false;
+
+ return true;
+
+ }
+
+ private static class FakeLocation {
+ Double latitude;
+ Double longitude;
+
+ public FakeLocation(Double latitude, Double longitude) {
+ this.latitude = latitude;
+ this.longitude = longitude;
+ }
+ }
+
+ private static FakeLocation getFakeLocation(Context context, String packageName, int uid) {
+ try {
+ Bundle extra = new Bundle();
+ extra.putInt(PARAM_UID, uid);
+ Bundle result = context.getContentResolver().call(
+ Uri.parse(FAKE_LOCATIONS_URI),
+ "",
+ packageName,
+ extra
+ );
+
+ if (result != null && result.containsKey(PARAM_LATITUDE) && result.containsKey(PARAM_LONGITUDE)) {
+ return new FakeLocation(result.getDouble(PARAM_LATITUDE), result.getDouble(PARAM_LONGITUDE));
+ }
+ } catch(Exception e) {
+ Log.w("AP-FakeLocation", "Can't getFakeLocation", e);
+ }
+ return null;
+ }
+
+ private static List overrideLatLons(List baseLocations, FakeLocation latLon) {
+ ArrayList fakedLocations = new ArrayList(baseLocations.size());
+ for (Location location: baseLocations) {
+ Location fakedLocation = overrideLatLons(location, latLon);
+
+ fakedLocations.add(fakedLocation);
+ }
+ return fakedLocations;
+ }
+
+ private static Location overrideLatLons(Location baseLocation, FakeLocation latLon) {
+ Location fakedLocation = new Location(baseLocation);
+ fakedLocation.setLatitude(latLon.latitude);
+ fakedLocation.setLongitude(latLon.longitude);
+ fakedLocation.setAltitude(3.0);
+ fakedLocation.setSpeed(0.01f);
+
+ return fakedLocation;
+ }
+}
diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/LocationResult.java b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/LocationResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..16c454d2765989847d3fd3812435162e9709fe86
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/aosp/LocationResult.java
@@ -0,0 +1,22 @@
+package foundation.e.privacymodules.fakelocationdemo.aosp;
+
+import android.location.Location;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class LocationResult {
+ private ArrayList mLocations;
+
+ public LocationResult(ArrayList locations) {
+ mLocations = locations;
+ }
+ public static LocationResult wrap(List locations) {
+ return new LocationResult(new ArrayList<>(locations));
+ }
+
+ public List asList() {
+ return Collections.unmodifiableList(mLocations);
+ }
+}
diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/microg/FakeLocationResolver.kt b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/microg/FakeLocationResolver.kt
new file mode 100644
index 0000000000000000000000000000000000000000..37f3352ac8182bc599bf19658950db5771c02d68
--- /dev/null
+++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/microg/FakeLocationResolver.kt
@@ -0,0 +1,55 @@
+package foundation.e.privacymodules.fakelocationdemo.microg
+
+import android.content.Context
+import android.location.Location
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+
+data class ClientIdentity(val packageName: String, val uid: Int)
+object FakeLocationResolver {
+ private val FAKE_LOCATIONS_URI = "content://foundation.e.advancedprivacy.fakelocations"
+
+ private val PARAM_UID = "uid"
+ private val PARAM_LATITUDE = "latitude"
+ private val PARAM_LONGITUDE = "longitude"
+
+ private data class FakeLocation(var latitude: Double, var longitude: Double)
+
+ fun fakeLocations(context: Context, identity: ClientIdentity, baseLocations: List): List {
+ Log.w("AP-FakeLocation", "FakeLocationResolver::fakeLocations ${identity.packageName}")
+ val latLon =
+ getFakeLocation(context, identity.packageName, identity.uid) ?: return baseLocations
+ Log.w("AP-FakeLocation", "FakeLocationResolver::fakeLocation ${identity.packageName} has fakelocation: $latLon")
+
+ return baseLocations.map { overrideLatLon(it, latLon) }
+ }
+
+ private fun getFakeLocation(context: Context, packageName: String, uid: Int): FakeLocation? {
+ return runCatching {
+ context.contentResolver.call(
+ Uri.parse(FAKE_LOCATIONS_URI),
+ "",
+ packageName,
+ Bundle().apply {
+ putInt(PARAM_UID, uid)
+ }
+ )?.let {
+ FakeLocation(
+ it.getDouble(PARAM_LATITUDE),
+ it.getDouble(PARAM_LONGITUDE)
+ )
+ }
+ }.getOrNull()
+ }
+
+ private fun overrideLatLon(baseLocation: Location, latLont: FakeLocation): Location {
+ val location = Location(baseLocation)
+ location.latitude = latLont.latitude
+ location.longitude = latLont.longitude
+ location.altitude = 3.0
+ location.speed = 0.01f
+
+ return location
+ }
+}
diff --git a/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml b/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml
index 33fce699a4bc61e33b29f66650f19e19d290445c..cb5cd2c203f9cf36fccb016ef610f5ad769dece0 100644
--- a/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml
+++ b/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml
@@ -15,15 +15,8 @@
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see .
-->
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
\ No newline at end of file
+
\ No newline at end of file