Merge "Prepare to make origin list mutable" into sc-dev

This commit is contained in:
Neil Fuller
2021-04-14 18:36:32 +00:00
committed by Android (Google) Code Review
9 changed files with 234 additions and 131 deletions

View File

@@ -16,26 +16,22 @@
package com.android.server.timedetector;
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.stringToOrigin;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.AlarmManager;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Build;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Slog;
import com.android.internal.R;
import com.android.server.timedetector.TimeDetectorStrategy.Origin;
import com.android.internal.annotations.GuardedBy;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import java.time.Instant;
import java.util.Objects;
@@ -43,62 +39,71 @@ import java.util.Objects;
/**
* The real implementation of {@link TimeDetectorStrategyImpl.Environment} used on device.
*/
public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
private static final String TAG = TimeDetectorService.TAG;
private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
/**
* Time in the past. If automatic time suggestion is before this point, it's
* incorrect for sure.
*/
private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli(
Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME));
/**
* By default telephony and network only suggestions are accepted and telephony takes
* precedence over network.
*/
private static final @Origin int[] DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES =
{ ORIGIN_TELEPHONY, ORIGIN_NETWORK };
/**
* If a newly calculated system clock time and the current system clock time differs by this or
* more the system clock will actually be updated. Used to prevent the system clock being set
* for only minor differences.
*/
private final int mSystemClockUpdateThresholdMillis;
private static final String LOG_TAG = TimeDetectorService.TAG;
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
@NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
@NonNull private final ContentResolver mContentResolver;
@NonNull private final PowerManager.WakeLock mWakeLock;
@NonNull private final AlarmManager mAlarmManager;
@NonNull private final UserManager mUserManager;
@NonNull private final int[] mOriginPriorities;
public EnvironmentImpl(@NonNull Context context) {
// @NonNull after setConfigChangeListener() is called.
@GuardedBy("this")
private ConfigurationChangeListener mConfigChangeListener;
EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
@NonNull ServiceConfigAccessor serviceConfigAccessor) {
mContext = Objects.requireNonNull(context);
mContentResolver = Objects.requireNonNull(context.getContentResolver());
mHandler = Objects.requireNonNull(handler);
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
PowerManager powerManager = context.getSystemService(PowerManager.class);
mWakeLock = Objects.requireNonNull(
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG));
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG));
mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class));
mUserManager = Objects.requireNonNull(context.getSystemService(UserManager.class));
mSystemClockUpdateThresholdMillis =
SystemProperties.getInt("ro.sys.time_detector_update_diff",
SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
// Wire up the config change listeners. All invocations are performed on the mHandler
// thread.
mOriginPriorities = getOriginPriorities(context);
ContentResolver contentResolver = context.getContentResolver();
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
handleAutoTimeDetectionChangedOnHandlerThread();
}
});
}
/** Internal method for handling the auto time setting being changed. */
private void handleAutoTimeDetectionChangedOnHandlerThread() {
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);
}
}
@Override
public int systemClockUpdateThresholdMillis() {
return mSystemClockUpdateThresholdMillis;
return mServiceConfigAccessor.systemClockUpdateThresholdMillis();
}
@Override
@@ -112,12 +117,12 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme
@Override
public Instant autoTimeLowerBound() {
return TIME_LOWER_BOUND;
return mServiceConfigAccessor.autoTimeLowerBound();
}
@Override
public int[] autoOriginPriorities() {
return mOriginPriorities;
return mServiceConfigAccessor.getOriginPriorities();
}
@Override
@@ -131,7 +136,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme
@Override
public void acquireWakeLock() {
if (mWakeLock.isHeld()) {
Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held");
Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " already held");
}
mWakeLock.acquire();
}
@@ -160,7 +165,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme
private void checkWakeLockHeld() {
if (!mWakeLock.isHeld()) {
Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held");
Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " not held");
}
}
@@ -168,20 +173,4 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme
UserHandle userHandle = UserHandle.of(userId);
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
}
private static int[] getOriginPriorities(@NonNull Context context) {
String[] originStrings =
context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority);
if (originStrings.length == 0) {
return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES;
} else {
int[] origins = new int[originStrings.length];
for (int i = 0; i < originStrings.length; i++) {
int origin = stringToOrigin(originStrings[i]);
origins[i] = origin;
}
return origins;
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2021 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.server.timedetector;
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.stringToOrigin;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Build;
import android.os.SystemProperties;
import android.util.ArraySet;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import java.time.Instant;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
/**
* A singleton that provides access to service configuration for time detection. This hides how
* configuration is split between static, compile-time config and dynamic, server-pushed flags. It
* provides a rudimentary mechanism to signal when values have changed.
*/
final class ServiceConfigAccessor {
private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000;
/**
* By default telephony and network only suggestions are accepted and telephony takes
* precedence over network.
*/
private static final @TimeDetectorStrategy.Origin int[]
DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = { ORIGIN_TELEPHONY, ORIGIN_NETWORK };
/**
* Time in the past. If an automatic time suggestion is before this point, it is sure to be
* incorrect.
*/
private static final Instant TIME_LOWER_BOUND_DEFAULT = Instant.ofEpochMilli(
Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME));
private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
new ArraySet<>(new String[] {
}));
private static final Object SLOCK = new Object();
/** The singleton instance. Initialized once in {@link #getInstance(Context)}. */
@GuardedBy("SLOCK")
@Nullable
private static ServiceConfigAccessor sInstance;
@NonNull private final Context mContext;
@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
* more the system clock will actually be updated. Used to prevent the system clock being set
* for only minor differences.
*/
private final int mSystemClockUpdateThresholdMillis;
private ServiceConfigAccessor(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
mServerFlags = ServerFlags.getInstance(mContext);
mOriginPriorities = getOriginPrioritiesInternal();
mSystemClockUpdateThresholdMillis =
SystemProperties.getInt("ro.sys.time_detector_update_diff",
SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
}
/** Returns the singleton instance. */
static ServiceConfigAccessor getInstance(Context context) {
synchronized (SLOCK) {
if (sInstance == null) {
sInstance = new ServiceConfigAccessor(context);
}
return sInstance;
}
}
/**
* Adds a listener that will be called when server flags related to this class change. The
* callbacks are delivered on the main looper thread.
*
* <p>Note: Only for use by long-lived objects. There is deliberately no associated remove
* method.
*/
void addListener(@NonNull ConfigurationChangeListener listener) {
mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH);
}
@NonNull
int[] getOriginPriorities() {
return mOriginPriorities;
}
int systemClockUpdateThresholdMillis() {
return mSystemClockUpdateThresholdMillis;
}
Instant autoTimeLowerBound() {
return TIME_LOWER_BOUND_DEFAULT;
}
private int[] getOriginPrioritiesInternal() {
String[] originStrings =
mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority);
if (originStrings.length == 0) {
return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES;
} else {
int[] origins = new int[originStrings.length];
for (int i = 0; i < originStrings.length; i++) {
int origin = stringToOrigin(originStrings[i]);
origins[i] = origin;
}
return origins;
}
}
}

