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

Commit fc1d073b authored by Neil Fuller's avatar Neil Fuller
Browse files

Move time zone detection logic to system server

See the associated change in frameworks/base/

Bug: 140712361
Test: atest com.android.internal.telephony.nitz
Test: atest com.android.internal.telephony.NitzStateMachineImplTest
Test: build / boot
Change-Id: I2c301e53cda63f670c20d3b5e361dcf2318537d3
parent c2ea7e8b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.internal.telephony.nitz;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timedetector.PhoneTimeSuggestion;
import android.app.timezonedetector.PhoneTimeZoneSuggestion;
import android.content.Context;
import android.telephony.Rlog;
import android.util.TimestampedValue;
@@ -28,7 +29,6 @@ import com.android.internal.telephony.NitzData;
import com.android.internal.telephony.NitzStateMachine;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TimeZoneLookupHelper;
import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
import com.android.internal.util.IndentingPrintWriter;

import java.io.FileDescriptor;
@@ -287,6 +287,7 @@ public final class NewNitzStateMachineImpl implements NitzStateMachine {
            PhoneTimeZoneSuggestion suggestion =
                    mTimeZoneSuggester.getTimeZoneSuggestion(mPhoneId, countryIsoCode, nitzSignal);
            suggestion.addDebugInfo("Detection reason=" + reason);

            if (DBG) {
                Rlog.d(LOG_TAG, "doTimeZoneDetection: countryIsoCode=" + countryIsoCode
                        + ", nitzSignal=" + nitzSignal + ", suggestion=" + suggestion
+1 −1
Original line number Diff line number Diff line
@@ -19,8 +19,8 @@ package com.android.internal.telephony.nitz;
import android.annotation.NonNull;
import android.app.timedetector.PhoneTimeSuggestion;
import android.app.timedetector.TimeDetector;
import android.app.timezonedetector.PhoneTimeZoneSuggestion;

import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
import com.android.internal.util.IndentingPrintWriter;

import java.io.PrintWriter;
+5 −8
Original line number Diff line number Diff line
@@ -20,14 +20,14 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timedetector.PhoneTimeSuggestion;
import android.app.timedetector.TimeDetector;
import android.app.timezonedetector.PhoneTimeZoneSuggestion;
import android.app.timezonedetector.TimeZoneDetector;
import android.content.Context;
import android.util.LocalLog;
import android.util.TimestampedValue;

import com.android.internal.telephony.Phone;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
import com.android.internal.telephony.nitz.service.TimeZoneDetectionService;
import com.android.internal.util.IndentingPrintWriter;

import java.io.PrintWriter;
@@ -40,7 +40,7 @@ public final class NewTimeServiceHelperImpl implements NewTimeServiceHelper {

    private final int mPhoneId;
    private final TimeDetector mTimeDetector;
    private final TimeZoneDetectionService mTimeZoneDetector;
    private final TimeZoneDetector mTimeZoneDetector;

    private final LocalLog mTimeZoneLog = new LocalLog(30);
    private final LocalLog mTimeLog = new LocalLog(30);
@@ -57,7 +57,8 @@ public final class NewTimeServiceHelperImpl implements NewTimeServiceHelper {
        mPhoneId = phone.getPhoneId();
        Context context = Objects.requireNonNull(phone.getContext());
        mTimeDetector = Objects.requireNonNull(context.getSystemService(TimeDetector.class));
        mTimeZoneDetector = Objects.requireNonNull(TimeZoneDetectionService.getInstance(context));
        mTimeZoneDetector =
                Objects.requireNonNull(context.getSystemService(TimeZoneDetector.class));
    }

    @Override
@@ -110,14 +111,10 @@ public final class NewTimeServiceHelperImpl implements NewTimeServiceHelper {
        mTimeZoneLog.dump(ipw);
        ipw.decreaseIndent();
        ipw.decreaseIndent();

        // TODO Remove this line when the service moves to the system server.
        mTimeZoneDetector.dumpLogs(ipw);
    }

    @Override
    public void dumpState(PrintWriter pw) {
        pw.println(" NewTimeServiceHelperImpl.mLastSuggestedTimeZone=" + mLastSuggestedTimeZone);
        mTimeZoneDetector.dumpState(pw);
    }
}
+70 −60
Original line number Diff line number Diff line
@@ -16,14 +16,11 @@

package com.android.internal.telephony.nitz;

import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_DEFAULT_BOOSTED;
import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS;
import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_MULTIPLE_ZONES_SAME_OFFSET;
import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_SINGLE_ZONE;
import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.createEmptySuggestion;
import static android.app.timezonedetector.PhoneTimeZoneSuggestion.createEmptySuggestion;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timezonedetector.PhoneTimeZoneSuggestion;
import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.TimestampedValue;
@@ -34,7 +31,6 @@ import com.android.internal.telephony.NitzStateMachine.DeviceState;
import com.android.internal.telephony.TimeZoneLookupHelper;
import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
import com.android.internal.telephony.nitz.NewNitzStateMachineImpl.TimeZoneSuggester;
import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;

import java.util.Objects;

@@ -66,11 +62,13 @@ public class TimeZoneSuggesterImpl implements TimeZoneSuggester {
            if (nitzSignal != null) {
                NitzData nitzData = nitzSignal.getValue();
                if (nitzData.getEmulatorHostTimeZone() != null) {
                    overridingSuggestion = new PhoneTimeZoneSuggestion(phoneId);
                    overridingSuggestion.setZoneId(nitzData.getEmulatorHostTimeZone().getID());
                    overridingSuggestion.setMatchType(PhoneTimeZoneSuggestion.EMULATOR_ZONE_ID);
                    overridingSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
                    overridingSuggestion.addDebugInfo("Emulator time zone override: " + nitzData);
                    PhoneTimeZoneSuggestion.Builder builder =
                            new PhoneTimeZoneSuggestion.Builder(phoneId)
                            .setZoneId(nitzData.getEmulatorHostTimeZone().getID())
                            .setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID)
                            .setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
                            .addDebugInfo("Emulator time zone override: " + nitzData);
                    overridingSuggestion = builder.build();
                }
            }

@@ -125,7 +123,6 @@ public class TimeZoneSuggesterImpl implements TimeZoneSuggester {
                    + ", nitzSignal=" + nitzSignal
                    + ", e=" + e.getMessage();
            PhoneTimeZoneSuggestion errorSuggestion = createEmptySuggestion(phoneId, message);
            errorSuggestion.addDebugInfo(message);
            Rlog.w(LOG_TAG, message, e);
            return errorSuggestion;
        }
@@ -142,21 +139,25 @@ public class TimeZoneSuggesterImpl implements TimeZoneSuggester {
        Objects.requireNonNull(nitzSignal);
        NitzData nitzData = Objects.requireNonNull(nitzSignal.getValue());

        PhoneTimeZoneSuggestion result = new PhoneTimeZoneSuggestion(phoneId);
        result.addDebugInfo("findTimeZoneForTestNetwork: nitzSignal=" + nitzSignal);
        PhoneTimeZoneSuggestion.Builder suggestionBuilder =
                new PhoneTimeZoneSuggestion.Builder(phoneId);
        suggestionBuilder.addDebugInfo("findTimeZoneForTestNetwork: nitzSignal=" + nitzSignal);
        TimeZoneLookupHelper.OffsetResult lookupResult =
                mTimeZoneLookupHelper.lookupByNitz(nitzData);
        if (lookupResult == null) {
            result.addDebugInfo("findTimeZoneForTestNetwork: No zone found");
            suggestionBuilder.addDebugInfo("findTimeZoneForTestNetwork: No zone found");
        } else {
            result.setZoneId(lookupResult.getTimeZone().getID());
            result.setMatchType(PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY);
            int quality = lookupResult.getIsOnlyMatch() ? PhoneTimeZoneSuggestion.SINGLE_ZONE
                    : PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET;
            result.setQuality(quality);
            result.addDebugInfo("findTimeZoneForTestNetwork: lookupResult=" + lookupResult);
            suggestionBuilder.setZoneId(lookupResult.getTimeZone().getID());
            suggestionBuilder.setMatchType(
                    PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY);
            int quality = lookupResult.getIsOnlyMatch()
                    ? PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE
                    : PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
            suggestionBuilder.setQuality(quality);
            suggestionBuilder.addDebugInfo(
                    "findTimeZoneForTestNetwork: lookupResult=" + lookupResult);
        }
        return result;
        return suggestionBuilder.build();
    }

    /**
@@ -169,27 +170,32 @@ public class TimeZoneSuggesterImpl implements TimeZoneSuggester {
        Objects.requireNonNull(countryIsoCode);
        Objects.requireNonNull(nitzSignal);

        PhoneTimeZoneSuggestion suggestion = new PhoneTimeZoneSuggestion(phoneId);
        suggestion.addDebugInfo("findTimeZoneFromCountryAndNitz: countryIsoCode=" + countryIsoCode
        PhoneTimeZoneSuggestion.Builder suggestionBuilder =
                new PhoneTimeZoneSuggestion.Builder(phoneId);
        suggestionBuilder.addDebugInfo("findTimeZoneFromCountryAndNitz:"
                + " countryIsoCode=" + countryIsoCode
                + ", nitzSignal=" + nitzSignal);
        NitzData nitzData = Objects.requireNonNull(nitzSignal.getValue());
        if (isNitzSignalOffsetInfoBogus(countryIsoCode, nitzData)) {
            suggestion.addDebugInfo("findTimeZoneFromCountryAndNitz: NITZ signal looks bogus");
            return suggestion;
            suggestionBuilder.addDebugInfo(
                    "findTimeZoneFromCountryAndNitz: NITZ signal looks bogus");
            return suggestionBuilder.build();
        }

        // Try to find a match using both country + NITZ signal.
        TimeZoneLookupHelper.OffsetResult lookupResult =
                mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, countryIsoCode);
        if (lookupResult != null) {
            suggestion.setZoneId(lookupResult.getTimeZone().getID());
            suggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
            suggestionBuilder.setZoneId(lookupResult.getTimeZone().getID());
            suggestionBuilder.setMatchType(
                    PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET);
            int quality = lookupResult.getIsOnlyMatch()
                    ? PhoneTimeZoneSuggestion.SINGLE_ZONE
                    : PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET;
            suggestion.setQuality(quality);
            suggestion.addDebugInfo("findTimeZoneFromCountryAndNitz: lookupResult=" + lookupResult);
            return suggestion;
                    ? PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE
                    : PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
            suggestionBuilder.setQuality(quality);
            suggestionBuilder.addDebugInfo("findTimeZoneFromCountryAndNitz:"
                    + " lookupResult=" + lookupResult);
            return suggestionBuilder.build();
        }

        // The country + offset provided no match, so see if the country by itself would be enough.
@@ -197,29 +203,29 @@ public class TimeZoneSuggesterImpl implements TimeZoneSuggester {
                countryIsoCode, nitzData.getCurrentTimeInMillis());
        if (countryResult == null) {
            // Country not recognized.
            suggestion.addDebugInfo(
            suggestionBuilder.addDebugInfo(
                    "findTimeZoneFromCountryAndNitz: lookupByCountry() country not recognized");
            return suggestion;
            return suggestionBuilder.build();
        }

        // If the country has a single zone, or it has multiple zones but the default zone is
        // "boosted" (i.e. the country default is considered a good suggestion in most cases) then
        // use it.
        if (countryResult.quality == QUALITY_SINGLE_ZONE
                || countryResult.quality == QUALITY_DEFAULT_BOOSTED) {
            suggestion.setZoneId(countryResult.zoneId);
            suggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
            suggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
            suggestion.addDebugInfo(
        if (countryResult.quality == CountryResult.QUALITY_SINGLE_ZONE
                || countryResult.quality == CountryResult.QUALITY_DEFAULT_BOOSTED) {
            suggestionBuilder.setZoneId(countryResult.zoneId);
            suggestionBuilder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
            suggestionBuilder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
            suggestionBuilder.addDebugInfo(
                    "findTimeZoneFromCountryAndNitz: high quality country-only suggestion:"
                            + " countryResult=" + countryResult);
            return suggestion;
            return suggestionBuilder.build();
        }

        // Quality is not high enough to set the zone using country only.
        suggestion.addDebugInfo("findTimeZoneFromCountryAndNitz: country-only suggestion quality"
                + " not high enough. countryResult=" + countryResult);
        return suggestion;
        suggestionBuilder.addDebugInfo("findTimeZoneFromCountryAndNitz: country-only suggestion"
                + " quality not high enough. countryResult=" + countryResult);
        return suggestionBuilder.build();
    }

    /**
@@ -237,35 +243,39 @@ public class TimeZoneSuggesterImpl implements TimeZoneSuggester {
            throw new IllegalArgumentException("countryIsoCode must not be empty");
        }

        PhoneTimeZoneSuggestion result = new PhoneTimeZoneSuggestion(phoneId);
        result.addDebugInfo("findTimeZoneFromNetworkCountryCode:"
        PhoneTimeZoneSuggestion.Builder suggestionBuilder =
                new PhoneTimeZoneSuggestion.Builder(phoneId);
        suggestionBuilder.addDebugInfo("findTimeZoneFromNetworkCountryCode:"
                + " whenMillis=" + whenMillis + ", countryIsoCode=" + countryIsoCode);
        CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
                countryIsoCode, whenMillis);
        if (lookupResult != null) {
            result.setZoneId(lookupResult.zoneId);
            result.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
            suggestionBuilder.setZoneId(lookupResult.zoneId);
            suggestionBuilder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);

            int quality;
            if (lookupResult.quality == QUALITY_SINGLE_ZONE
                    || lookupResult.quality == QUALITY_DEFAULT_BOOSTED) {
                quality = PhoneTimeZoneSuggestion.SINGLE_ZONE;
            } else if (lookupResult.quality == QUALITY_MULTIPLE_ZONES_SAME_OFFSET) {
                quality = PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET;
            } else if (lookupResult.quality == QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS) {
                quality = PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
            if (lookupResult.quality == CountryResult.QUALITY_SINGLE_ZONE
                    || lookupResult.quality == CountryResult.QUALITY_DEFAULT_BOOSTED) {
                quality = PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
            } else if (lookupResult.quality == CountryResult.QUALITY_MULTIPLE_ZONES_SAME_OFFSET) {
                quality = PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
            } else if (lookupResult.quality
                    == CountryResult.QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS) {
                quality = PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
            } else {
                // This should never happen.
                throw new IllegalArgumentException(
                        "lookupResult.quality not recognized: countryIsoCode=" + countryIsoCode
                                + ", whenMillis=" + whenMillis + ", lookupResult=" + lookupResult);
            }
            result.setQuality(quality);
            result.addDebugInfo("findTimeZoneFromNetworkCountryCode: lookupResult=" + lookupResult);
            suggestionBuilder.setQuality(quality);
            suggestionBuilder.addDebugInfo(
                    "findTimeZoneFromNetworkCountryCode: lookupResult=" + lookupResult);
        } else {
            result.addDebugInfo("findTimeZoneFromNetworkCountryCode: Country not recognized?");
            suggestionBuilder.addDebugInfo(
                    "findTimeZoneFromNetworkCountryCode: Country not recognized?");
        }
        return result;
        return suggestionBuilder.build();
    }

    /**
+0 −254
Original line number Diff line number Diff line
/*
 * Copyright 2019 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.internal.telephony.nitz.service;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * A suggested time zone from a Phone-based signal, e.g. from MCC and NITZ information.
 */
public final class PhoneTimeZoneSuggestion implements Parcelable {

    public static final Creator<PhoneTimeZoneSuggestion> CREATOR =
            new Creator<PhoneTimeZoneSuggestion>() {
                public PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
                    return PhoneTimeZoneSuggestion.createFromParcel(in);
                }

                public PhoneTimeZoneSuggestion[] newArray(int size) {
                    return new PhoneTimeZoneSuggestion[size];
                }
            };

    /**
     * Creates an empty time zone suggestion, i.e. one that will cancel previous suggestions with
     * the same {@code phoneId}.
     */
    @NonNull
    public static PhoneTimeZoneSuggestion createEmptySuggestion(
            int phoneId, @NonNull String debugInfo) {
        PhoneTimeZoneSuggestion timeZoneSuggestion = new PhoneTimeZoneSuggestion(phoneId);
        timeZoneSuggestion.addDebugInfo(debugInfo);
        return timeZoneSuggestion;
    }

    @IntDef({ MATCH_TYPE_NA, NETWORK_COUNTRY_ONLY, NETWORK_COUNTRY_AND_OFFSET, EMULATOR_ZONE_ID,
            TEST_NETWORK_OFFSET_ONLY })
    @Retention(RetentionPolicy.SOURCE)
    public @interface MatchType {}

    /** Used when match type is not applicable. */
    public static final int MATCH_TYPE_NA = 0;

    /**
     * Only the network country is known.
     */
    public static final int NETWORK_COUNTRY_ONLY = 2;

    /**
     * Both the network county and offset were known.
     */
    public static final int NETWORK_COUNTRY_AND_OFFSET = 3;

    /**
     * The device is running in an emulator and an NITZ signal was simulated containing an
     * Android extension with an explicit Olson ID.
     */
    public static final int EMULATOR_ZONE_ID = 4;

    /**
     * The phone is most likely running in a test network not associated with a country (this is
     * distinct from the country just not being known yet).
     * Historically, Android has just picked an arbitrary time zone with the correct offset when
     * on a test network.
     */
    public static final int TEST_NETWORK_OFFSET_ONLY = 5;

    @IntDef({ QUALITY_NA, SINGLE_ZONE, MULTIPLE_ZONES_WITH_SAME_OFFSET,
            MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Quality {}

    /** Used when quality is not applicable. */
    public static final int QUALITY_NA = 0;

    /** There is only one answer */
    public static final int SINGLE_ZONE = 1;

    /**
     * There are multiple answers, but they all shared the same offset / DST state at the time
     * the suggestion was created. i.e. it might be the wrong zone but the user won't notice
     * immediately if it is wrong.
     */
    public static final int MULTIPLE_ZONES_WITH_SAME_OFFSET = 2;

    /**
     * There are multiple answers with different offsets. The one given is just one possible.
     */
    public static final int MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3;

    /**
     * The ID of the phone this suggestion is associated with. For multiple-sim devices this
     * helps to establish origin so filtering / stickiness can be implemented.
     */
    private final int mPhoneId;

    /**
     * The suggestion. {@code null} means there is no current suggestion and any previous suggestion
     * should be forgotten.
     */
    private String mZoneId;

    /**
     * The type of "match" used to establish the time zone.
     */
    @MatchType
    private int mMatchType;

    /**
     * A measure of the quality of the time zone suggestion, i.e. how confident one could be in
     * it.
     */
    @Quality
    private int mQuality;

    /**
     * Free-form debug information about how the signal was derived. Used for debug only,
     * intentionally not used in equals(), etc.
     */
    private List<String> mDebugInfo;

    public PhoneTimeZoneSuggestion(int phoneId) {
        this.mPhoneId = phoneId;
    }

    @SuppressWarnings("unchecked")
    private static PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
        int phoneId = in.readInt();
        PhoneTimeZoneSuggestion phoneTimeZoneSuggestion = new PhoneTimeZoneSuggestion(phoneId);
        phoneTimeZoneSuggestion.mZoneId = in.readString();
        phoneTimeZoneSuggestion.mMatchType = in.readInt();
        phoneTimeZoneSuggestion.mQuality = in.readInt();
        phoneTimeZoneSuggestion.mDebugInfo =
                (List<String>) in.readArrayList(PhoneTimeZoneSuggestion.class.getClassLoader());
        return phoneTimeZoneSuggestion;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mPhoneId);
        dest.writeString(mZoneId);
        dest.writeInt(mMatchType);
        dest.writeInt(mQuality);
        dest.writeList(mDebugInfo);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public int getPhoneId() {
        return mPhoneId;
    }

    @Nullable
    public String getZoneId() {
        return mZoneId;
    }

    public void setZoneId(@Nullable String zoneId) {
        this.mZoneId = zoneId;
    }


    @MatchType
    public int getMatchType() {
        return mMatchType;
    }

    public void setMatchType(@MatchType int matchType) {
        this.mMatchType = matchType;
    }
    @Quality
    public int getQuality() {
        return mQuality;
    }

    public void setQuality(@Quality int quality) {
        this.mQuality = quality;
    }

    public List<String> getDebugInfo() {
        return Collections.unmodifiableList(mDebugInfo);
    }

    /**
     * Associates information with the instance that can be useful for debugging / logging. The
     * information is present in {@link #toString()} but is not considered for
     * {@link #equals(Object)} and {@link #hashCode()}.
     */
    public void addDebugInfo(String... debugInfos) {
        if (mDebugInfo == null) {
            mDebugInfo = new ArrayList<>();
        }
        mDebugInfo.addAll(Arrays.asList(debugInfos));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        PhoneTimeZoneSuggestion that = (PhoneTimeZoneSuggestion) o;
        return mPhoneId == that.mPhoneId
                && mMatchType == that.mMatchType
                && mQuality == that.mQuality
                && Objects.equals(mZoneId, that.mZoneId);
    }

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

    @Override
    public String toString() {
        return "PhoneTimeZoneSuggestion{"
                + "mPhoneId=" + mPhoneId
                + ", mZoneId='" + mZoneId + '\''
                + ", mMatchType=" + mMatchType
                + ", mQuality=" + mQuality
                + ", mDebugInfo=" + mDebugInfo
                + '}';
    }
}
Loading