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

Commit 434af556 authored by Guillaume Jacquart's avatar Guillaume Jacquart Committed by Nishith Khanna
Browse files

feat:2400: fake location from AdvancedPrivacy ContentProvider.

parent cfe0f426
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -258,6 +258,7 @@ package android {
    field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS";
    field @FlaggedApi("com.android.server.feature.flags.enable_read_dropbox_permission") public static final String READ_DROPBOX_DATA = "android.permission.READ_DROPBOX_DATA";
    field public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
    field public static final String READ_FAKE_LOCATION_SETTINGS = "foundation.e.permission.READ_FAKE_LOCATION_SETTINGS";
    field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA";
    field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
    field public static final String READ_LOGS = "android.permission.READ_LOGS";
+2 −0
Original line number Diff line number Diff line
@@ -1627,3 +1627,5 @@ UnflaggedApi: android.view.inputmethod.InlineSuggestionsRequest.Builder#setClien
    New API must be flagged with @FlaggedApi: method android.view.inputmethod.InlineSuggestionsRequest.Builder.setClientSupported(boolean)
UnflaggedApi: android.view.inputmethod.InlineSuggestionsRequest.Builder#setServiceSupported(boolean):
    New API must be flagged with @FlaggedApi: method android.view.inputmethod.InlineSuggestionsRequest.Builder.setServiceSupported(boolean)
UnflaggedApi: android.Manifest.permission#READ_FAKE_LOCATION_SETTINGS:
    New API must be flagged with @FlaggedApi: field android.Manifest.permission.READ_FAKE_LOCATION_SETTINGS
+5 −0
Original line number Diff line number Diff line
@@ -8996,6 +8996,11 @@
    <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
                android:protectionLevel="signature"/>
    
    <!-- /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" />

    <!-- @SystemApi
        @FlaggedApi("android.media.tv.flags.kids_mode_tvdb_sharing")
        This permission is required when accessing information related to
+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;
    }
}
+4 −0
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.FgThread;
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;
@@ -351,6 +352,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