aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Fuller <nfuller@google.com>2021-02-02 14:33:00 +0000
committerNeil Fuller <nfuller@google.com>2021-02-03 18:51:53 +0000
commit426f074f42621dae930475c9c68063afd24acc83 (patch)
treeca5262621d2f2fed36c7eac023f04a108176cf84
parent6fc3df7e916dae48fa67a740b2da8d27e8b82f95 (diff)
downloadGeoTZ-426f074f42621dae930475c9c68063afd24acc83.tar.gz
Add DeviceConfig integration for com.android.geotz
Add DeviceConfig integration for reusable code and configure it for com.android.geotz. Test: Some manual testing / treehugger Bug: 152746105 Change-Id: I32ad81f7c97fac09ca27a3f93b3455480998e117
-rw-r--r--apex/com.android.geotz/resources/offlineltzprovider.properties8
-rw-r--r--locationtzprovider/src/main/java/com/android/timezone/location/provider/DelegatingLocationListeningAccountant.java85
-rw-r--r--locationtzprovider/src/main/java/com/android/timezone/location/provider/EnvironmentImpl.java172
-rw-r--r--locationtzprovider/src/main/java/com/android/timezone/location/provider/RealLocationListeningAccountant.java (renamed from locationtzprovider/src/main/java/com/android/timezone/location/provider/core/LocationListeningAccountantImpl.java)25
-rw-r--r--locationtzprovider/src/main/java/com/android/timezone/location/provider/core/Environment.java6
-rw-r--r--locationtzprovider/src/main/java/com/android/timezone/location/provider/core/LocationListeningAccountant.java12
-rw-r--r--locationtzprovider/src/main/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegate.java58
-rw-r--r--locationtzprovider/src/test/java/com/android/timezone/location/provider/RealLocationListeningAccountantTest.java (renamed from locationtzprovider/src/test/java/com/android/timezone/location/provider/core/LocationListeningAccountantImplTest.java)10
-rw-r--r--locationtzprovider/src/test/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegateTest.java23
9 files changed, 311 insertions, 88 deletions
diff --git a/apex/com.android.geotz/resources/offlineltzprovider.properties b/apex/com.android.geotz/resources/offlineltzprovider.properties
index 24d4ead..f188942 100644
--- a/apex/com.android.geotz/resources/offlineltzprovider.properties
+++ b/apex/com.android.geotz/resources/offlineltzprovider.properties
@@ -4,3 +4,11 @@
# The location of the tzs2.dat file to use for time zone boundaries.
geodata.path=/apex/com.android.geotz/etc/tzs2.dat
+
+# The namespace to pass to android.provider.DeviceConfig for server-pushed configuration.
+# Because com.android.geotz is provided by the Google Android platform team we can reuse the
+# platform experiment namespace.
+deviceconfig.namespace=system_time
+
+# The prefix that should be applied to keys passed to android.provider.DeviceConfig.
+deviceconfig.key_prefix=geotz_ \ No newline at end of file
diff --git a/locationtzprovider/src/main/java/com/android/timezone/location/provider/DelegatingLocationListeningAccountant.java b/locationtzprovider/src/main/java/com/android/timezone/location/provider/DelegatingLocationListeningAccountant.java
new file mode 100644
index 0000000..ea2fb42
--- /dev/null
+++ b/locationtzprovider/src/main/java/com/android/timezone/location/provider/DelegatingLocationListeningAccountant.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 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 com.android.timezone.location.provider;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.timezone.location.provider.core.Environment;
+import com.android.timezone.location.provider.core.LocationListeningAccountant;
+
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * An implementation of {@link LocationListeningAccountant} that delegates to another instance.
+ * The underlying instance can be swapped in a thread-safe fashion using {@link
+ * #replaceDelegate(LocationListeningAccountant)}, i.e. so the class holding the reference doesn't
+ * need to know.
+ */
+final class DelegatingLocationListeningAccountant implements LocationListeningAccountant {
+
+ @NonNull
+ private LocationListeningAccountant mDelegate;
+
+ DelegatingLocationListeningAccountant(@NonNull LocationListeningAccountant delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /**
+ * Replaces the delegate with {@code replacement}, carrying over as much of the current balance
+ * as allowed by the replacement's configuration.
+ */
+ synchronized void replaceDelegate(@NonNull LocationListeningAccountant replacement) {
+ Objects.requireNonNull(replacement);
+
+ Duration balanceToTransfer = mDelegate.withdrawActiveListeningBalance();
+ replacement.depositActiveListeningAmount(balanceToTransfer);
+ mDelegate = replacement;
+ }
+
+ @Override
+ public synchronized void depositActiveListeningAmount(@NonNull Duration amount) {
+ mDelegate.depositActiveListeningAmount(amount);
+ }
+
+ @Override
+ public synchronized void accrueActiveListeningBudget(@NonNull Duration timeInPassiveMode) {
+ mDelegate.accrueActiveListeningBudget(timeInPassiveMode);
+ }
+
+ @NonNull
+ @Override
+ public synchronized ListeningInstruction getNextListeningInstruction(
+ long elapsedRealtimeMillis,
+ @Nullable Environment.LocationListeningResult lastLocationListeningResult) {
+ return mDelegate.getNextListeningInstruction(
+ elapsedRealtimeMillis, lastLocationListeningResult);
+ }
+
+ @NonNull
+ @Override
+ public synchronized Duration withdrawActiveListeningBalance() {
+ return mDelegate.withdrawActiveListeningBalance();
+ }
+
+ @Override
+ public synchronized String toString() {
+ return "DelegatingLocationListeningAccountant{"
+ + "mDelegate=" + mDelegate
+ + '}';
+ }
+}
diff --git a/locationtzprovider/src/main/java/com/android/timezone/location/provider/EnvironmentImpl.java b/locationtzprovider/src/main/java/com/android/timezone/location/provider/EnvironmentImpl.java
index b6d0bb3..5fa3c10 100644
--- a/locationtzprovider/src/main/java/com/android/timezone/location/provider/EnvironmentImpl.java
+++ b/locationtzprovider/src/main/java/com/android/timezone/location/provider/EnvironmentImpl.java
@@ -34,13 +34,15 @@ import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.provider.DeviceConfig;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.timezone.location.lookup.GeoTimeZonesFinder;
import com.android.timezone.location.provider.core.Cancellable;
import com.android.timezone.location.provider.core.Environment;
-import com.android.timezone.location.lookup.GeoTimeZonesFinder;
+import com.android.timezone.location.provider.core.LocationListeningAccountant;
import com.android.timezone.location.provider.core.TimeZoneProviderResult;
import java.io.File;
@@ -58,24 +60,80 @@ import java.util.function.Consumer;
*/
class EnvironmentImpl implements Environment {
- private static final String RESOURCE_CONFIG_PROPERTIES = "offlineltzprovider.properties";
- private static final String CONFIG_KEY_GEODATA_PATH = "geodata.path";
+ private static final String RESOURCE_CONFIG_PROPERTIES_NAME = "offlineltzprovider.properties";
+
+ /**
+ * The config properties key to get the location of the tzs2.dat file to use for time zone
+ * boundaries.
+ */
+ private static final String RESOURCE_CONFIG_KEY_GEODATA_PATH = "geodata.path";
+
+ /**
+ * The config properties key for the namespace to pass to {@link android.provider.DeviceConfig}
+ * for server-pushed configuration.
+ */
+ private static final String RESOURCE_CONFIG_KEY_DEVICE_CONFIG_NAMESPACE =
+ "deviceconfig.namespace";
+
+ /**
+ * The config properties key for the prefix that should be applied to keys passed to
+ * {@link android.provider.DeviceConfig}.
+ */
+ private static final String RESOURCE_CONFIG_KEY_DEVICE_CONFIG_KEY_PREFIX =
+ "deviceconfig.key_prefix";
/** An arbitrary value larger than the largest time we might want to hold a wake lock. */
private static final long WAKELOCK_ACQUIRE_MILLIS = Duration.ofMinutes(1).toMillis();
- @NonNull
- private final LocationManager mLocationManager;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final Consumer<TimeZoneProviderResult> mResultConsumer;
- @NonNull
- private final HandlerExecutor mExecutor;
- @NonNull
- private final File mGeoDataFile;
- @NonNull
- private final PowerManager.WakeLock mWakeLock;
+ /**
+ * For every hour spent passive listening, 40 seconds of active listening are allowed, i.e.
+ * 90 passive time units : 1 active time units.
+ */
+ private static final long DEFAULT_PASSIVE_TO_ACTIVE_RATIO = (60 * 60) / 40;
+ private static final String DEVICE_CONFIG_KEY_SUFFIX_PASSIVE_TO_ACTIVE_RATIO =
+ "passive_to_active_ratio";
+
+ private static final Duration DEFAULT_MINIMUM_PASSIVE_LISTENING_DURATION =
+ Duration.ofMinutes(2);
+ private static final String DEVICE_CONFIG_KEY_SUFFIX_MINIMUM_PASSIVE_LISTENING_DURATION_MILLIS =
+ "min_passive_listening_duration_millis";
+
+ private static final Duration DEFAULT_LOCATION_NOT_KNOWN_AGE_THRESHOLD = Duration.ofMinutes(1);
+ private static final String DEVICE_CONFIG_KEY_SUFFIX_LOCATION_NOT_KNOWN_AGE_THRESHOLD_MILLIS =
+ "location_not_known_age_threshold_millis";
+
+ private static final Duration DEFAULT_LOCATION_KNOWN_AGE_THRESHOLD = Duration.ofMinutes(15);
+ private static final String DEVICE_CONFIG_KEY_SUFFIX_LOCATION_KNOWN_AGE_THRESHOLD_MILLIS =
+ "location_known_age_threshold_millis";
+
+ private static final Duration DEFAULT_MINIMUM_ACTIVE_LISTENING_DURATION = Duration.ofSeconds(5);
+ private static final String DEVICE_CONFIG_KEY_SUFFIX_MINIMUM_ACTIVE_LISTENING_DURATION_MILLIS =
+ "min_active_listening_duration_millis";
+
+ private static final Duration DEFAULT_MAXIMUM_ACTIVE_LISTENING_DURATION =
+ Duration.ofSeconds(10);
+ private static final String DEVICE_CONFIG_KEY_SUFFIX_MAXIMUM_ACTIVE_LISTENING_DURATION_MILLIS =
+ "max_active_listening_duration_millis";
+
+ private static final Duration DEFAULT_INITIAL_ACTIVE_LISTENING_BUDGET =
+ DEFAULT_MINIMUM_ACTIVE_LISTENING_DURATION;
+ private static final String DEVICE_CONFIG_KEY_SUFFIX_INITIAL_ACTIVE_LISTENING_BUDGET_MILLIS =
+ "min_initial_active_listening_budget_millis";
+
+ private static final Duration DEFAULT_MAX_ACTIVE_LISTENING_BUDGET =
+ DEFAULT_MAXIMUM_ACTIVE_LISTENING_DURATION.multipliedBy(4);
+ private static final String DEVICE_CONFIG_KEY_SUFFIX_MAX_ACTIVE_LISTENING_BUDGET_MILLIS =
+ "max_active_listening_budget_millis";
+
+ @NonNull private final LocationManager mLocationManager;
+ @NonNull private final Handler mHandler;
+ @NonNull private final Consumer<TimeZoneProviderResult> mResultConsumer;
+ @NonNull private final HandlerExecutor mExecutor;
+ @NonNull private final File mGeoDataFile;
+ @NonNull private final PowerManager.WakeLock mWakeLock;
+ @NonNull private final String mDeviceConfigNamespace;
+ @NonNull private final String mDeviceConfigKeyPrefix;
+ @NonNull private final DelegatingLocationListeningAccountant mLocationListeningAccountant;
EnvironmentImpl(@NonNull Context context,
@NonNull Consumer<TimeZoneProviderResult> resultConsumer) {
@@ -89,25 +147,84 @@ class EnvironmentImpl implements Environment {
mExecutor = new HandlerExecutor(mHandler);
Properties configProperties = loadConfigProperties(getClass().getClassLoader());
- mGeoDataFile = new File(configProperties.getProperty(CONFIG_KEY_GEODATA_PATH));
+ mGeoDataFile = new File(configProperties.getProperty(RESOURCE_CONFIG_KEY_GEODATA_PATH));
+ mDeviceConfigNamespace = Objects.requireNonNull(
+ configProperties.getProperty(RESOURCE_CONFIG_KEY_DEVICE_CONFIG_NAMESPACE));
+ mDeviceConfigKeyPrefix = Objects.requireNonNull(
+ configProperties.getProperty(RESOURCE_CONFIG_KEY_DEVICE_CONFIG_KEY_PREFIX));
+
+ LocationListeningAccountant realLocationListeningAccountant =
+ createRealLocationListeningAccountant();
+ mLocationListeningAccountant =
+ new DelegatingLocationListeningAccountant(realLocationListeningAccountant);
+
+ Duration initialActiveListeningBudget = getDeviceConfigDuration(
+ DEVICE_CONFIG_KEY_SUFFIX_INITIAL_ACTIVE_LISTENING_BUDGET_MILLIS,
+ DEFAULT_INITIAL_ACTIVE_LISTENING_BUDGET);
+ mLocationListeningAccountant.depositActiveListeningAmount(initialActiveListeningBudget);
+
+ // Monitor for changes that affect the accountant's configuration.
+ DeviceConfig.addOnPropertiesChangedListener(
+ mDeviceConfigNamespace, mExecutor, this::handleDeviceConfigChanged);
}
private static Properties loadConfigProperties(ClassLoader classLoader) {
Properties configProperties = new Properties();
try (InputStream configStream =
- classLoader.getResourceAsStream(RESOURCE_CONFIG_PROPERTIES)) {
+ classLoader.getResourceAsStream(RESOURCE_CONFIG_PROPERTIES_NAME)) {
if (configStream == null) {
throw new IllegalStateException("Unable to find config properties"
- + " resource=" + RESOURCE_CONFIG_PROPERTIES);
+ + " resource=" + RESOURCE_CONFIG_PROPERTIES_NAME);
}
configProperties.load(configStream);
} catch (IOException e) {
throw new IllegalStateException("Unable to load config properties from"
- + " resource=" + RESOURCE_CONFIG_PROPERTIES, e);
+ + " resource=" + RESOURCE_CONFIG_PROPERTIES_NAME, e);
}
return configProperties;
}
+ private void handleDeviceConfigChanged(@NonNull DeviceConfig.Properties properties) {
+ LocationListeningAccountant newAccountant = createRealLocationListeningAccountant();
+ mLocationListeningAccountant.replaceDelegate(newAccountant);
+ }
+
+ @NonNull
+ private LocationListeningAccountant createRealLocationListeningAccountant() {
+ Duration minPassiveListeningDuration = getDeviceConfigDuration(
+ DEVICE_CONFIG_KEY_SUFFIX_MINIMUM_PASSIVE_LISTENING_DURATION_MILLIS,
+ DEFAULT_MINIMUM_PASSIVE_LISTENING_DURATION);
+ Duration maxActiveListeningBalance = getDeviceConfigDuration(
+ DEVICE_CONFIG_KEY_SUFFIX_MAX_ACTIVE_LISTENING_BUDGET_MILLIS,
+ DEFAULT_MAX_ACTIVE_LISTENING_BUDGET);
+ Duration minActiveListeningDuration = getDeviceConfigDuration(
+ DEVICE_CONFIG_KEY_SUFFIX_MINIMUM_ACTIVE_LISTENING_DURATION_MILLIS,
+ DEFAULT_MINIMUM_ACTIVE_LISTENING_DURATION);
+ Duration maxActiveListeningDuration = getDeviceConfigDuration(
+ DEVICE_CONFIG_KEY_SUFFIX_MAXIMUM_ACTIVE_LISTENING_DURATION_MILLIS,
+ DEFAULT_MAXIMUM_ACTIVE_LISTENING_DURATION);
+ Duration locationNotKnownAgeThreshold = getDeviceConfigDuration(
+ DEVICE_CONFIG_KEY_SUFFIX_LOCATION_NOT_KNOWN_AGE_THRESHOLD_MILLIS,
+ DEFAULT_LOCATION_NOT_KNOWN_AGE_THRESHOLD);
+ Duration locationKnownAgeThreshold = getDeviceConfigDuration(
+ DEVICE_CONFIG_KEY_SUFFIX_LOCATION_KNOWN_AGE_THRESHOLD_MILLIS,
+ DEFAULT_LOCATION_KNOWN_AGE_THRESHOLD);
+ long passiveToActiveRatio = getDeviceConfigLong(
+ DEVICE_CONFIG_KEY_SUFFIX_PASSIVE_TO_ACTIVE_RATIO,
+ DEFAULT_PASSIVE_TO_ACTIVE_RATIO);
+ return new RealLocationListeningAccountant(
+ minPassiveListeningDuration, maxActiveListeningBalance,
+ minActiveListeningDuration, maxActiveListeningDuration,
+ locationNotKnownAgeThreshold, locationKnownAgeThreshold,
+ passiveToActiveRatio);
+ }
+
+ @Override
+ @NonNull
+ public LocationListeningAccountant getLocationListeningAccountant() {
+ return mLocationListeningAccountant;
+ }
+
@Override
@NonNull
public <T> Cancellable requestDelayedCallback(
@@ -292,6 +409,22 @@ class EnvironmentImpl implements Environment {
return SystemClock.elapsedRealtime();
}
+ @NonNull
+ private Duration getDeviceConfigDuration(@NonNull String key, @NonNull Duration defaultValue) {
+ Objects.requireNonNull(defaultValue);
+
+ long deviceConfigValue = getDeviceConfigLong(key, -1);
+ if (deviceConfigValue < 0) {
+ return defaultValue;
+ }
+ return Duration.ofMillis(deviceConfigValue);
+ }
+
+ private long getDeviceConfigLong(@NonNull String keySuffix, long defaultValue) {
+ String key = mDeviceConfigKeyPrefix + keySuffix;
+ return DeviceConfig.getLong(mDeviceConfigNamespace, key, defaultValue);
+ }
+
private static class HandlerExecutor implements Executor {
private final Handler mHandler;
@@ -332,6 +465,5 @@ class EnvironmentImpl implements Environment {
+ ", mCancelled=" + mCancelled
+ '}';
}
-
}
}
diff --git a/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/LocationListeningAccountantImpl.java b/locationtzprovider/src/main/java/com/android/timezone/location/provider/RealLocationListeningAccountant.java
index 82e6d7b..8467ffb 100644
--- a/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/LocationListeningAccountantImpl.java
+++ b/locationtzprovider/src/main/java/com/android/timezone/location/provider/RealLocationListeningAccountant.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.timezone.location.provider.core;
+package com.android.timezone.location.provider;
import static com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_ACTIVE;
import static com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_PASSIVE;
@@ -23,18 +23,23 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.timezone.location.provider.core.Environment.LocationListeningResult;
+import com.android.timezone.location.provider.core.LocationListeningAccountant;
import java.time.Duration;
import java.util.Objects;
/**
- * The real implementation of {@link LocationListeningAccountant}.
+ * A real implementation of {@link LocationListeningAccountant}.
+ *
+ * <p> A newly created instance starts with an active listening balance of zero. An instance holds
+ * the current active listening balance in memory (i.e. it is cleared if the device reboots or if
+ * the process is stopped).
*/
-final class LocationListeningAccountantImpl implements LocationListeningAccountant {
+final class RealLocationListeningAccountant implements LocationListeningAccountant {
/**
- * An amount added to passive listening times when accruing active time to ensure enough active
- * time will be accrued even if there is some measurement error.
+ * An amount added to passive listening durations when accruing active time to ensure enough
+ * active time will be accrued even if there is some measurement error.
*/
private static final Duration ACCRUAL_INCREMENT = Duration.ofMinutes(1);
@@ -67,7 +72,7 @@ final class LocationListeningAccountantImpl implements LocationListeningAccounta
* @param passiveToActiveRatio the amount of passive listening before a unit of active listening
* is accrued
*/
- LocationListeningAccountantImpl(
+ RealLocationListeningAccountant(
@NonNull Duration minPassiveListeningDuration,
@NonNull Duration maxActiveListeningBalance,
@NonNull Duration minActiveListeningDuration,
@@ -165,6 +170,14 @@ final class LocationListeningAccountantImpl implements LocationListeningAccounta
}
}
+ @Override
+ @NonNull
+ public synchronized Duration withdrawActiveListeningBalance() {
+ long toReturn = mActiveListeningBalanceMillis;
+ mActiveListeningBalanceMillis = 0;
+ return Duration.ofMillis(toReturn);
+ }
+
@NonNull
private Duration calculateAdditionalActiveListeningBudgetRequired(
@NonNull Duration activeListeningBudget) {
diff --git a/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/Environment.java b/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/Environment.java
index 915e88d..535d1c8 100644
--- a/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/Environment.java
+++ b/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/Environment.java
@@ -42,6 +42,12 @@ import java.util.function.Consumer;
public interface Environment {
/**
+ * Returns the {@link LocationListeningAccountant}. Callers may safely retain a reference.
+ */
+ @NonNull
+ LocationListeningAccountant getLocationListeningAccountant();
+
+ /**
* Requests a callback to {@code callback} with {@code callbackToken} after at least
* {@code delayMillis}. An object is returned that can be used to cancel the callback later.
*/
diff --git a/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/LocationListeningAccountant.java b/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/LocationListeningAccountant.java
index fb6f6c3..4d7449b 100644
--- a/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/LocationListeningAccountant.java
+++ b/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/LocationListeningAccountant.java
@@ -49,11 +49,8 @@ import java.util.Objects;
* #depositActiveListeningAmount(Duration)} (e.g. to return previously allocated but unused
* active listening time), or indirectly via {@link #accrueActiveListeningBudget(Duration)}, which
* is called to record time elapsed while <em>not</em> active listening.
- *
- * <p>An instance holds the balance of active listening in memory (i.e. it is cleared if the device
- * reboots or if the process is stopped).
*/
-interface LocationListeningAccountant {
+public interface LocationListeningAccountant {
/**
* Deposit an amount of active listening. Used when budget previously allocated with
@@ -115,4 +112,11 @@ interface LocationListeningAccountant {
@NonNull
ListeningInstruction getNextListeningInstruction(long elapsedRealtimeMillis,
@Nullable LocationListeningResult lastLocationListeningResult);
+
+ /**
+ * Withdraws the current active listening balance, leaving it at zero. Used when transferring
+ * budget from one accountant to another.
+ */
+ @NonNull
+ Duration withdrawActiveListeningBalance();
}
diff --git a/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegate.java b/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegate.java
index 02993ac..30ee77b 100644
--- a/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegate.java
+++ b/locationtzprovider/src/main/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegate.java
@@ -57,7 +57,8 @@ import java.util.Objects;
* <p>Implementation details:
*
* <p>The instance interacts with multiple threads, but state changes occur in a single-threaded
- * manner through the use of a lock object, {@link #mLock}.
+ * manner through the use of a lock object, {@link #mLock}, obtained from {@link
+ * Environment#getSharedLockObject()}.
*
* <p>There are two listening modes:
* <ul>
@@ -107,43 +108,14 @@ public final class OfflineLocationTimeZoneDelegate {
@ListenModeEnum
public static final int LOCATION_LISTEN_MODE_PASSIVE = 2;
- /**
- * The ratio of how much time must part to accue a unit of active listening time.
- *
- * <p>Value detail: For every hour spent passive listening, 40 seconds of active
- * listening are allowed, i.e. 90 passive time units : 1 active time units.
- */
- private static final long PASSIVE_TO_ACTIVE_RATIO = (60 * 60) / 40;
-
- /** The minimum time to spend in passive listening. */
- private static final Duration MINIMUM_PASSIVE_LISTENING_DURATION = Duration.ofMinutes(2);
-
- /** The age before a "location not known" result is considered too stale to use. */
- private static final Duration LOCATION_NOT_KNOWN_AGE_THRESHOLD = Duration.ofMinutes(1);
-
- /** The age before a "location known" result is considered too stale to use. */
- private static final Duration LOCATION_KNOWN_AGE_THRESHOLD = Duration.ofMinutes(15);
-
- /** The shortest active listening time allowed. */
- private static final Duration MINIMUM_ACTIVE_LISTENING_DURATION = Duration.ofSeconds(5);
-
- /** The maximum time to stay active listening in one go. */
- private static final Duration MAXIMUM_ACTIVE_LISTENING_DURATION = Duration.ofSeconds(10);
-
- /** The amount of active listening budget the instance starts with. */
- private static final Duration INITIAL_ACTIVE_LISTENING_BUDGET =
- MINIMUM_ACTIVE_LISTENING_DURATION;
-
- /** The cap on the amount of active listening that can be accrued. */
- private static final Duration MAX_ACTIVE_LISTENING_BUDGET =
- MAXIMUM_ACTIVE_LISTENING_DURATION.multipliedBy(4);
-
@NonNull
private final Environment mEnvironment;
- private final Object mLock = new Object();
+
@NonNull
private final LocationListeningAccountant mLocationListeningAccountant;
+ private final Object mLock = new Object();
+
/** The current mode of the provider. See {@link Mode} for details. */
@GuardedBy("mLock")
private final ReferenceWithHistory<Mode> mCurrentMode = new ReferenceWithHistory<>(10);
@@ -186,25 +158,13 @@ public final class OfflineLocationTimeZoneDelegate {
@NonNull
public static OfflineLocationTimeZoneDelegate create(@NonNull Environment environment) {
- LocationListeningAccountantImpl locationListeningAccountant =
- new LocationListeningAccountantImpl(
- MINIMUM_PASSIVE_LISTENING_DURATION, MAX_ACTIVE_LISTENING_BUDGET,
- MINIMUM_ACTIVE_LISTENING_DURATION, MAXIMUM_ACTIVE_LISTENING_DURATION,
- LOCATION_NOT_KNOWN_AGE_THRESHOLD, LOCATION_KNOWN_AGE_THRESHOLD,
- PASSIVE_TO_ACTIVE_RATIO);
- // Start with a non-zero budget for active listening so we start with a period of active
- // listening. This will be reset each time a provider is created.
- locationListeningAccountant.depositActiveListeningAmount(INITIAL_ACTIVE_LISTENING_BUDGET);
-
- return new OfflineLocationTimeZoneDelegate(environment, locationListeningAccountant);
+ return new OfflineLocationTimeZoneDelegate(environment);
}
// @VisibleForTesting
- OfflineLocationTimeZoneDelegate(
- @NonNull Environment environment,
- @NonNull LocationListeningAccountant locationListeningAccountant) {
+ OfflineLocationTimeZoneDelegate(@NonNull Environment environment) {
mEnvironment = Objects.requireNonNull(environment);
- mLocationListeningAccountant = Objects.requireNonNull(locationListeningAccountant);
+ mLocationListeningAccountant = environment.getLocationListeningAccountant();
synchronized (mLock) {
mCurrentMode.set(Mode.createStoppedMode());
@@ -310,7 +270,7 @@ public final class OfflineLocationTimeZoneDelegate {
// State and constants.
pw.println("mInitializationTimeoutCancellable=" + mInitializationTimeoutCancellable);
- pw.println("mActiveListeningAccountant=" + mLocationListeningAccountant);
+ pw.println("mLocationListeningAccountant=" + mLocationListeningAccountant);
pw.println("mLastLocationToken=" + mLastLocationToken);
pw.println();
pw.println("Mode history:");
diff --git a/locationtzprovider/src/test/java/com/android/timezone/location/provider/core/LocationListeningAccountantImplTest.java b/locationtzprovider/src/test/java/com/android/timezone/location/provider/RealLocationListeningAccountantTest.java
index 468e512..1dbd440 100644
--- a/locationtzprovider/src/test/java/com/android/timezone/location/provider/core/LocationListeningAccountantImplTest.java
+++ b/locationtzprovider/src/test/java/com/android/timezone/location/provider/RealLocationListeningAccountantTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.timezone.location.provider.core;
+package com.android.timezone.location.provider;
import static com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_ACTIVE;
import static com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_PASSIVE;
@@ -33,8 +33,8 @@ import org.junit.Test;
import java.time.Duration;
-/** Unit tests for {@link LocationListeningAccountantImpl}. */
-public class LocationListeningAccountantImplTest {
+/** Unit tests for {@link RealLocationListeningAccountant}. */
+public class RealLocationListeningAccountantTest {
private static final Duration MAX_ACTIVE_LISTENING_BALANCE = seconds(500);
private static final Duration MIN_PASSIVE_LISTENING_DURATION = seconds(3);
@@ -48,12 +48,12 @@ public class LocationListeningAccountantImplTest {
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 54321;
private static final Duration ARBITRARY_LISTENING_DURATION = seconds(5);
- private LocationListeningAccountantImpl mAccountant;
+ private RealLocationListeningAccountant mAccountant;
@Before
public void setUp() {
- mAccountant = new LocationListeningAccountantImpl(
+ mAccountant = new RealLocationListeningAccountant(
MIN_PASSIVE_LISTENING_DURATION, MAX_ACTIVE_LISTENING_BALANCE,
MIN_ACTIVE_LISTENING_DURATION, MAX_ACTIVE_LISTENING_DURATION,
LOCATION_NOT_KNOWN_AGE_THRESHOLD, LOCATION_KNOWN_AGE_THRESHOLD,
diff --git a/locationtzprovider/src/test/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegateTest.java b/locationtzprovider/src/test/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegateTest.java
index 4c61937..9b13d13 100644
--- a/locationtzprovider/src/test/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegateTest.java
+++ b/locationtzprovider/src/test/java/com/android/timezone/location/provider/core/OfflineLocationTimeZoneDelegateTest.java
@@ -66,9 +66,8 @@ public class OfflineLocationTimeZoneDelegateTest {
public void setUp() {
mTestEnvironment = new FakeEnvironment();
mTestGeoTimeZoneFinder = mTestEnvironment.mFakeGeoTimeZonesFinder;
- mTestLocationListeningAccountant = new FakeLocationListeningAccountant();
- mDelegate = new OfflineLocationTimeZoneDelegate(
- mTestEnvironment, mTestLocationListeningAccountant);
+ mTestLocationListeningAccountant = mTestEnvironment.mLocationListeningAccountant;
+ mDelegate = new OfflineLocationTimeZoneDelegate(mTestEnvironment);
}
@Test
@@ -183,6 +182,9 @@ public class OfflineLocationTimeZoneDelegateTest {
private static class FakeEnvironment implements Environment {
private final FakeGeoTimeZonesFinder mFakeGeoTimeZonesFinder = new FakeGeoTimeZonesFinder();
+ private final FakeLocationListeningAccountant mLocationListeningAccountant =
+ new FakeLocationListeningAccountant();
+
private long mElapsedRealtimeMillis;
private PassiveLocationListenerState mPassiveLocationListeningState;
private ActiveLocationListenerState mActiveLocationListeningState;
@@ -191,6 +193,12 @@ public class OfflineLocationTimeZoneDelegateTest {
@NonNull
@Override
+ public LocationListeningAccountant getLocationListeningAccountant() {
+ return mLocationListeningAccountant;
+ }
+
+ @NonNull
+ @Override
public <T> Cancellable requestDelayedCallback(@NonNull Consumer<T> callback,
@Nullable T callbackToken, @NonNull Duration delay) {
TestTimeoutState<T> timeoutState =
@@ -226,7 +234,7 @@ public class OfflineLocationTimeZoneDelegateTest {
@NonNull
@Override
- public FakeGeoTimeZonesFinder createGeoTimeZoneFinder() throws IOException {
+ public GeoTimeZonesFinder createGeoTimeZoneFinder() throws IOException {
return mFakeGeoTimeZonesFinder;
}
@@ -564,6 +572,13 @@ public class OfflineLocationTimeZoneDelegateTest {
return mListeningInstructions.removeFirst();
}
+ @Override
+ @NonNull
+ public Duration withdrawActiveListeningBalance() {
+ // Not implemented for tests.
+ throw new UnsupportedOperationException();
+ }
+
FakeLocationListeningAccountant addInstruction(ListeningInstruction listeningInstruction) {
mListeningInstructions.addLast(listeningInstruction);
return this;