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

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

Add the ability to override time origin priorities

Add the ability to override time detector "origin priorities" config
with device_config. This is primarily intended for use by tests to set
the device into known states, regardless of the static xml OEM partner
config.

For example, to test the new "external" suggestion code in CTS, even a
device that does not support it "out of the box" can be set to just use
external suggestions and verify the associated code paths are working.
Previously, external time suggestions were untestable on devices that
didn't use it "out of the box".

Similarly, carrier acceptance tests could use this to just set "nitz"
from the priority list and ensure that NITZ behavior is working as
expected.

An initial TimeDetectorShellCommand implementation has also been written
to provide help that lists the device_config key, and expose settings
state. The set_auto_detection_enabled command can be added after bug
172891783 is complete.

This change also makes the origin priorities more dynamic. i.e. config
overlays like locale or MCC-based resource configs could now also affect
the origin priorities, previously the string was read once and cached
forever.

Bug: 183239968
Bug: 172891783
Bug: 172230856
Test: build / treehugger only
Change-Id: Ic67f35aa6d417bbbb4172643c10a796d484ccd1f
parent 650a27bf
Loading
Loading
Loading
Loading
+12 −0
Original line number Original line Diff line number Diff line
@@ -31,6 +31,18 @@ import android.os.TimestampedValue;
@SystemService(Context.TIME_DETECTOR_SERVICE)
@SystemService(Context.TIME_DETECTOR_SERVICE)
public interface TimeDetector {
public interface TimeDetector {


    /**
     * The name of the service for shell commands.
     * @hide
     */
    String SHELL_COMMAND_SERVICE_NAME = "time_detector";

    /**
     * A shell command that prints the current "auto time detection" global setting value.
     * @hide
     */
    String SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED = "is_auto_detection_enabled";

    /**
    /**
     * A shared utility method to create a {@link ManualTimeSuggestion}.
     * A shared utility method to create a {@link ManualTimeSuggestion}.
     *
     *
+3 −0
Original line number Original line Diff line number Diff line
@@ -82,6 +82,9 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
                        handleAutoTimeDetectionChangedOnHandlerThread();
                        handleAutoTimeDetectionChangedOnHandlerThread();
                    }
                    }
                });
                });
        mServiceConfigAccessor.addListener(
                () -> mHandler.post(
                        EnvironmentImpl.this::handleAutoTimeDetectionChangedOnHandlerThread));
    }
    }


    /** Internal method for handling the auto time setting being changed. */
    /** Internal method for handling the auto time setting being changed. */
+22 −0
Original line number Original line Diff line number Diff line
@@ -135,6 +135,15 @@ public final class ServerFlags {
    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT =
    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT =
            "location_time_zone_detection_setting_enabled_default";
            "location_time_zone_detection_setting_enabled_default";


    /**
     * The key to override the time detector origin priorities configuration. A comma-separated list
     * of strings that will be passed to {@link TimeDetectorStrategy#stringToOrigin(String)}.
     * All values must be recognized or the override value will be ignored.
     */
    @DeviceConfigKey
    public static final String KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE =
            "time_detector_origin_priorities_override";

    @GuardedBy("mListeners")
    @GuardedBy("mListeners")
    private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>();
    private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>();


@@ -208,6 +217,19 @@ public final class ServerFlags {
        return Optional.ofNullable(value);
        return Optional.ofNullable(value);
    }
    }


    /**
     * Returns an optional string array value from {@link DeviceConfig} from the system_time
     * namespace, returns {@link Optional#empty()} if there is no explicit value set.
     */
    @NonNull
    public Optional<String[]> getOptionalStringArray(@DeviceConfigKey String key) {
        Optional<String> string = getOptionalString(key);
        if (!string.isPresent()) {
            return Optional.empty();
        }
        return Optional.of(string.get().split(","));
    }

