Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39611ec2 authored by Geoffrey Boullanger's avatar Geoffrey Boullanger
Browse files

Make TelephonyTimeZoneSuggestion carry TelephonySignal

This is needed to be able to record information like the MCC/MNC and Nitz in the suggestion metadata. This will later be used in the Fused Time Zone Detector.

go/android-tz-detector
go/ftzd-algo
go/ftzd-cases
go/ftzd-scenarios

Test: atest FrameworksTimeCoreTests
Test: atest FrameworksTimeServicesTests
Test: atest FrameworksTelephonyTests
Flag: android.timezone.flags.enable_fused_time_zone_detector
Bug: 394770805
Change-Id: I660c5a8cc1379396f8cb1031c810dd1c4304ce0d
parent 9a368d9b
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
/*
 * Copyright 2025 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.app.timezonedetector;

parcelable NitzSignal;
 No newline at end of file
+20 −0
Original line number Diff line number Diff line
/*
 * Copyright 2025 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.app.timezonedetector;

import android.app.timezonedetector.NitzSignal;

parcelable TelephonySignal;
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -16,4 +16,6 @@

package android.app.timezonedetector;

import android.app.timezonedetector.TelephonySignal;

parcelable TelephonyTimeZoneSuggestion;
+54 −15
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
    /** @hide */
    @NonNull
    public static final Creator<TelephonyTimeZoneSuggestion> CREATOR =
            new Creator<TelephonyTimeZoneSuggestion>() {
            new Creator<>() {
                public TelephonyTimeZoneSuggestion createFromParcel(Parcel in) {
                    return TelephonyTimeZoneSuggestion.createFromParcel(in);
                }
@@ -150,6 +150,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
    @MatchType private final int mMatchType;
    @Quality private final int mQuality;
    @Nullable private List<String> mDebugInfo;
    @Nullable private final TelephonySignal mTelephonySignal;

    private TelephonyTimeZoneSuggestion(Builder builder) {
        mSlotIndex = builder.mSlotIndex;
@@ -158,17 +159,23 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
        mMatchType = builder.mMatchType;
        mQuality = builder.mQuality;
        mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
        mTelephonySignal = builder.mTelephonySignal;
    }

    @SuppressWarnings("unchecked")
    private static TelephonyTimeZoneSuggestion createFromParcel(Parcel in) {
        // Use the Builder so we get validation during build().
        int slotIndex = in.readInt();
        TelephonyTimeZoneSuggestion suggestion = new Builder(slotIndex)
        TelephonyTimeZoneSuggestion suggestion =
                new Builder(slotIndex)
                        .setZoneId(in.readString())
                        .setCountryIsoCode(in.readString())
                        .setMatchType(in.readInt())
                        .setQuality(in.readInt())
                        .setTelephonySignal(
                                in.readParcelable(
                                        TelephonySignal.class.getClassLoader(),
                                        TelephonySignal.class))
                        .build();
        List<String> debugInfo =
                in.readArrayList(TelephonyTimeZoneSuggestion.class.getClassLoader(), java.lang.String.class);
@@ -185,6 +192,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
        dest.writeString(mCountryIsoCode);
        dest.writeInt(mMatchType);
        dest.writeInt(mQuality);
        dest.writeParcelable(mTelephonySignal, flags);
        dest.writeList(mDebugInfo);
    }

@@ -256,6 +264,12 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
                ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
    }

    /** Returns the {@link TelephonySignal} associated with this suggestion, or {@code null}. */
    @Nullable
    public TelephonySignal getTelephonySignal() {
        return mTelephonySignal;
    }

    /**
     * Associates information with the instance that can be useful for debugging / logging.
     *
@@ -293,23 +307,35 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
                && mMatchType == that.mMatchType
                && mQuality == that.mQuality
                && Objects.equals(mZoneId, that.mZoneId)
                && Objects.equals(mCountryIsoCode, that.mCountryIsoCode);
                && Objects.equals(mCountryIsoCode, that.mCountryIsoCode)
                && Objects.equals(mTelephonySignal, that.mTelephonySignal);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mSlotIndex, mZoneId, mCountryIsoCode, mMatchType, mQuality);
        return Objects.hash(
                mSlotIndex, mZoneId, mCountryIsoCode, mMatchType, mQuality, mTelephonySignal);
    }

    @Override
    public String toString() {
        return "TelephonyTimeZoneSuggestion{"
                + "mSlotIndex=" + mSlotIndex
                + ", mZoneId='" + mZoneId + '\''
                + ", mCountryIsoCode='" + mCountryIsoCode + '\''
                + ", mMatchType=" + mMatchType
                + ", mQuality=" + mQuality
                + ", mDebugInfo=" + mDebugInfo
                + "mSlotIndex="
                + mSlotIndex
                + ", mZoneId='"
                + mZoneId
                + '\''
                + ", mCountryIsoCode='"
                + mCountryIsoCode
                + '\''
                + ", mMatchType="
                + mMatchType
                + ", mQuality="
                + mQuality
                + ", mDebugInfo="
                + mDebugInfo
                + ", mTelephonySignal="
                + mTelephonySignal
                + '}';
    }

@@ -325,6 +351,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
        @MatchType private int mMatchType;
        @Quality private int mQuality;
        @Nullable private List<String> mDebugInfo;
        @Nullable private TelephonySignal mTelephonySignal;

        /**
         * Creates a builder with the specified {@code slotIndex}.
@@ -394,6 +421,16 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
            return this;
        }

        /**
         * Sets the {@link TelephonySignal} for this suggestion. Returns the builder for call
         * chaining.
         */
        @NonNull
        public Builder setTelephonySignal(@Nullable TelephonySignal telephonySignal) {
            mTelephonySignal = telephonySignal;
            return this;
        }

        /**
         * Performs basic structural validation of this instance. e.g. Are all the fields populated
         * that must be? Are the enum ints set to valid values?
@@ -460,6 +497,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
                    matchType = parseMatchTypeCommandLineArg(cmd.getNextArgRequired());
                    break;
                }
                // TelephonySignal ignored
                default: {
                    throw new IllegalArgumentException("Unknown option: " + opt);
                }
@@ -526,6 +564,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable {
        pw.println("    --quality <single|multiple_same|multiple_different>");
        pw.println("    --match_type <emulator|country_with_offset|country|test_network>");
        pw.println();
        pw.println("Note: TelephonySignal data is not directly configurable via command line.");
        pw.println("See " + TelephonyTimeZoneSuggestion.class.getName() + " for more information");
    }
}
+41 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;

import android.app.timezonedetector.TelephonySignal;
import android.os.ShellCommand;
import android.platform.test.annotations.Presubmit;

@@ -35,6 +36,7 @@ import org.junit.runner.RunWith;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Set;

@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -134,6 +136,39 @@ public class TelephonyTimeZoneSuggestionTest {
            two.addDebugInfo("Debug info 2");
            assertEquals(one, two);
        }

        TelephonySignal signal1 = new TelephonySignal("310", null, "us", Set.of("us"), null);
        builder1.setTelephonySignal(signal1);
        {
            TelephonyTimeZoneSuggestion one = builder1.build();
            TelephonyTimeZoneSuggestion two = builder2.build();
            assertNotEquals(one, two);
        }

        TelephonySignal signal2 = new TelephonySignal("310", null, "us", Set.of("us"), null);
        builder2.setTelephonySignal(signal2);
        {
            TelephonyTimeZoneSuggestion one = builder1.build();
            TelephonyTimeZoneSuggestion two = builder2.build();
            assertEquals(one, two);
        }

        // DebugInfo must not be considered in equals().
        {
            TelephonyTimeZoneSuggestion one = builder1.build();
            TelephonyTimeZoneSuggestion two = builder2.build();
            one.addDebugInfo("Debug info 1");
            two.addDebugInfo("Debug info 2");
            assertEquals(one, two);
        }

        TelephonySignal signal3 = new TelephonySignal("310", "780", "us", Set.of("us"), null);
        builder2.setTelephonySignal(signal3);
        {
            TelephonyTimeZoneSuggestion one = builder1.build();
            TelephonyTimeZoneSuggestion two = builder2.build();
            assertNotEquals(one, two);
        }
    }

    @Test(expected = RuntimeException.class)
@@ -166,6 +201,12 @@ public class TelephonyTimeZoneSuggestionTest {
        TelephonyTimeZoneSuggestion suggestion1 = builder.build();
        assertRoundTripParcelable(suggestion1);

        TelephonySignal signal = new TelephonySignal("310", null, "us", Set.of("us"), null);
        builder.setTelephonySignal(signal);
        TelephonyTimeZoneSuggestion suggestion2 = builder.build();
        assertRoundTripParcelable(suggestion2);
        assertEquals(signal, suggestion2.getTelephonySignal());

        // DebugInfo should also be stored (but is not checked by equals()
        String debugString = "This is debug info";
        suggestion1.addDebugInfo(debugString);