diff options
| author | 2024-12-13 03:22:19 -0800 | |
|---|---|---|
| committer | 2024-12-13 03:22:19 -0800 | |
| commit | c3fc17ebaa433f3df0c00e69213e86a91fd6a35e (patch) | |
| tree | 1d14e6c350aef8db44293b35afca509314fe583c | |
| parent | a632f1469ebd8aaa62afe5e137fe2916d7b2be84 (diff) | |
| parent | afd9d8e38e8307d3f5aea0e726d96731bd332b35 (diff) | |
Merge "LocationFudger: cache must fetch default when missing" into main
4 files changed, 104 insertions, 18 deletions
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudger.java b/services/core/java/com/android/server/location/fudger/LocationFudger.java index 881538237746..bbd8aa1aac44 100644 --- a/services/core/java/com/android/server/location/fudger/LocationFudger.java +++ b/services/core/java/com/android/server/location/fudger/LocationFudger.java @@ -16,6 +16,9 @@ package com.android.server.location.fudger; +import static com.android.internal.location.geometry.S2CellIdUtils.LAT_INDEX; +import static com.android.internal.location.geometry.S2CellIdUtils.LNG_INDEX; + import android.annotation.FlaggedApi; import android.annotation.Nullable; import android.location.Location; @@ -184,31 +187,33 @@ public class LocationFudger { synchronized (this) { cacheCopy = mLocationFudgerCache; } - + double[] coarsened = new double[] {0.0, 0.0}; // TODO(b/381204398): To ensure a safe rollout, two algorithms co-exist. The first is the // new density-based algorithm, while the second is the traditional coarsening algorithm. // Once rollout is done, clean up the unused algorithm. + // The new algorithm is applied if and only if (1) the flag is on, (2) the cache has been + // set, and (3) the cache has successfully queried the provider for the default coarsening + // value. if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations() - && cacheCopy != null && cacheCopy.hasDefaultValue()) { - int level = cacheCopy.getCoarseningLevel(latitude, longitude); - double[] center = snapToCenterOfS2Cell(latitude, longitude, level); - latitude = center[S2CellIdUtils.LAT_INDEX]; - longitude = center[S2CellIdUtils.LNG_INDEX]; + && cacheCopy != null) { + if (cacheCopy.hasDefaultValue()) { + // New algorithm that snaps to the center of a S2 cell. + int level = cacheCopy.getCoarseningLevel(latitude, longitude); + coarsened = snapToCenterOfS2Cell(latitude, longitude, level); + } 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(); + // Previous algorithm that snaps to a grid of width mAccuracyM. + coarsened = snapToGrid(latitude, longitude); + } } else { - // quantize location by snapping to a grid. this is the primary means of obfuscation. it - // gives nice consistent results and is very effective at hiding the true location (as - // long as you are not sitting on a grid boundary, which the random offsets mitigate). - // - // note that we quantize the latitude first, since the longitude quantization depends on - // the latitude value and so leaks information about the latitude - double latGranularity = metersToDegreesLatitude(mAccuracyM); - latitude = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity); - double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude); - longitude = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity); + // Previous algorithm that snaps to a grid of width mAccuracyM. + coarsened = snapToGrid(latitude, longitude); } - coarse.setLatitude(latitude); - coarse.setLongitude(longitude); + coarse.setLatitude(coarsened[LAT_INDEX]); + coarse.setLongitude(coarsened[LNG_INDEX]); coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy())); synchronized (this) { @@ -219,6 +224,21 @@ public class LocationFudger { return coarse; } + // quantize location by snapping to a grid. this is the primary means of obfuscation. it + // gives nice consistent results and is very effective at hiding the true location (as + // long as you are not sitting on a grid boundary, which the random offsets mitigate). + // + // note that we quantize the latitude first, since the longitude quantization depends on + // the latitude value and so leaks information about the latitude + private double[] snapToGrid(double latitude, double longitude) { + double[] center = new double[] {0.0, 0.0}; + double latGranularity = metersToDegreesLatitude(mAccuracyM); + center[LAT_INDEX] = wrapLatitude(Math.round(latitude / latGranularity) * latGranularity); + double lonGranularity = metersToDegreesLongitude(mAccuracyM, latitude); + center[LNG_INDEX] = wrapLongitude(Math.round(longitude / lonGranularity) * lonGranularity); + return center; + } + @VisibleForTesting protected double[] snapToCenterOfS2Cell(double latDegrees, double lngDegrees, int level) { long leafCell = S2CellIdUtils.fromLatLngDegrees(latDegrees, lngDegrees); diff --git a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java index ce8bec8f0147..19ec38ce8ccc 100644 --- a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java +++ b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java @@ -76,6 +76,13 @@ public class LocationFudgerCache { asyncFetchDefaultCoarseningLevel(); } + /** If the cache's default coarsening value hasn't been set, asynchronously fetches it. */ + public void fetchDefaultCoarseningLevelIfNeeded() { + if (!hasDefaultValue()) { + asyncFetchDefaultCoarseningLevel(); + } + } + /** Returns true if the cache has successfully received a default value from the provider. */ public boolean hasDefaultValue() { synchronized (mLock) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java index 6b7eda26b945..c89048a06ce2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java @@ -280,6 +280,51 @@ public class LocationFudgerCacheTest { eq(POINT_IN_TIMES_SQUARE[1]), eq(numAdditionalCells), any()); } + @Test + public void fetchDefaultCoarseningLevelIfNeeded_withDefaultValue_doesNotQueryProvider() + throws RemoteException { + // Arrange. + ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class); + LocationFudgerCache cache = new LocationFudgerCache(provider); + + ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass( + IS2LevelCallback.class); + verify(provider, times(1)).getDefaultCoarseningLevel(argumentCaptor.capture()); + + IS2LevelCallback cb = argumentCaptor.getValue(); + cb.onResult(10); + + assertThat(cache.hasDefaultValue()).isTrue(); + + // Act. + cache.fetchDefaultCoarseningLevelIfNeeded(); + + // Assert. The method is not called again. + verify(provider, times(1)).getDefaultCoarseningLevel(any()); + } + + @Test + public void fetchDefaultCoarseningLevelIfNeeded_withoutDefaultValue_doesQueryProvider() + throws RemoteException { + // Arrange. + ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class); + LocationFudgerCache cache = new LocationFudgerCache(provider); + + ArgumentCaptor<IS2LevelCallback> argumentCaptor = ArgumentCaptor.forClass( + IS2LevelCallback.class); + verify(provider, times(1)).getDefaultCoarseningLevel(argumentCaptor.capture()); + + IS2LevelCallback cb = argumentCaptor.getValue(); + cb.onError(); + + assertThat(cache.hasDefaultValue()).isFalse(); + + // Act. + cache.fetchDefaultCoarseningLevelIfNeeded(); + + // Assert. The method is called again. + verify(provider, times(2)).getDefaultCoarseningLevel(any()); + } @Test public void locationFudgerCache_canContainUpToMaxSizeItems() { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java index 49ff041e10b7..2e4652e6f48b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java @@ -222,6 +222,20 @@ public class LocationFudgerTest { } @Test + public void testDensityBasedCoarsening_ifFeatureIsEnabledButNoDefaultValue_defaultIsFetched() { + mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS); + mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER); + LocationFudgerCache cache = mock(LocationFudgerCache.class); + doReturn(false).when(cache).hasDefaultValue(); + + mFudger.setLocationFudgerCache(cache); + + mFudger.createCoarse(createLocation("test", mRandom)); + + verify(cache).fetchDefaultCoarseningLevelIfNeeded(); + } + + @Test public void testDensityBasedCoarsening_ifFeatureIsEnabledAndDefaultIsSet_cacheIsUsed() { mSetFlagsRule.enableFlags(Flags.FLAG_DENSITY_BASED_COARSE_LOCATIONS); mSetFlagsRule.enableFlags(Flags.FLAG_POPULATION_DENSITY_PROVIDER); |