Merge "Add shell commands and other support for host CTS"
This commit is contained in:
committed by
Android (Google) Code Review
commit
53239cfc22
@@ -40,17 +40,58 @@ public final class LocationTimeZoneManager {
|
||||
public static final String SHELL_COMMAND_SERVICE_NAME = "location_time_zone_manager";
|
||||
|
||||
/**
|
||||
* Shell command that starts the service (after stop).
|
||||
* A shell command that starts the service (after stop).
|
||||
*/
|
||||
public static final String SHELL_COMMAND_START = "start";
|
||||
|
||||
/**
|
||||
* Shell command that stops the service.
|
||||
* A shell command that stops the service.
|
||||
*/
|
||||
public static final String SHELL_COMMAND_STOP = "stop";
|
||||
|
||||
/**
|
||||
* Shell command that sends test commands to a provider
|
||||
* A shell command that can put providers into different modes. Takes effect next time the
|
||||
* service is started.
|
||||
*/
|
||||
public static final String SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE =
|
||||
"set_provider_mode_override";
|
||||
|
||||
/**
|
||||
* The default provider mode.
|
||||
* For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
|
||||
*/
|
||||
public static final String PROVIDER_MODE_OVERRIDE_NONE = "none";
|
||||
|
||||
/**
|
||||
* The "simulated" provider mode.
|
||||
* For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
|
||||
*/
|
||||
public static final String PROVIDER_MODE_OVERRIDE_SIMULATED = "simulated";
|
||||
|
||||
/**
|
||||
* The "disabled" provider mode (equivalent to there being no provider configured).
|
||||
* For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
|
||||
*/
|
||||
public static final String PROVIDER_MODE_OVERRIDE_DISABLED = "disabled";
|
||||
|
||||
/**
|
||||
* A shell command that tells the service to record state information during tests. The next
|
||||
* argument value is "true" or "false".
|
||||
*/
|
||||
public static final String SHELL_COMMAND_RECORD_PROVIDER_STATES = "record_provider_states";
|
||||
|
||||
/**
|
||||
* A shell command that tells the service to dump its current state.
|
||||
*/
|
||||
public static final String SHELL_COMMAND_DUMP_STATE = "dump_state";
|
||||
|
||||
/**
|
||||
* Option for {@link #SHELL_COMMAND_DUMP_STATE} that tells it to dump state as a binary proto.
|
||||
*/
|
||||
public static final String DUMP_STATE_OPTION_PROTO = "--proto";
|
||||
|
||||
/**
|
||||
* A shell command that sends test commands to a provider
|
||||
*/
|
||||
public static final String SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND =
|
||||
"send_provider_test_command";
|
||||
@@ -88,35 +129,6 @@ public final class LocationTimeZoneManager {
|
||||
*/
|
||||
public static final String SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN = "uncertain";
|
||||
|
||||
private static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX =
|
||||
"persist.sys.geotz.";
|
||||
|
||||
/**
|
||||
* The name of the system property that can be used to set the primary provider into test mode
|
||||
* (value = {@link #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED}) or disabled (value = {@link
|
||||
* #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED}).
|
||||
*/
|
||||
public static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY =
|
||||
SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX + PRIMARY_PROVIDER_NAME;
|
||||
|
||||
/**
|
||||
* The name of the system property that can be used to set the secondary provider into test mode
|
||||
* (value = {@link #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED}) or disabled (value = {@link
|
||||
* #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED}).
|
||||
*/
|
||||
public static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY =
|
||||
SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX + SECONDARY_PROVIDER_NAME;
|
||||
|
||||
/**
|
||||
* The value of the provider mode system property to put a provider into test mode.
|
||||
*/
|
||||
public static final String SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED = "simulated";
|
||||
|
||||
/**
|
||||
* The value of the provider mode system property to put a provider into disabled mode.
|
||||
*/
|
||||
public static final String SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED = "disabled";
|
||||
|
||||
private LocationTimeZoneManager() {
|
||||
// No need to instantiate.
|
||||
}
|
||||
|
||||
@@ -29,11 +29,69 @@ import android.content.Context;
|
||||
@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
|
||||
public interface TimeZoneDetector {
|
||||
|
||||
/** @hide */
|
||||
/**
|
||||
* The name of the service for shell commands.
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_SERVICE_NAME = "time_zone_detector";
|
||||
|
||||
/**
|
||||
* A shell command that prints the current "auto time zone detection" global setting value.
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED = "is_auto_detection_enabled";
|
||||
|
||||
/**
|
||||
* A shell command that sets the current "auto time zone detection" global setting value.
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED = "set_auto_detection_enabled";
|
||||
|
||||
/**
|
||||
* A shell command that prints whether the geolocation-based time zone detection feature is
|
||||
* supported on the device.
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED = "is_geo_detection_supported";
|
||||
|
||||
/**
|
||||
* A shell command that prints the current user's "location enabled" setting.
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_IS_LOCATION_ENABLED = "is_location_enabled";
|
||||
|
||||
/**
|
||||
* A shell command that prints the current user's "location-based time zone detection enabled"
|
||||
* setting.
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_IS_GEO_DETECTION_ENABLED = "is_geo_detection_enabled";
|
||||
|
||||
/**
|
||||
* A shell command that sets the current user's "location-based time zone detection enabled"
|
||||
* setting.
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_SET_GEO_DETECTION_ENABLED = "set_geo_detection_enabled";
|
||||
|
||||
/**
|
||||
* A shell command that injects a geolocation time zone suggestion (as if from the
|
||||
* location_time_zone_manager).
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE = "suggest_geo_location_time_zone";
|
||||
/** @hide */
|
||||
|
||||
/**
|
||||
* A shell command that injects a manual time zone suggestion (as if from the SettingsUI or
|
||||
* similar).
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE = "suggest_manual_time_zone";
|
||||
/** @hide */
|
||||
|
||||
/**
|
||||
* A shell command that injects a telephony time zone suggestion (as if from the phone app).
|
||||
* @hide
|
||||
*/
|
||||
String SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE = "suggest_telephony_time_zone";
|
||||
|
||||
/**
|
||||
|
||||
@@ -610,6 +610,38 @@ class ControllerImpl extends LocationTimeZoneProviderController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the controller should record provider state changes for later dumping via
|
||||
* {@link #getStateForTests()}.
|
||||
*/
|
||||
void setProviderStateRecordingEnabled(boolean enabled) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
mPrimaryProvider.setStateChangeRecordingEnabled(enabled);
|
||||
mSecondaryProvider.setStateChangeRecordingEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the current controller state for tests.
|
||||
*/
|
||||
@NonNull
|
||||
LocationTimeZoneManagerServiceState getStateForTests() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
LocationTimeZoneManagerServiceState.Builder builder =
|
||||
new LocationTimeZoneManagerServiceState.Builder();
|
||||
if (mLastSuggestion != null) {
|
||||
builder.setLastSuggestion(mLastSuggestion);
|
||||
}
|
||||
builder.setPrimaryProviderStateChanges(mPrimaryProvider.getRecordedStates())
|
||||
.setSecondaryProviderStateChanges(mSecondaryProvider.getRecordedStates());
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private LocationTimeZoneProvider getLocationTimeZoneProvider(@NonNull String providerName) {
|
||||
LocationTimeZoneProvider targetProvider;
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
package com.android.server.location.timezone;
|
||||
|
||||
import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
|
||||
import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
|
||||
import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY;
|
||||
import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY;
|
||||
import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED;
|
||||
import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -33,7 +32,6 @@ import android.os.Handler;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ShellCallback;
|
||||
import android.os.SystemProperties;
|
||||
import android.service.timezone.TimeZoneProviderService;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Log;
|
||||
@@ -42,6 +40,7 @@ import android.util.Slog;
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
|
||||
@@ -161,6 +160,14 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
@GuardedBy("mSharedLock")
|
||||
private ControllerEnvironmentImpl mEnvironment;
|
||||
|
||||
@GuardedBy("mSharedLock")
|
||||
@NonNull
|
||||
private String mPrimaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE;
|
||||
|
||||
@GuardedBy("mSharedLock")
|
||||
@NonNull
|
||||
private String mSecondaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE;
|
||||
|
||||
LocationTimeZoneManagerService(Context context) {
|
||||
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
|
||||
mHandler = FgThread.getHandler();
|
||||
@@ -231,9 +238,9 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
|
||||
private LocationTimeZoneProvider createPrimaryProvider() {
|
||||
LocationTimeZoneProviderProxy proxy;
|
||||
if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) {
|
||||
if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) {
|
||||
proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else if (isDisabled(PRIMARY_PROVIDER_NAME)) {
|
||||
} else if (isProviderDisabled(PRIMARY_PROVIDER_NAME)) {
|
||||
proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else {
|
||||
proxy = new RealLocationTimeZoneProviderProxy(
|
||||
@@ -250,9 +257,9 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
|
||||
private LocationTimeZoneProvider createSecondaryProvider() {
|
||||
LocationTimeZoneProviderProxy proxy;
|
||||
if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) {
|
||||
if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) {
|
||||
proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else if (isDisabled(SECONDARY_PROVIDER_NAME)) {
|
||||
} else if (isProviderDisabled(SECONDARY_PROVIDER_NAME)) {
|
||||
proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else {
|
||||
proxy = new RealLocationTimeZoneProviderProxy(
|
||||
@@ -268,16 +275,14 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
}
|
||||
|
||||
/** Used for bug triage and in tests to simulate provider events. */
|
||||
private static boolean isInSimulationMode(String providerName) {
|
||||
return isProviderModeSetInSystemProperties(providerName,
|
||||
SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED);
|
||||
private boolean isProviderInSimulationMode(String providerName) {
|
||||
return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED);
|
||||
}
|
||||
|
||||
/** Used for bug triage, tests and experiments to remove a provider. */
|
||||
private boolean isDisabled(String providerName) {
|
||||
private boolean isProviderDisabled(String providerName) {
|
||||
return !isProviderEnabledInConfig(providerName)
|
||||
|| isProviderModeSetInSystemProperties(
|
||||
providerName, SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED);
|
||||
|| isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED);
|
||||
}
|
||||
|
||||
private boolean isProviderEnabledInConfig(String providerName) {
|
||||
@@ -299,25 +304,18 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
return resources.getBoolean(providerEnabledConfigId);
|
||||
}
|
||||
|
||||
private static boolean isProviderModeSetInSystemProperties(
|
||||
@NonNull String providerName, @NonNull String mode) {
|
||||
String systemPropertyKey;
|
||||
private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) {
|
||||
switch (providerName) {
|
||||
case PRIMARY_PROVIDER_NAME: {
|
||||
systemPropertyKey = SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY;
|
||||
break;
|
||||
return Objects.equals(mPrimaryProviderModeOverride, mode);
|
||||
}
|
||||
case SECONDARY_PROVIDER_NAME: {
|
||||
systemPropertyKey = SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY;
|
||||
break;
|
||||
return Objects.equals(mSecondaryProviderModeOverride, mode);
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException(providerName);
|
||||
}
|
||||
}
|
||||
|
||||
String systemPropertyProviderMode = SystemProperties.get(systemPropertyKey, null);
|
||||
return Objects.equals(systemPropertyProviderMode, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,6 +345,67 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
this, in, out, err, args, callback, resultReceiver);
|
||||
}
|
||||
|
||||
/** Sets this service into provider state recording mode for tests. */
|
||||
void setProviderModeOverride(@NonNull String providerName, @NonNull String mode) {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
Preconditions.checkArgument(
|
||||
PRIMARY_PROVIDER_NAME.equals(providerName)
|
||||
|| SECONDARY_PROVIDER_NAME.equals(providerName));
|
||||
Preconditions.checkArgument(PROVIDER_MODE_OVERRIDE_DISABLED.equals(mode)
|
||||
|| PROVIDER_MODE_OVERRIDE_SIMULATED.equals(mode)
|
||||
|| PROVIDER_MODE_OVERRIDE_NONE.equals(mode));
|
||||
|
||||
mThreadingDomain.postAndWait(() -> {
|
||||
synchronized (mSharedLock) {
|
||||
switch (providerName) {
|
||||
case PRIMARY_PROVIDER_NAME: {
|
||||
mPrimaryProviderModeOverride = mode;
|
||||
break;
|
||||
}
|
||||
case SECONDARY_PROVIDER_NAME: {
|
||||
mSecondaryProviderModeOverride = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
|
||||
}
|
||||
|
||||
/** Sets this service into provider state recording mode for tests. */
|
||||
void setProviderStateRecordingEnabled(boolean enabled) {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
mThreadingDomain.postAndWait(() -> {
|
||||
synchronized (mSharedLock) {
|
||||
if (mLocationTimeZoneDetectorController != null) {
|
||||
mLocationTimeZoneDetectorController.setProviderStateRecordingEnabled(enabled);
|
||||
}
|
||||
}
|
||||
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
|
||||
}
|
||||
|
||||
/** Returns a snapshot of the current controller state for tests. */
|
||||
@NonNull
|
||||
LocationTimeZoneManagerServiceState getStateForTests() {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
try {
|
||||
return mThreadingDomain.postAndWait(
|
||||
() -> {
|
||||
synchronized (mSharedLock) {
|
||||
if (mLocationTimeZoneDetectorController == null) {
|
||||
return null;
|
||||
}
|
||||
return mLocationTimeZoneDetectorController.getStateForTests();
|
||||
}
|
||||
},
|
||||
BLOCKING_OP_WAIT_DURATION_MILLIS);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes a {@link TestCommand} to the specified provider and waits for the response.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.location.timezone;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
|
||||
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/** A snapshot of the location time zone manager service's state for tests. */
|
||||
final class LocationTimeZoneManagerServiceState {
|
||||
|
||||
@Nullable private final GeolocationTimeZoneSuggestion mLastSuggestion;
|
||||
@NonNull private final List<ProviderState> mPrimaryProviderStates;
|
||||
@NonNull private final List<ProviderState> mSecondaryProviderStates;
|
||||
|
||||
LocationTimeZoneManagerServiceState(@NonNull Builder builder) {
|
||||
mLastSuggestion = builder.mLastSuggestion;
|
||||
mPrimaryProviderStates = Objects.requireNonNull(builder.mPrimaryProviderStates);
|
||||
mSecondaryProviderStates = Objects.requireNonNull(builder.mSecondaryProviderStates);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GeolocationTimeZoneSuggestion getLastSuggestion() {
|
||||
return mLastSuggestion;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<ProviderState> getPrimaryProviderStates() {
|
||||
return Collections.unmodifiableList(mPrimaryProviderStates);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<ProviderState> getSecondaryProviderStates() {
|
||||
return Collections.unmodifiableList(mSecondaryProviderStates);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LocationTimeZoneManagerServiceState{"
|
||||
+ "mLastSuggestion=" + mLastSuggestion
|
||||
+ ", mPrimaryProviderStates=" + mPrimaryProviderStates
|
||||
+ ", mSecondaryProviderStates=" + mSecondaryProviderStates
|
||||
+ '}';
|
||||
}
|
||||
|
||||
static final class Builder {
|
||||
|
||||
private GeolocationTimeZoneSuggestion mLastSuggestion;
|
||||
private List<ProviderState> mPrimaryProviderStates;
|
||||
private List<ProviderState> mSecondaryProviderStates;
|
||||
|
||||
@NonNull
|
||||
Builder setLastSuggestion(@NonNull GeolocationTimeZoneSuggestion lastSuggestion) {
|
||||
mLastSuggestion = Objects.requireNonNull(lastSuggestion);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
Builder setPrimaryProviderStateChanges(@NonNull List<ProviderState> primaryProviderStates) {
|
||||
mPrimaryProviderStates = new ArrayList<>(primaryProviderStates);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
Builder setSecondaryProviderStateChanges(
|
||||
@NonNull List<ProviderState> secondaryProviderStates) {
|
||||
mSecondaryProviderStates = new ArrayList<>(secondaryProviderStates);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
LocationTimeZoneManagerServiceState build() {
|
||||
return new LocationTimeZoneManagerServiceState(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,23 +15,46 @@
|
||||
*/
|
||||
package com.android.server.location.timezone;
|
||||
|
||||
import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
|
||||
import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
|
||||
import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
|
||||
import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY;
|
||||
import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY;
|
||||
import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED;
|
||||
import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED;
|
||||
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.time.GeolocationTimeZoneSuggestionProto;
|
||||
import android.app.time.LocationTimeZoneManagerProto;
|
||||
import android.app.time.LocationTimeZoneManagerServiceStateProto;
|
||||
import android.app.time.TimeZoneProviderStateProto;
|
||||
import android.os.Bundle;
|
||||
import android.os.ShellCommand;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.util.dump.DualDumpOutputStream;
|
||||
import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
|
||||
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/** Implements the shell command interface for {@link LocationTimeZoneManagerService}. */
|
||||
class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
@@ -58,9 +81,18 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
case SHELL_COMMAND_STOP: {
|
||||
return runStop();
|
||||
}
|
||||
case SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE: {
|
||||
return runSetProviderModeOverride();
|
||||
}
|
||||
case SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND: {
|
||||
return runSendProviderTestCommand();
|
||||
}
|
||||
case SHELL_COMMAND_RECORD_PROVIDER_STATES: {
|
||||
return runRecordProviderStates();
|
||||
}
|
||||
case SHELL_COMMAND_DUMP_STATE: {
|
||||
return runDumpControllerState();
|
||||
}
|
||||
default: {
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
@@ -70,35 +102,42 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
@Override
|
||||
public void onHelp() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
pw.println("Location Time Zone Manager (location_time_zone_manager) commands:");
|
||||
pw.println("Location Time Zone Manager (location_time_zone_manager) commands for tests:");
|
||||
pw.println(" help");
|
||||
pw.println(" Print this help text.");
|
||||
pw.printf(" %s\n", SHELL_COMMAND_START);
|
||||
pw.println(" Starts the location_time_zone_manager, creating time zone providers.");
|
||||
pw.printf(" %s\n", SHELL_COMMAND_STOP);
|
||||
pw.println(" Stops the location_time_zone_manager, destroying time zone providers.");
|
||||
pw.printf(" %s <provider name> <mode>\n", SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE);
|
||||
pw.println(" Sets a provider into a test mode next time the service started.");
|
||||
pw.printf(" Values: %s|%s|%s\n", PROVIDER_MODE_OVERRIDE_NONE,
|
||||
PROVIDER_MODE_OVERRIDE_DISABLED, PROVIDER_MODE_OVERRIDE_SIMULATED);
|
||||
pw.printf(" %s (true|false)\n", SHELL_COMMAND_RECORD_PROVIDER_STATES);
|
||||
pw.printf(" Enables / disables provider state recording mode. See also %s. The default"
|
||||
+ " state is always \"false\".\n", SHELL_COMMAND_DUMP_STATE);
|
||||
pw.println(" Note: When enabled, this mode consumes memory and it is only intended for"
|
||||
+ " testing.");
|
||||
pw.println(" It should be disabled after use, or the device can be rebooted to"
|
||||
+ " reset the mode to disabled.");
|
||||
pw.println(" Disabling (or enabling repeatedly) clears any existing stored states.");
|
||||
pw.printf(" %s [%s]\n", SHELL_COMMAND_DUMP_STATE, DUMP_STATE_OPTION_PROTO);
|
||||
pw.println(" Dumps Location Time Zone Manager state for tests as text or binary proto"
|
||||
+ " form.");
|
||||
pw.println(" See the LocationTimeZoneManagerServiceStateProto definition for details.");
|
||||
pw.printf(" %s <provider name> <test command>\n",
|
||||
SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
|
||||
pw.println(" Passes a test command to the named provider.");
|
||||
pw.println();
|
||||
pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
|
||||
pw.println();
|
||||
pw.printf("<provider name> = One of %s\n", VALID_PROVIDER_NAMES);
|
||||
pw.println();
|
||||
pw.println("<test command> encoding:");
|
||||
pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
|
||||
pw.println();
|
||||
pw.println("Provider <test command> encoding:");
|
||||
pw.println();
|
||||
TestCommand.printShellCommandEncodingHelp(pw);
|
||||
pw.println();
|
||||
pw.printf("Provider modes can be modified by setting the \"%s\" or \"%s\"\n system"
|
||||
+ " property and restarting the service or rebooting the device.\n",
|
||||
SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY,
|
||||
SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY);
|
||||
pw.println("Values are:");
|
||||
pw.printf(" %s - simulation mode (see below for commands)\n",
|
||||
SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED);
|
||||
pw.printf(" %s - disabled mode\n", SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED);
|
||||
pw.println();
|
||||
pw.println("Simulated providers can be used to test the system server behavior or to"
|
||||
pw.println("Simulated provider mode can be used to test the system server behavior or to"
|
||||
+ " reproduce bugs without the complexity of using real providers.");
|
||||
pw.println();
|
||||
pw.println("The test commands for simulated providers are:");
|
||||
@@ -132,6 +171,119 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runSetProviderModeOverride() {
|
||||
PrintWriter outPrintWriter = getOutPrintWriter();
|
||||
try {
|
||||
String providerName = getNextArgRequired();
|
||||
String modeOverride = getNextArgRequired();
|
||||
outPrintWriter.println("Setting provider mode override for " + providerName
|
||||
+ " to " + modeOverride);
|
||||
mService.setProviderModeOverride(providerName, modeOverride);
|
||||
} catch (RuntimeException e) {
|
||||
reportError(e);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runRecordProviderStates() {
|
||||
PrintWriter outPrintWriter = getOutPrintWriter();
|
||||
boolean enabled;
|
||||
try {
|
||||
String nextArg = getNextArgRequired();
|
||||
enabled = Boolean.parseBoolean(nextArg);
|
||||
} catch (RuntimeException e) {
|
||||
reportError(e);
|
||||
return 1;
|
||||
}
|
||||
|
||||
outPrintWriter.println("Setting provider state recording to " + enabled);
|
||||
try {
|
||||
mService.setProviderStateRecordingEnabled(enabled);
|
||||
} catch (IllegalStateException e) {
|
||||
reportError(e);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runDumpControllerState() {
|
||||
LocationTimeZoneManagerServiceState state;
|
||||
try {
|
||||
state = mService.getStateForTests();
|
||||
} catch (RuntimeException e) {
|
||||
reportError(e);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DualDumpOutputStream outputStream;
|
||||
boolean useProto = Objects.equals(DUMP_STATE_OPTION_PROTO, getNextOption());
|
||||
if (useProto) {
|
||||
FileDescriptor outFd = getOutFileDescriptor();
|
||||
outputStream = new DualDumpOutputStream(new ProtoOutputStream(outFd));
|
||||
} else {
|
||||
outputStream = new DualDumpOutputStream(
|
||||
new IndentingPrintWriter(getOutPrintWriter(), " "));
|
||||
}
|
||||
if (state.getLastSuggestion() != null) {
|
||||
GeolocationTimeZoneSuggestion lastSuggestion = state.getLastSuggestion();
|
||||
long lastSuggestionToken = outputStream.start(
|
||||
"last_suggestion", LocationTimeZoneManagerServiceStateProto.LAST_SUGGESTION);
|
||||
for (String zoneId : lastSuggestion.getZoneIds()) {
|
||||
outputStream.write(
|
||||
"zone_ids" , GeolocationTimeZoneSuggestionProto.ZONE_IDS, zoneId);
|
||||
}
|
||||
for (String debugInfo : lastSuggestion.getDebugInfo()) {
|
||||
outputStream.write(
|
||||
"debug_info", GeolocationTimeZoneSuggestionProto.DEBUG_INFO, debugInfo);
|
||||
}
|
||||
outputStream.end(lastSuggestionToken);
|
||||
}
|
||||
|
||||
writeProviderStates(outputStream, state.getPrimaryProviderStates(),
|
||||
"primary_provider_states",
|
||||
LocationTimeZoneManagerServiceStateProto.PRIMARY_PROVIDER_STATES);
|
||||
writeProviderStates(outputStream, state.getSecondaryProviderStates(),
|
||||
"secondary_provider_states",
|
||||
LocationTimeZoneManagerServiceStateProto.SECONDARY_PROVIDER_STATES);
|
||||
outputStream.flush();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void writeProviderStates(DualDumpOutputStream outputStream,
|
||||
List<LocationTimeZoneProvider.ProviderState> providerStates, String fieldName,
|
||||
long fieldId) {
|
||||
for (LocationTimeZoneProvider.ProviderState providerState : providerStates) {
|
||||
long providerStateToken = outputStream.start(fieldName, fieldId);
|
||||
outputStream.write("state", TimeZoneProviderStateProto.STATE,
|
||||
convertProviderStateEnumToProtoEnum(providerState.stateEnum));
|
||||
outputStream.end(providerStateToken);
|
||||
}
|
||||
}
|
||||
|
||||
private static int convertProviderStateEnumToProtoEnum(@ProviderStateEnum int stateEnum) {
|
||||
switch (stateEnum) {
|
||||
case PROVIDER_STATE_UNKNOWN:
|
||||
return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_UNKNOWN;
|
||||
case PROVIDER_STATE_STARTED_INITIALIZING:
|
||||
return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_INITIALIZING;
|
||||
case PROVIDER_STATE_STARTED_CERTAIN:
|
||||
return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_CERTAIN;
|
||||
case PROVIDER_STATE_STARTED_UNCERTAIN:
|
||||
return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_UNCERTAIN;
|
||||
case PROVIDER_STATE_STOPPED:
|
||||
return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_DISABLED;
|
||||
case PROVIDER_STATE_PERM_FAILED:
|
||||
return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_PERM_FAILED;
|
||||
case PROVIDER_STATE_DESTROYED:
|
||||
return LocationTimeZoneManagerProto.TIME_ZONE_PROVIDER_STATE_DESTROYED;
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown stateEnum=" + stateEnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int runSendProviderTestCommand() {
|
||||
PrintWriter outPrintWriter = getOutPrintWriter();
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ import com.android.server.timezonedetector.Dumpable;
|
||||
import com.android.server.timezonedetector.ReferenceWithHistory;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -338,12 +340,21 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
@NonNull final Object mSharedLock;
|
||||
@NonNull final String mProviderName;
|
||||
|
||||
/**
|
||||
* Usually {@code false} but can be set to {@code true} for testing.
|
||||
*/
|
||||
@GuardedBy("mSharedLock")
|
||||
private boolean mStateChangeRecording;
|
||||
|
||||
@GuardedBy("mSharedLock")
|
||||
@NonNull
|
||||
private final ArrayList<ProviderState> mRecordedStates = new ArrayList<>(0);
|
||||
|
||||
/**
|
||||
* The current state (with history for debugging).
|
||||
*/
|
||||
@GuardedBy("mSharedLock")
|
||||
final ReferenceWithHistory<ProviderState> mCurrentState =
|
||||
new ReferenceWithHistory<>(10);
|
||||
final ReferenceWithHistory<ProviderState> mCurrentState = new ReferenceWithHistory<>(10);
|
||||
|
||||
/**
|
||||
* Used for scheduling initialization timeouts, i.e. for providers that have just been started.
|
||||
@@ -422,6 +433,28 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
@GuardedBy("mSharedLock")
|
||||
abstract void onDestroy();
|
||||
|
||||
/**
|
||||
* Sets the provider into state recording mode for tests.
|
||||
*/
|
||||
final void setStateChangeRecordingEnabled(boolean enabled) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
synchronized (mSharedLock) {
|
||||
mStateChangeRecording = enabled;
|
||||
mRecordedStates.clear();
|
||||
mRecordedStates.trimToSize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns recorded states.
|
||||
*/
|
||||
final List<ProviderState> getRecordedStates() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
synchronized (mSharedLock) {
|
||||
return new ArrayList<>(mRecordedStates);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current state, for use by this class and subclasses only. If {@code #notifyChanges}
|
||||
* is {@code true} and {@code newState} is not equal to the old state, then {@link
|
||||
@@ -434,8 +467,11 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
ProviderState oldState = mCurrentState.get();
|
||||
mCurrentState.set(newState);
|
||||
onSetCurrentState(newState);
|
||||
if (notifyChanges) {
|
||||
if (!Objects.equals(newState, oldState)) {
|
||||
if (!Objects.equals(newState, oldState)) {
|
||||
if (mStateChangeRecording) {
|
||||
mRecordedStates.add(newState);
|
||||
}
|
||||
if (notifyChanges) {
|
||||
mProviderListener.onProviderStateChange(newState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.app.timezonedetector.ITimeZoneDetectorService;
|
||||
import android.app.timezonedetector.ManualTimeZoneSuggestion;
|
||||
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
|
||||
import android.content.Context;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -311,6 +312,19 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
|
||||
mHandler.post(() -> mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion));
|
||||
}
|
||||
|
||||
boolean isGeoTimeZoneDetectionSupported() {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
return isGeoLocationTimeZoneDetectionEnabled(mContext);
|
||||
}
|
||||
|
||||
boolean isLocationEnabled() {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
return mContext.getSystemService(LocationManager.class)
|
||||
.isLocationEnabledForUser(mContext.getUser());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
|
||||
@Nullable String[] args) {
|
||||
|
||||
@@ -15,10 +15,17 @@
|
||||
*/
|
||||
package com.android.server.timezonedetector;
|
||||
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_ENABLED;
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED;
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_LOCATION_ENABLED;
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED;
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SET_GEO_DETECTION_ENABLED;
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE;
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE;
|
||||
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE;
|
||||
|
||||
import android.app.time.TimeZoneConfiguration;
|
||||
import android.app.timezonedetector.ManualTimeZoneSuggestion;
|
||||
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
|
||||
import android.os.ShellCommand;
|
||||
@@ -43,6 +50,18 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED:
|
||||
return runIsAutoDetectionEnabled();
|
||||
case SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED:
|
||||
return runSetAutoDetectionEnabled();
|
||||
case SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED:
|
||||
return runIsGeoDetectionSupported();
|
||||
case SHELL_COMMAND_IS_LOCATION_ENABLED:
|
||||
return runIsLocationEnabled();
|
||||
case SHELL_COMMAND_IS_GEO_DETECTION_ENABLED:
|
||||
return runIsGeoDetectionEnabled();
|
||||
case SHELL_COMMAND_SET_GEO_DETECTION_ENABLED:
|
||||
return runSetGeoDetectionEnabled();
|
||||
case SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE:
|
||||
return runSuggestGeolocationTimeZone();
|
||||
case SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE:
|
||||
@@ -55,6 +74,54 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private int runIsAutoDetectionEnabled() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
boolean enabled = mInterface.getCapabilitiesAndConfig()
|
||||
.getConfiguration()
|
||||
.isAutoDetectionEnabled();
|
||||
pw.println(enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runIsGeoDetectionSupported() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
boolean enabled = mInterface.isGeoTimeZoneDetectionSupported();
|
||||
pw.println(enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runIsLocationEnabled() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
boolean enabled = mInterface.isLocationEnabled();
|
||||
pw.println(enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runIsGeoDetectionEnabled() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
boolean enabled = mInterface.getCapabilitiesAndConfig()
|
||||
.getConfiguration()
|
||||
.isGeoDetectionEnabled();
|
||||
pw.println(enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runSetAutoDetectionEnabled() {
|
||||
boolean enabled = Boolean.parseBoolean(getNextArgRequired());
|
||||
TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder()
|
||||
.setAutoDetectionEnabled(enabled)
|
||||
.build();
|
||||
return mInterface.updateConfiguration(configuration) ? 0 : 1;
|
||||
}
|
||||
|
||||
private int runSetGeoDetectionEnabled() {
|
||||
boolean enabled = Boolean.parseBoolean(getNextArgRequired());
|
||||
TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder()
|
||||
.setGeoDetectionEnabled(enabled)
|
||||
.build();
|
||||
return mInterface.updateConfiguration(configuration) ? 0 : 1;
|
||||
}
|
||||
|
||||
private int runSuggestGeolocationTimeZone() {
|
||||
return runSuggestTimeZone(
|
||||
() -> GeolocationTimeZoneSuggestion.parseCommandLineArg(this),
|
||||
@@ -96,6 +163,20 @@ class TimeZoneDetectorShellCommand extends ShellCommand {
|
||||
pw.println("Time Zone Detector (time_zone_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 tz detection setting");
|
||||
pw.printf(" %s true|false\n", SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED);
|
||||
pw.println(" Sets the automatic tz detection setting.");
|
||||
pw.printf(" %s\n", SHELL_COMMAND_IS_GEO_DETECTION_SUPPORTED);
|
||||
pw.println(" Prints true/false according to whether geolocation time zone detection is"
|
||||
+ " supported on this device");
|
||||
pw.printf(" %s\n", SHELL_COMMAND_IS_LOCATION_ENABLED);
|
||||
pw.println(" Prints true/false according to whether the master location toggle is"
|
||||
+ " enabled for the current user");
|
||||
pw.printf(" %s\n", SHELL_COMMAND_IS_GEO_DETECTION_ENABLED);
|
||||
pw.println(" Prints true/false according to the geolocation tz detection setting");
|
||||
pw.printf(" %s true|false\n", SHELL_COMMAND_SET_GEO_DETECTION_ENABLED);
|
||||
pw.println(" Sets the geolocation tz detection setting.");
|
||||
pw.printf(" %s <geolocation suggestion opts>\n",
|
||||
SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE);
|
||||
pw.printf(" %s <manual suggestion opts>\n",
|
||||
|
||||
@@ -28,6 +28,7 @@ import static com.android.server.location.timezone.TimeZoneProviderEvent.createU
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@@ -923,6 +924,74 @@ public class ControllerImplTest {
|
||||
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateRecording() {
|
||||
ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
|
||||
mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
|
||||
TestEnvironment testEnvironment = new TestEnvironment(
|
||||
mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
|
||||
|
||||
// Initialize and check initial state.
|
||||
controllerImpl.initialize(testEnvironment, mTestCallback);
|
||||
|
||||
{
|
||||
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
|
||||
assertNull(state.getLastSuggestion());
|
||||
assertTrue(state.getPrimaryProviderStates().isEmpty());
|
||||
assertTrue(state.getSecondaryProviderStates().isEmpty());
|
||||
}
|
||||
|
||||
// State recording and simulate some provider behavior that will show up in the state
|
||||
// recording.
|
||||
controllerImpl.setProviderStateRecordingEnabled(true);
|
||||
|
||||
// Simulate an uncertain event from the primary. This will start the secondary.
|
||||
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
|
||||
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
|
||||
|
||||
{
|
||||
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
|
||||
assertNull(state.getLastSuggestion());
|
||||
List<LocationTimeZoneProvider.ProviderState> primaryProviderStates =
|
||||
state.getPrimaryProviderStates();
|
||||
assertEquals(1, primaryProviderStates.size());
|
||||
assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN,
|
||||
primaryProviderStates.get(0).stateEnum);
|
||||
List<LocationTimeZoneProvider.ProviderState> secondaryProviderStates =
|
||||
state.getSecondaryProviderStates();
|
||||
assertEquals(1, secondaryProviderStates.size());
|
||||
assertEquals(PROVIDER_STATE_STARTED_INITIALIZING,
|
||||
secondaryProviderStates.get(0).stateEnum);
|
||||
}
|
||||
|
||||
// Simulate an uncertain event from the primary. This will start the secondary.
|
||||
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
|
||||
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
|
||||
|
||||
{
|
||||
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
|
||||
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
|
||||
state.getLastSuggestion().getZoneIds());
|
||||
List<LocationTimeZoneProvider.ProviderState> primaryProviderStates =
|
||||
state.getPrimaryProviderStates();
|
||||
assertEquals(1, primaryProviderStates.size());
|
||||
assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, primaryProviderStates.get(0).stateEnum);
|
||||
List<LocationTimeZoneProvider.ProviderState> secondaryProviderStates =
|
||||
state.getSecondaryProviderStates();
|
||||
assertEquals(2, secondaryProviderStates.size());
|
||||
assertEquals(PROVIDER_STATE_STARTED_CERTAIN, secondaryProviderStates.get(1).stateEnum);
|
||||
}
|
||||
|
||||
controllerImpl.setProviderStateRecordingEnabled(false);
|
||||
{
|
||||
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
|
||||
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
|
||||
state.getLastSuggestion().getZoneIds());
|
||||
assertTrue(state.getPrimaryProviderStates().isEmpty());
|
||||
assertTrue(state.getSecondaryProviderStates().isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertUncertaintyTimeoutSet(
|
||||
LocationTimeZoneProviderController.Environment environment,
|
||||
LocationTimeZoneProviderController controller) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.location.timezone;
|
||||
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
|
||||
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
|
||||
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
|
||||
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
|
||||
@@ -49,6 +50,7 @@ import org.junit.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
@@ -174,6 +176,48 @@ public class LocationTimeZoneProviderTest {
|
||||
assertNotNull(result.getString(TEST_COMMAND_RESULT_ERROR_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateRecording() {
|
||||
String providerName = "primary";
|
||||
TestLocationTimeZoneProvider provider =
|
||||
new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
|
||||
provider.setStateChangeRecordingEnabled(true);
|
||||
|
||||
// initialize()
|
||||
provider.initialize(mProviderListener);
|
||||
provider.assertLatestRecordedState(PROVIDER_STATE_STOPPED);
|
||||
|
||||
// startUpdates()
|
||||
ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
|
||||
Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
|
||||
Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
|
||||
provider.startUpdates(config, arbitraryInitializationTimeout,
|
||||
arbitraryInitializationTimeoutFuzz);
|
||||
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_INITIALIZING);
|
||||
|
||||
// Simulate a suggestion event being received.
|
||||
TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
|
||||
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
|
||||
.setTimeZoneIds(Arrays.asList("Europe/London"))
|
||||
.build();
|
||||
TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
|
||||
provider.simulateProviderEventReceived(event);
|
||||
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_CERTAIN);
|
||||
|
||||
// Simulate an uncertain event being received.
|
||||
event = TimeZoneProviderEvent.createUncertainEvent();
|
||||
provider.simulateProviderEventReceived(event);
|
||||
provider.assertLatestRecordedState(PROVIDER_STATE_STARTED_UNCERTAIN);
|
||||
|
||||
// stopUpdates()
|
||||
provider.stopUpdates();
|
||||
provider.assertLatestRecordedState(PROVIDER_STATE_STOPPED);
|
||||
|
||||
// destroy()
|
||||
provider.destroy();
|
||||
provider.assertLatestRecordedState(PROVIDER_STATE_DESTROYED);
|
||||
}
|
||||
|
||||
/** A test stand-in for the real {@link LocationTimeZoneProviderController}'s listener. */
|
||||
private static class TestProviderListener implements ProviderListener {
|
||||
|
||||
@@ -257,5 +301,11 @@ public class LocationTimeZoneProviderTest {
|
||||
void assertOnDestroyCalled() {
|
||||
assertTrue(mOnDestroyCalled);
|
||||
}
|
||||
|
||||
void assertLatestRecordedState(@ProviderState.ProviderStateEnum int expectedStateEnum) {
|
||||
List<ProviderState> recordedStates = getRecordedStates();
|
||||
assertEquals(expectedStateEnum,
|
||||
recordedStates.get(recordedStates.size() - 1).stateEnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user