diff options
author | Neil Fuller <nfuller@google.com> | 2021-02-02 14:33:00 +0000 |
---|---|---|
committer | Neil Fuller <nfuller@google.com> | 2021-02-03 18:51:53 +0000 |
commit | 426f074f42621dae930475c9c68063afd24acc83 (patch) | |
tree | ca5262621d2f2fed36c7eac023f04a108176cf84 | |
parent | 6fc3df7e916dae48fa67a740b2da8d27e8b82f95 (diff) | |
download | GeoTZ-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
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; |