summaryrefslogtreecommitdiff
path: root/location
diff options
context:
space:
mode:
author Ludovic Barman <ludovicb@google.com> 2024-11-20 15:45:48 +0000
committer Ludovic Barman <ludovicb@google.com> 2024-11-27 09:43:13 +0000
commit4f699aab2a43ec7d5fddcbb7b1be1f1dabd47057 (patch)
tree619f4af241b28ae32ec27db5c1e81b4076a1477b /location
parentcd0151ceaa4460e4136435dba42d98aebe20290f (diff)
Add PopulationDensityProvider
This change adds the concept of population density providers in AOSP. It allows the framework to query the population density at a given location. Internally, this is backed by a new type of Provider, following the current conventions in the location package. There is no app-facing change, only the LocationManagerService will access this provider (see other CLs in the topic). Because the provider will be implemented by Play Services (or OEM implementations), we add a proxy mechanism identical to the current ProxyLocationProvider, which reads the config.xml and instantiates the correct provider. The core of the change is IPopulationDensityProvider.aidl. In details: - We add a new IPopulationDensityProvider IDL with its associated implementation PopulationDensityProviderBase. - We add the IDL for two callback type for the S2CellId and the default coarsening level. - We change the LocationManagerService to instantiate a ProxyPopulationDensityProvider. - This ProxyPopulationDensityProvider creates the correct provider given the XML config. Tests: - atest CtsLocationNoneTestCases:PopulationDensityProviderBaseTest - atest FrameworksMockingServicesTests:LocationManagerServiceTest NB: I also did a manual test by implementing a fake provider and querying it, this works on Pixel 7 pro, see linked commits with topic "population-density-provider-test-dns" Test: manual atest on Pixel 7 pro (see above) Bug: 376198890 Flag: android.location.flags.population_density_provider Change-Id: I90663d478200c734abbf5f442dfb1bb8bb79a875
Diffstat (limited to 'location')
-rw-r--r--location/api/system-current.txt8
-rw-r--r--location/java/android/location/provider/IPopulationDensityProvider.aidl45
-rw-r--r--location/java/android/location/provider/IS2CellIdsCallback.aidl36
-rw-r--r--location/java/android/location/provider/IS2LevelCallback.aidl34
-rw-r--r--location/java/android/location/provider/PopulationDensityProviderBase.java192
5 files changed, 315 insertions, 0 deletions
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index cf3f74085d66..8cd08d3aad6c 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -642,6 +642,14 @@ package android.location.provider {
method public void onFlushComplete();
}
+ @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 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";
+ }
+
public final class ProviderRequest implements android.os.Parcelable {
method public int describeContents();
method @IntRange(from=0) public long getIntervalMillis();
diff --git a/location/java/android/location/provider/IPopulationDensityProvider.aidl b/location/java/android/location/provider/IPopulationDensityProvider.aidl
new file mode 100644
index 000000000000..9b5cb5ae8c7a
--- /dev/null
+++ b/location/java/android/location/provider/IPopulationDensityProvider.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.os.Bundle;
+
+import android.location.Location;
+import android.location.provider.IS2CellIdsCallback;
+import android.location.provider.IS2LevelCallback;
+
+/**
+ * Binder interface for services that implement a population density provider. Do not implement this
+ * directly, extend {@link PopulationDensityProviderBase} instead.
+ * @hide
+ */
+oneway interface IPopulationDensityProvider {
+ /**
+ * Gets the default S2 level to be used to coarsen any location, in case a more precise answer
+ * from the method below can't be obtained.
+ */
+ void getDefaultCoarseningLevel(in IS2LevelCallback callback);
+
+ /**
+ * Returns 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.
+ */
+ void getCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees, in IS2CellIdsCallback
+ callback);
+}
diff --git a/location/java/android/location/provider/IS2CellIdsCallback.aidl b/location/java/android/location/provider/IS2CellIdsCallback.aidl
new file mode 100644
index 000000000000..f583045ebb26
--- /dev/null
+++ b/location/java/android/location/provider/IS2CellIdsCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.Location;
+
+/**
+ * Binder interface for S2 cell IDs callbacks.
+ * @hide
+ */
+oneway interface IS2CellIdsCallback {
+
+ /**
+ * Called with the resulting list of S2 cell IDs. The first cell is expected to contain
+ * the requested latitude/longitude. Its level represent the population density. Optionally,
+ * the list can also contain additional nearby cells.
+ */
+ void onResult(in long[] s2CellIds);
+
+ /** Called if any error occurs while processing the query. */
+ void onError();
+}
diff --git a/location/java/android/location/provider/IS2LevelCallback.aidl b/location/java/android/location/provider/IS2LevelCallback.aidl
new file mode 100644
index 000000000000..49f96ef7e3e2
--- /dev/null
+++ b/location/java/android/location/provider/IS2LevelCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.location.Location;
+
+/**
+ * Binder interface for S2 level callback.
+ * @hide
+ */
+oneway interface IS2LevelCallback {
+ /**
+ * Called with the resulting default S2 level for coarsening a location, in case a better
+ * answer cannot be obtained for a latitude/longitude.
+ */
+ void onResult(int s2Level);
+
+ /** Called if any error occurs while processing the query. */
+ void onError();
+}
diff --git a/location/java/android/location/provider/PopulationDensityProviderBase.java b/location/java/android/location/provider/PopulationDensityProviderBase.java
new file mode 100644
index 000000000000..3907516f6aaa
--- /dev/null
+++ b/location/java/android/location/provider/PopulationDensityProviderBase.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.location.flags.Flags;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A provider for population density.
+ * The population density is defined as the S2 level at which the S2 cell around the latitude /
+ * longitude contains at least a thousand people.
+ * It exposes two methods: one about providing population density around a latitude / longitude,
+ * and one about providing a "default" population density to fall back to in case the first API
+ * can't be used or returns an error.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_POPULATION_DENSITY_PROVIDER)
+public abstract class PopulationDensityProviderBase {
+
+ final String mTag;
+ final @Nullable String mAttributionTag;
+ final IBinder mBinder;
+
+ /**
+ * The action the wrapping service should have in its intent filter to implement the
+ * PopulationDensity provider.
+ */
+ @SuppressLint("ActionValue")
+ public static final String ACTION_POPULATION_DENSITY_PROVIDER =
+ "com.android.location.service.PopulationDensityProvider";
+
+ public PopulationDensityProviderBase(@NonNull Context context, @NonNull String tag) {
+ mTag = tag;
+ mAttributionTag = context.getAttributionTag();
+ mBinder = new Service();
+ }
+
+ /**
+ * Returns the IBinder instance that should be returned from the
+ * {@link android.app.Service#onBind(Intent)} method of the wrapping service.
+ */
+ public final @Nullable IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Called upon receiving a new request for the default coarsening level.
+ * 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 the coarsening level, or an error.
+ */
+ public abstract void onGetDefaultCoarseningLevel(@NonNull OutcomeReceiver<Integer, Throwable>
+ callback);
+
+ /**
+ * 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.
+ *
+ * @param callback A single-use callback that either returns S2CellIds, or an error.
+ */
+ public abstract void onGetCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees,
+ @NonNull OutcomeReceiver<long[], Throwable> callback);
+
+ private final class Service extends IPopulationDensityProvider.Stub {
+ @Override
+ public void getDefaultCoarseningLevel(@NonNull IS2LevelCallback callback) {
+ try {
+ onGetDefaultCoarseningLevel(new SingleUseS2LevelCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+
+ @Override
+ public void getCoarsenedS2Cell(double latitudeDegrees, double longitudeDegrees,
+ @NonNull IS2CellIdsCallback callback) {
+ try {
+ onGetCoarsenedS2Cell(latitudeDegrees, longitudeDegrees,
+ new SingleUseS2CellIdsCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+ }
+
+ private static class SingleUseS2LevelCallback implements OutcomeReceiver<Integer, Throwable> {
+
+ private final AtomicReference<IS2LevelCallback> mCallback;
+
+ SingleUseS2LevelCallback(IS2LevelCallback callback) {
+ mCallback = new AtomicReference<>(callback);
+ }
+
+ @Override
+ public void onResult(Integer level) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onResult(level);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onError();
+ } catch (RemoteException r) {
+ throw r.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private static class SingleUseS2CellIdsCallback implements OutcomeReceiver<long[], Throwable> {
+
+ private final AtomicReference<IS2CellIdsCallback> mCallback;
+
+ SingleUseS2CellIdsCallback(IS2CellIdsCallback callback) {
+ mCallback = new AtomicReference<>(callback);
+ }
+
+ @Override
+ public void onResult(long[] s2CellIds) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onResult(s2CellIds);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onError();
+ } catch (RemoteException r) {
+ throw r.rethrowFromSystemServer();
+ }
+ }
+ }
+}