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

Commit 7c39347b authored by Neil Fuller's avatar Neil Fuller Committed by android-build-merger
Browse files

Merge changes from topic "telephony_aosp_cp" am: 64f7d20e

am: e0d10db0

Change-Id: I50b54cee0ac9e37d478bbf7c3d8321f8b9632d11
parents 270af1d7 e0d10db0
Loading
Loading
Loading
Loading
+4 −44
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import android.telephony.Rlog;
import com.android.internal.annotations.VisibleForTesting;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
@@ -35,7 +34,6 @@ import java.util.TimeZone;
@VisibleForTesting(visibility = PACKAGE)
public final class NitzData {
    private static final String LOG_TAG = ServiceStateTracker.LOG_TAG;
    private static final int MS_PER_HOUR = 60 * 60 * 1000;
    private static final int MS_PER_QUARTER_HOUR = 15 * 60 * 1000;

    /* Time stamp after 19 January 2038 is not supported under 32 bit */
@@ -53,7 +51,7 @@ public final class NitzData {
    private final TimeZone mEmulatorHostTimeZone;

    private NitzData(String originalString, int zoneOffsetMillis, Integer dstOffsetMillis,
            long utcTimeMillis, TimeZone timeZone) {
            long utcTimeMillis, TimeZone emulatorHostTimeZone) {
        if (originalString == null) {
            throw new NullPointerException("originalString==null");
        }
@@ -61,7 +59,7 @@ public final class NitzData {
        this.mZoneOffset = zoneOffsetMillis;
        this.mDstOffset = dstOffsetMillis;
        this.mCurrentTimeMillis = utcTimeMillis;
        this.mEmulatorHostTimeZone = timeZone;
        this.mEmulatorHostTimeZone = emulatorHostTimeZone;
    }

    /**
@@ -139,9 +137,9 @@ public final class NitzData {

    /** A method for use in tests to create NitzData instances. */
    public static NitzData createForTests(int zoneOffsetMillis, Integer dstOffsetMillis,
            long utcTimeMillis, TimeZone timeZone) {
            long utcTimeMillis, TimeZone emulatorHostTimeZone) {
        return new NitzData("Test data", zoneOffsetMillis, dstOffsetMillis, utcTimeMillis,
                timeZone);
                emulatorHostTimeZone);
    }

    /**
@@ -187,44 +185,6 @@ public final class NitzData {
        return mEmulatorHostTimeZone;
    }

    /**
     * Using information present in the supplied {@link NitzData} object, guess the time zone.
     * Because multiple time zones can have the same offset / DST state at a given time this process
     * is error prone; an arbitrary match is returned when there are multiple candidates. The
     * algorithm can also return a non-exact match by assuming that the DST information provided by
     * NITZ is incorrect. This method can return {@code null} if no time zones are found.
     */
    public static TimeZone guessTimeZone(NitzData nitzData) {
        int offset = nitzData.getLocalOffsetMillis();
        boolean dst = nitzData.isDst();
        long when = nitzData.getCurrentTimeInMillis();
        TimeZone guess = findTimeZone(offset, dst, when);
        if (guess == null) {
            // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
            guess = findTimeZone(offset, !dst, when);
        }
        return guess;
    }

    private static TimeZone findTimeZone(int offset, boolean dst, long when) {
        int rawOffset = offset;
        if (dst) {
            rawOffset -= MS_PER_HOUR;
        }
        String[] zones = TimeZone.getAvailableIDs(rawOffset);
        TimeZone guess = null;
        Date d = new Date(when);
        for (String zone : zones) {
            TimeZone tz = TimeZone.getTimeZone(zone);
            if (tz.getOffset(when) == offset && tz.inDaylightTime(d) == dst) {
                guess = tz;
                break;
            }
        }

        return guess;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
+191 −237

File changed.

Preview size limit exceeded, changes collapsed.

+57 −75
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ import com.android.internal.telephony.uicc.SIMRecords;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.internal.telephony.util.TimeStampedValue;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;

@@ -559,7 +560,7 @@ public class ServiceStateTracker extends Handler {
        mMin = null;
        mPrlVersion = null;
        mIsMinInfoReady = false;
        mNitzState.clearNitzTimeZoneDetectionSuccessful();
        mNitzState.handleNetworkUnavailable();

        //cancel any pending pollstate request on voice tech switching
        cancelPollState();
@@ -2547,8 +2548,7 @@ public class ServiceStateTracker extends Handler {
                mNewSS.setStateOutOfService();
                mNewCellLoc.setStateInvalid();
                setSignalStrengthDefaultValues();
                mNitzState.setNetworkCountryIsoAvailable(false);
                mNitzState.clearNitzTimeZoneDetectionSuccessful();
                mNitzState.handleNetworkUnavailable();
                pollStateDone();
                break;

@@ -2556,8 +2556,7 @@ public class ServiceStateTracker extends Handler {
                mNewSS.setStateOff();
                mNewCellLoc.setStateInvalid();
                setSignalStrengthDefaultValues();
                mNitzState.setNetworkCountryIsoAvailable(false);
                mNitzState.clearNitzTimeZoneDetectionSuccessful();
                mNitzState.handleNetworkUnavailable();
                // don't poll when device is shutting down or the poll was not modemTrigged
                // (they sent us new radio data) and current network is not IWLAN
                if (mDeviceShuttingDown ||
@@ -2788,18 +2787,12 @@ public class ServiceStateTracker extends Handler {

        if (hasRegistered) {
            mNetworkAttachedRegistrants.notifyRegistrants();

            if (DBG) {
                log("pollStateDone: hasRegistered,"
                        + " current mNitzState.getNitzTimeZoneDetectionSuccessful()="
                        + mNitzState.getNitzTimeZoneDetectionSuccessful()
                        + ". Calling mNitzState.clearNitzTimeZoneDetectionSuccessful()");
            }
            mNitzState.clearNitzTimeZoneDetectionSuccessful();
            mNitzState.handleNetworkAvailable();
        }

        if (hasDeregistered) {
            mNetworkDetachedRegistrants.notifyRegistrants();
            mNitzState.handleNetworkUnavailable();
        }

        if (hasRejectCauseChanged) {
@@ -2812,6 +2805,7 @@ public class ServiceStateTracker extends Handler {
            tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlpha());

            String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId());
            String prevCountryIsoCode = tm.getNetworkCountryIso(mPhone.getPhoneId());
            String operatorNumeric = mSS.getOperatorNumeric();

            if (!mPhone.isPhoneTypeGsm()) {
@@ -2828,48 +2822,47 @@ public class ServiceStateTracker extends Handler {
            if (isInvalidOperatorNumeric(operatorNumeric)) {
                if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid");
                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
                mNitzState.setNetworkCountryIsoAvailable(false);
                mNitzState.clearNitzTimeZoneDetectionSuccessful();
                mNitzState.handleNetworkUnavailable();
            } else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
                // Update time zone, ISO, and IDD.
                //
                // If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of
                // the SIM as if we were talking to towers. Telephony code then uses that with
                // mccTable to suggest a timezone. We shouldn't do that if the MCC/MNC is from IWLAN

                String iso = "";
                String mcc = "";
                // Update IDD.
                if (!mPhone.isPhoneTypeGsm()) {
                    setOperatorIdd(operatorNumeric);
                }

                // Update ISO.
                String countryIsoCode = "";
                try {
                    mcc = operatorNumeric.substring(0, 3);
                    iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
                    String mcc = operatorNumeric.substring(0, 3);
                    countryIsoCode = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
                } catch (NumberFormatException | StringIndexOutOfBoundsException ex) {
                    loge("pollStateDone: countryCodeForMcc error: " + ex);
                }
                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), countryIsoCode);

                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), iso);
                mNitzState.setNetworkCountryIsoAvailable(true);

                if (!mcc.equals("000")
                        && !TextUtils.isEmpty(iso)
                        && mNitzState.shouldUpdateTimeZoneUsingCountryCode()) {
                    mNitzState.updateTimeZoneByNetworkCountryCode(iso);
                }

                if (!mPhone.isPhoneTypeGsm()) {
                    setOperatorIdd(operatorNumeric);
                }
                // Update Time Zone.
                boolean iccCardExists = iccCardExists();
                boolean networkIsoChanged =
                        networkCountryIsoChanged(countryIsoCode, prevCountryIsoCode);

                boolean mccChanged = mccChanged(operatorNumeric, prevOperatorNumeric);
                boolean fixTimeZoneCallNeeded = mNitzState.fixTimeZoneCallNeeded();
                if (mccChanged || fixTimeZoneCallNeeded) {
                    // fixTimeZoneCallNeeded == need to fix it because when the NITZ time
                    // came in we didn't know the country code.
                // Determine countryChanged: networkIso is only reliable if there's an ICC card.
                boolean countryChanged = iccCardExists && networkIsoChanged;
                if (DBG) {
                        log("shouldFixTimeZoneNow: mccChanged=" + mccChanged
                                + " fixTimeZoneCallNeeded=" + fixTimeZoneCallNeeded);
                    }
                    mNitzState.fixTimeZone(iso);
                    long ctm = System.currentTimeMillis();
                    log("Before handleNetworkCountryCodeKnown:"
                            + " countryChanged=" + countryChanged
                            + " iccCardExist=" + iccCardExists
                            + " countryIsoChanged=" + networkIsoChanged
                            + " operatorNumeric=" + operatorNumeric
                            + " prevOperatorNumeric=" + prevOperatorNumeric
                            + " countryIsoCode=" + countryIsoCode
                            + " prevCountryIsoCode=" + prevCountryIsoCode
                            + " ltod=" + TimeUtils.logTimeOfDay(ctm));
                }
                mNitzState.handleNetworkCountryCodeSet(countryChanged);
            }

            tm.setNetworkRoamingForPhone(mPhone.getPhoneId(),
@@ -3092,7 +3085,7 @@ public class ServiceStateTracker extends Handler {
            if (lastNitzData == null) {
                tzone = null;
            } else {
                tzone = NitzData.guessTimeZone(lastNitzData);
                tzone = TimeZoneLookupHelper.guessZoneByNitzStatic(lastNitzData);
                if (ServiceStateTracker.DBG) {
                    log("fixUnknownMcc(): guessNitzTimeZone returned "
                            + (tzone == null ? tzone : tzone.getID()));
@@ -3433,7 +3426,9 @@ public class ServiceStateTracker extends Handler {
        NitzData newNitzData = NitzData.parse(nitzString);
        if (newNitzData != null) {
            try {
                mNitzState.setTimeAndTimeZoneFromNitz(newNitzData, nitzReceiveTime);
                TimeStampedValue<NitzData> nitzSignal =
                        new TimeStampedValue<>(newNitzData, nitzReceiveTime);
                mNitzState.handleNitzReceived(nitzSignal);
            } finally {
                if (DBG) {
                    long end = SystemClock.elapsedRealtime();
@@ -4034,49 +4029,36 @@ public class ServiceStateTracker extends Handler {
    }

    /**
     * Return true if the operator changed.
     * Return true if the network operator's country code changed.
     */
    private boolean mccChanged(String operatorNumeric, String prevOperatorNumeric) {
        // Return false if the mcc isn't valid as we don't know where we are.
        // Return true if we have an IccCard and the mcc changed.
    private boolean networkCountryIsoChanged(String newCountryIsoCode, String prevCountryIsoCode) {
        // Return false if the new ISO code isn't valid as we don't know where we are.
        // Return true if the previous ISO code wasn't valid, or if it was and the new one differs.

        // If mcc is invalid then we'll return false
        int mcc;
        try {
            mcc = Integer.parseInt(operatorNumeric.substring(0, 3));
        } catch (Exception e) {
        // If newCountryIsoCode is invalid then we'll return false
        if (TextUtils.isEmpty(newCountryIsoCode)) {
            if (DBG) {
                log("mccChanged: no mcc, operatorNumeric=" + operatorNumeric + " retVal=false");
                log("countryIsoChanged: no new country ISO code");
            }
            return false;
        }

        // If prevMcc is invalid will make it different from mcc
        // so we'll return true if the card exists.
        int prevMcc;
        try {
            prevMcc = Integer.parseInt(prevOperatorNumeric.substring(0, 3));
        } catch (Exception e) {
            prevMcc = mcc + 1;
        if (TextUtils.isEmpty(prevCountryIsoCode)) {
            if (DBG) {
                log("countryIsoChanged: no previous country ISO code");
            }
            return true;
        }
        return !newCountryIsoCode.equals(prevCountryIsoCode);
    }

    // Determine if the Icc card exists
    private boolean iccCardExists() {
        boolean iccCardExist = false;
        if (mUiccApplcation != null) {
            iccCardExist = mUiccApplcation.getState() != AppState.APPSTATE_UNKNOWN;
        }

        // Determine retVal
        boolean retVal = iccCardExist && (mcc != prevMcc);
        if (DBG) {
            long ctm = System.currentTimeMillis();
            log("shouldFixTimeZoneNow: retVal=" + retVal +
                    " iccCardExist=" + iccCardExist +
                    " operatorNumeric=" + operatorNumeric + " mcc=" + mcc +
                    " prevOperatorNumeric=" + prevOperatorNumeric + " prevMcc=" + prevMcc +
                    " ltod=" + TimeUtils.logTimeOfDay(ctm));
        }
        return retVal;
        return iccCardExist;
    }

    public String getSystemProperty(String property, String defValue) {
+173 −0
Original line number Diff line number Diff line
/*
 * Copyright 2017 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;

import android.util.TimeUtils;

import libcore.util.CountryTimeZones;
import libcore.util.TimeZoneFinder;

import java.util.Arrays;
import java.util.Date;
import java.util.TimeZone;

/**
 * An interface to various time zone lookup behaviors.
 */
// Non-final to allow mocking.
public class TimeZoneLookupHelper {
    private static final int MS_PER_HOUR = 60 * 60 * 1000;

    /**
     * List of ISO codes for countries that can have an offset of
     * GMT+0 when not in daylight savings time.  This ignores some
     * small places such as the Canary Islands (Spain) and
     * Danmarkshavn (Denmark).  The list must be sorted by code.
     */
    private static final String[] GMT_COUNTRY_CODES = {
            "bf", // Burkina Faso
            "ci", // Cote d'Ivoire
            "eh", // Western Sahara
            "fo", // Faroe Islands, Denmark
            "gb", // United Kingdom of Great Britain and Northern Ireland
            "gh", // Ghana
            "gm", // Gambia
            "gn", // Guinea
            "gw", // Guinea Bissau
            "ie", // Ireland
            "lr", // Liberia
            "is", // Iceland
            "ma", // Morocco
            "ml", // Mali
            "mr", // Mauritania
            "pt", // Portugal
            "sl", // Sierra Leone
            "sn", // Senegal
            "st", // Sao Tome and Principe
            "tg", // Togo
    };

    public TimeZoneLookupHelper() {}

    /**
     * Finds a time zone ID that fits the supplied NITZ and country information.
     *
     * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates
     * will be returned. If the current device default zone matches it will be returned in
     * preference to other candidates. This method can return {@code null} if no matching time
     * zones are found.
     */
    public String guessZoneIdByNitzCountry(NitzData nitzData, String isoCountryCode) {
        return guessZoneIdByInstantOffsetDstCountry(
                nitzData.getCurrentTimeInMillis(),
                nitzData.getLocalOffsetMillis(),
                nitzData.isDst(),
                isoCountryCode);
    }

    /**
     * Finds a time zone ID that fits the supplied time / offset and country information.
     *
     * <p><em>Note:</em> When there are multiple matching zones then one of the matching candidates
     * will be returned. If the current device default zone matches it will be returned in
     * preference to other candidates. This method can return {@code null} if no matching time
     * zones are found.
     */
    public String guessZoneIdByInstantOffsetDstCountry(
            long timeMillis, int utcOffsetMillis, boolean isDst, String isoCountryCode) {
        TimeZone timeZone =
                TimeUtils.getTimeZone(utcOffsetMillis, isDst, timeMillis, isoCountryCode);
        return timeZone == null ? null : timeZone.getID();
    }

    /**
     * Finds a time zone ID using only information present in the supplied {@link NitzData} object.
     *
     * <p><em>Note:</em> Because multiple time zones can have the same offset / DST state at a given
     * time this process is error prone; an arbitrary match is returned when there are multiple
     * candidates. The algorithm can also return a non-exact match by assuming that the DST
     * information provided by NITZ is incorrect. This method can return {@code null} if no matching
     * time zones are found.
     */
    public String guessZoneIdByNitz(NitzData nitzData) {
        TimeZone zone = guessZoneByNitzStatic(nitzData);
        return zone == null ? null : zone.getID();
    }

    /**
     * Returns a time zone ID for the country if possible. For counties that use a single time zone
     * this will provide a good choice. For countries with multiple time zones, a time zone is
     * returned if all time zones used in the country currently have the same offset (currently ==
     * according to the device's current system clock time). If this is not the case then
     * {@code null} can be returned.
     */
    public String guessZoneIdByCountry(String isoCountryCode, long whenMillis) {
        CountryTimeZones countryTimeZones =
                TimeZoneFinder.getInstance().lookupCountryTimeZones(isoCountryCode);
        if (countryTimeZones == null) {
            // Unknown country code.
            return null;
        }

        if (countryTimeZones.isDefaultOkForCountryTimeZoneDetection(whenMillis)) {
            return countryTimeZones.getDefaultTimeZoneId();
        }
        return null;
    }

    /** Static method for use by {@link ServiceStateTracker}. */
    static TimeZone guessZoneByNitzStatic(NitzData nitzData) {
        int utcOffsetMillis = nitzData.getLocalOffsetMillis();
        boolean isDst = nitzData.isDst();
        long timeMillis = nitzData.getCurrentTimeInMillis();

        TimeZone guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst);
        if (guess == null) {
            // Couldn't find a proper timezone.  Perhaps the DST data is wrong.
            guess = guessByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst);
        }
        return guess;
    }

    private static TimeZone guessByInstantOffsetDst(long timeMillis, int utcOffsetMillis,
            boolean isDst) {
        int rawOffset = utcOffsetMillis;
        if (isDst) {
            rawOffset -= MS_PER_HOUR;
        }
        String[] zones = TimeZone.getAvailableIDs(rawOffset);
        TimeZone guess = null;
        Date d = new Date(timeMillis);
        for (String zone : zones) {
            TimeZone tz = TimeZone.getTimeZone(zone);
            if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) {
                guess = tz;
                break;
            }
        }

        return guess;
    }

    /**
     * Returns {@code true} if the supplied (lower-case) ISO country code is for a country known to
     * use a raw offset of zero from UTC.
     */
    public boolean countryUsesUtc(String isoCountryCode) {
        return Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) >= 0;
    }
}
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright 2017 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.util;

import android.os.SystemClock;

/**
 * A pair containing a value and an associated time stamp.
 *
 * @param <T> The type of the value.
 */
public final class TimeStampedValue<T> {

    /** The value. */
    public final T mValue;

    /**
     * The value of {@link SystemClock#elapsedRealtime} or equivalent when value was
     * determined.
     */
    public final long mElapsedRealtime;

    public TimeStampedValue(T value, long elapsedRealtime) {
        this.mValue = value;
        this.mElapsedRealtime = elapsedRealtime;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        TimeStampedValue<?> that = (TimeStampedValue<?>) o;

        if (mElapsedRealtime != that.mElapsedRealtime) {
            return false;
        }
        return mValue != null ? mValue.equals(that.mValue) : that.mValue == null;
    }

    @Override
    public int hashCode() {
        int result = mValue != null ? mValue.hashCode() : 0;
        result = 31 * result + (int) (mElapsedRealtime ^ (mElapsedRealtime >>> 32));
        return result;
    }

    @Override
    public String toString() {
        return "TimeStampedValue{"
                + "mValue=" + mValue
                + ", elapsedRealtime=" + mElapsedRealtime
                + '}';
    }
}
Loading