Population Density Provider: add cache hint
Adds a parameter to indicate the desired answer size when querying the coarsened cells. This allows the framework to indicate its cache size, and the provider to answer with the right amount of cells.
Tests:
- atest CtsLocationNoneTestCases:PopulationDensityProviderBaseTest
- atest FrameworksMockingServicesTests:LocationProviderManagerTest
- atest FrameworksMockingServicesTests:LocationFudgerTest
- atest FrameworksMockingServicesTests:LocationFudgerCacheTest
Test: manual atest on Pixel 7 pro (see above)
Bug: 381286551
Flag: android.location.flags.density_based_coarse_locations
Change-Id: Ib0d26e8ce0e1164ff30b45e2d3deddb67f68a83b
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index 8cd08d3..9478e35 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -645,7 +645,7 @@
@FlaggedApi("android.location.flags.population_density_provider") public abstract class PopulationDensityProviderBase {
ctor public PopulationDensityProviderBase(@NonNull android.content.Context, @NonNull String);
method @Nullable public final android.os.IBinder getBinder();
- method public abstract void onGetCoarsenedS2Cell(double, double, @NonNull android.os.OutcomeReceiver<long[],java.lang.Throwable>);
+ method public abstract void onGetCoarsenedS2Cells(double, double, @IntRange(from=0) int, @NonNull android.os.OutcomeReceiver<long[],java.lang.Throwable>);
method public abstract void onGetDefaultCoarseningLevel(@NonNull android.os.OutcomeReceiver<java.lang.Integer,java.lang.Throwable>);
field public static final String ACTION_POPULATION_DENSITY_PROVIDER = "com.android.location.service.PopulationDensityProvider";
}
diff --git a/location/java/android/location/provider/IPopulationDensityProvider.aidl b/location/java/android/location/provider/IPopulationDensityProvider.aidl
index 9b5cb5a..41fe500 100644
--- a/location/java/android/location/provider/IPopulationDensityProvider.aidl
+++ b/location/java/android/location/provider/IPopulationDensityProvider.aidl
@@ -35,11 +35,11 @@
void getDefaultCoarseningLevel(in IS2LevelCallback callback);
/**
- * Returns a list of IDs of the S2 cells to be used to coarsen a location. The answer should
+ * Requests a list of IDs of the S2 cells to be used to coarsen a location. The answer should
* contain at least one S2 cell, which should contain the requested location. Its level
- * represents the population density. Optionally, additional nearby cells can be also returned,
- * to assist in coarsening nearby locations.
+ * represents the population density. Optionally, if numAdditionalCells is greater than 0,
+ * additional nearby cells can be also returned, to assist in coarsening nearby locations.
*/
- void getCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees, in IS2CellIdsCallback
- callback);
+ void getCoarsenedS2Cells(double latitudeDegrees, double longitudeDegrees,
+ int numAdditionalCells, in IS2CellIdsCallback callback);
}
diff --git a/location/java/android/location/provider/PopulationDensityProviderBase.java b/location/java/android/location/provider/PopulationDensityProviderBase.java
index 3907516..0177cf8 100644
--- a/location/java/android/location/provider/PopulationDensityProviderBase.java
+++ b/location/java/android/location/provider/PopulationDensityProviderBase.java
@@ -17,6 +17,7 @@
package android.location.provider;
import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -89,17 +90,18 @@
* Called upon receiving a new request for population density at a specific latitude/longitude,
* expressed in degrees.
* The answer is at least one S2CellId corresponding to the coarsening level at the specified
- * location. This must be the first element of the result array. Optionally, additional nearby
- * S2CellIds can be returned. One use for the optional nearby cells is when the client has a
- * local cache that needs to be filled with the local area around a certain latitude/longitude.
- * The callback {@link OutcomeReceiver#onResult} should be called with the result; or, in case
- * an error occurs, {@link OutcomeReceiver#onError} should be called.
- * The callback is single-use, calling more than any one of these two methods throws an
- * AssertionException.
+ * location. This must be the first element of the result array. Optionally, if
+ * numAdditionalCells is greater than zero, additional nearby S2CellIds can be returned. One use
+ * for the optional nearby cells is when the client has a local cache that needs to be filled
+ * with the local area around a certain latitude/longitude. The callback
+ * {@link OutcomeReceiver#onResult} should be called with the result; or, in case an error
+ * occurs, {@link OutcomeReceiver#onError} should be called. The callback is single-use, calling
+ * more than any one of these two methods throws an AssertionException.
*
* @param callback A single-use callback that either returns S2CellIds, or an error.
*/
- public abstract void onGetCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees,
+ public abstract void onGetCoarsenedS2Cells(double latitudeDegrees, double longitudeDegrees,
+ @IntRange(from = 0) int numAdditionalCells,
@NonNull OutcomeReceiver<long[], Throwable> callback);
private final class Service extends IPopulationDensityProvider.Stub {
@@ -119,10 +121,10 @@
}
@Override
- public void getCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees,
- @NonNull IS2CellIdsCallback callback) {
+ public void getCoarsenedS2Cells(double latitudeDegrees, double longitudeDegrees,
+ int numAdditionalCells, @NonNull IS2CellIdsCallback callback) {
try {
- onGetCoarsenedS2Cell(latitudeDegrees, longitudeDegrees,
+ onGetCoarsenedS2Cells(latitudeDegrees, longitudeDegrees, numAdditionalCells,
new SingleUseS2CellIdsCallback(callback));
} catch (RuntimeException e) {
// exceptions on one-way binder threads are dropped - move to a different thread
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 3670c1f..ce8bec8 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
@@ -180,7 +180,8 @@
Log.e(sTAG, "could not get population density");
}
};
- mPopulationDensityProvider.getCoarsenedS2Cell(latitude, longitude, callback);
+ mPopulationDensityProvider.getCoarsenedS2Cells(latitude, longitude, MAX_CACHE_SIZE - 1,
+ callback);
}
/**
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyPopulationDensityProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyPopulationDensityProvider.java
index b0a0f0b..7b454e4 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyPopulationDensityProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyPopulationDensityProvider.java
@@ -96,14 +96,15 @@
/** Gets the population density at the requested location. */
- public void getCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees,
- IS2CellIdsCallback callback) {
+ public void getCoarsenedS2Cells(double latitudeDegrees, double longitudeDegrees,
+ int numAdditionalCells, IS2CellIdsCallback callback) {
mServiceWatcher.runOnBinder(
new ServiceWatcher.BinderOperation() {
@Override
public void run(IBinder binder) throws RemoteException {
IPopulationDensityProvider.Stub.asInterface(binder)
- .getCoarsenedS2Cell(latitudeDegrees, longitudeDegrees, callback);
+ .getCoarsenedS2Cells(latitudeDegrees, longitudeDegrees,
+ numAdditionalCells, callback);
}
@Override
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 04b82c4..6b7eda2 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
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -226,8 +227,8 @@
cache.getCoarseningLevel(POINT_IN_TIMES_SQUARE[0], POINT_IN_TIMES_SQUARE[1]);
- verify(provider).getCoarsenedS2Cell(eq(POINT_IN_TIMES_SQUARE[0]),
- eq(POINT_IN_TIMES_SQUARE[1]), any());
+ verify(provider).getCoarsenedS2Cells(eq(POINT_IN_TIMES_SQUARE[0]),
+ eq(POINT_IN_TIMES_SQUARE[1]), anyInt(), any());
}
@Test
@@ -242,8 +243,8 @@
ArgumentCaptor<IS2CellIdsCallback> argumentCaptor = ArgumentCaptor.forClass(
IS2CellIdsCallback.class);
- verify(provider).getCoarsenedS2Cell(eq(POINT_IN_TIMES_SQUARE[0]),
- eq(POINT_IN_TIMES_SQUARE[1]), argumentCaptor.capture());
+ verify(provider).getCoarsenedS2Cells(eq(POINT_IN_TIMES_SQUARE[0]),
+ eq(POINT_IN_TIMES_SQUARE[1]), anyInt(), argumentCaptor.capture());
// Results from the proxy should set the cache
int expectedLevel = 4;
@@ -264,10 +265,23 @@
cache.addToCache(TIMES_SQUARE_S2_ID);
- verify(provider, never()).getCoarsenedS2Cell(anyDouble(), anyDouble(), any());
+ verify(provider, never()).getCoarsenedS2Cells(anyDouble(), anyDouble(), anyInt(), any());
}
@Test
+ public void locationFudgerCache_whenQueryIsCached_askForMaxCacheSizeElems() {
+ ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+ LocationFudgerCache cache = new LocationFudgerCache(provider);
+ int numAdditionalCells = cache.MAX_CACHE_SIZE - 1;
+
+ cache.getCoarseningLevel(POINT_IN_TIMES_SQUARE[0], POINT_IN_TIMES_SQUARE[1]);
+
+ verify(provider).getCoarsenedS2Cells(eq(POINT_IN_TIMES_SQUARE[0]),
+ eq(POINT_IN_TIMES_SQUARE[1]), eq(numAdditionalCells), any());
+ }
+
+
+ @Test
public void locationFudgerCache_canContainUpToMaxSizeItems() {
// This test has two sequences of arrange-act-assert.
// The first checks that the cache correctly store up to MAX_CACHE_SIZE items.
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index cd19904..6d78def 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -1448,7 +1448,7 @@
Location test = new Location("any-provider");
mManager.getPermittedLocation(test, PERMISSION_COARSE);
- verify(provider, never()).getCoarsenedS2Cell(anyDouble(), anyDouble(), any());
+ verify(provider, never()).getCoarsenedS2Cells(anyDouble(), anyDouble(), anyInt(), any());
}
@Test
@@ -1472,7 +1472,7 @@
Location test = new Location("any-provider");
mManager.getPermittedLocation(test, PERMISSION_COARSE);
- verify(provider, never()).getCoarsenedS2Cell(anyDouble(), anyDouble(), any());
+ verify(provider, never()).getCoarsenedS2Cells(anyDouble(), anyDouble(), anyInt(), any());
}
@Test
@@ -1499,7 +1499,7 @@
// We can't test that 10.0, 20.0 was passed due to the offset. We only test that a call
// happened.
- verify(provider).getCoarsenedS2Cell(anyDouble(), anyDouble(), any());
+ verify(provider).getCoarsenedS2Cells(anyDouble(), anyDouble(), anyInt(), any());
}
@MediumTest