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

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

Remove Mockito from NitzStateMachineImplTest

Remove Mockito from NitzStateMachineImplTest in favor of fakes.  Mockito
was getting in the way more than helping. Instead, interfaces have been
extracted and two fakes created.

This commit contains various other refactorings to try to improve
readability and prepare for the next commit, which puts extra
requirements on the test support code.

Bug: 133492648
Test: atest com.android.internal.telephony.NitzStateMachineImplTest
Change-Id: I7595c81274e695c6d35a6ce508aed9bc4a0f62e6
parent ae03222d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -357,11 +357,11 @@ public final class MccTable {
     * @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
     */
    private static void setTimezoneFromMccIfNeeded(Context context, int mcc) {
        if (!TimeServiceHelper.isTimeZoneSettingInitializedStatic()) {
        if (!TimeServiceHelperImpl.isTimeZoneSettingInitializedStatic()) {
            String zoneId = defaultTimeZoneForMcc(mcc);
            if (zoneId != null && zoneId.length() > 0) {
                // Set time zone based on MCC
                TimeServiceHelper.setDeviceTimeZoneStatic(context, zoneId);
                TimeServiceHelperImpl.setDeviceTimeZoneStatic(context, zoneId);
                Slog.d(LOG_TAG, "timezone set to " + zoneId);
            }
        }
+44 −19
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;

/**
 * An interface for the Android component that handles NITZ and related signals for time and time
 * zone detection.
 *
 * {@hide}
 */
public interface NitzStateMachine {
@@ -87,11 +90,42 @@ public interface NitzStateMachine {
    String getSavedTimeZoneId();

    /**
     * A proxy over device state that allows things like system properties, system clock
     * to be faked for tests.
     * A proxy over read-only device state that allows things like system properties, elapsed
     * realtime clock to be faked for tests.
     */
    interface DeviceState {

        /**
         * If time between NITZ updates is less than {@link #getNitzUpdateSpacingMillis()} the
         * update may be ignored.
         */
        int getNitzUpdateSpacingMillis();

        /**
         * If {@link #getNitzUpdateSpacingMillis()} hasn't been exceeded but update is >
         * {@link #getNitzUpdateDiffMillis()} do the update
         */
        int getNitzUpdateDiffMillis();

        /**
         * Returns true if the {@code gsm.ignore-nitz} system property is set to "yes".
         */
        boolean getIgnoreNitz();

        String getNetworkCountryIsoForPhone();

        /**
         * Returns the same value as {@link SystemClock#elapsedRealtime()}.
         */
        long elapsedRealtime();
    }

    /**
     * The real implementation of {@link DeviceState}.
     *
     * {@hide}
     */
    // Non-final to allow mocking.
    class DeviceState {
    class DeviceStateImpl implements DeviceState {
        private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
        private final int mNitzUpdateSpacing;

@@ -102,7 +136,7 @@ public interface NitzStateMachine {
        private final TelephonyManager mTelephonyManager;
        private final ContentResolver mCr;

        public DeviceState(GsmCdmaPhone phone) {
        DeviceStateImpl(GsmCdmaPhone phone) {
            mPhone = phone;

            Context context = phone.getContext();
@@ -115,38 +149,29 @@ public interface NitzStateMachine {
                    SystemProperties.getInt("ro.nitz_update_diff", NITZ_UPDATE_DIFF_DEFAULT);
        }

        /**
         * If time between NITZ updates is less than {@link #getNitzUpdateSpacingMillis()} the
         * update may be ignored.
         */
        @Override
        public int getNitzUpdateSpacingMillis() {
            return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_SPACING,
                    mNitzUpdateSpacing);
        }

        /**
         * If {@link #getNitzUpdateSpacingMillis()} hasn't been exceeded but update is >
         * {@link #getNitzUpdateDiffMillis()} do the update
         */
        @Override
        public int getNitzUpdateDiffMillis() {
            return Settings.Global.getInt(mCr, Settings.Global.NITZ_UPDATE_DIFF, mNitzUpdateDiff);
        }

        /**
         * Returns true if the {@code gsm.ignore-nitz} system property is set to "yes".
         */
        @Override
        public boolean getIgnoreNitz() {
            String ignoreNitz = SystemProperties.get("gsm.ignore-nitz");
            return ignoreNitz != null && ignoreNitz.equals("yes");
        }

        @Override
        public String getNetworkCountryIsoForPhone() {
            return mTelephonyManager.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
        }

        /**
         * Returns the same value as {@link SystemClock#elapsedRealtime()}.
         */
        @Override
        public long elapsedRealtime() {
            return SystemClock.elapsedRealtime();
        }
+2 −2
Original line number Diff line number Diff line
@@ -100,8 +100,8 @@ public final class NitzStateMachineImpl implements NitzStateMachine {

    public NitzStateMachineImpl(GsmCdmaPhone phone) {
        this(phone,
                new TimeServiceHelper(phone.getContext()),
                new DeviceState(phone),
                new TimeServiceHelperImpl(phone.getContext()),
                new DeviceStateImpl(phone),
                new TimeZoneLookupHelper());
    }

+8 −97
Original line number Diff line number Diff line
@@ -16,97 +16,45 @@

package com.android.internal.telephony;

import android.app.AlarmManager;
import android.app.timedetector.TimeDetector;
import android.app.timedetector.TimeSignal;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.TimestampedValue;

/**
 * An interface to various time / time zone detection behaviors that should be centralized into a
 * new service.
 */
// Non-final to allow mocking.
public class TimeServiceHelper {
public interface TimeServiceHelper {

    /**
     * Callback interface for automatic detection enable/disable changes.
     */
    public interface Listener {
    interface Listener {
        /**
         * Automatic time zone detection has been enabled or disabled.
         */
        void onTimeZoneDetectionChange(boolean enabled);
    }

    private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";

    private final Context mContext;
    private final ContentResolver mCr;
    private final TimeDetector mTimeDetector;

    private Listener mListener;

    /** Creates a TimeServiceHelper */
    public TimeServiceHelper(Context context) {
        mContext = context;
        mCr = context.getContentResolver();
        mTimeDetector = context.getSystemService(TimeDetector.class);
    }

    /**
     * Sets a listener that will be called when the automatic time / time zone detection setting
     * changes.
     */
    public void setListener(Listener listener) {
        if (listener == null) {
            throw new NullPointerException("listener==null");
        }
        if (mListener != null) {
            throw new IllegalStateException("listener already set");
        }
        this.mListener = listener;
        mCr.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
                new ContentObserver(new Handler()) {
                    public void onChange(boolean selfChange) {
                        listener.onTimeZoneDetectionChange(isTimeZoneDetectionEnabled());
                    }
                });
    }
    void setListener(Listener listener);

    /**
     * Returns the same value as {@link System#currentTimeMillis()}.
     */
    public long currentTimeMillis() {
        return System.currentTimeMillis();
    }
    long currentTimeMillis();

    /**
     * Returns true if the device has an explicit time zone set.
     */
    public boolean isTimeZoneSettingInitialized() {
        return isTimeZoneSettingInitializedStatic();

    }
    boolean isTimeZoneSettingInitialized();

    /**
     * Returns true if automatic time zone detection is enabled in settings.
     */
    public boolean isTimeZoneDetectionEnabled() {
        try {
            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
        } catch (Settings.SettingNotFoundException snfe) {
            return true;
        }
    }
    boolean isTimeZoneDetectionEnabled();

    /**
     * Set the device time zone and send out a sticky broadcast so the system can
@@ -114,50 +62,13 @@ public class TimeServiceHelper {
     *
     * @param zoneId timezone set by carrier
     */
    public void setDeviceTimeZone(String zoneId) {
        setDeviceTimeZoneStatic(mContext, zoneId);
    }
    void setDeviceTimeZone(String zoneId);

    /**
     * Suggest the time to the {@link TimeDetector}.
     *
     * @param signalTimeMillis the signal time as received from the network
     */
    public void suggestDeviceTime(TimestampedValue<Long> signalTimeMillis) {
        TimeSignal timeSignal = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, signalTimeMillis);
        mTimeDetector.suggestTime(timeSignal);
    }

    /**
     * Static implementation of isTimeZoneSettingInitialized() for use from {@link MccTable}. This
     * is a hack to deflake TelephonyTests when running on a device with a real SIM: in that
     * situation real service events may come in while a TelephonyTest is running, leading to flakes
     * as the real / fake instance of TimeServiceHelper is swapped in and out from
     * {@link TelephonyComponentFactory}.
     */
    static boolean isTimeZoneSettingInitializedStatic() {
        // timezone.equals("GMT") will be true and only true if the timezone was
        // set to a default value by the system server (when starting, system server
        // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
        // any code that sets it explicitly (in case where something sets GMT explicitly,
        // "Etc/GMT" Olsen ID would be used).
        // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
        // better way of telling if the value has been defaulted.

        String timeZoneId = SystemProperties.get(TIMEZONE_PROPERTY);
        return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
    }
    void suggestDeviceTime(TimestampedValue<Long> signalTimeMillis);

    /**
     * Static method for use by MccTable. See {@link #isTimeZoneSettingInitializedStatic()} for
     * explanation.
     */
    static void setDeviceTimeZoneStatic(Context context, String zoneId) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.setTimeZone(zoneId);
        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        intent.putExtra("time-zone", zoneId);
        context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    }
}
+134 −0
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;

import android.app.AlarmManager;
import android.app.timedetector.TimeDetector;
import android.app.timedetector.TimeSignal;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.TimestampedValue;

/**
 * An interface to various time / time zone detection behaviors that should be centralized into a
 * new service.
 */
public final class TimeServiceHelperImpl implements TimeServiceHelper {

    private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";

    private final Context mContext;
    private final ContentResolver mCr;
    private final TimeDetector mTimeDetector;

    private Listener mListener;

    /** Creates a TimeServiceHelper */
    public TimeServiceHelperImpl(Context context) {
        mContext = context;
        mCr = context.getContentResolver();
        mTimeDetector = context.getSystemService(TimeDetector.class);
    }

    @Override
    public void setListener(Listener listener) {
        if (listener == null) {
            throw new NullPointerException("listener==null");
        }
        if (mListener != null) {
            throw new IllegalStateException("listener already set");
        }
        this.mListener = listener;
        mCr.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
                new ContentObserver(new Handler()) {
                    public void onChange(boolean selfChange) {
                        listener.onTimeZoneDetectionChange(isTimeZoneDetectionEnabled());
                    }
                });
    }

    @Override
    public long currentTimeMillis() {
        return System.currentTimeMillis();
    }

    @Override
    public boolean isTimeZoneSettingInitialized() {
        return isTimeZoneSettingInitializedStatic();

    }

    @Override
    public boolean isTimeZoneDetectionEnabled() {
        try {
            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
        } catch (Settings.SettingNotFoundException snfe) {
            return true;
        }
    }

    @Override
    public void setDeviceTimeZone(String zoneId) {
        setDeviceTimeZoneStatic(mContext, zoneId);
    }

    @Override
    public void suggestDeviceTime(TimestampedValue<Long> signalTimeMillis) {
        TimeSignal timeSignal = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, signalTimeMillis);
        mTimeDetector.suggestTime(timeSignal);
    }

    /**
     * Static implementation of isTimeZoneSettingInitialized() for use from {@link MccTable}. This
     * is a hack to deflake TelephonyTests when running on a device with a real SIM: in that
     * situation real service events may come in while a TelephonyTest is running, leading to flakes
     * as the real / fake instance of TimeServiceHelper is swapped in and out from
     * {@link TelephonyComponentFactory}.
     */
    static boolean isTimeZoneSettingInitializedStatic() {
        // timezone.equals("GMT") will be true and only true if the timezone was
        // set to a default value by the system server (when starting, system server
        // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
        // any code that sets it explicitly (in case where something sets GMT explicitly,
        // "Etc/GMT" Olsen ID would be used).
        // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
        // better way of telling if the value has been defaulted.

        String timeZoneId = SystemProperties.get(TIMEZONE_PROPERTY);
        return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
    }

    /**
     * Static method for use by MccTable. See {@link #isTimeZoneSettingInitializedStatic()} for
     * explanation.
     */
    static void setDeviceTimeZoneStatic(Context context, String zoneId) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.setTimeZone(zoneId);
        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        intent.putExtra("time-zone", zoneId);
        context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    }
}
Loading