Merge "Population Density Provider: add cache hint" into main
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