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

Commit 1a5bacc5 authored by Ludovic Barman's avatar Ludovic Barman Committed by Android (Google) Code Review
Browse files

Merge "Coarse locations: Add logging to LocationFudger and LocationManagerService" into main

parents 0ce99b1c 563888b2
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -537,11 +538,16 @@ public class LocationManagerService extends ILocationManager.Stub implements
        }

        if (Flags.populationDensityProvider()) {
            long startTime = System.currentTimeMillis();
            setProxyPopulationDensityProvider(
                    ProxyPopulationDensityProvider.createAndRegister(mContext));
            int duration = (int) (System.currentTimeMillis() - startTime);
            if (mPopulationDensityProvider == null) {
                Log.e(TAG, "no population density provider found");
            }
            FrameworkStatsLog.write(FrameworkStatsLog.POPULATION_DENSITY_PROVIDER_LOADING_REPORTED,
                /* provider_null= */ (mPopulationDensityProvider == null),
                /* provider_start_time_millis= */ duration);
        }
        if (mPopulationDensityProvider != null && Flags.densityBasedCoarseLocations()) {
            setLocationFudgerCache(new LocationFudgerCache(mPopulationDensityProvider));
+1 −1
Original line number Diff line number Diff line
@@ -203,7 +203,7 @@ public class LocationFudger {
            } else {
                // Try to fetch the default value. The answer won't come in time, but will be used
                // for the next location to coarsen.
                cacheCopy.fetchDefaultCoarseningLevelIfNeeded();
                cacheCopy.onDefaultCoarseningLevelNotSet();
                // Previous algorithm that snaps to a grid of width mAccuracyM.
                coarsened = snapToGrid(latitude, longitude);
            }
+59 −3
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.geometry.S2CellIdUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.location.provider.proxy.ProxyPopulationDensityProvider;

import java.util.Objects;
@@ -68,6 +69,15 @@ public class LocationFudgerCache {
    // The provider that asynchronously provides what is stored in the cache.
    private final ProxyPopulationDensityProvider mPopulationDensityProvider;

    // If two calls to logDensityBasedLocsUsed are made in an interval shorter than this value,
    // the second is dropped.
    protected static final int LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS = 1000 * 60 * 10; // 10 min

    // The system time at which the last query to logDensityBasedLocsUsed was made.
    // Initialized to -LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS, so even if made at time 0, the
    // first call succeeds.
    private long mLastQueryToLogDensityBasedLocsUsedMs = -LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS;

    private static String sTAG = "LocationFudgerCache";

    public LocationFudgerCache(@NonNull ProxyPopulationDensityProvider provider) {
@@ -76,11 +86,17 @@ public class LocationFudgerCache {
        asyncFetchDefaultCoarseningLevel();
    }

    /** If the cache's default coarsening value hasn't been set, asynchronously fetches it. */
    public void fetchDefaultCoarseningLevelIfNeeded() {
    /**
     * Called by the LocationFudger when a query couldn't be fulfilled because the cache isn't set.
     */
    public void onDefaultCoarseningLevelNotSet() {
        if (!hasDefaultValue()) {
            asyncFetchDefaultCoarseningLevel();
        }
        logDensityBasedLocsUsed(/* nowMs=*/ System.currentTimeMillis(),
            /* skippedNoDefault= */ true,
            /* isCacheHit= */ false,
            /* defaultCoarseningLevel= */ -1);
    }

    /** Returns true if the cache has successfully received a default value from the provider. */
@@ -101,16 +117,45 @@ public class LocationFudgerCache {
            asyncFetchDefaultCoarseningLevel();
        }
        Long s2CellId = readCacheForLatLng(latitudeDegrees, longitudeDegrees);
        int defaultLevel = getDefaultCoarseningLevel();
        if (s2CellId == null) {
            // Asynchronously queries the density from the provider. The answer won't come in time,
            // but it will update the cache for the following queries.
            refreshCache(latitudeDegrees, longitudeDegrees);

            return getDefaultCoarseningLevel();
            logDensityBasedLocsUsed(/* nowMs=*/ System.currentTimeMillis(),
                    /* skippedNoDefault= */ false,
                    /* isCacheHit= */ false,
                    /* defaultCoarseningLevel= */ defaultLevel);
            return defaultLevel;
        }
        logDensityBasedLocsUsed(/* nowMs=*/ System.currentTimeMillis(),
            /* skippedNoDefault= */ false,
            /* isCacheHit= */ true,
            /* defaultCoarseningLevel= */ defaultLevel);
        return S2CellIdUtils.getLevel(s2CellId);
    }

    /**
     * A simple wrapper around FrameworkStatsLog.write() that rate-limits the calls.
     * Returns true on success, false if the call was dropped.
     */
    protected boolean logDensityBasedLocsUsed(long nowMs, boolean skippedNoDefault,
            boolean isCacheHit, int defaultCoarseningLevel) {

        if (nowMs - mLastQueryToLogDensityBasedLocsUsedMs
                < LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS) {
            return false;
        }
        mLastQueryToLogDensityBasedLocsUsedMs = nowMs;

        FrameworkStatsLog.write(FrameworkStatsLog.DENSITY_BASED_COARSE_LOCATIONS_USAGE_REPORTED,
                /* skipped_no_default= */ skippedNoDefault,
                /* is_cache_hit= */ isCacheHit,
                /* default_coarsening_level= */ defaultCoarseningLevel);
        return true;
    }

    /**
     * If the cache contains the current location, returns the corresponding S2 cell id.
     * Otherwise, returns null.
@@ -176,15 +221,26 @@ public class LocationFudgerCache {
     *  Queries the population density provider and store the result in the cache.
     */
    private void refreshCache(double latitude, double longitude) {
        long startTime = System.currentTimeMillis();
        IS2CellIdsCallback callback = new IS2CellIdsCallback.Stub() {
            @Override
            public void onResult(long[] s2CellIds) {
                int durationMs = (int) (System.currentTimeMillis() - startTime);
                FrameworkStatsLog.write(
                        FrameworkStatsLog.DENSITY_BASED_COARSE_LOCATIONS_PROVIDER_QUERY_REPORTED,
                        /* query_duration_millis= */ durationMs,
                        /* is_error= */ false);
                addToCache(s2CellIds);
            }

            @Override
            public void onError() {
                Log.e(sTAG, "could not get population density");
                int durationMs = (int) (System.currentTimeMillis() - startTime);
                FrameworkStatsLog.write(
                        FrameworkStatsLog.DENSITY_BASED_COARSE_LOCATIONS_PROVIDER_QUERY_REPORTED,
                        /* query_duration_millis= */ durationMs,
                        /* is_error= */ true);
            }
        };
        mPopulationDensityProvider.getCoarsenedS2Cells(latitude, longitude, MAX_CACHE_SIZE - 1,
+56 −4
Original line number Diff line number Diff line
@@ -281,7 +281,7 @@ public class LocationFudgerCacheTest {
    }

    @Test
    public void fetchDefaultCoarseningLevelIfNeeded_withDefaultValue_doesNotQueryProvider()
    public void onDefaultCoarseningLevelNotSet_withDefaultValue_doesNotQueryProvider()
            throws RemoteException {
        // Arrange.
        ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
@@ -297,14 +297,14 @@ public class LocationFudgerCacheTest {
        assertThat(cache.hasDefaultValue()).isTrue();

        // Act.
        cache.fetchDefaultCoarseningLevelIfNeeded();
        cache.onDefaultCoarseningLevelNotSet();

        // Assert. The method is not called again.
        verify(provider, times(1)).getDefaultCoarseningLevel(any());
    }

    @Test
    public void fetchDefaultCoarseningLevelIfNeeded_withoutDefaultValue_doesQueryProvider()
    public void onDefaultCoarseningLevelNotSet_withoutDefaultValue_doesQueryProvider()
            throws RemoteException {
        // Arrange.
        ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
@@ -320,7 +320,7 @@ public class LocationFudgerCacheTest {
        assertThat(cache.hasDefaultValue()).isFalse();

        // Act.
        cache.fetchDefaultCoarseningLevelIfNeeded();
        cache.onDefaultCoarseningLevelNotSet();

        // Assert. The method is called again.
        verify(provider, times(2)).getDefaultCoarseningLevel(any());
@@ -383,4 +383,56 @@ public class LocationFudgerCacheTest {
        assertThat(cache.getCoarseningLevel(latlngs[size - 1][0], latlngs[size - 1][1]))
                .isEqualTo(0);
    }

    @Test
    public void logDensityBasedLocsUsed_rateLimitsTheSecondCall() {
        // To avoid having to mock the logger, logDensityBasedLocsUsed returns a boolean indicating
        // if the log was successful or rate-limited.

        ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
        LocationFudgerCache cache = new LocationFudgerCache(provider);
        boolean skippedNoDefault = false;
        boolean isCacheHit = false;
        int defaultCoarseningLevel = 3;
        long time1 = 0;
        // 7 min later. Can be any value < time1 + LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS
        long time2 = time1 + 7 * 60 * 1000;

        boolean success1 = cache.logDensityBasedLocsUsed(time1, skippedNoDefault, isCacheHit,
                defaultCoarseningLevel);
        boolean success2 = cache.logDensityBasedLocsUsed(time2, skippedNoDefault, isCacheHit,
                defaultCoarseningLevel);

        assertThat(success1).isTrue();  // log OK
        assertThat(success2).isFalse();  // dropped
    }

    @Test
    public void logDensityBasedLocsUsed_rateLimitOf3rdCall_isNotAffectedByDropped2ndCall() {
        // To avoid having to mock the logger, logDensityBasedLocsUsed returns a boolean indicating
        // if the log was successful or rate-limited.

        ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
        LocationFudgerCache cache = new LocationFudgerCache(provider);
        boolean skippedNoDefault = false;
        boolean isCacheHit = false;
        int defaultCoarseningLevel = 3;
        long time1 = 0;
        // 7 min later. Can be any value < time1 + LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS
        long time2 = time1 + 7 * 60 * 1000;
        // 11 min later. Can be any value >= time1 + LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS
        long time3 = time1 + 11 * 60 * 1000;

        boolean success1 = cache.logDensityBasedLocsUsed(time1, skippedNoDefault, isCacheHit,
                defaultCoarseningLevel);
        boolean success2 = cache.logDensityBasedLocsUsed(time2, skippedNoDefault, isCacheHit,
                defaultCoarseningLevel);
        boolean success3 = cache.logDensityBasedLocsUsed(time3, skippedNoDefault, isCacheHit,
                defaultCoarseningLevel);

        assertThat(success1).isTrue();  // log OK
        assertThat(success2).isFalse();  // dropped
        assertThat(success3).isTrue();  // log OK
    }

}
+1 −1
Original line number Diff line number Diff line
@@ -232,7 +232,7 @@ public class LocationFudgerTest {

        mFudger.createCoarse(createLocation("test", mRandom));

        verify(cache).fetchDefaultCoarseningLevelIfNeeded();
        verify(cache).onDefaultCoarseningLevelNotSet();
    }

    @Test