Loading core/java/android/app/timezonedetector/NitzSignal.java 0 → 100644 +213 −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.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; import java.util.TimeZone; import android.annotation.ElapsedRealtimeLong; import android.annotation.DurationMillisLong; /** * A data class that captures information about a Network Identity and Timezone (NITZ) signal. This * information can be used by the device's time zone detector. * * @hide */ public final class NitzSignal implements Parcelable { /** * The elapsed realtime ({@link android.os.SystemClock#elapsedRealtime()}) when this NITZ signal * was received by the device. */ @ElapsedRealtimeLong private final long mReceiptElapsedMillis; /** The age of the NITZ signal in milliseconds, measured from its reception time. */ @DurationMillisLong private final long mAgeMillis; /** The time zone offset from UTC in milliseconds, provided by the NITZ signal. */ private final int mZoneOffset; /** * The daylight saving time (DST) offset from standard time in milliseconds, provided by the * NITZ signal. {@code null} if no DST information is available. */ private final Integer mDstOffset; /** The System.currentTimeMillis() corresponding to the NITZ signal's time. */ private final long mCurrentTimeMillis; /** * The TimeZone object representing the host time zone of the emulator, if applicable. This is * useful for testing purposes. */ @Nullable private final TimeZone mEmulatorHostTimeZone; /** * Creates a new {@link NitzSignal} instance. * * @param receiptElapsedMillis The elapsed realtime when the NITZ signal was received. * @param ageMillis The age of the NITZ signal in milliseconds. * @param zoneOffset The time zone offset from UTC in milliseconds. * @param dstOffset The DST offset in milliseconds, or {@code null}. * @param currentTimeMillis The System.currentTimeMillis() from the signal. * @param emulatorHostTimeZone The emulator host time zone, or a default if not applicable. */ public NitzSignal( long receiptElapsedMillis, long ageMillis, int zoneOffset, Integer dstOffset, long currentTimeMillis, TimeZone emulatorHostTimeZone) { this.mReceiptElapsedMillis = receiptElapsedMillis; this.mAgeMillis = ageMillis; this.mZoneOffset = zoneOffset; this.mDstOffset = dstOffset; this.mCurrentTimeMillis = currentTimeMillis; this.mEmulatorHostTimeZone = emulatorHostTimeZone; } /** Returns the elapsed realtime when the NITZ signal was received. */ public long getReceiptElapsedMillis() { return mReceiptElapsedMillis; } /** Returns the age of the NITZ signal in milliseconds. */ public long getAgeMillis() { return mAgeMillis; } /** Returns the time zone offset from UTC in milliseconds. */ public int getZoneOffset() { return mZoneOffset; } /** Returns the DST offset in milliseconds, or {@code null}. */ public Integer getDstOffset() { return mDstOffset; } /** Returns the System.currentTimeMillis() from the signal. */ public long getCurrentTimeMillis() { return mCurrentTimeMillis; } /** Returns the emulator host time zone. */ @Nullable public TimeZone getEmulatorHostTimeZone() { return mEmulatorHostTimeZone; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof NitzSignal that) { return mReceiptElapsedMillis == that.mReceiptElapsedMillis && mAgeMillis == that.mAgeMillis && mZoneOffset == that.mZoneOffset && mCurrentTimeMillis == that.mCurrentTimeMillis && Objects.equals(mDstOffset, that.mDstOffset) && Objects.equals(mEmulatorHostTimeZone, that.mEmulatorHostTimeZone); } return false; } @Override public int hashCode() { return Objects.hash( mReceiptElapsedMillis, mAgeMillis, mZoneOffset, mDstOffset, mCurrentTimeMillis, mEmulatorHostTimeZone); } @Override public String toString() { return "NitzSignal{" + "mReceiptElapsedMillis=" + mReceiptElapsedMillis + ", mAgeMillis=" + mAgeMillis + ", mZoneOffset=" + mZoneOffset + ", mDstOffset=" + mDstOffset + ", mCurrentTimeMillis=" + mCurrentTimeMillis + ", mEmulatorHostTimeZone=" + (mEmulatorHostTimeZone == null ? null : mEmulatorHostTimeZone.getID()) + '}'; } /** Implement the {@link Parcelable} interface. */ @Override public int describeContents() { return 0; // No special object types } /** * Writes the object's data to the parcel. * * @param dest The parcel to which the object's data is written. * @param flags Additional flags about how the object should be written. May be 0 or {@link * #PARCELABLE_WRITE_RETURN_VALUE}. */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mReceiptElapsedMillis); dest.writeLong(mAgeMillis); dest.writeInt(mZoneOffset); dest.writeSerializable(mDstOffset); dest.writeLong(mCurrentTimeMillis); dest.writeSerializable(mEmulatorHostTimeZone); } /** Helper to create {@link NitzSignal} objects from a {@link Parcel}. */ public static final Creator<NitzSignal> CREATOR = new Creator<>() { @Override public NitzSignal createFromParcel(Parcel in) { long receiptElapsedMillis = in.readLong(); long ageMillis = in.readLong(); int zoneOffset = in.readInt(); Integer dstOffset = in.readSerializable(Integer.class.getClassLoader(), Integer.class); long currentTimeMillis = in.readLong(); TimeZone emulatorHostTimeZone = in.readSerializable(TimeZone.class.getClassLoader(), TimeZone.class); return new NitzSignal( receiptElapsedMillis, ageMillis, zoneOffset, dstOffset, currentTimeMillis, emulatorHostTimeZone); } @Override public NitzSignal[] newArray(int size) { return new NitzSignal[size]; } }; } core/java/android/app/timezonedetector/TelephonySignal.java 0 → 100644 +191 −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.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; /** * A data class that captures information about a telephony signal. This information can be used by * the device's time zone detector to infer the device's time zone. * * @hide */ public final class TelephonySignal implements Parcelable { /** The Mobile Country Code (MCC) of the registered network. */ private final String mMcc; /** The Mobile Network Code (MNC) of the registered network. Can be {@code null}. */ @Nullable private final String mMnc; /** The default ISO 3166-1 alpha-2 country code derived from the network information. */ private final String mDefaultCountryIsoCode; /** A set of all possible ISO 3166-1 alpha-2 country codes associated with the network. */ private final Set<String> mCountryIsoCodes; /** * The NITZ (Network Identity and Timezone) signal information received, if available. Can be * {@code null}. */ @Nullable private final NitzSignal mNitzSignal; /** * Creates a new {@link TelephonySignal} instance. * * @param mcc The Mobile Country Code (MCC) of the registered network. * @param mnc The Mobile Network Code (MNC) of the registered network. Can be {@code null}. * @param defaultCountryIsoCode The default ISO 3166-1 alpha-2 country code. * @param countryIsoCodes A set of all possible ISO 3166-1 alpha-2 country codes. * @param nitzSignal The NITZ signal information, or {@code null}. */ public TelephonySignal( String mcc, @Nullable String mnc, String defaultCountryIsoCode, Set<String> countryIsoCodes, @Nullable NitzSignal nitzSignal) { this.mMcc = Objects.requireNonNull(mcc); this.mMnc = mnc; this.mDefaultCountryIsoCode = Objects.requireNonNull(defaultCountryIsoCode); this.mCountryIsoCodes = Set.copyOf(Objects.requireNonNull(countryIsoCodes)); this.mNitzSignal = nitzSignal; } /** Returns the Mobile Country Code (MCC). */ @NonNull public String getMcc() { return mMcc; } /** Returns the Mobile Network Code (MNC), or {@code null}. */ @Nullable public String getMnc() { return mMnc; } /** Returns the default country ISO code. */ @NonNull public String getDefaultCountryIsoCode() { return mDefaultCountryIsoCode; } /** Returns an unmodifiable set of associated country ISO codes. */ @NonNull public Set<String> getCountryIsoCodes() { return mCountryIsoCodes; } /** Returns the NITZ signal information, or {@code null}. */ @Nullable public NitzSignal getNitzSignal() { return mNitzSignal; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof TelephonySignal that) { return Objects.equals(mMcc, that.mMcc) && Objects.equals(mMnc, that.mMnc) && Objects.equals(mDefaultCountryIsoCode, that.mDefaultCountryIsoCode) && Objects.equals(mCountryIsoCodes, that.mCountryIsoCodes) && Objects.equals(mNitzSignal, that.mNitzSignal); } return false; } @Override public int hashCode() { return Objects.hash(mMcc, mMnc, mDefaultCountryIsoCode, mCountryIsoCodes, mNitzSignal); } @Override public String toString() { return "TelephonySignal{" + "mcc='" + mMcc + '\'' + ", mnc='" + mMnc + '\'' + ", defaultCountryIsoCode='" + mDefaultCountryIsoCode + '\'' + ", countryIsoCodes=" + mCountryIsoCodes + ", mNitzSignal=" + mNitzSignal + '}'; } /** Implement the {@link Parcelable} interface. */ @Override public int describeContents() { return 0; // No special object types } /** * Writes the object's data to the parcel. * * @param dest The parcel to which the object's data is written. * @param flags Additional flags about how the object should be written. May be 0 or {@link * #PARCELABLE_WRITE_RETURN_VALUE}. */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString8(mMcc); dest.writeString8(mMnc); dest.writeString8(mDefaultCountryIsoCode); // Convert Set to List for parceling dest.writeStringList(new ArrayList<>(mCountryIsoCodes)); dest.writeParcelable(mNitzSignal, flags); } /** Helper to create {@link TelephonySignal} objects from a {@link Parcel}. */ public static final Creator<TelephonySignal> CREATOR = new Creator<>() { @Override public TelephonySignal createFromParcel(Parcel in) { String mcc = in.readString8(); String mnc = in.readString8(); String defaultCountryIsoCode = in.readString8(); // Read List and convert back to Set List<String> tempCountryIsoCodes = new ArrayList<>(); in.readStringList(tempCountryIsoCodes); Set<String> countryIsoCodes = new HashSet<>(tempCountryIsoCodes); NitzSignal nitzSignal = in.readParcelable(NitzSignal.class.getClassLoader(), NitzSignal.class); return new TelephonySignal( mcc, mnc, defaultCountryIsoCode, countryIsoCodes, nitzSignal); } @Override public TelephonySignal[] newArray(int size) { return new TelephonySignal[size]; } }; } core/tests/timetests/src/android/app/timezonedetector/NitzSignalTest.java 0 → 100644 +186 −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 static android.app.time.ParcelableTestSupport.assertRoundTripParcelable; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.TimeZone; @RunWith(AndroidJUnit4.class) @SmallTest @Presubmit public class NitzSignalTest { private static final long RECEIPT_ELAPSED_MILLIS = 1234L; private static final long AGE_MILLIS = 5678L; private static final int ZONE_OFFSET = 3600000; private static final Integer DST_OFFSET = 3600000; private static final long CURRENT_TIME_MILLIS = 987654321L; private static final TimeZone EMU_HOST_TZ = TimeZone.getTimeZone("America/Los_Angeles"); @Test public void testEquals() { NitzSignal signal1 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); NitzSignal signal2 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertEquals(signal1, signal2); assertEquals(signal1.hashCode(), signal2.hashCode()); NitzSignal signal3 = new NitzSignal( RECEIPT_ELAPSED_MILLIS + 1, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertNotEquals(signal1, signal3); NitzSignal signal4 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS + 1, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertNotEquals(signal1, signal4); NitzSignal signal5 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET + 1, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertNotEquals(signal1, signal5); NitzSignal signal6 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, null, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertNotEquals(signal1, signal6); NitzSignal signal7 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS + 1, EMU_HOST_TZ); assertNotEquals(signal1, signal7); NitzSignal signal8 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, TimeZone.getTimeZone("Europe/London")); assertNotEquals(signal1, signal8); } @Test public void testGetters() { NitzSignal signal = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertEquals(RECEIPT_ELAPSED_MILLIS, signal.getReceiptElapsedMillis()); assertEquals(AGE_MILLIS, signal.getAgeMillis()); assertEquals(ZONE_OFFSET, signal.getZoneOffset()); assertEquals(DST_OFFSET, signal.getDstOffset()); assertEquals(CURRENT_TIME_MILLIS, signal.getCurrentTimeMillis()); assertEquals(EMU_HOST_TZ, signal.getEmulatorHostTimeZone()); } @Test public void testParcelable() { NitzSignal signalWithDst = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertRoundTripParcelable(signalWithDst); NitzSignal signalWithoutDst = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, null, CURRENT_TIME_MILLIS, null); assertRoundTripParcelable(signalWithoutDst); } @Test public void testToString() { NitzSignal signal = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); String str = signal.toString(); // A basic check that the toString() method doesn't crash and contains some info. assertTrue(str.contains(String.valueOf(RECEIPT_ELAPSED_MILLIS))); assertTrue(str.contains(String.valueOf(CURRENT_TIME_MILLIS))); assertTrue(str.contains(EMU_HOST_TZ.getID())); } } core/tests/timetests/src/android/app/timezonedetector/TelephonySignalTest.java 0 → 100644 +137 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/timezonedetector/NitzSignal.java 0 → 100644 +213 −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.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; import java.util.TimeZone; import android.annotation.ElapsedRealtimeLong; import android.annotation.DurationMillisLong; /** * A data class that captures information about a Network Identity and Timezone (NITZ) signal. This * information can be used by the device's time zone detector. * * @hide */ public final class NitzSignal implements Parcelable { /** * The elapsed realtime ({@link android.os.SystemClock#elapsedRealtime()}) when this NITZ signal * was received by the device. */ @ElapsedRealtimeLong private final long mReceiptElapsedMillis; /** The age of the NITZ signal in milliseconds, measured from its reception time. */ @DurationMillisLong private final long mAgeMillis; /** The time zone offset from UTC in milliseconds, provided by the NITZ signal. */ private final int mZoneOffset; /** * The daylight saving time (DST) offset from standard time in milliseconds, provided by the * NITZ signal. {@code null} if no DST information is available. */ private final Integer mDstOffset; /** The System.currentTimeMillis() corresponding to the NITZ signal's time. */ private final long mCurrentTimeMillis; /** * The TimeZone object representing the host time zone of the emulator, if applicable. This is * useful for testing purposes. */ @Nullable private final TimeZone mEmulatorHostTimeZone; /** * Creates a new {@link NitzSignal} instance. * * @param receiptElapsedMillis The elapsed realtime when the NITZ signal was received. * @param ageMillis The age of the NITZ signal in milliseconds. * @param zoneOffset The time zone offset from UTC in milliseconds. * @param dstOffset The DST offset in milliseconds, or {@code null}. * @param currentTimeMillis The System.currentTimeMillis() from the signal. * @param emulatorHostTimeZone The emulator host time zone, or a default if not applicable. */ public NitzSignal( long receiptElapsedMillis, long ageMillis, int zoneOffset, Integer dstOffset, long currentTimeMillis, TimeZone emulatorHostTimeZone) { this.mReceiptElapsedMillis = receiptElapsedMillis; this.mAgeMillis = ageMillis; this.mZoneOffset = zoneOffset; this.mDstOffset = dstOffset; this.mCurrentTimeMillis = currentTimeMillis; this.mEmulatorHostTimeZone = emulatorHostTimeZone; } /** Returns the elapsed realtime when the NITZ signal was received. */ public long getReceiptElapsedMillis() { return mReceiptElapsedMillis; } /** Returns the age of the NITZ signal in milliseconds. */ public long getAgeMillis() { return mAgeMillis; } /** Returns the time zone offset from UTC in milliseconds. */ public int getZoneOffset() { return mZoneOffset; } /** Returns the DST offset in milliseconds, or {@code null}. */ public Integer getDstOffset() { return mDstOffset; } /** Returns the System.currentTimeMillis() from the signal. */ public long getCurrentTimeMillis() { return mCurrentTimeMillis; } /** Returns the emulator host time zone. */ @Nullable public TimeZone getEmulatorHostTimeZone() { return mEmulatorHostTimeZone; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof NitzSignal that) { return mReceiptElapsedMillis == that.mReceiptElapsedMillis && mAgeMillis == that.mAgeMillis && mZoneOffset == that.mZoneOffset && mCurrentTimeMillis == that.mCurrentTimeMillis && Objects.equals(mDstOffset, that.mDstOffset) && Objects.equals(mEmulatorHostTimeZone, that.mEmulatorHostTimeZone); } return false; } @Override public int hashCode() { return Objects.hash( mReceiptElapsedMillis, mAgeMillis, mZoneOffset, mDstOffset, mCurrentTimeMillis, mEmulatorHostTimeZone); } @Override public String toString() { return "NitzSignal{" + "mReceiptElapsedMillis=" + mReceiptElapsedMillis + ", mAgeMillis=" + mAgeMillis + ", mZoneOffset=" + mZoneOffset + ", mDstOffset=" + mDstOffset + ", mCurrentTimeMillis=" + mCurrentTimeMillis + ", mEmulatorHostTimeZone=" + (mEmulatorHostTimeZone == null ? null : mEmulatorHostTimeZone.getID()) + '}'; } /** Implement the {@link Parcelable} interface. */ @Override public int describeContents() { return 0; // No special object types } /** * Writes the object's data to the parcel. * * @param dest The parcel to which the object's data is written. * @param flags Additional flags about how the object should be written. May be 0 or {@link * #PARCELABLE_WRITE_RETURN_VALUE}. */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mReceiptElapsedMillis); dest.writeLong(mAgeMillis); dest.writeInt(mZoneOffset); dest.writeSerializable(mDstOffset); dest.writeLong(mCurrentTimeMillis); dest.writeSerializable(mEmulatorHostTimeZone); } /** Helper to create {@link NitzSignal} objects from a {@link Parcel}. */ public static final Creator<NitzSignal> CREATOR = new Creator<>() { @Override public NitzSignal createFromParcel(Parcel in) { long receiptElapsedMillis = in.readLong(); long ageMillis = in.readLong(); int zoneOffset = in.readInt(); Integer dstOffset = in.readSerializable(Integer.class.getClassLoader(), Integer.class); long currentTimeMillis = in.readLong(); TimeZone emulatorHostTimeZone = in.readSerializable(TimeZone.class.getClassLoader(), TimeZone.class); return new NitzSignal( receiptElapsedMillis, ageMillis, zoneOffset, dstOffset, currentTimeMillis, emulatorHostTimeZone); } @Override public NitzSignal[] newArray(int size) { return new NitzSignal[size]; } }; }
core/java/android/app/timezonedetector/TelephonySignal.java 0 → 100644 +191 −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.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; /** * A data class that captures information about a telephony signal. This information can be used by * the device's time zone detector to infer the device's time zone. * * @hide */ public final class TelephonySignal implements Parcelable { /** The Mobile Country Code (MCC) of the registered network. */ private final String mMcc; /** The Mobile Network Code (MNC) of the registered network. Can be {@code null}. */ @Nullable private final String mMnc; /** The default ISO 3166-1 alpha-2 country code derived from the network information. */ private final String mDefaultCountryIsoCode; /** A set of all possible ISO 3166-1 alpha-2 country codes associated with the network. */ private final Set<String> mCountryIsoCodes; /** * The NITZ (Network Identity and Timezone) signal information received, if available. Can be * {@code null}. */ @Nullable private final NitzSignal mNitzSignal; /** * Creates a new {@link TelephonySignal} instance. * * @param mcc The Mobile Country Code (MCC) of the registered network. * @param mnc The Mobile Network Code (MNC) of the registered network. Can be {@code null}. * @param defaultCountryIsoCode The default ISO 3166-1 alpha-2 country code. * @param countryIsoCodes A set of all possible ISO 3166-1 alpha-2 country codes. * @param nitzSignal The NITZ signal information, or {@code null}. */ public TelephonySignal( String mcc, @Nullable String mnc, String defaultCountryIsoCode, Set<String> countryIsoCodes, @Nullable NitzSignal nitzSignal) { this.mMcc = Objects.requireNonNull(mcc); this.mMnc = mnc; this.mDefaultCountryIsoCode = Objects.requireNonNull(defaultCountryIsoCode); this.mCountryIsoCodes = Set.copyOf(Objects.requireNonNull(countryIsoCodes)); this.mNitzSignal = nitzSignal; } /** Returns the Mobile Country Code (MCC). */ @NonNull public String getMcc() { return mMcc; } /** Returns the Mobile Network Code (MNC), or {@code null}. */ @Nullable public String getMnc() { return mMnc; } /** Returns the default country ISO code. */ @NonNull public String getDefaultCountryIsoCode() { return mDefaultCountryIsoCode; } /** Returns an unmodifiable set of associated country ISO codes. */ @NonNull public Set<String> getCountryIsoCodes() { return mCountryIsoCodes; } /** Returns the NITZ signal information, or {@code null}. */ @Nullable public NitzSignal getNitzSignal() { return mNitzSignal; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof TelephonySignal that) { return Objects.equals(mMcc, that.mMcc) && Objects.equals(mMnc, that.mMnc) && Objects.equals(mDefaultCountryIsoCode, that.mDefaultCountryIsoCode) && Objects.equals(mCountryIsoCodes, that.mCountryIsoCodes) && Objects.equals(mNitzSignal, that.mNitzSignal); } return false; } @Override public int hashCode() { return Objects.hash(mMcc, mMnc, mDefaultCountryIsoCode, mCountryIsoCodes, mNitzSignal); } @Override public String toString() { return "TelephonySignal{" + "mcc='" + mMcc + '\'' + ", mnc='" + mMnc + '\'' + ", defaultCountryIsoCode='" + mDefaultCountryIsoCode + '\'' + ", countryIsoCodes=" + mCountryIsoCodes + ", mNitzSignal=" + mNitzSignal + '}'; } /** Implement the {@link Parcelable} interface. */ @Override public int describeContents() { return 0; // No special object types } /** * Writes the object's data to the parcel. * * @param dest The parcel to which the object's data is written. * @param flags Additional flags about how the object should be written. May be 0 or {@link * #PARCELABLE_WRITE_RETURN_VALUE}. */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString8(mMcc); dest.writeString8(mMnc); dest.writeString8(mDefaultCountryIsoCode); // Convert Set to List for parceling dest.writeStringList(new ArrayList<>(mCountryIsoCodes)); dest.writeParcelable(mNitzSignal, flags); } /** Helper to create {@link TelephonySignal} objects from a {@link Parcel}. */ public static final Creator<TelephonySignal> CREATOR = new Creator<>() { @Override public TelephonySignal createFromParcel(Parcel in) { String mcc = in.readString8(); String mnc = in.readString8(); String defaultCountryIsoCode = in.readString8(); // Read List and convert back to Set List<String> tempCountryIsoCodes = new ArrayList<>(); in.readStringList(tempCountryIsoCodes); Set<String> countryIsoCodes = new HashSet<>(tempCountryIsoCodes); NitzSignal nitzSignal = in.readParcelable(NitzSignal.class.getClassLoader(), NitzSignal.class); return new TelephonySignal( mcc, mnc, defaultCountryIsoCode, countryIsoCodes, nitzSignal); } @Override public TelephonySignal[] newArray(int size) { return new TelephonySignal[size]; } }; }
core/tests/timetests/src/android/app/timezonedetector/NitzSignalTest.java 0 → 100644 +186 −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 static android.app.time.ParcelableTestSupport.assertRoundTripParcelable; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.TimeZone; @RunWith(AndroidJUnit4.class) @SmallTest @Presubmit public class NitzSignalTest { private static final long RECEIPT_ELAPSED_MILLIS = 1234L; private static final long AGE_MILLIS = 5678L; private static final int ZONE_OFFSET = 3600000; private static final Integer DST_OFFSET = 3600000; private static final long CURRENT_TIME_MILLIS = 987654321L; private static final TimeZone EMU_HOST_TZ = TimeZone.getTimeZone("America/Los_Angeles"); @Test public void testEquals() { NitzSignal signal1 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); NitzSignal signal2 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertEquals(signal1, signal2); assertEquals(signal1.hashCode(), signal2.hashCode()); NitzSignal signal3 = new NitzSignal( RECEIPT_ELAPSED_MILLIS + 1, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertNotEquals(signal1, signal3); NitzSignal signal4 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS + 1, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertNotEquals(signal1, signal4); NitzSignal signal5 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET + 1, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertNotEquals(signal1, signal5); NitzSignal signal6 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, null, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertNotEquals(signal1, signal6); NitzSignal signal7 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS + 1, EMU_HOST_TZ); assertNotEquals(signal1, signal7); NitzSignal signal8 = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, TimeZone.getTimeZone("Europe/London")); assertNotEquals(signal1, signal8); } @Test public void testGetters() { NitzSignal signal = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertEquals(RECEIPT_ELAPSED_MILLIS, signal.getReceiptElapsedMillis()); assertEquals(AGE_MILLIS, signal.getAgeMillis()); assertEquals(ZONE_OFFSET, signal.getZoneOffset()); assertEquals(DST_OFFSET, signal.getDstOffset()); assertEquals(CURRENT_TIME_MILLIS, signal.getCurrentTimeMillis()); assertEquals(EMU_HOST_TZ, signal.getEmulatorHostTimeZone()); } @Test public void testParcelable() { NitzSignal signalWithDst = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); assertRoundTripParcelable(signalWithDst); NitzSignal signalWithoutDst = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, null, CURRENT_TIME_MILLIS, null); assertRoundTripParcelable(signalWithoutDst); } @Test public void testToString() { NitzSignal signal = new NitzSignal( RECEIPT_ELAPSED_MILLIS, AGE_MILLIS, ZONE_OFFSET, DST_OFFSET, CURRENT_TIME_MILLIS, EMU_HOST_TZ); String str = signal.toString(); // A basic check that the toString() method doesn't crash and contains some info. assertTrue(str.contains(String.valueOf(RECEIPT_ELAPSED_MILLIS))); assertTrue(str.contains(String.valueOf(CURRENT_TIME_MILLIS))); assertTrue(str.contains(EMU_HOST_TZ.getID())); } }
core/tests/timetests/src/android/app/timezonedetector/TelephonySignalTest.java 0 → 100644 +137 −0 File added.Preview size limit exceeded, changes collapsed. Show changes