    /**
    /**
     * Returns an optional boolean value from {@link DeviceConfig} from the system_time
     * Returns an optional boolean value from {@link DeviceConfig} from the system_time
     * namespace, returns {@link Optional#empty()} if there is no explicit value set.
     * namespace, returns {@link Optional#empty()} if there is no explicit value set.
+102 −17
Original line number Original line Diff line number Diff line
@@ -15,9 +15,9 @@
 */
 */
package com.android.server.timedetector;
package com.android.server.timedetector;


import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY;
import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -28,12 +28,17 @@ import android.util.ArraySet;


import com.android.internal.R;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.timedetector.TimeDetectorStrategy.Origin;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ConfigurationChangeListener;


import java.time.Instant;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collections;
import java.util.Objects;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Set;
import java.util.function.Supplier;


/**
/**
 * A singleton that provides access to service configuration for time detection. This hides how
 * A singleton that provides access to service configuration for time detection. This hides how
@@ -48,7 +53,7 @@ final class ServiceConfigAccessor {
     * By default telephony and network only suggestions are accepted and telephony takes
     * By default telephony and network only suggestions are accepted and telephony takes
     * precedence over network.
     * precedence over network.
     */
     */
    private static final @TimeDetectorStrategy.Origin int[]
    private static final @Origin int[]
            DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = { ORIGIN_TELEPHONY, ORIGIN_NETWORK };
            DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = { ORIGIN_TELEPHONY, ORIGIN_NETWORK };


    /**
    /**
@@ -60,6 +65,7 @@ final class ServiceConfigAccessor {


    private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
    private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
            new ArraySet<>(new String[] {
            new ArraySet<>(new String[] {
                    KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
            }));
            }));


    private static final Object SLOCK = new Object();
    private static final Object SLOCK = new Object();
@@ -70,8 +76,9 @@ final class ServiceConfigAccessor {
    private static ServiceConfigAccessor sInstance;
    private static ServiceConfigAccessor sInstance;


    @NonNull private final Context mContext;
    @NonNull private final Context mContext;
    @NonNull private final ConfigOriginPrioritiesSupplier mConfigOriginPrioritiesSupplier;
    @NonNull private final ServerFlagsOriginPrioritiesSupplier mServerFlagsOriginPrioritiesSupplier;
    @NonNull private final ServerFlags mServerFlags;
    @NonNull private final ServerFlags mServerFlags;
    @NonNull private final int[] mOriginPriorities;


    /**
    /**
     * If a newly calculated system clock time and the current system clock time differs by this or
     * If a newly calculated system clock time and the current system clock time differs by this or
@@ -83,7 +90,9 @@ final class ServiceConfigAccessor {
    private ServiceConfigAccessor(@NonNull Context context) {
    private ServiceConfigAccessor(@NonNull Context context) {
        mContext = Objects.requireNonNull(context);
        mContext = Objects.requireNonNull(context);
        mServerFlags = ServerFlags.getInstance(mContext);
        mServerFlags = ServerFlags.getInstance(mContext);
        mOriginPriorities = getOriginPrioritiesInternal();
        mConfigOriginPrioritiesSupplier = new ConfigOriginPrioritiesSupplier(context);
        mServerFlagsOriginPrioritiesSupplier =
                new ServerFlagsOriginPrioritiesSupplier(mServerFlags);
        mSystemClockUpdateThresholdMillis =
        mSystemClockUpdateThresholdMillis =
                SystemProperties.getInt("ro.sys.time_detector_update_diff",
                SystemProperties.getInt("ro.sys.time_detector_update_diff",
                        SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
                        SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
@@ -111,8 +120,17 @@ final class ServiceConfigAccessor {
    }
    }


    @NonNull
    @NonNull
    int[] getOriginPriorities() {
    @Origin int[] getOriginPriorities() {
        return mOriginPriorities;
        int[] serverFlagsValue = mServerFlagsOriginPrioritiesSupplier.get();
        if (serverFlagsValue != null) {
            return serverFlagsValue;
        }

        int[] configValue = mConfigOriginPrioritiesSupplier.get();
        if (configValue != null) {
            return configValue;
        }
        return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES;
    }
    }


    int systemClockUpdateThresholdMillis() {
    int systemClockUpdateThresholdMillis() {
@@ -123,19 +141,86 @@ final class ServiceConfigAccessor {
        return TIME_LOWER_BOUND_DEFAULT;
        return TIME_LOWER_BOUND_DEFAULT;
    }
    }


    private int[] getOriginPrioritiesInternal() {
    /**
        String[] originStrings =
     * A base supplier of an array of time origin integers in priority order.
                mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority);
     * It handles memoization of the result to avoid repeated string parsing when nothing has
        if (originStrings.length == 0) {
     * changed.
            return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES;
     */
        } else {
    private abstract static class BaseOriginPrioritiesSupplier implements Supplier<@Origin int[]> {
            int[] origins = new int[originStrings.length];
        @GuardedBy("this") @Nullable private String[] mLastPriorityStrings;
            for (int i = 0; i < originStrings.length; i++) {
        @GuardedBy("this") @Nullable private int[] mLastPriorityInts;
                int origin = stringToOrigin(originStrings[i]);

                origins[i] = origin;
        /** Returns an array of {@code ORIGIN_*} values, or {@code null}. */
        @Override
        @Nullable
        public @Origin int[] get() {
            String[] priorityStrings = lookupPriorityStrings();
            synchronized (this) {
                if (Arrays.equals(mLastPriorityStrings, priorityStrings)) {
                    return mLastPriorityInts;
                }

                int[] priorityInts = null;
                if (priorityStrings != null && priorityStrings.length > 0) {
                    priorityInts = new int[priorityStrings.length];
                    try {
                        for (int i = 0; i < priorityInts.length; i++) {
                            String priorityString = priorityStrings[i];
                            Preconditions.checkArgument(priorityString != null);

                            priorityString = priorityString.trim();
                            priorityInts[i] = TimeDetectorStrategy.stringToOrigin(priorityString);
                        }
                    } catch (IllegalArgumentException e) {
                        // If any strings were bad and they were ignored then the semantics of the
                        // whole list could change, so return null.
                        priorityInts = null;
                    }
                }
                mLastPriorityStrings = priorityStrings;
                mLastPriorityInts = priorityInts;
                return priorityInts;
            }
        }

        @Nullable
        protected abstract String[] lookupPriorityStrings();
    }
    }


            return origins;
    /** Supplies origin priorities from config_autoTimeSourcesPriority. */
    private static class ConfigOriginPrioritiesSupplier extends BaseOriginPrioritiesSupplier {

        @NonNull private final Context mContext;

        private ConfigOriginPrioritiesSupplier(Context context) {
            mContext = Objects.requireNonNull(context);
        }

        @Override
        @Nullable
        protected String[] lookupPriorityStrings() {
            return mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority);
        }
    }

