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

Commit 300b75b2 authored by Neil Fuller's avatar Neil Fuller Committed by Android (Google) Code Review
Browse files

Merge "Rewire config for time zone detection services"

parents 46d71637 c881a147
Loading
Loading
Loading
Loading
+7 −163
Original line number Diff line number Diff line
@@ -16,126 +16,44 @@

package com.android.server.timezonedetector;

import static android.content.Intent.ACTION_USER_SWITCHED;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.time.TimeZoneConfiguration;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.location.LocationManager;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;

import java.util.Objects;
import java.util.Optional;

/**
 * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
 */
final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment {

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

    @NonNull private final Context mContext;
    @NonNull private final Handler mHandler;
    @NonNull private final ContentResolver mCr;
    @NonNull private final UserManager mUserManager;
    @NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
    @NonNull private final LocationManager mLocationManager;

    // @NonNull after setConfigChangeListener() is called.
    @GuardedBy("this")
    private ConfigurationChangeListener mConfigChangeListener;

    EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
            @NonNull ServiceConfigAccessor serviceConfigAccessor) {
        mContext = Objects.requireNonNull(context);
        mHandler = Objects.requireNonNull(handler);
        mCr = context.getContentResolver();
        mUserManager = context.getSystemService(UserManager.class);
        mLocationManager = context.getSystemService(LocationManager.class);
        mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);

        // Wire up the config change listeners for anything that could affect the return values from
        // this object. All listener invocations are performed on the mHandler thread.

        // Listen for the user changing / the user's location mode changing.
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_USER_SWITCHED);
        filter.addAction(LocationManager.MODE_CHANGED_ACTION);
        mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                handleConfigChangeOnHandlerThread();
    }
        }, filter, null, mHandler);

        // Add async callbacks for global settings being changed.
        ContentResolver contentResolver = mContext.getContentResolver();
        ContentObserver contentObserver = new ContentObserver(mHandler) {
    @Override
            public void onChange(boolean selfChange) {
                handleConfigChangeOnHandlerThread();
            }
        };
        contentResolver.registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true, contentObserver);

        // Add async callbacks for user scoped location settings being changed.
        contentResolver.registerContentObserver(
                Settings.Secure.getUriFor(Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED),
                true, contentObserver, UserHandle.USER_ALL);
    }

    private void handleConfigChangeOnHandlerThread() {
        synchronized (this) {
            if (mConfigChangeListener == null) {
                Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
            }
            mConfigChangeListener.onChange();
        }
    }

    @Override
    public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
        synchronized (this) {
            mConfigChangeListener = Objects.requireNonNull(listener);
        }
    public void setConfigurationInternalChangeListener(
            @NonNull ConfigurationChangeListener listener) {
        ConfigurationChangeListener configurationChangeListener =
                () -> mHandler.post(listener::onChange);
        mServiceConfigAccessor.addConfigurationInternalChangeListener(configurationChangeListener);
    }

    @Override
    public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
        return new ConfigurationInternal.Builder(userId)
                .setTelephonyDetectionFeatureSupported(
                        mServiceConfigAccessor.isTelephonyTimeZoneDetectionFeatureSupported())
                .setGeoDetectionFeatureSupported(
                        mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported())
                .setAutoDetectionEnabled(isAutoDetectionEnabled())
                .setUserConfigAllowed(isUserConfigAllowed(userId))
                .setLocationEnabled(isLocationEnabled(userId))
                .setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
                .build();
    }

    @Override
    public @UserIdInt int getCurrentUserId() {
        return LocalServices.getService(ActivityManagerInternal.class).getCurrentUserId();
    public ConfigurationInternal getCurrentUserConfigurationInternal() {
        return mServiceConfigAccessor.getCurrentUserConfigurationInternal();
    }

    @Override
@@ -161,78 +79,4 @@ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment
        AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
        alarmManager.setTimeZone(zoneId);
    }

    @Override
    public void storeConfiguration(@UserIdInt int userId, TimeZoneConfiguration configuration) {
        Objects.requireNonNull(configuration);

        // Avoid writing the auto detection enabled setting for devices that do not support auto
        // time zone detection: if we wrote it down then we'd set the value explicitly, which would
        // prevent detecting "default" later. That might influence what happens on later releases
        // that support new types of auto detection on the same hardware.
        if (mServiceConfigAccessor.isAutoDetectionFeatureSupported()) {
            final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
            setAutoDetectionEnabledIfRequired(autoDetectionEnabled);

            // Avoid writing the geo detection enabled setting for devices with settings that
            // are currently overridden by server flags: otherwise we might overwrite a droidfood
            // user's real setting permanently.
            // Also avoid writing the geo detection enabled setting for devices that do not support
            // geo time zone detection: if we wrote it down then we'd set the value explicitly,
            // which would prevent detecting "default" later. That might influence what happens on
            // later releases that start to support geo detection on the same hardware.
            if (!mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride().isPresent()
                    && mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) {
                final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
                setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
            }
        }
    }

    private boolean isUserConfigAllowed(@UserIdInt int userId) {
        UserHandle userHandle = UserHandle.of(userId);
        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
    }

    private boolean isAutoDetectionEnabled() {
        return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
    }

    private void setAutoDetectionEnabledIfRequired(boolean enabled) {
        // This check is racey, but the whole settings update process is racey. This check prevents
        // a ConfigurationChangeListener callback triggering due to ContentObserver's still
        // triggering *sometimes* for no-op updates. Because callbacks are async this is necessary
        // for stable behavior during tests.
        if (isAutoDetectionEnabled() != enabled) {
            Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, enabled ? 1 : 0);
        }
    }

    private boolean isLocationEnabled(@UserIdInt int userId) {
        return mLocationManager.isLocationEnabledForUser(UserHandle.of(userId));
    }

    private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
        // We may never use this, but it gives us a way to force location-based time zone detection
        // on/off for testers (but only where their other settings would allow them to turn it on
        // for themselves).
        Optional<Boolean> override = mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride();
        if (override.isPresent()) {
            return override.get();
        }

        final boolean geoDetectionEnabledByDefault =
                mServiceConfigAccessor.isGeoDetectionEnabledForUsersByDefault();
        return Settings.Secure.getIntForUser(mCr,
                Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
                (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
    }

    private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
        // See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
        if (isGeoDetectionEnabled(userId) != enabled) {
            Settings.Secure.putIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
                    enabled ? 1 : 0, userId);
        }
    }
}
+61 −253

