Merge "Consider equivalent time zone IDs as equal in metrics." into sc-dev

This commit is contained in:
Almaz Mingaleev
2021-05-04 18:57:26 +00:00
committed by Android (Google) Code Review
7 changed files with 140 additions and 10 deletions

View File

@@ -89,8 +89,6 @@ public final class MetricsTimeZoneDetectorState {
@Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion,
@Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) {
// TODO(b/172934905) Add logic to canonicalize the time zone IDs to Android's preferred IDs
// so that the ordinals will match even when the ID is not identical, just equivalent.
int deviceTimeZoneIdOrdinal =
tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId));
MetricsTimeZoneSuggestion latestObfuscatedManualSuggestion =

View File

@@ -15,9 +15,12 @@
*/
package com.android.server.timezonedetector;
import android.annotation.NonNull;
import android.util.ArraySet;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
/**
* A helper class that turns a set of objects into ordinal values, i.e. each object is offered
@@ -30,11 +33,19 @@ import java.util.List;
class OrdinalGenerator<T> {
private final ArraySet<T> mKnownIds = new ArraySet<>();
private final @NonNull Function<T, T> mCanonicalizationFunction;
OrdinalGenerator(@NonNull Function<T, T> canonicalizationFunction) {
mCanonicalizationFunction = Objects.requireNonNull(canonicalizationFunction);
}
int ordinal(T object) {
int ordinal = mKnownIds.indexOf(object);
T canonical = mCanonicalizationFunction.apply(object);
int ordinal = mKnownIds.indexOf(canonical);
if (ordinal < 0) {
ordinal = mKnownIds.size();
mKnownIds.add(object);
mKnownIds.add(canonical);
}
return ordinal;
}

View File

@@ -0,0 +1,35 @@
/*
* 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.server.timezonedetector;
import com.android.i18n.timezone.TimeZoneFinder;
import java.util.function.Function;
/**
* Returns preferred time zone ID if {@code timeZoneId} was deprecated. For example, returns
* America/Nuuk for America/Godthab.
*/
final class TimeZoneCanonicalizer implements Function<String, String> {
@Override
public String apply(String timeZoneId) {
String canonicialZoneId = TimeZoneFinder.getInstance().getCountryZonesFinder()
.findCanonicalTimeZoneId(timeZoneId);
return canonicialZoneId == null ? timeZoneId : canonicialZoneId;
}
}

View File

@@ -383,7 +383,8 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
bestQualifiedTelephonySuggestion == null
? null : bestQualifiedTelephonySuggestion.suggestion;
// A new generator is created each time: we don't want / require consistency.
OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
OrdinalGenerator<String> tzIdOrdinalGenerator =
new OrdinalGenerator<>(new TimeZoneCanonicalizer());
return MetricsTimeZoneDetectorState.create(
tzIdOrdinalGenerator,
getConfigurationInternal(currentUserId),

View File

@@ -25,13 +25,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.function.Function;
@RunWith(AndroidJUnit4.class)
public class OrdinalGeneratorTest {
@Test
public void testOrdinal() {
OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
public void testOrdinal_withIdentityFunction() {
OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>(Function.identity());
int oneOrd = ordinalGenerator.ordinal("One");
int twoOrd = ordinalGenerator.ordinal("Two");
assertNotEquals(oneOrd, twoOrd);
@@ -45,8 +46,8 @@ public class OrdinalGeneratorTest {
}
@Test
public void testOrdinals() {
OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
public void testOrdinals_withIdentityFunction() {
OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>(Function.identity());
int[] oneTwoOrds = ordinalGenerator.ordinals(Arrays.asList("One", "Two"));
int[] twoThreeOrds = ordinalGenerator.ordinals(Arrays.asList("Two", "Three"));
assertEquals(oneTwoOrds[0], ordinalGenerator.ordinal("One"));
@@ -54,4 +55,33 @@ public class OrdinalGeneratorTest {
assertEquals(twoThreeOrds[0], ordinalGenerator.ordinal("Two"));
assertEquals(twoThreeOrds[1], ordinalGenerator.ordinal("Three"));
}
@Test
public void testOrdinal_withCanonicalizationFunction() {
OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>(String::toLowerCase);
int oneOrd = ordinalGenerator.ordinal("One");
int twoOrd = ordinalGenerator.ordinal("Two");
assertNotEquals(oneOrd, twoOrd);
assertEquals(oneOrd, ordinalGenerator.ordinal("ONE"));
assertEquals(twoOrd, ordinalGenerator.ordinal("two"));
int threeOrd = ordinalGenerator.ordinal("Three");
assertNotEquals(oneOrd, threeOrd);
assertNotEquals(twoOrd, threeOrd);
}
@Test
public void testOrdinals_withCanonicalizationFunction() {
OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>(String::toLowerCase);
int[] oneTwoOrds = ordinalGenerator.ordinals(Arrays.asList("One", "Two"));
int[] twoThreeOrds = ordinalGenerator.ordinals(Arrays.asList("Two", "Three"));
assertEquals(oneTwoOrds[0], ordinalGenerator.ordinal("ONE"));
assertEquals(oneTwoOrds[1], ordinalGenerator.ordinal("two"));
assertEquals(twoThreeOrds[0], ordinalGenerator.ordinal("TWO"));
assertEquals(twoThreeOrds[1], ordinalGenerator.ordinal("threE"));
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.server.timezonedetector;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class TimeZoneCanonicalizerTest {
TimeZoneCanonicalizer mFunction = new TimeZoneCanonicalizer();
@Test
public void deprecatedTimeZonesAreEqualToCanonical() {
assertThat(mFunction.apply("America/Godthab")).isEqualTo("America/Nuuk");
assertThat(mFunction.apply("Australia/Currie")).isEqualTo("Australia/Hobart");
}
@Test
public void wellKnownCanonicalIDs() {
assertThat(mFunction.apply("America/Detroit")).isEqualTo("America/Detroit");
assertThat(mFunction.apply("Europe/London")).isEqualTo("Europe/London");
assertThat(mFunction.apply("America/New_York")).isEqualTo("America/New_York");
assertThat(mFunction.apply("Europe/Volgograd")).isEqualTo("Europe/Volgograd");
}
@Test
public void timeZonesAsGmtOffsetsTreatedAsCanonical() {
assertThat(mFunction.apply("Etc/GMT-11")).isEqualTo("Etc/GMT-11");
}
@Test
public void nonExistingOneMappedToThemselves() {
assertThat(mFunction.apply("Mars/Base")).isEqualTo("Mars/Base");
}
}

View File

@@ -56,6 +56,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
/**
* White-box unit tests for {@link TimeZoneDetectorStrategyImpl}.
@@ -1008,7 +1009,7 @@ public class TimeZoneDetectorStrategyImplTest {
// Check the various feature state values are what we expect.
assertFeatureStateMatchesConfig(expectedInternalConfig, actualState, expectedDetectionMode);
OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>(Function.identity());
MetricsTimeZoneDetectorState expectedState =
MetricsTimeZoneDetectorState.create(
tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId,