Loading core/res/AndroidManifest.xml +5 −0 Original line number Diff line number Diff line Loading @@ -5859,6 +5859,11 @@ <permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" android:protectionLevel="internal|role" /> <!-- /e/OS addition: read fake location set by the user in AdvancedPrivacy. --> <permission android:name="foundation.e.permission.READ_FAKE_LOCATION_SETTINGS" android:protectionLevel="signature" /> <uses-permission android:name="foundation.e.permission.READ_FAKE_LOCATION_SETTINGS" /> <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> Loading location/java/android/location/Location.java +1 −1 Original line number Diff line number Diff line Loading @@ -1305,7 +1305,7 @@ public class Location implements Parcelable { * @see LocationManager#addTestProvider */ public boolean isMock() { return false; return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0; } /** Loading services/core/java/com/android/server/location/FakeLocationResolver.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 com.android.server.location; import android.annotation.NonNull; import android.annotation.Nullable; 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 { public static final String TAG = "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 @Nullable LocationResult fakeLocations( @NonNull Context context, @Nullable LocationResult baseLocations, @NonNull CallerIdentity identity) { final int uid = identity.getUid(); final String packageName = identity.getPackageName(); if (baseLocations == null || packageName == null || uid < 0) { Log.w(TAG, "FakeLocationResolver::fakeLocations invalid parameters"); return baseLocations; } final FakeLocation latLon = getFakeLocation(context, packageName, uid); if (latLon == null) return baseLocations; return LocationResult.wrap(overrideLatLon(baseLocations.asList(), latLon)); } public static @Nullable Location fakeLocation( @NonNull Context context, @Nullable Location baseLocation, @NonNull CallerIdentity identity) { final int uid = identity.getUid(); final String packageName = identity.getPackageName(); if (baseLocation == null || packageName == null || uid < 0) { Log.w(TAG, "FakeLocationResolver::fakeLocation invalid parameters"); return baseLocation; } final FakeLocation latLon = getFakeLocation(context, packageName, uid); if (latLon == null) return baseLocation; return overrideLatLon(baseLocation, latLon); } public static boolean hasFakeLocation(@NonNull Context context, @NonNull CallerIdentity identity) { final int uid = identity.getUid(); final String packageName = identity.getPackageName(); if (packageName == null || uid < 0) { Log.w(TAG, "FakeLocationResolver::hasFakeLocation invalid parameters"); return false; } final 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 @Nullable FakeLocation getFakeLocation(@NonNull Context context, @NonNull String packageName, int uid) { try { final Bundle extra = new Bundle(); extra.putInt(PARAM_UID, uid); final 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(TAG, "Can't getFakeLocation", e); } return null; } private static @NonNull List<Location> overrideLatLon(@NonNull List<Location> baseLocations, @NonNull FakeLocation latLon) { final ArrayList<Location> fakedLocations = new ArrayList<Location>(baseLocations.size()); for (Location location: baseLocations) { Location fakedLocation = overrideLatLon(location, latLon); fakedLocations.add(fakedLocation); } return fakedLocations; } private static @NonNull Location overrideLatLon(@NonNull Location baseLocation, @NonNull FakeLocation latLon) { final Location fakedLocation = new Location(baseLocation); fakedLocation.setLatitude(latLon.latitude); fakedLocation.setLongitude(latLon.longitude); fakedLocation.setAltitude(3.0); fakedLocation.setSpeed(0.01f); return fakedLocation; } } services/core/java/com/android/server/location/geofence/GeofenceManager.java +4 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.server.PendingIntentUtils; import com.android.server.location.FakeLocationResolver; import com.android.server.location.LocationPermissions; import com.android.server.location.injector.Injector; import com.android.server.location.injector.LocationPermissionsHelper; Loading Loading @@ -298,6 +299,9 @@ public class GeofenceManager extends CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, AppOpsManager.toReceiverId(pendingIntent)); if (FakeLocationResolver.hasFakeLocation(mContext, identity)) { return; } final long ident = Binder.clearCallingIdentity(); try { Loading services/core/java/com/android/server/location/provider/LocationProviderManager.java +7 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.location.FakeLocationResolver; import com.android.server.location.LocationPermissions; import com.android.server.location.LocationPermissions.PermissionLevel; import com.android.server.location.fudger.LocationFudger; Loading Loading @@ -974,6 +975,8 @@ public class LocationProviderManager extends deliverLocationResult = locationResult; } deliverLocationResult = FakeLocationResolver.fakeLocations(mContext, deliverLocationResult, getIdentity()); listener.deliverOnLocationChanged(deliverLocationResult, mUseWakeLock ? mWakeLockReleaser : null); EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(), Loading Loading @@ -1277,6 +1280,8 @@ public class LocationProviderManager extends deliverLocationResult = locationResult; } deliverLocationResult = FakeLocationResolver.fakeLocations(mContext, deliverLocationResult, getIdentity()); // we currently don't hold a wakelock for getCurrentLocation deliveries listener.deliverOnLocationChanged(deliverLocationResult, null); EVENT_LOG.logProviderDeliveredLocations(mName, Loading Loading @@ -1656,6 +1661,8 @@ public class LocationProviderManager extends Long.MAX_VALUE), permissionLevel); location = FakeLocationResolver.fakeLocation(mContext, location, identity); if (location != null && identity.getPid() == Process.myPid()) { // if delivering to the same process, make a copy of the location first (since // location is mutable) Loading Loading
core/res/AndroidManifest.xml +5 −0 Original line number Diff line number Diff line Loading @@ -5859,6 +5859,11 @@ <permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA" android:protectionLevel="internal|role" /> <!-- /e/OS addition: read fake location set by the user in AdvancedPrivacy. --> <permission android:name="foundation.e.permission.READ_FAKE_LOCATION_SETTINGS" android:protectionLevel="signature" /> <uses-permission android:name="foundation.e.permission.READ_FAKE_LOCATION_SETTINGS" /> <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> Loading
location/java/android/location/Location.java +1 −1 Original line number Diff line number Diff line Loading @@ -1305,7 +1305,7 @@ public class Location implements Parcelable { * @see LocationManager#addTestProvider */ public boolean isMock() { return false; return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0; } /** Loading
services/core/java/com/android/server/location/FakeLocationResolver.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 com.android.server.location; import android.annotation.NonNull; import android.annotation.Nullable; 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 { public static final String TAG = "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 @Nullable LocationResult fakeLocations( @NonNull Context context, @Nullable LocationResult baseLocations, @NonNull CallerIdentity identity) { final int uid = identity.getUid(); final String packageName = identity.getPackageName(); if (baseLocations == null || packageName == null || uid < 0) { Log.w(TAG, "FakeLocationResolver::fakeLocations invalid parameters"); return baseLocations; } final FakeLocation latLon = getFakeLocation(context, packageName, uid); if (latLon == null) return baseLocations; return LocationResult.wrap(overrideLatLon(baseLocations.asList(), latLon)); } public static @Nullable Location fakeLocation( @NonNull Context context, @Nullable Location baseLocation, @NonNull CallerIdentity identity) { final int uid = identity.getUid(); final String packageName = identity.getPackageName(); if (baseLocation == null || packageName == null || uid < 0) { Log.w(TAG, "FakeLocationResolver::fakeLocation invalid parameters"); return baseLocation; } final FakeLocation latLon = getFakeLocation(context, packageName, uid); if (latLon == null) return baseLocation; return overrideLatLon(baseLocation, latLon); } public static boolean hasFakeLocation(@NonNull Context context, @NonNull CallerIdentity identity) { final int uid = identity.getUid(); final String packageName = identity.getPackageName(); if (packageName == null || uid < 0) { Log.w(TAG, "FakeLocationResolver::hasFakeLocation invalid parameters"); return false; } final 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 @Nullable FakeLocation getFakeLocation(@NonNull Context context, @NonNull String packageName, int uid) { try { final Bundle extra = new Bundle(); extra.putInt(PARAM_UID, uid); final 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(TAG, "Can't getFakeLocation", e); } return null; } private static @NonNull List<Location> overrideLatLon(@NonNull List<Location> baseLocations, @NonNull FakeLocation latLon) { final ArrayList<Location> fakedLocations = new ArrayList<Location>(baseLocations.size()); for (Location location: baseLocations) { Location fakedLocation = overrideLatLon(location, latLon); fakedLocations.add(fakedLocation); } return fakedLocations; } private static @NonNull Location overrideLatLon(@NonNull Location baseLocation, @NonNull FakeLocation latLon) { final Location fakedLocation = new Location(baseLocation); fakedLocation.setLatitude(latLon.latitude); fakedLocation.setLongitude(latLon.longitude); fakedLocation.setAltitude(3.0); fakedLocation.setSpeed(0.01f); return fakedLocation; } }
services/core/java/com/android/server/location/geofence/GeofenceManager.java +4 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.server.PendingIntentUtils; import com.android.server.location.FakeLocationResolver; import com.android.server.location.LocationPermissions; import com.android.server.location.injector.Injector; import com.android.server.location.injector.LocationPermissionsHelper; Loading Loading @@ -298,6 +299,9 @@ public class GeofenceManager extends CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, AppOpsManager.toReceiverId(pendingIntent)); if (FakeLocationResolver.hasFakeLocation(mContext, identity)) { return; } final long ident = Binder.clearCallingIdentity(); try { Loading
services/core/java/com/android/server/location/provider/LocationProviderManager.java +7 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.location.FakeLocationResolver; import com.android.server.location.LocationPermissions; import com.android.server.location.LocationPermissions.PermissionLevel; import com.android.server.location.fudger.LocationFudger; Loading Loading @@ -974,6 +975,8 @@ public class LocationProviderManager extends deliverLocationResult = locationResult; } deliverLocationResult = FakeLocationResolver.fakeLocations(mContext, deliverLocationResult, getIdentity()); listener.deliverOnLocationChanged(deliverLocationResult, mUseWakeLock ? mWakeLockReleaser : null); EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(), Loading Loading @@ -1277,6 +1280,8 @@ public class LocationProviderManager extends deliverLocationResult = locationResult; } deliverLocationResult = FakeLocationResolver.fakeLocations(mContext, deliverLocationResult, getIdentity()); // we currently don't hold a wakelock for getCurrentLocation deliveries listener.deliverOnLocationChanged(deliverLocationResult, null); EVENT_LOG.logProviderDeliveredLocations(mName, Loading Loading @@ -1656,6 +1661,8 @@ public class LocationProviderManager extends Long.MAX_VALUE), permissionLevel); location = FakeLocationResolver.fakeLocation(mContext, location, identity); if (location != null && identity.getPid() == Process.myPid()) { // if delivering to the same process, make a copy of the location first (since // location is mutable) Loading