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

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

Add a state to LocationTimeZoneProviderController

Add an explicit state field to LocationTimeZoneProviderController to
help with telemetry / metrics. Previously, this state information was
implicit.

This commit adds the code necessary to support metrics, but does not
actually integrate with the metrics system. A TODO has been left in the
code to complete the plumbing.

The state information records the state as would be reflected by the
controller's actions. For example, the controller may have received an
"uncertain" suggestion from a provider, but still be in the uncertainty
timeout period, and therefore the state could still be considered
"INITIALIZING" or "CERTAIN" until the controller has actually
communicated it is uncertain.

The intent of adding this explicit state information is to help with
metrics and telemetry. It also helps with testing and debugging to
ensure the controller is transitioning through all the correct states on
the way to a final correct state.

The main indicator desired from this state is to enable metrics that
record how much time the controller spends active / inactive, as well as
certain and uncertain. Previous to this change, similar information was
captured at the location time zone provider (LTZP) level, but
LTZP-level information would be difficult to use when multiple LTZPs are
running concurrently. For example, in order to tell what percentage of
time the geolocation detection system as a whole is certain / uncertain,
it is necessary to understand how much time the geolocation detection
system as a whole was actually running.

Bug: 200279201
Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/
Test: atest cts/hostsidetests/time/host/src/android/time/cts/host/
Change-Id: I8154caee41e96b03e6a43b51ee8d1556ba77bb4d
parent d3dadfbd
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -23,6 +23,19 @@ import "frameworks/base/core/proto/android/privacy.proto";
option java_multiple_files = true;
option java_outer_classname = "LocationTimeZoneManagerProto";

// A state enum that matches states for LocationTimeZoneProviderController. See that class for
// details.
enum ControllerStateEnum {
  CONTROLLER_STATE_UNKNOWN = 0;
  CONTROLLER_STATE_PROVIDERS_INITIALIZING = 1;
  CONTROLLER_STATE_STOPPED = 2;
  CONTROLLER_STATE_INITIALIZING = 3;
  CONTROLLER_STATE_UNCERTAIN = 4;
  CONTROLLER_STATE_CERTAIN = 5;
  CONTROLLER_STATE_FAILED = 6;
  CONTROLLER_STATE_DESTROYED = 7;
}