File changed.

Preview size limit exceeded, changes collapsed.

+556 −0

File added.

Preview size limit exceeded, changes collapsed.

+0 −17
Original line number Diff line number Diff line
@@ -27,23 +27,6 @@ import android.annotation.NonNull;
 */
public interface TimeZoneDetectorInternal {

    /** Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. */
    void addConfigurationListener(@NonNull ConfigurationChangeListener listener);

    /**
     * Removes a listener previously added via {@link
     * #addConfigurationListener(ConfigurationChangeListener)}.
     */
    void removeConfigurationListener(@NonNull ConfigurationChangeListener listener);

    /**
     * Returns a snapshot of the {@link ConfigurationInternal} for the current user. This is only a
     * snapshot so callers must use {@link #addConfigurationListener(ConfigurationChangeListener)}
     * to be notified when it changes.
     */
    @NonNull
    ConfigurationInternal getCurrentUserConfigurationInternal();

    /**
     * Suggests the current time zone, determined using geolocation, to the detector. The
     * detector may ignore the signal based on system settings, whether better information is
+0 −36
Original line number Diff line number Diff line
@@ -20,8 +20,6 @@ import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
@@ -34,46 +32,12 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter
    @NonNull private final Context mContext;
    @NonNull private final Handler mHandler;
    @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
    @NonNull private final List<ConfigurationChangeListener> mConfigurationListeners =
            new ArrayList<>();

    public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler,
            @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
        mContext = Objects.requireNonNull(context);
        mHandler = Objects.requireNonNull(handler);
        mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy);

        // Wire up a change listener so that any downstream listeners can be notified when
        // the configuration changes for any reason.
        mTimeZoneDetectorStrategy.addConfigChangeListener(this::handleConfigurationChanged);
    }

    private void handleConfigurationChanged() {
        synchronized (mConfigurationListeners) {
            for (ConfigurationChangeListener listener : mConfigurationListeners) {
                listener.onChange();
            }
        }
    }

    @Override
    public void addConfigurationListener(ConfigurationChangeListener listener) {
        synchronized (mConfigurationListeners) {
            mConfigurationListeners.add(Objects.requireNonNull(listener));
        }
    }

    @Override
    public void removeConfigurationListener(ConfigurationChangeListener listener) {
        synchronized (mConfigurationListeners) {
            mConfigurationListeners.remove(Objects.requireNonNull(listener));
        }
    }

    @Override
    @NonNull
    public ConfigurationInternal getCurrentUserConfigurationInternal() {
        return mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal();
    }

    @Override
Loading