Merge "Add the ability to override time origin priorities" into sc-dev
This commit is contained in:
@@ -31,6 +31,18 @@ import android.os.TimestampedValue;
|
||||
@SystemService(Context.TIME_DETECTOR_SERVICE)
|
||||
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}.
|
||||
*
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -208,6 +217,19 @@ public final class ServerFlags {
|
||||
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
|
||||
* namespace, returns {@link Optional#empty()} if there is no explicit value set.
|
||||
|
||||
@@ -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;
|
||||
|
||||
return origins;
|
||||
/** 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected String[] lookupPriorityStrings() {
|
||||
Optional<String[]> priorityStrings = mServerFlags.getOptionalStringArray(
|
||||
KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE);
|
||||
return priorityStrings.orElse(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user