// Represents the state of the LocationTimeZoneManagerService for use in tests.
message LocationTimeZoneManagerServiceStateProto {
  option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -30,6 +43,7 @@ message LocationTimeZoneManagerServiceStateProto {
  optional GeolocationTimeZoneSuggestionProto last_suggestion = 1;
  repeated TimeZoneProviderStateProto primary_provider_states = 2;
  repeated TimeZoneProviderStateProto secondary_provider_states = 3;
  repeated ControllerStateEnum controller_states = 4;
}

// The state tracked for a LocationTimeZoneProvider.
+4 −3
Original line number Diff line number Diff line
@@ -172,12 +172,13 @@ public interface ServiceConfigAccessor {
     * Enables/disables the state recording mode for tests. The value is reset with {@link
     * #resetVolatileTestConfig()}.
     */
    void setRecordProviderStateChanges(boolean enabled);
    void setRecordStateChangesForTests(boolean enabled);

    /**
     * Returns {@code true} if providers are expected to record their state changes for tests.
     * Returns {@code true} if the controller / providers are expected to record their state changes
     * for tests.
     */
    boolean getRecordProviderStateChanges();
    boolean getRecordStateChangesForTests();

    /**
     * Returns the mode for the primary location time zone provider.
+6 −6
Original line number Diff line number Diff line
@@ -150,7 +150,7 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
     * See also {@link #resetVolatileTestConfig()}.
     */
    @GuardedBy("this")
    private boolean mRecordProviderStateChanges;
    private boolean mRecordStateChangesForTests;

    private ServiceConfigAccessorImpl(@NonNull Context context) {
        mContext = Objects.requireNonNull(context);
@@ -453,13 +453,13 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
    }

    @Override
    public synchronized void setRecordProviderStateChanges(boolean enabled) {
        mRecordProviderStateChanges = enabled;
    public synchronized void setRecordStateChangesForTests(boolean enabled) {
        mRecordStateChangesForTests = enabled;
    }

    @Override
    public synchronized boolean getRecordProviderStateChanges() {
        return mRecordProviderStateChanges;
    public synchronized boolean getRecordStateChangesForTests() {
        return mRecordStateChangesForTests;
    }

    @Override
@@ -548,7 +548,7 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
        mTestPrimaryLocationTimeZoneProviderMode = null;
        mTestSecondaryLocationTimeZoneProviderPackageName = null;
        mTestSecondaryLocationTimeZoneProviderMode = null;
        mRecordProviderStateChanges = false;
        mRecordStateChangesForTests = false;
    }

    private boolean isTelephonyFallbackSupported() {
+16 −7
Original line number Diff line number Diff line
@@ -247,8 +247,7 @@ public class LocationTimeZoneManagerService extends Binder {
     * completion, it cannot be called from the {@code mThreadingDomain} thread.
     */
    void startWithTestProviders(@Nullable String testPrimaryProviderPackageName,
            @Nullable String testSecondaryProviderPackageName,
            boolean recordProviderStateChanges) {
            @Nullable String testSecondaryProviderPackageName, boolean recordStateChanges) {
        enforceManageTimeZoneDetectorPermission();

        if (testPrimaryProviderPackageName == null && testSecondaryProviderPackageName == null) {
@@ -263,7 +262,7 @@ public class LocationTimeZoneManagerService extends Binder {
                        testPrimaryProviderPackageName);
                mServiceConfigAccessor.setTestSecondaryLocationTimeZoneProviderPackageName(
                        testSecondaryProviderPackageName);
                mServiceConfigAccessor.setRecordProviderStateChanges(recordProviderStateChanges);
                mServiceConfigAccessor.setRecordStateChangesForTests(recordStateChanges);
                startOnDomainThread();
            }
        }, BLOCKING_OP_WAIT_DURATION_MILLIS);
@@ -281,10 +280,20 @@ public class LocationTimeZoneManagerService extends Binder {
            if (mLocationTimeZoneProviderController == null) {
                LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider();
                LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider();
                LocationTimeZoneProviderController.MetricsLogger metricsLogger =
                        new LocationTimeZoneProviderController.MetricsLogger() {
                            @Override
                            public void onStateChange(
                                    @LocationTimeZoneProviderController.State String state) {
                                // TODO b/200279201 - wire this up to metrics code
                                // No-op.
                            }
                        };

                boolean recordStateChanges = mServiceConfigAccessor.getRecordStateChangesForTests();
                LocationTimeZoneProviderController controller =
                        new LocationTimeZoneProviderController(
                                mThreadingDomain, primary, secondary);
                        new LocationTimeZoneProviderController(mThreadingDomain, metricsLogger,
                                primary, secondary, recordStateChanges);
                LocationTimeZoneProviderControllerEnvironmentImpl environment =
                        new LocationTimeZoneProviderControllerEnvironmentImpl(
                                mThreadingDomain, mServiceConfigAccessor, controller);
@@ -342,7 +351,7 @@ public class LocationTimeZoneManagerService extends Binder {
        mThreadingDomain.postAndWait(() -> {
            synchronized (mSharedLock) {
                if (mLocationTimeZoneProviderController != null) {
                    mLocationTimeZoneProviderController.clearRecordedProviderStates();
                    mLocationTimeZoneProviderController.clearRecordedStates();
                }
            }
        }, BLOCKING_OP_WAIT_DURATION_MILLIS);
@@ -450,7 +459,7 @@ public class LocationTimeZoneManagerService extends Binder {
            ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(mIndex);
            return new BinderLocationTimeZoneProvider(
                    providerMetricsLogger, mThreadingDomain, mName, proxy,
                    mServiceConfigAccessor.getRecordProviderStateChanges());
                    mServiceConfigAccessor.getRecordStateChangesForTests());
        }

        @Override
+31 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.Nullable;

import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
import com.android.server.timezonedetector.location.LocationTimeZoneProviderController.State;

import java.util.ArrayList;
import java.util.Collections;
@@ -30,21 +31,34 @@ import java.util.Objects;
/** A snapshot of the location time zone manager service's state for tests. */
final class LocationTimeZoneManagerServiceState {

    private final @State String mControllerState;
    @Nullable private final GeolocationTimeZoneSuggestion mLastSuggestion;
    @NonNull private final List<@State String> mControllerStates;
    @NonNull private final List<ProviderState> mPrimaryProviderStates;
    @NonNull private final List<ProviderState> mSecondaryProviderStates;

    LocationTimeZoneManagerServiceState(@NonNull Builder builder) {
        mControllerState = builder.mControllerState;
        mLastSuggestion = builder.mLastSuggestion;
        mControllerStates = Objects.requireNonNull(builder.mControllerStates);
        mPrimaryProviderStates = Objects.requireNonNull(builder.mPrimaryProviderStates);
        mSecondaryProviderStates = Objects.requireNonNull(builder.mSecondaryProviderStates);
    }

    public @State String getControllerState() {
        return mControllerState;
    }

    @Nullable
    public GeolocationTimeZoneSuggestion getLastSuggestion() {
        return mLastSuggestion;
    }

    @NonNull
    public List<@State String> getControllerStates() {
        return mControllerStates;
    }

    @NonNull
    public List<ProviderState> getPrimaryProviderStates() {
        return Collections.unmodifiableList(mPrimaryProviderStates);
@@ -58,7 +72,9 @@ final class LocationTimeZoneManagerServiceState {
    @Override
    public String toString() {
        return "LocationTimeZoneManagerServiceState{"
                + "mLastSuggestion=" + mLastSuggestion
                + "mControllerState=" + mControllerState
                + ", mLastSuggestion=" + mLastSuggestion
                + ", mControllerStates=" + mControllerStates
                + ", mPrimaryProviderStates=" + mPrimaryProviderStates
                + ", mSecondaryProviderStates=" + mSecondaryProviderStates
                + '}';
@@ -66,16 +82,30 @@ final class LocationTimeZoneManagerServiceState {

    static final class Builder {

        private @State String mControllerState;
        private GeolocationTimeZoneSuggestion mLastSuggestion;
        private List<@State String> mControllerStates;
        private List<ProviderState> mPrimaryProviderStates;
        private List<ProviderState> mSecondaryProviderStates;

        @NonNull
        public Builder setControllerState(@State String stateEnum) {
            mControllerState = stateEnum;
            return this;
        }

        @NonNull
        Builder setLastSuggestion(@NonNull GeolocationTimeZoneSuggestion lastSuggestion) {
            mLastSuggestion = Objects.requireNonNull(lastSuggestion);
            return this;
        }

        @NonNull
        public Builder setStateChanges(@NonNull List<@State String> states) {
            mControllerStates = new ArrayList<>(states);
            return this;
        }

        @NonNull
        Builder setPrimaryProviderStateChanges(@NonNull List<ProviderState> primaryProviderStates) {
            mPrimaryProviderStates = new ArrayList<>(primaryProviderStates);
Loading