View File

@@ -27,12 +27,9 @@ import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Binder;
import android.os.Handler;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,7 +61,16 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
@Override
public void onStart() {
TimeDetectorService service = TimeDetectorService.create(getContext());
Context context = getContext();
Handler handler = FgThread.getHandler();
ServiceConfigAccessor serviceConfigAccessor =
ServiceConfigAccessor.getInstance(context);
TimeDetectorStrategy timeDetectorStrategy =
TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);
TimeDetectorService service =
new TimeDetectorService(context, handler, timeDetectorStrategy);
// Publish the binder service so it can be accessed from other (appropriately
// permissioned) processes.
@@ -77,28 +83,6 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
@NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
@NonNull private final CallerIdentityInjector mCallerIdentityInjector;
private static TimeDetectorService create(@NonNull Context context) {
TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context);
TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(environment);
Handler handler = FgThread.getHandler();
TimeDetectorService timeDetectorService =
new TimeDetectorService(context, handler, timeDetectorStrategy);
// Wire up event listening.
ContentResolver contentResolver = context.getContentResolver();
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
new ContentObserver(handler) {
@Override
public void onChange(boolean selfChange) {
timeDetectorService.handleAutoTimeDetectionChanged();
}
});
return timeDetectorService;
}
@VisibleForTesting
public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
@NonNull TimeDetectorStrategy timeDetectorStrategy) {
@@ -185,12 +169,6 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
mHandler.post(() -> mTimeDetectorStrategy.suggestExternalTime(timeSignal));
}
/** Internal method for handling the auto time setting being changed. */
@VisibleForTesting
public void handleAutoTimeDetectionChanged() {
mHandler.post(mTimeDetectorStrategy::handleAutoTimeConfigChanged);
}
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {

View File

@@ -92,12 +92,6 @@ public interface TimeDetectorStrategy extends Dumpable {
/** Returns the configuration that controls time detector behaviour for specified user. */
ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
/**
* Handles the auto-time configuration changing For example, when the auto-time setting is
* toggled on or off.
*/
void handleAutoTimeConfigChanged();
// Utility methods below are to be moved to a better home when one becomes more obvious.
/**

View File

@@ -29,6 +29,8 @@ import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.content.Context;
import android.os.Handler;
import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;
@@ -37,6 +39,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.timezonedetector.ArrayMapWithHistory;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ReferenceWithHistory;
import java.time.Instant;
@@ -131,6 +134,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
*/
public interface Environment {
/**
* Sets a {@link ConfigurationChangeListener} that will be invoked when there are any
* changes that could affect time detection. This is invoked during system server setup.
*/
void setConfigChangeListener(@NonNull ConfigurationChangeListener listener);
/**
* The absolute threshold below which the system clock need not be updated. i.e. if setting
* the system clock would adjust it by less than this (either backwards or forwards) then it
@@ -178,8 +187,19 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
void releaseWakeLock();
}
static TimeDetectorStrategy create(
@NonNull Context context, @NonNull Handler handler,
@NonNull ServiceConfigAccessor serviceConfigAccessor) {
TimeDetectorStrategyImpl.Environment environment =
new EnvironmentImpl(context, handler, serviceConfigAccessor);
return new TimeDetectorStrategyImpl(environment);
}
@VisibleForTesting
TimeDetectorStrategyImpl(@NonNull Environment environment) {
mEnvironment = Objects.requireNonNull(environment);
mEnvironment.setConfigChangeListener(this::handleAutoTimeConfigChanged);
}
@Override
@@ -279,8 +299,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
return mEnvironment.configurationInternal(userId);
}
@Override
public synchronized void handleAutoTimeConfigChanged() {
private synchronized void handleAutoTimeConfigChanged() {
boolean enabled = mEnvironment.isAutoTimeDetectionEnabled();
// When automatic time detection is enabled we update the system clock instantly if we can.
// Conversely, when automatic time detection is disabled we leave the clock as it is.

View File

@@ -47,7 +47,7 @@ import java.util.Optional;
/**
* The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
*/
public final class EnvironmentImpl implements 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";

View File

@@ -133,8 +133,8 @@ public final class ServiceConfigAccessor {
}
/**
* Adds a listener that will be called server flags related to this class change. The callbacks
* are delivered on the main looper thread.
* Adds a listener that will be called when server flags related to this class change. The
* callbacks are delivered on the main looper thread.
*
* <p>Note: Only for use by long-lived objects. There is deliberately no associated remove
* method.

View File

@@ -248,21 +248,6 @@ public class TimeDetectorServiceTest {
mStubbedTimeDetectorStrategy.verifyDumpCalled();
}
@Test
public void testAutoTimeDetectionToggle() throws Exception {
mTimeDetectorService.handleAutoTimeDetectionChanged();
mTestHandler.assertTotalMessagesEnqueued(1);
mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
mStubbedTimeDetectorStrategy.resetCallTracking();
mTimeDetectorService.handleAutoTimeDetectionChanged();
mTestHandler.assertTotalMessagesEnqueued(2);
mTestHandler.waitForMessagesToBeProcessed();
mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
}
private static TelephonyTimeSuggestion createTelephonyTimeSuggestion() {
int slotIndex = 1234;
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
@@ -298,7 +283,6 @@ public class TimeDetectorServiceTest {
private NetworkTimeSuggestion mLastNetworkSuggestion;
private GnssTimeSuggestion mLastGnssSuggestion;
private ExternalTimeSuggestion mLastExternalSuggestion;
private boolean mHandleAutoTimeDetectionChangedCalled;
private boolean mDumpCalled;
@Override
@@ -332,11 +316,6 @@ public class TimeDetectorServiceTest {
throw new UnsupportedOperationException();
}
@Override
public void handleAutoTimeConfigChanged() {
mHandleAutoTimeDetectionChangedCalled = true;
}
@Override
public void dump(IndentingPrintWriter pw, String[] args) {
mDumpCalled = true;
@@ -348,7 +327,6 @@ public class TimeDetectorServiceTest {
mLastNetworkSuggestion = null;
mLastGnssSuggestion = null;
mLastExternalSuggestion = null;
mHandleAutoTimeDetectionChangedCalled = false;
mDumpCalled = false;
}
@@ -372,10 +350,6 @@ public class TimeDetectorServiceTest {
assertEquals(expectedSuggestion, mLastExternalSuggestion);
}
void verifyHandleAutoTimeDetectionChangedCalled() {
assertTrue(mHandleAutoTimeDetectionChangedCalled);
}
void verifyDumpCalled() {
assertTrue(mDumpCalled);
}

View File

@@ -37,6 +37,7 @@ import android.os.TimestampedValue;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.timedetector.TimeDetectorStrategy.Origin;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +47,7 @@ import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
@RunWith(AndroidJUnit4.class)
public class TimeDetectorStrategyImplTest {
@@ -1133,10 +1135,16 @@ public class TimeDetectorStrategyImplTest {
private long mSystemClockMillis;
private int mSystemClockUpdateThresholdMillis = 2000;
private int[] mAutoOriginPriorities = PROVIDERS_PRIORITY;
private ConfigurationChangeListener mConfigChangeListener;
// Tracking operations.
private boolean mSystemClockWasSet;
@Override
public void setConfigChangeListener(ConfigurationChangeListener listener) {
mConfigChangeListener = Objects.requireNonNull(listener);
}
@Override
public int systemClockUpdateThresholdMillis() {
return mSystemClockUpdateThresholdMillis;
@@ -1230,6 +1238,7 @@ public class TimeDetectorStrategyImplTest {
void simulateAutoTimeZoneDetectionToggle() {
mAutoTimeDetectionEnabled = !mAutoTimeDetectionEnabled;
mConfigChangeListener.onChange();
}
void verifySystemClockNotSet() {
@@ -1330,7 +1339,6 @@ public class TimeDetectorStrategyImplTest {
Script simulateAutoTimeDetectionToggle() {
mFakeEnvironment.simulateAutoTimeZoneDetectionToggle();
mTimeDetectorStrategy.handleAutoTimeConfigChanged();
return this;
}