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

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

7530: POC fake location from AdvancedPrivacy ContentProvider.

parent f7df4585
Loading
Loading
Loading
Loading
+137 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 MURENA SAS
 *
 * 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;

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.telecom.Call;
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;

        Log.d("AP-FakeLocation", "FakeLocationResolver::fakeLocation faked location for " + packageName);
        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;

        Log.d("AP-FakeLocation", "FakeLocationResolver::fakeLocation faked location for " + packageName);
        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;

        Log.d("AP-FakeLocation", "FakeLocationResolver::fakeLocation faked location for " + packageName);
        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<Location> overrideLatLons(List<Location> baseLocations, FakeLocation latLon) {
        ArrayList<Location> fakedLocations = new ArrayList<Location>(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;
    }
}
+9 −1
Original line number Diff line number Diff line
@@ -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;
@@ -52,6 +53,7 @@ import com.android.server.location.injector.UserInfoHelper.UserListener;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.PendingIntentListenerRegistration;


import java.util.Collection;
import java.util.Objects;

@@ -295,12 +297,18 @@ public class GeofenceManager extends
    public void addGeofence(Geofence geofence, PendingIntent pendingIntent, String packageName,
            @Nullable String attributionTag) {
        Log.d("AP-FakeLocation", "GeofenceManager::addGeofence " + packageName);
        // TODO: should we block geofence for blacklisted app, or fake it ?

        LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE);

        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName,
                attributionTag, AppOpsManager.toReceiverId(pendingIntent));

        if (FakeLocationResolver.hasFakeLocation(mContext, identity)) {
            Log.d("AP-FakeLocation", "GeofenceManager::addGeofence drop geofence registration for " + packageName);
            return;
        }


        final long ident = Binder.clearCallingIdentity();
        try {
            putRegistration(new GeofenceKey(pendingIntent, geofence),
+14 −10
Original line number Diff line number Diff line
@@ -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;
@@ -982,17 +983,18 @@ public class LocationProviderManager extends
                    // location is mutable)
                    LocationResult deliverLocationResult;
                    Log.d("AP-FakeLocation", "LocationProviderManager::LocationRegistration::acceptLocationChange, ListenerOperation::operate, fakeLocation");
                    CallerIdentity identity = getIdentity();
                    Location trueLoc = locationResult.getLastLocation();
                    if (trueLoc != null) {
                        Log.d("AP-FakeLocation", "LocationRegistration...operate, true location is " + trueLoc.longitude + " ; " + trueLoc.latitude);
                        Log.d("AP-FakeLocation", "LocationRegistration...operate, true location for " + identity.getPackageName() + " is " + trueLoc.longitude + " ; " + trueLoc.latitude);
                    }
                    CallerIdentity identity = getIdentity();
                    Log.d("AP-FakeLocation", "LocationRegistration...operate, fakeLocation for app" + identity.getPackageName());

                    LocationResult fakedLocationResult = FakeLocationResolver.fakeLocation(mContext, locationResult, getIdentity()));

                    if (getIdentity().getPid() == Process.myPid()) {
                        deliverLocationResult = locationResult.deepCopy();
                        deliverLocationResult = fakedLocationResult.deepCopy();
                    } else {
                        deliverLocationResult = locationResult;
                        deliverLocationResult = fakedLocationResult;
                    }

                    listener.deliverOnLocationChanged(deliverLocationResult,
@@ -1294,18 +1296,18 @@ public class LocationProviderManager extends
                    LocationResult deliverLocationResult;
                    Log.d("AP-FakeLocation", "LocationProviderManager::GetCurrentLocationListenerRegistration::acceptLocationChange, ListenerOperation::operate, fakeLocation");
                    Location trueLoc = locationResult.getLastLocation();
                    CallerIdentity identity = getIdentity();
                    if (trueLoc != null) {
                        Log.d("AP-FakeLocation", "GetCurrentLocationListenerRegistration...operate, true location is " + trueLoc.longitude + " ; " + trueLoc.latitude);
                        Log.d("AP-FakeLocation", "LocationRegistration...operate, true location for " + identity.getPackageName() + " is " + trueLoc.longitude + " ; " + trueLoc.latitude);
                    }
                    CallerIdentity identity = getIdentity();
                    Log.d("AP-FakeLocation", "GetCurrentLocationListenerRegistration...operate, fakeLocation for app" + identity.getPackageName());

                    LocationResult fakedLocationResult = FakeLocationResolver.fakeLocation(mContext, locationResult, getIdentity()));

                    LocationResult deliverLocationResult;
                    if (getIdentity().getPid() == Process.myPid() && locationResult != null) {
                        deliverLocationResult = locationResult.deepCopy();
                        deliverLocationResult = fakedLocationResult.deepCopy();
                    } else {
                        deliverLocationResult = locationResult;
                        deliverLocationResult = fakedLocationResult;
                    }

                    // we currently don't hold a wakelock for getCurrentLocation deliveries
@@ -1690,6 +1692,8 @@ public class LocationProviderManager extends
        String locStr = (location != null) location.longitude + " ; " + location.latitude else "NO LOC";
        Log.d("AP-FakeLocation", "LocatonProviderManager::getLastLocation " + identity.getPackageName() + " true location: " + locStr);

        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)