summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/timedetector/TimeDetector.java12
-rw-r--r--services/core/java/com/android/server/timedetector/EnvironmentImpl.java3
-rw-r--r--services/core/java/com/android/server/timedetector/ServerFlags.java22
-rw-r--r--services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java119
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java12
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java82
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java11
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java15
8 files changed, 247 insertions, 29 deletions
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 52016b65688b..a3562301cde7 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -32,6 +32,18 @@ import android.os.TimestampedValue;
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}.
*
* @hide
diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
index 072cc16f680b..7649958fe6c9 100644
--- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
@@ -82,6 +82,9 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
handleAutoTimeDetectionChangedOnHandlerThread();
}
});
+ mServiceConfigAccessor.addListener(
+ () -> mHandler.post(
+ EnvironmentImpl.this::handleAutoTimeDetectionChangedOnHandlerThread));
}
/** Internal method for handling the auto time setting being changed. */
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index d91e9c258548..fda0e3cd947c 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -135,6 +135,15 @@ public final class ServerFlags {
public static final String KEY_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")
private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>();
@@ -209,6 +218,19 @@ public final class ServerFlags {
}
/**
+ * 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
* namespace, returns {@link Optional#empty()} if there is no explicit value set.
*/
diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
index be4432ab61bf..381b77e5d1d2 100644
--- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
@@ -15,9 +15,9 @@
*/
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_TELEPHONY;
-import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,12 +28,17 @@ import android.util.ArraySet;
import com.android.internal.R;
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 java.time.Instant;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
+import java.util.function.Supplier;
/**
* 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
* precedence over network.
*/
- private static final @TimeDetectorStrategy.Origin int[]
+ private static final @Origin int[]
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(
new ArraySet<>(new String[] {
+ KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
}));
private static final Object SLOCK = new Object();
@@ -70,8 +76,9 @@ final class ServiceConfigAccessor {
private static ServiceConfigAccessor sInstance;
@NonNull private final Context mContext;
+ @NonNull private final ConfigOriginPrioritiesSupplier mConfigOriginPrioritiesSupplier;
+ @NonNull private final ServerFlagsOriginPrioritiesSupplier mServerFlagsOriginPrioritiesSupplier;
@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
@@ -83,7 +90,9 @@ final class ServiceConfigAccessor {
private ServiceConfigAccessor(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
mServerFlags = ServerFlags.getInstance(mContext);
- mOriginPriorities = getOriginPrioritiesInternal();
+ mConfigOriginPrioritiesSupplier = new ConfigOriginPrioritiesSupplier(context);
+ mServerFlagsOriginPrioritiesSupplier =
+ new ServerFlagsOriginPrioritiesSupplier(mServerFlags);
mSystemClockUpdateThresholdMillis =
SystemProperties.getInt("ro.sys.time_detector_update_diff",
SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
@@ -111,8 +120,17 @@ final class ServiceConfigAccessor {
}
@NonNull
- int[] getOriginPriorities() {
- return mOriginPriorities;
+ @Origin int[] getOriginPriorities() {
+ 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() {
@@ -123,19 +141,86 @@ final class ServiceConfigAccessor {
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;
+ /**
+ * A base supplier of an array of time origin integers in priority order.
+ * It handles memoization of the result to avoid repeated string parsing when nothing has
+ * changed.
+ */
+ private abstract static class BaseOriginPrioritiesSupplier implements Supplier<@Origin int[]> {
+ @GuardedBy("this") @Nullable private String[] mLastPriorityStrings;
+ @GuardedBy("this") @Nullable private int[] mLastPriorityInts;
+
+ /** 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();
+ }
+
+ /** 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);
+ }
- return origins;
+ @Override
+ @Nullable
+ protected String[] lookupPriorityStrings() {
+ Optional<String[]> priorityStrings = mServerFlags.getOptionalStringArray(
+ KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE);
+ return priorityStrings.orElse(null);
}
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 14cab382d405..0f14af4b296b 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -30,6 +30,8 @@ import android.app.timedetector.TelephonyTimeSuggestion;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
@@ -100,6 +102,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
}
@Override
+ @NonNull
public TimeCapabilitiesAndConfig getCapabilitiesAndConfig() {
int userId = mCallerIdentityInjector.getCallingUserId();
return getTimeCapabilitiesAndConfig(userId);
@@ -119,7 +122,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
}
@Override
- public boolean updateConfiguration(TimeConfiguration timeConfiguration) {
+ public boolean updateConfiguration(@NonNull TimeConfiguration timeConfiguration) {
enforceManageTimeDetectorPermission();
// TODO(b/172891783) Add actual logic
return false;
@@ -180,6 +183,13 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
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() {
mContext.enforceCallingPermission(
android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE,
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
new file mode 100644
index 000000000000..3cb21aaf85ce
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 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 android.app.timedetector.TimeDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+
+import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/** Implements the shell command interface for {@link TimeDetectorService}. */
+class TimeDetectorShellCommand extends ShellCommand {
+
+ private final TimeDetectorService mInterface;
+
+ TimeDetectorShellCommand(TimeDetectorService timeDetectorService) {
+ mInterface = timeDetectorService;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ switch (cmd) {
+ case SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED:
+ return runIsAutoDetectionEnabled();
+ default: {
+ return handleDefaultCommands(cmd);
+ }
+ }
+ }
+
+ private int runIsAutoDetectionEnabled() {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean enabled = mInterface.getCapabilitiesAndConfig()
+ .getTimeConfiguration()
+ .isAutoDetectionEnabled();
+ pw.println(enabled);
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Time Detector (time_detector) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.printf(" %s\n", SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED);
+ pw.println(" Prints true/false according to the automatic time detection setting");
+ pw.println();
+ pw.printf("This service is also affected by the following device_config flags in the"
+ + " %s namespace:\n", NAMESPACE_SYSTEM_TIME);
+ pw.printf(" %s - [default=null], a comma separated list of origins. See"
+ + " TimeDetectorStrategy for details\n",
+ KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE);
+ pw.println();
+ pw.println("Example:");
+ pw.printf(" $ adb shell cmd device_config put %s %s %s\n",
+ NAMESPACE_SYSTEM_TIME, KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
+ "external");
+ pw.println("See adb shell cmd device_config for more information.");
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index be382f0409b1..ff5060e6618e 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -27,10 +27,13 @@ import android.app.timedetector.TelephonyTimeSuggestion;
import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.server.timezonedetector.Dumpable;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
* The interface for the class that implements the time detection algorithm used by the
@@ -44,9 +47,9 @@ import java.lang.annotation.RetentionPolicy;
*/
public interface TimeDetectorStrategy extends Dumpable {
- @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK, ORIGIN_GNSS,
- ORIGIN_EXTERNAL })
+ @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK, ORIGIN_GNSS, ORIGIN_EXTERNAL })
@Retention(RetentionPolicy.SOURCE)
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@interface Origin {}
/** Used when a time value originated from a telephony signal. */
@@ -126,9 +129,11 @@ public interface TimeDetectorStrategy extends Dumpable {
/**
* Converts a human readable config string to one of the {@code ORIGIN_} constants.
- * Throws an {@link IllegalArgumentException} if the value is unrecognized.
+ * Throws an {@link IllegalArgumentException} if the value is unrecognized or {@code null}.
*/
static @Origin int stringToOrigin(String originString) {
+ Preconditions.checkArgument(originString != null);
+
switch (originString) {
case "manual":
return ORIGIN_MANUAL;
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index c20400ae7a4b..457dc4325e62 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -321,6 +321,13 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
ipw.flush();
}
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ new TimeZoneDetectorShellCommand(this).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+
private void enforceManageTimeZoneDetectorPermission() {
mContext.enforceCallingPermission(
android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION,
@@ -346,13 +353,5 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
"suggest manual time and time zone");
}
-
- @Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) {
- new TimeZoneDetectorShellCommand(this).exec(
- this, in, out, err, args, callback, resultReceiver);
- }
}