Loading services/core/java/com/android/server/location/fudger/LocationFudger.java +27 −1 Original line number Original line Diff line number Diff line Loading @@ -68,6 +68,17 @@ public class LocationFudger { private static final double MAX_LATITUDE = private static final double MAX_LATITUDE = 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR); 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR); // The average edge length in km of an S2 cell, indexed by S2 levels 0 to // 13. Level 13 is the highest level used for coarsening. // This approximation assumes the S2 cells are squares. // For density-based coarsening, we use the edge to set the accuracy of the // coarsened location. // The values are from http://s2geometry.io/resources/s2cell_statistics.html // We take square root of the average area. private static final float[] S2_CELL_AVG_EDGE_PER_LEVEL = new float[] { 9220.14f, 4610.07f, 2305.04f, 1152.52f, 576.26f, 288.13f, 144.06f, 72.03f, 36.02f, 20.79f, 9f, 5.05f, 2.25f, 1.13f, 0.57f}; private final float mAccuracyM; private final float mAccuracyM; private final Clock mClock; private final Clock mClock; private final Random mRandom; private final Random mRandom; Loading Loading @@ -194,12 +205,14 @@ public class LocationFudger { // The new algorithm is applied if and only if (1) the flag is on, (2) the cache has been // 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 // set, and (3) the cache has successfully queried the provider for the default coarsening // value. // value. float accuracy = mAccuracyM; if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations() if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations() && cacheCopy != null) { && cacheCopy != null) { if (cacheCopy.hasDefaultValue()) { if (cacheCopy.hasDefaultValue()) { // New algorithm that snaps to the center of a S2 cell. // New algorithm that snaps to the center of a S2 cell. int level = cacheCopy.getCoarseningLevel(latitude, longitude); int level = cacheCopy.getCoarseningLevel(latitude, longitude); coarsened = snapToCenterOfS2Cell(latitude, longitude, level); coarsened = snapToCenterOfS2Cell(latitude, longitude, level); accuracy = getS2CellApproximateEdge(level); } else { } else { // Try to fetch the default value. The answer won't come in time, but will be used // Try to fetch the default value. The answer won't come in time, but will be used // for the next location to coarsen. // for the next location to coarsen. Loading @@ -214,7 +227,7 @@ public class LocationFudger { coarse.setLatitude(coarsened[LAT_INDEX]); coarse.setLatitude(coarsened[LAT_INDEX]); coarse.setLongitude(coarsened[LNG_INDEX]); coarse.setLongitude(coarsened[LNG_INDEX]); coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy())); coarse.setAccuracy(Math.max(accuracy, coarse.getAccuracy())); synchronized (this) { synchronized (this) { mCachedFineLocation = fine; mCachedFineLocation = fine; Loading @@ -224,6 +237,19 @@ public class LocationFudger { return coarse; return coarse; } } // Returns the average edge length in meters of an S2 cell at the given // level. This is computed as if the S2 cell were a square. We do not need // an exact value, only a rough approximation. @VisibleForTesting protected float getS2CellApproximateEdge(int level) { if (level < 0) { level = 0; } else if (level >= S2_CELL_AVG_EDGE_PER_LEVEL.length) { level = S2_CELL_AVG_EDGE_PER_LEVEL.length - 1; } return S2_CELL_AVG_EDGE_PER_LEVEL[level] * 1000; } // quantize location by snapping to a grid. this is the primary means of obfuscation. it // 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 // 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). // long as you are not sitting on a grid boundary, which the random offsets mitigate). Loading services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java +27 −0 Original line number Original line Diff line number Diff line Loading @@ -271,4 +271,31 @@ public class LocationFudgerTest { assertThat(center[0]).isEqualTo(expected[0]); assertThat(center[0]).isEqualTo(expected[0]); assertThat(center[1]).isEqualTo(expected[1]); assertThat(center[1]).isEqualTo(expected[1]); } } @Test public void getS2CellApproximateEdge_returnsCorrectRadius() { int level = 10; float radius = mFudger.getS2CellApproximateEdge(level); assertThat(radius).isEqualTo(9000); // in meters } @Test public void getS2CellApproximateEdge_doesNotThrow() { int level = -1; mFudger.getS2CellApproximateEdge(level); // No exception thrown. } @Test public void getS2CellApproximateEdge_doesNotThrow2() { int level = 14; mFudger.getS2CellApproximateEdge(level); // No exception thrown. } } } Loading
services/core/java/com/android/server/location/fudger/LocationFudger.java +27 −1 Original line number Original line Diff line number Diff line Loading @@ -68,6 +68,17 @@ public class LocationFudger { private static final double MAX_LATITUDE = private static final double MAX_LATITUDE = 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR); 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR); // The average edge length in km of an S2 cell, indexed by S2 levels 0 to // 13. Level 13 is the highest level used for coarsening. // This approximation assumes the S2 cells are squares. // For density-based coarsening, we use the edge to set the accuracy of the // coarsened location. // The values are from http://s2geometry.io/resources/s2cell_statistics.html // We take square root of the average area. private static final float[] S2_CELL_AVG_EDGE_PER_LEVEL = new float[] { 9220.14f, 4610.07f, 2305.04f, 1152.52f, 576.26f, 288.13f, 144.06f, 72.03f, 36.02f, 20.79f, 9f, 5.05f, 2.25f, 1.13f, 0.57f}; private final float mAccuracyM; private final float mAccuracyM; private final Clock mClock; private final Clock mClock; private final Random mRandom; private final Random mRandom; Loading Loading @@ -194,12 +205,14 @@ public class LocationFudger { // The new algorithm is applied if and only if (1) the flag is on, (2) the cache has been // 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 // set, and (3) the cache has successfully queried the provider for the default coarsening // value. // value. float accuracy = mAccuracyM; if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations() if (Flags.populationDensityProvider() && Flags.densityBasedCoarseLocations() && cacheCopy != null) { && cacheCopy != null) { if (cacheCopy.hasDefaultValue()) { if (cacheCopy.hasDefaultValue()) { // New algorithm that snaps to the center of a S2 cell. // New algorithm that snaps to the center of a S2 cell. int level = cacheCopy.getCoarseningLevel(latitude, longitude); int level = cacheCopy.getCoarseningLevel(latitude, longitude); coarsened = snapToCenterOfS2Cell(latitude, longitude, level); coarsened = snapToCenterOfS2Cell(latitude, longitude, level); accuracy = getS2CellApproximateEdge(level); } else { } else { // Try to fetch the default value. The answer won't come in time, but will be used // Try to fetch the default value. The answer won't come in time, but will be used // for the next location to coarsen. // for the next location to coarsen. Loading @@ -214,7 +227,7 @@ public class LocationFudger { coarse.setLatitude(coarsened[LAT_INDEX]); coarse.setLatitude(coarsened[LAT_INDEX]); coarse.setLongitude(coarsened[LNG_INDEX]); coarse.setLongitude(coarsened[LNG_INDEX]); coarse.setAccuracy(Math.max(mAccuracyM, coarse.getAccuracy())); coarse.setAccuracy(Math.max(accuracy, coarse.getAccuracy())); synchronized (this) { synchronized (this) { mCachedFineLocation = fine; mCachedFineLocation = fine; Loading @@ -224,6 +237,19 @@ public class LocationFudger { return coarse; return coarse; } } // Returns the average edge length in meters of an S2 cell at the given // level. This is computed as if the S2 cell were a square. We do not need // an exact value, only a rough approximation. @VisibleForTesting protected float getS2CellApproximateEdge(int level) { if (level < 0) { level = 0; } else if (level >= S2_CELL_AVG_EDGE_PER_LEVEL.length) { level = S2_CELL_AVG_EDGE_PER_LEVEL.length - 1; } return S2_CELL_AVG_EDGE_PER_LEVEL[level] * 1000; } // quantize location by snapping to a grid. this is the primary means of obfuscation. it // 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 // 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). // long as you are not sitting on a grid boundary, which the random offsets mitigate). Loading
services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java +27 −0 Original line number Original line Diff line number Diff line Loading @@ -271,4 +271,31 @@ public class LocationFudgerTest { assertThat(center[0]).isEqualTo(expected[0]); assertThat(center[0]).isEqualTo(expected[0]); assertThat(center[1]).isEqualTo(expected[1]); assertThat(center[1]).isEqualTo(expected[1]); } } @Test public void getS2CellApproximateEdge_returnsCorrectRadius() { int level = 10; float radius = mFudger.getS2CellApproximateEdge(level); assertThat(radius).isEqualTo(9000); // in meters } @Test public void getS2CellApproximateEdge_doesNotThrow() { int level = -1; mFudger.getS2CellApproximateEdge(level); // No exception thrown. } @Test public void getS2CellApproximateEdge_doesNotThrow2() { int level = 14; mFudger.getS2CellApproximateEdge(level); // No exception thrown. } } }