    /**
     * Supplies origin priorities from device_config (server flags), see
     * {@link ServerFlags#KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE}.
     */
    private static class ServerFlagsOriginPrioritiesSupplier extends BaseOriginPrioritiesSupplier {

        @NonNull private final ServerFlags mServerFlags;

        private ServerFlagsOriginPrioritiesSupplier(ServerFlags serverFlags) {
            mServerFlags = Objects.requireNonNull(serverFlags);
        }

        @Override
        @Nullable
        protected String[] lookupPriorityStrings() {
            Optional<String[]> priorityStrings = mServerFlags.getOptionalStringArray(
                    KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE);
            return priorityStrings.orElse(null);
        }
        }
    }
    }
}
}
+11 −1
Original line number Original line Diff line number Diff line
@@ -30,6 +30,8 @@ import android.app.timedetector.TelephonyTimeSuggestion;
import android.content.Context;
import android.content.Context;
import android.os.Binder;
import android.os.Binder;
import android.os.Handler;
import android.os.Handler;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.IndentingPrintWriter;
import android.util.IndentingPrintWriter;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
@@ -100,6 +102,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
    }
    }


    @Override
    @Override
    @NonNull
    public TimeCapabilitiesAndConfig getCapabilitiesAndConfig() {
    public TimeCapabilitiesAndConfig getCapabilitiesAndConfig() {
        int userId = mCallerIdentityInjector.getCallingUserId();
        int userId = mCallerIdentityInjector.getCallingUserId();
        return getTimeCapabilitiesAndConfig(userId);
        return getTimeCapabilitiesAndConfig(userId);
@@ -119,7 +122,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
    }
    }


    @Override
    @Override
    public boolean updateConfiguration(TimeConfiguration timeConfiguration) {
    public boolean updateConfiguration(@NonNull TimeConfiguration timeConfiguration) {
        enforceManageTimeDetectorPermission();
        enforceManageTimeDetectorPermission();
        // TODO(b/172891783) Add actual logic
        // TODO(b/172891783) Add actual logic
        return false;
        return false;
@@ -180,6 +183,13 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
        ipw.flush();
        ipw.flush();
    }
    }


    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
        new TimeDetectorShellCommand(this).exec(
                this, in, out, err, args, callback, resultReceiver);
    }

    private void enforceSuggestTelephonyTimePermission() {
    private void enforceSuggestTelephonyTimePermission() {
        mContext.enforceCallingPermission(
        mContext.enforceCallingPermission(
                android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE,
                android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE,
Loading