Merge "Remove simulated LTZP and enable replacement"
This commit is contained in:
@@ -50,10 +50,10 @@ public final class LocationTimeZoneManager {
|
||||
public static final String SHELL_COMMAND_STOP = "stop";
|
||||
|
||||
/**
|
||||
* A shell command that tells the service to record state information during tests. The next
|
||||
* argument value is "true" or "false".
|
||||
* A shell command that clears recorded provider state information during tests.
|
||||
*/
|
||||
public static final String SHELL_COMMAND_RECORD_PROVIDER_STATES = "record_provider_states";
|
||||
public static final String SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES =
|
||||
"clear_recorded_provider_states";
|
||||
|
||||
/**
|
||||
* A shell command that tells the service to dump its current state.
|
||||
@@ -65,44 +65,15 @@ public final class LocationTimeZoneManager {
|
||||
*/
|
||||
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";
|
||||
/** A shell command that starts the location_time_zone_manager with named test providers. */
|
||||
public static final String SHELL_COMMAND_START_WITH_TEST_PROVIDERS =
|
||||
"start_with_test_providers";
|
||||
|
||||
/**
|
||||
* Simulated provider test command that simulates the bind succeeding.
|
||||
* The token that can be passed to {@link #SHELL_COMMAND_START_WITH_TEST_PROVIDERS} to indicate
|
||||
* there is no provider.
|
||||
*/
|
||||
public static final String SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND = "on_bind";
|
||||
|
||||
/**
|
||||
* Simulated provider test command that simulates the provider unbinding.
|
||||
*/
|
||||
public static final String SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND = "on_unbind";
|
||||
|
||||
/**
|
||||
* Simulated provider test command that simulates the provider entering the "permanent failure"
|
||||
* state.
|
||||
*/
|
||||
public static final String SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE = "perm_fail";
|
||||
|
||||
/**
|
||||
* Simulated provider test command that simulates the provider entering the "success" (time
|
||||
* zone(s) detected) state.
|
||||
*/
|
||||
public static final String SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS = "success";
|
||||
|
||||
/**
|
||||
* Argument for {@link #SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS} to specify TZDB time zone IDs.
|
||||
*/
|
||||
public static final String SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ = "tz";
|
||||
|
||||
/**
|
||||
* Simulated provider test command that simulates the provider entering the "uncertain"
|
||||
* state.
|
||||
*/
|
||||
public static final String SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN = "uncertain";
|
||||
public static final String NULL_PACKAGE_NAME_TOKEN = "@null";
|
||||
|
||||
private LocationTimeZoneManager() {
|
||||
// No need to instantiate.
|
||||
|
||||
@@ -46,18 +46,11 @@ import java.util.Set;
|
||||
public final class ServiceConfigAccessor {
|
||||
|
||||
@StringDef(prefix = "PROVIDER_MODE_",
|
||||
value = { PROVIDER_MODE_SIMULATED, PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED})
|
||||
value = { PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
|
||||
@interface ProviderMode {}
|
||||
|
||||
/**
|
||||
* The "simulated" provider mode.
|
||||
* For use with {@link #getPrimaryLocationTimeZoneProviderMode()} and {@link
|
||||
* #getSecondaryLocationTimeZoneProviderMode()}.
|
||||
*/
|
||||
public static final @ProviderMode String PROVIDER_MODE_SIMULATED = "simulated";
|
||||
|
||||
/**
|
||||
* The "disabled" provider mode. For use with {@link #getPrimaryLocationTimeZoneProviderMode()}
|
||||
* and {@link #getSecondaryLocationTimeZoneProviderMode()}.
|
||||
@@ -110,6 +103,47 @@ public final class ServiceConfigAccessor {
|
||||
|
||||
@NonNull private final ServerFlags mServerFlags;
|
||||
|
||||
/**
|
||||
* The mode to use for the primary location time zone provider in a test. Setting this
|
||||
* disables some permission checks.
|
||||
* This state is volatile: it is never written to storage / never survives a reboot. This is to
|
||||
* avoid a test provider accidentally being left configured on a device.
|
||||
* See also {@link #resetVolatileTestConfig()}.
|
||||
*/
|
||||
@Nullable
|
||||
private String mTestPrimaryLocationTimeZoneProviderMode;
|
||||
|
||||
/**
|
||||
* The package name to use for the primary location time zone provider in a test.
|
||||
* This state is volatile: it is never written to storage / never survives a reboot. This is to
|
||||
* avoid a test provider accidentally being left configured on a device.
|
||||
* See also {@link #resetVolatileTestConfig()}.
|
||||
*/
|
||||
@Nullable
|
||||
private String mTestPrimaryLocationTimeZoneProviderPackageName;
|
||||
|
||||
/**
|
||||
* See {@link #mTestPrimaryLocationTimeZoneProviderMode}; this is the equivalent for the
|
||||
* secondary provider.
|
||||
*/
|
||||
@Nullable
|
||||
private String mTestSecondaryLocationTimeZoneProviderMode;
|
||||
|
||||
/**
|
||||
* See {@link #mTestPrimaryLocationTimeZoneProviderPackageName}; this is the equivalent for the
|
||||
* secondary provider.
|
||||
*/
|
||||
@Nullable
|
||||
private String mTestSecondaryLocationTimeZoneProviderPackageName;
|
||||
|
||||
/**
|
||||
* Whether to record state changes for tests.
|
||||
* This state is volatile: it is never written to storage / never survives a reboot. This is to
|
||||
* avoid a test state accidentally being left configured on a device.
|
||||
* See also {@link #resetVolatileTestConfig()}.
|
||||
*/
|
||||
private boolean mRecordProviderStateChanges;
|
||||
|
||||
private ServiceConfigAccessor(@NonNull Context context) {
|
||||
mContext = Objects.requireNonNull(context);
|
||||
|
||||
@@ -200,23 +234,98 @@ public final class ServiceConfigAccessor {
|
||||
defaultEnabled);
|
||||
}
|
||||
|
||||
/** Returns the package name of the app hosting the primary location time zone provider. */
|
||||
@NonNull
|
||||
public String getPrimaryLocationTimeZoneProviderPackageName() {
|
||||
if (mTestPrimaryLocationTimeZoneProviderMode != null) {
|
||||
// In test mode: use the test setting value.
|
||||
return mTestPrimaryLocationTimeZoneProviderPackageName;
|
||||
}
|
||||
return mContext.getResources().getString(
|
||||
R.string.config_primaryLocationTimeZoneProviderPackageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the package name of the app hosting the primary location time zone provider for tests.
|
||||
* Setting a {@code null} value means the provider is to be disabled.
|
||||
* The values are reset with {@link #resetVolatileTestConfig()}.
|
||||
*/
|
||||
public void setTestPrimaryLocationTimeZoneProviderPackageName(
|
||||
@Nullable String testPrimaryLocationTimeZoneProviderPackageName) {
|
||||
mTestPrimaryLocationTimeZoneProviderPackageName =
|
||||
testPrimaryLocationTimeZoneProviderPackageName;
|
||||
mTestPrimaryLocationTimeZoneProviderMode =
|
||||
mTestPrimaryLocationTimeZoneProviderPackageName == null
|
||||
? PROVIDER_MODE_DISABLED : PROVIDER_MODE_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the usual permission checks are to be bypassed for the primary
|
||||
* provider. Returns {@code true} only if {@link
|
||||
* #setTestPrimaryLocationTimeZoneProviderPackageName} has been called.
|
||||
*/
|
||||
public boolean isTestPrimaryLocationTimeZoneProvider() {
|
||||
return mTestPrimaryLocationTimeZoneProviderMode != null;
|
||||
}
|
||||
|
||||
/** Returns the package name of the app hosting the secondary location time zone provider. */
|
||||
@NonNull
|
||||
public String getSecondaryLocationTimeZoneProviderPackageName() {
|
||||
if (mTestSecondaryLocationTimeZoneProviderMode != null) {
|
||||
// In test mode: use the test setting value.
|
||||
return mTestSecondaryLocationTimeZoneProviderPackageName;
|
||||
}
|
||||
return mContext.getResources().getString(
|
||||
R.string.config_secondaryLocationTimeZoneProviderPackageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the primary location time zone provider can be used.
|
||||
* Sets the package name of the app hosting the secondary location time zone provider for tests.
|
||||
* Setting a {@code null} value means the provider is to be disabled.
|
||||
* The values are reset with {@link #resetVolatileTestConfig()}.
|
||||
*/
|
||||
public void setTestSecondaryLocationTimeZoneProviderPackageName(
|
||||
@Nullable String testSecondaryLocationTimeZoneProviderPackageName) {
|
||||
mTestSecondaryLocationTimeZoneProviderPackageName =
|
||||
testSecondaryLocationTimeZoneProviderPackageName;
|
||||
mTestSecondaryLocationTimeZoneProviderMode =
|
||||
mTestSecondaryLocationTimeZoneProviderPackageName == null
|
||||
? PROVIDER_MODE_DISABLED : PROVIDER_MODE_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the usual permission checks are to be bypassed for the secondary
|
||||
* provider. Returns {@code true} only if {@link
|
||||
* #setTestSecondaryLocationTimeZoneProviderPackageName} has been called.
|
||||
*/
|
||||
public boolean isTestSecondaryLocationTimeZoneProvider() {
|
||||
return mTestSecondaryLocationTimeZoneProviderMode != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables the state recording mode for tests. The value is reset with {@link
|
||||
* #resetVolatileTestConfig()}.
|
||||
*/
|
||||
public void setRecordProviderStateChanges(boolean enabled) {
|
||||
mRecordProviderStateChanges = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if providers are expected to record their state changes for tests.
|
||||
*/
|
||||
public boolean getRecordProviderStateChanges() {
|
||||
return mRecordProviderStateChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode for the primary location time zone provider.
|
||||
*/
|
||||
@NonNull
|
||||
public @ProviderMode String getPrimaryLocationTimeZoneProviderMode() {
|
||||
if (mTestPrimaryLocationTimeZoneProviderMode != null) {
|
||||
// In test mode: use the test setting value.
|
||||
return mTestPrimaryLocationTimeZoneProviderMode;
|
||||
}
|
||||
return mServerFlags.getOptionalString(
|
||||
ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
|
||||
.orElse(getPrimaryLocationTimeZoneProviderModeFromConfig());
|
||||
@@ -230,9 +339,13 @@ public final class ServiceConfigAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode for the secondary location time zone provider can be used.
|
||||
* Returns the mode for the secondary location time zone provider.
|
||||
*/
|
||||
public @ProviderMode String getSecondaryLocationTimeZoneProviderMode() {
|
||||
if (mTestSecondaryLocationTimeZoneProviderMode != null) {
|
||||
// In test mode: use the test setting value.
|
||||
return mTestSecondaryLocationTimeZoneProviderMode;
|
||||
}
|
||||
return mServerFlags.getOptionalString(
|
||||
ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
|
||||
.orElse(getSecondaryLocationTimeZoneProviderModeFromConfig());
|
||||
@@ -298,6 +411,15 @@ public final class ServiceConfigAccessor {
|
||||
DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
|
||||
}
|
||||
|
||||
/** Clears all in-memory test config. */
|
||||
public void resetVolatileTestConfig() {
|
||||
mTestPrimaryLocationTimeZoneProviderPackageName = null;
|
||||
mTestPrimaryLocationTimeZoneProviderMode = null;
|
||||
mTestSecondaryLocationTimeZoneProviderPackageName = null;
|
||||
mTestSecondaryLocationTimeZoneProviderMode = null;
|
||||
mRecordProviderStateChanges = false;
|
||||
}
|
||||
|
||||
private boolean getConfigBoolean(int providerEnabledConfigId) {
|
||||
Resources resources = mContext.getResources();
|
||||
return resources.getBoolean(providerEnabledConfigId);
|
||||
|
||||
@@ -26,7 +26,6 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneProvi
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.RemoteCallback;
|
||||
import android.util.IndentingPrintWriter;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -45,9 +44,10 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
|
||||
@NonNull ProviderMetricsLogger providerMetricsLogger,
|
||||
@NonNull ThreadingDomain threadingDomain,
|
||||
@NonNull String providerName,
|
||||
@NonNull LocationTimeZoneProviderProxy proxy) {
|
||||
@NonNull LocationTimeZoneProviderProxy proxy,
|
||||
boolean recordStateChanges) {
|
||||
super(providerMetricsLogger, threadingDomain, providerName,
|
||||
new ZoneInfoDbTimeZoneProviderEventPreProcessor());
|
||||
new ZoneInfoDbTimeZoneProviderEventPreProcessor(), recordStateChanges);
|
||||
mProxy = Objects.requireNonNull(proxy);
|
||||
}
|
||||
|
||||
@@ -125,16 +125,6 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
|
||||
mProxy.setRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes the supplied test command to the current proxy.
|
||||
*/
|
||||
@Override
|
||||
void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
mProxy.handleTestCommand(testCommand, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
|
||||
synchronized (mSharedLock) {
|
||||
|
||||
@@ -33,7 +33,6 @@ import android.annotation.DurationMillisLong;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.RemoteCallback;
|
||||
import android.util.IndentingPrintWriter;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -590,41 +589,14 @@ class ControllerImpl extends LocationTimeZoneProviderController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes a test command to the specified provider. If the provider name does not match a
|
||||
* known provider, then the command is logged and discarded.
|
||||
* Clears recorded provider state changes (for use during tests).
|
||||
*/
|
||||
void handleProviderTestCommand(
|
||||
@IntRange(from = 0, to = 1) int providerIndex, @NonNull TestCommand testCommand,
|
||||
@Nullable RemoteCallback callback) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerIndex);
|
||||
if (targetProvider == null) {
|
||||
warnLog("Unable to process test command:"
|
||||
+ " providerIndex=" + providerIndex + ", testCommand=" + testCommand);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
try {
|
||||
targetProvider.handleTestCommand(testCommand, callback);
|
||||
} catch (Exception e) {
|
||||
warnLog("Unable to process test command:"
|
||||
+ " providerIndex=" + providerIndex + ", testCommand=" + testCommand, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the controller should record provider state changes for later dumping via
|
||||
* {@link #getStateForTests()}.
|
||||
*/
|
||||
void setProviderStateRecordingEnabled(boolean enabled) {
|
||||
void clearRecordedProviderStates() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
mPrimaryProvider.setStateChangeRecordingEnabled(enabled);
|
||||
mSecondaryProvider.setStateChangeRecordingEnabled(enabled);
|
||||
mPrimaryProvider.clearRecordedStates();
|
||||
mSecondaryProvider.clearRecordedStates();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,16 +19,13 @@ package com.android.server.timezonedetector.location;
|
||||
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
|
||||
|
||||
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
|
||||
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ShellCallback;
|
||||
import android.service.timezone.TimeZoneProviderService;
|
||||
@@ -50,9 +47,6 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* A service class that acts as a container for the {@link LocationTimeZoneProviderController},
|
||||
@@ -70,12 +64,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
* one indicated by {@link ThreadingDomain}. Because methods like {@link #dump} can be invoked on
|
||||
* another thread, the service and its related objects must still be thread-safe.
|
||||
*
|
||||
* <p>For testing / reproduction of bugs, it is possible to put providers into "simulation
|
||||
* mode" where the real binder clients are replaced by {@link
|
||||
* SimulatedLocationTimeZoneProviderProxy}. This means that the real client providers are never
|
||||
* bound (ensuring no real location events will be received) and simulated events / behaviors
|
||||
* can be injected via the command line.
|
||||
*
|
||||
* <p>See {@code adb shell cmd location_time_zone_manager help}" for details and more options.
|
||||
*/
|
||||
public class LocationTimeZoneManagerService extends Binder {
|
||||
@@ -247,6 +235,36 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the service with fake provider package names configured for tests. The config is
|
||||
* cleared when the service next stops.
|
||||
*
|
||||
* <p>Because this method posts work to the {@code mThreadingDomain} thread and waits for
|
||||
* completion, it cannot be called from the {@code mThreadingDomain} thread.
|
||||
*/
|
||||
void startWithTestProviders(@Nullable String testPrimaryProviderPackageName,
|
||||
@Nullable String testSecondaryProviderPackageName,
|
||||
boolean recordProviderStateChanges) {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
if (testPrimaryProviderPackageName == null && testSecondaryProviderPackageName == null) {
|
||||
throw new IllegalArgumentException("One or both test package names must be provided.");
|
||||
}
|
||||
|
||||
mThreadingDomain.postAndWait(() -> {
|
||||
synchronized (mSharedLock) {
|
||||
stopOnDomainThread();
|
||||
|
||||
mServiceConfigAccessor.setTestPrimaryLocationTimeZoneProviderPackageName(
|
||||
testPrimaryProviderPackageName);
|
||||
mServiceConfigAccessor.setTestSecondaryLocationTimeZoneProviderPackageName(
|
||||
testSecondaryProviderPackageName);
|
||||
mServiceConfigAccessor.setRecordProviderStateChanges(recordProviderStateChanges);
|
||||
startOnDomainThread();
|
||||
}
|
||||
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
|
||||
}
|
||||
|
||||
private void startOnDomainThread() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
@@ -295,6 +313,9 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
mLocationTimeZoneDetectorController = null;
|
||||
mEnvironment.destroy();
|
||||
mEnvironment = null;
|
||||
|
||||
// Clear test state so it won't be used the next time the service is started.
|
||||
mServiceConfigAccessor.resetVolatileTestConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,14 +328,14 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
this, in, out, err, args, callback, resultReceiver);
|
||||
}
|
||||
|
||||
/** Sets this service into provider state recording mode for tests. */
|
||||
void setProviderStateRecordingEnabled(boolean enabled) {
|
||||
/** Clears recorded provider state for tests. */
|
||||
void clearRecordedProviderStates() {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
mThreadingDomain.postAndWait(() -> {
|
||||
synchronized (mSharedLock) {
|
||||
if (mLocationTimeZoneDetectorController != null) {
|
||||
mLocationTimeZoneDetectorController.setProviderStateRecordingEnabled(enabled);
|
||||
mLocationTimeZoneDetectorController.clearRecordedProviderStates();
|
||||
}
|
||||
}
|
||||
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
|
||||
@@ -344,48 +365,6 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes a {@link TestCommand} to the specified provider and waits for the response.
|
||||
*/
|
||||
@NonNull
|
||||
Bundle handleProviderTestCommand(@IntRange(from = 0, to = 1) int providerIndex,
|
||||
@NonNull TestCommand testCommand) {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
// Because this method blocks and posts work to the threading domain thread, it would cause
|
||||
// a deadlock if it were called by the threading domain thread.
|
||||
mThreadingDomain.assertNotCurrentThread();
|
||||
|
||||
AtomicReference<Bundle> resultReference = new AtomicReference<>();
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
RemoteCallback remoteCallback = new RemoteCallback(x -> {
|
||||
resultReference.set(x);
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
mThreadingDomain.post(() -> {
|
||||
synchronized (mSharedLock) {
|
||||
if (mLocationTimeZoneDetectorController == null) {
|
||||
remoteCallback.sendResult(null);
|
||||
return;
|
||||
}
|
||||
mLocationTimeZoneDetectorController.handleProviderTestCommand(
|
||||
providerIndex, testCommand, remoteCallback);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Wait, but not indefinitely.
|
||||
if (!latch.await(BLOCKING_OP_WAIT_DURATION_MILLIS, TimeUnit.MILLISECONDS)) {
|
||||
throw new RuntimeException("Command did not complete in time");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
return resultReference.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
|
||||
@Nullable String[] args) {
|
||||
@@ -463,7 +442,8 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
LocationTimeZoneProviderProxy proxy = createProxy();
|
||||
ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(mIndex);
|
||||
return new BinderLocationTimeZoneProvider(
|
||||
providerMetricsLogger, mThreadingDomain, mName, proxy);
|
||||
providerMetricsLogger, mThreadingDomain, mName, proxy,
|
||||
mServiceConfigAccessor.getRecordProviderStateChanges());
|
||||
}
|
||||
|
||||
@GuardedBy("mSharedLock")
|
||||
@@ -476,9 +456,7 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
@NonNull
|
||||
private LocationTimeZoneProviderProxy createProxy() {
|
||||
String mode = getMode();
|
||||
if (Objects.equals(mode, PROVIDER_MODE_SIMULATED)) {
|
||||
return new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else if (Objects.equals(mode, PROVIDER_MODE_DISABLED)) {
|
||||
if (Objects.equals(mode, PROVIDER_MODE_DISABLED)) {
|
||||
return new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else {
|
||||
// mode == PROVIDER_MODE_OVERRIDE_ENABLED (or unknown).
|
||||
@@ -486,7 +464,7 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the mode of the provider. */
|
||||
/** Returns the mode of the provider (enabled/disabled). */
|
||||
@NonNull
|
||||
private String getMode() {
|
||||
if (mIndex == 0) {
|
||||
@@ -499,10 +477,19 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
@NonNull
|
||||
private RealLocationTimeZoneProviderProxy createRealProxy() {
|
||||
String providerServiceAction = mServiceAction;
|
||||
boolean isTestProvider = isTestProvider();
|
||||
String providerPackageName = getPackageName();
|
||||
return new RealLocationTimeZoneProviderProxy(
|
||||
mContext, mHandler, mThreadingDomain, providerServiceAction,
|
||||
providerPackageName);
|
||||
providerPackageName, isTestProvider);
|
||||
}
|
||||
|
||||
private boolean isTestProvider() {
|
||||
if (mIndex == 0) {
|
||||
return mServiceConfigAccessor.isTestPrimaryLocationTimeZoneProvider();
|
||||
} else {
|
||||
return mServiceConfigAccessor.isTestSecondaryLocationTimeZoneProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
package com.android.server.timezonedetector.location;
|
||||
|
||||
import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
|
||||
import static android.app.time.LocationTimeZoneManager.NULL_PACKAGE_NAME_TOKEN;
|
||||
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES;
|
||||
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_START;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START_WITH_TEST_PROVIDERS;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
|
||||
|
||||
@@ -31,7 +32,6 @@ import static com.android.server.timedetector.ServerFlags.KEY_PRIMARY_LOCATION_T
|
||||
import static com.android.server.timedetector.ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
|
||||
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
|
||||
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_ENABLED;
|
||||
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
|
||||
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
|
||||
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
|
||||
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
|
||||
@@ -41,12 +41,12 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneProvi
|
||||
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.time.GeolocationTimeZoneSuggestionProto;
|
||||
import android.app.time.LocationTimeZoneManagerProto;
|
||||
import android.app.time.LocationTimeZoneManagerServiceStateProto;
|
||||
import android.app.time.TimeZoneProviderStateProto;
|
||||
import android.app.timezonedetector.TimeZoneDetector;
|
||||
import android.os.Bundle;
|
||||
import android.os.ShellCommand;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
@@ -79,14 +79,14 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
case SHELL_COMMAND_START: {
|
||||
return runStart();
|
||||
}
|
||||
case SHELL_COMMAND_START_WITH_TEST_PROVIDERS: {
|
||||
return runStartWithTestProviders();
|
||||
}
|
||||
case SHELL_COMMAND_STOP: {
|
||||
return runStop();
|
||||
}
|
||||
case SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND: {
|
||||
return runSendProviderTestCommand();
|
||||
}
|
||||
case SHELL_COMMAND_RECORD_PROVIDER_STATES: {
|
||||
return runRecordProviderStates();
|
||||
case SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES: {
|
||||
return runClearRecordedProviderStates();
|
||||
}
|
||||
case SHELL_COMMAND_DUMP_STATE: {
|
||||
return runDumpControllerState();
|
||||
@@ -105,47 +105,33 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
pw.printf(" Print this help text.\n");
|
||||
pw.printf(" %s\n", SHELL_COMMAND_START);
|
||||
pw.printf(" Starts the service, creating location time zone providers.\n");
|
||||
pw.printf(" %s <primary package name|%2$s> <secondary package name|%2$s>"
|
||||
+ " <record states>\n",
|
||||
SHELL_COMMAND_START_WITH_TEST_PROVIDERS, NULL_PACKAGE_NAME_TOKEN);
|
||||
pw.printf(" Starts the service with test provider packages configured / provider"
|
||||
+ " permission checks disabled.\n");
|
||||
pw.printf(" <record states> - true|false, determines whether state recording is enabled."
|
||||
+ "\n");
|
||||
pw.printf(" See %s and %s.\n", SHELL_COMMAND_DUMP_STATE,
|
||||
SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES);
|
||||
pw.printf(" %s\n", SHELL_COMMAND_STOP);
|
||||
pw.printf(" Stops the service, destroying location time zone providers.\n");
|
||||
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.printf(" Note: When enabled, this mode consumes memory and it is only intended for"
|
||||
+ " testing.\n");
|
||||
pw.printf(" It should be disabled after use, or the device can be rebooted to"
|
||||
+ " reset the mode to disabled.\n");
|
||||
pw.printf(" Disabling (or enabling repeatedly) clears any existing stored states.\n");
|
||||
pw.printf(" %s\n", SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES);
|
||||
pw.printf(" Clears recorded provider state. See also %s and %s.\n",
|
||||
SHELL_COMMAND_START_WITH_TEST_PROVIDERS, SHELL_COMMAND_DUMP_STATE);
|
||||
pw.printf(" Note: This is only intended for use during testing.\n");
|
||||
pw.printf(" %s [%s]\n", SHELL_COMMAND_DUMP_STATE, DUMP_STATE_OPTION_PROTO);
|
||||
pw.printf(" Dumps service state for tests as text or binary proto form.\n");
|
||||
pw.printf(" See the LocationTimeZoneManagerServiceStateProto definition for details.\n");
|
||||
pw.printf(" %s <provider index> <test command>\n",
|
||||
SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
|
||||
pw.printf(" Passes a test command to the named provider.\n");
|
||||
pw.println();
|
||||
pw.printf("<provider index> = 0 (primary), 1 (secondary)\n");
|
||||
pw.println();
|
||||
pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
|
||||
pw.println();
|
||||
pw.printf("Provider <test command> encoding:\n");
|
||||
pw.println();
|
||||
TestCommand.printShellCommandEncodingHelp(pw);
|
||||
pw.println();
|
||||
pw.printf("Simulated provider mode can be used to test the system server behavior or to"
|
||||
+ " reproduce bugs without the complexity of using real providers.\n");
|
||||
pw.println();
|
||||
pw.printf("The test commands for simulated providers are:\n");
|
||||
SimulatedLocationTimeZoneProviderProxy.printTestCommandShellHelp(pw);
|
||||
pw.println();
|
||||
pw.printf("Test commands cannot currently be passed to real provider implementations.\n");
|
||||
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\n", KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
|
||||
pw.printf(" Overrides the mode of the primary provider. Values=%s|%s|%s\n",
|
||||
PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
|
||||
pw.printf(" Overrides the mode of the primary provider. Values=%s|%s\n",
|
||||
PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED);
|
||||
pw.printf(" %s\n", KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
|
||||
pw.printf(" Overrides the mode of the secondary provider. Values=%s|%s|%s\n",
|
||||
PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
|
||||
pw.printf(" Overrides the mode of the secondary provider. Values=%s|%s\n",
|
||||
PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED);
|
||||
pw.printf(" %s\n", KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS);
|
||||
pw.printf(" Sets the amount of time the service waits when uncertain before making an"
|
||||
+ " 'uncertain' suggestion to the time zone detector.\n");
|
||||
@@ -178,6 +164,23 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runStartWithTestProviders() {
|
||||
String testPrimaryProviderPackageName = parseProviderPackageName(getNextArgRequired());
|
||||
String testSecondaryProviderPackageName = parseProviderPackageName(getNextArgRequired());
|
||||
boolean recordProviderStateChanges = Boolean.parseBoolean(getNextArgRequired());
|
||||
|
||||
try {
|
||||
mService.startWithTestProviders(testPrimaryProviderPackageName,
|
||||
testSecondaryProviderPackageName, recordProviderStateChanges);
|
||||
} catch (RuntimeException e) {
|
||||
reportError(e);
|
||||
return 1;
|
||||
}
|
||||
PrintWriter outPrintWriter = getOutPrintWriter();
|
||||
outPrintWriter.println("Service started (test mode)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runStop() {
|
||||
try {
|
||||
mService.stop();
|
||||
@@ -190,20 +193,9 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runRecordProviderStates() {
|
||||
PrintWriter outPrintWriter = getOutPrintWriter();
|
||||
boolean enabled;
|
||||
private int runClearRecordedProviderStates() {
|
||||
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);
|
||||
mService.clearRecordedProviderStates();
|
||||
} catch (IllegalStateException e) {
|
||||
reportError(e);
|
||||
return 2;
|
||||
@@ -293,47 +285,17 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private int runSendProviderTestCommand() {
|
||||
PrintWriter outPrintWriter = getOutPrintWriter();
|
||||
|
||||
int providerIndex;
|
||||
TestCommand testCommand;
|
||||
try {
|
||||
providerIndex = parseProviderIndex(getNextArgRequired());
|
||||
testCommand = createTestCommandFromNextShellArg();
|
||||
} catch (RuntimeException e) {
|
||||
reportError(e);
|
||||
return 1;
|
||||
}
|
||||
|
||||
outPrintWriter.println("Injecting testCommand=" + testCommand
|
||||
+ " to providerIndex=" + providerIndex);
|
||||
try {
|
||||
Bundle result = mService.handleProviderTestCommand(providerIndex, testCommand);
|
||||
outPrintWriter.println(result);
|
||||
} catch (RuntimeException e) {
|
||||
reportError(e);
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private TestCommand createTestCommandFromNextShellArg() {
|
||||
return TestCommand.createFromShellCommandArgs(this);
|
||||
}
|
||||
|
||||
private void reportError(Throwable e) {
|
||||
private void reportError(@NonNull Throwable e) {
|
||||
PrintWriter errPrintWriter = getErrPrintWriter();
|
||||
errPrintWriter.println("Error: ");
|
||||
e.printStackTrace(errPrintWriter);
|
||||
}
|
||||
|
||||
private static int parseProviderIndex(@NonNull String providerIndexString) {
|
||||
int providerIndex = Integer.parseInt(providerIndexString);
|
||||
if (providerIndex < 0 || providerIndex > 1) {
|
||||
throw new IllegalArgumentException(providerIndexString);
|
||||
@Nullable
|
||||
private static String parseProviderPackageName(@NonNull String providerPackageNameString) {
|
||||
if (providerPackageNameString.equals(NULL_PACKAGE_NAME_TOKEN)) {
|
||||
return null;
|
||||
}
|
||||
return providerIndex;
|
||||
return providerPackageNameString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package com.android.server.timezonedetector.location;
|
||||
|
||||
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.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
|
||||
import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
|
||||
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
|
||||
@@ -35,9 +32,7 @@ import android.annotation.ElapsedRealtimeLong;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -358,8 +353,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
/**
|
||||
* Usually {@code false} but can be set to {@code true} for testing.
|
||||
*/
|
||||
@GuardedBy("mSharedLock")
|
||||
private boolean mStateChangeRecording;
|
||||
private final boolean mRecordStateChanges;
|
||||
|
||||
@GuardedBy("mSharedLock")
|
||||
@NonNull
|
||||
@@ -385,7 +379,8 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
|
||||
@NonNull ThreadingDomain threadingDomain,
|
||||
@NonNull String providerName,
|
||||
@NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor) {
|
||||
@NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor,
|
||||
boolean recordStateChanges) {
|
||||
mThreadingDomain = Objects.requireNonNull(threadingDomain);
|
||||
mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger);
|
||||
mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
|
||||
@@ -393,6 +388,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
mProviderName = Objects.requireNonNull(providerName);
|
||||
mTimeZoneProviderEventPreProcessor =
|
||||
Objects.requireNonNull(timeZoneProviderEventPreProcessor);
|
||||
mRecordStateChanges = recordStateChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -456,12 +452,11 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
abstract void onDestroy();
|
||||
|
||||
/**
|
||||
* Sets the provider into state recording mode for tests.
|
||||
* Clears recorded state changes.
|
||||
*/
|
||||
final void setStateChangeRecordingEnabled(boolean enabled) {
|
||||
final void clearRecordedStates() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
synchronized (mSharedLock) {
|
||||
mStateChangeRecording = enabled;
|
||||
mRecordedStates.clear();
|
||||
mRecordedStates.trimToSize();
|
||||
}
|
||||
@@ -478,12 +473,11 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* ProviderListener#onProviderStateChange(ProviderState)} must be called on
|
||||
* {@link #mProviderListener}.
|
||||
* Sets the current state. If {@code #notifyChanges} is {@code true} and {@code newState} is not
|
||||
* equal to the old state, then {@link ProviderListener#onProviderStateChange(ProviderState)}
|
||||
* will be called on {@link #mProviderListener}.
|
||||
*/
|
||||
final void setCurrentState(@NonNull ProviderState newState, boolean notifyChanges) {
|
||||
private void setCurrentState(@NonNull ProviderState newState, boolean notifyChanges) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
synchronized (mSharedLock) {
|
||||
ProviderState oldState = mCurrentState.get();
|
||||
@@ -491,7 +485,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
onSetCurrentState(newState);
|
||||
if (!Objects.equals(newState, oldState)) {
|
||||
mProviderMetricsLogger.onProviderStateChanged(newState.stateEnum);
|
||||
if (mStateChangeRecording) {
|
||||
if (mRecordStateChanges) {
|
||||
mRecordedStates.add(newState);
|
||||
}
|
||||
if (notifyChanges) {
|
||||
@@ -609,23 +603,6 @@ abstract class LocationTimeZoneProvider implements Dumpable {
|
||||
*/
|
||||
abstract void onStopUpdates();
|
||||
|
||||
/**
|
||||
* Overridden by subclasses to handle the supplied {@link TestCommand}. If {@code callback} is
|
||||
* non-null, the default implementation sends a result {@link Bundle} with {@link
|
||||
* android.service.timezone.TimeZoneProviderService#TEST_COMMAND_RESULT_SUCCESS_KEY} set to
|
||||
* {@code false} and a "Not implemented" error message.
|
||||
*/
|
||||
void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
|
||||
Objects.requireNonNull(testCommand);
|
||||
|
||||
if (callback != null) {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
|
||||
result.putString(TEST_COMMAND_RESULT_ERROR_KEY, "Not implemented");
|
||||
callback.sendResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
/** For subclasses to invoke when a {@link TimeZoneProviderEvent} has been received. */
|
||||
final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteCallback;
|
||||
import android.util.IndentingPrintWriter;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -112,13 +111,6 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable {
|
||||
*/
|
||||
abstract void setRequest(@NonNull TimeZoneProviderRequest request);
|
||||
|
||||
/**
|
||||
* Processes the supplied test command. An optional callback can be supplied to listen for a
|
||||
* response.
|
||||
*/
|
||||
abstract void handleTestCommand(@NonNull TestCommand testCommand,
|
||||
@Nullable RemoteCallback callback);
|
||||
|
||||
/**
|
||||
* Handles a {@link TimeZoneProviderEvent} from a remote process.
|
||||
*/
|
||||
|
||||
@@ -19,9 +19,6 @@ package com.android.server.timezonedetector.location;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteCallback;
|
||||
import android.service.timezone.TimeZoneProviderService;
|
||||
import android.util.IndentingPrintWriter;
|
||||
|
||||
/**
|
||||
@@ -66,17 +63,6 @@ class NullLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
|
||||
if (callback != null) {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY, false);
|
||||
result.putString(TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY,
|
||||
"Provider is disabled");
|
||||
callback.sendResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
|
||||
synchronized (mSharedLock) {
|
||||
|
||||
@@ -18,16 +18,12 @@ package com.android.server.timezonedetector.location;
|
||||
|
||||
import static android.Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE;
|
||||
import static android.Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE;
|
||||
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
|
||||
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteCallback;
|
||||
import android.service.timezone.ITimeZoneProvider;
|
||||
import android.service.timezone.ITimeZoneProviderManager;
|
||||
import android.service.timezone.TimeZoneProviderSuggestion;
|
||||
@@ -62,19 +58,27 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy im
|
||||
RealLocationTimeZoneProviderProxy(
|
||||
@NonNull Context context, @NonNull Handler handler,
|
||||
@NonNull ThreadingDomain threadingDomain, @NonNull String action,
|
||||
@NonNull String providerPackageName) {
|
||||
@NonNull String providerPackageName, boolean isTestProvider) {
|
||||
super(context, threadingDomain);
|
||||
mManagerProxy = null;
|
||||
mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
|
||||
|
||||
Objects.requireNonNull(providerPackageName);
|
||||
mServiceWatcher = ServiceWatcher.create(context,
|
||||
handler,
|
||||
"RealLocationTimeZoneProviderProxy",
|
||||
CurrentUserServiceSupplier.create(context, action,
|
||||
providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
|
||||
INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE),
|
||||
this);
|
||||
|
||||
CurrentUserServiceSupplier serviceSupplier;
|
||||
if (isTestProvider) {
|
||||
// For tests it is possible to bypass the provider service permission checks, since
|
||||
// the tests are expected to install fake providers.
|
||||
serviceSupplier = CurrentUserServiceSupplier.createUnsafeForTestsOnly(
|
||||
context, action, providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
|
||||
/*servicePermission=*/null);
|
||||
} else {
|
||||
serviceSupplier = CurrentUserServiceSupplier.create(context, action,
|
||||
providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
|
||||
INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE);
|
||||
}
|
||||
mServiceWatcher = ServiceWatcher.create(
|
||||
context, handler, "RealLocationTimeZoneProviderProxy", serviceSupplier, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,21 +159,6 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy im
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A stubbed implementation.
|
||||
*/
|
||||
@Override
|
||||
void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
if (callback != null) {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
|
||||
result.putString(TEST_COMMAND_RESULT_ERROR_KEY, "Not implemented");
|
||||
callback.sendResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
|
||||
synchronized (mSharedLock) {
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
/*
|
||||
* 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.timezonedetector.location;
|
||||
|
||||
import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND;
|
||||
import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND;
|
||||
import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE;
|
||||
import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS;
|
||||
import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ;
|
||||
import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN;
|
||||
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
|
||||
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.SystemClock;
|
||||
import android.service.timezone.TimeZoneProviderSuggestion;
|
||||
import android.util.IndentingPrintWriter;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.timezonedetector.ReferenceWithHistory;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A replacement for a real binder proxy for use during integration testing
|
||||
* that can be used to inject simulated {@link LocationTimeZoneProviderProxy} behavior.
|
||||
*/
|
||||
class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
|
||||
|
||||
@GuardedBy("mSharedLock")
|
||||
@NonNull private TimeZoneProviderRequest mRequest;
|
||||
|
||||
@GuardedBy("mSharedLock")
|
||||
@NonNull private final ReferenceWithHistory<String> mLastEvent = new ReferenceWithHistory<>(50);
|
||||
|
||||
SimulatedLocationTimeZoneProviderProxy(
|
||||
@NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
|
||||
super(context, threadingDomain);
|
||||
mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
void onInitialize() {
|
||||
// No-op - nothing to do for the simulated provider.
|
||||
}
|
||||
|
||||
@Override
|
||||
void onDestroy() {
|
||||
// No-op - nothing to do for the simulated provider.
|
||||
}
|
||||
|
||||
void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
Objects.requireNonNull(testCommand);
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
Bundle resultBundle = new Bundle();
|
||||
switch (testCommand.getName()) {
|
||||
case SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND: {
|
||||
mLastEvent.set("Simulating onProviderBound(), testCommand=" + testCommand);
|
||||
mThreadingDomain.post(this::onBindOnHandlerThread);
|
||||
resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
|
||||
break;
|
||||
}
|
||||
case SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND: {
|
||||
mLastEvent.set("Simulating onProviderUnbound(), testCommand=" + testCommand);
|
||||
mThreadingDomain.post(this::onUnbindOnHandlerThread);
|
||||
resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
|
||||
break;
|
||||
}
|
||||
case SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE:
|
||||
case SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN:
|
||||
case SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS: {
|
||||
if (!mRequest.sendUpdates()) {
|
||||
String errorMsg = "testCommand=" + testCommand
|
||||
+ " is testing an invalid case:"
|
||||
+ " updates are off. mRequest=" + mRequest;
|
||||
mLastEvent.set(errorMsg);
|
||||
resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
|
||||
resultBundle.putString(TEST_COMMAND_RESULT_ERROR_KEY, errorMsg);
|
||||
break;
|
||||
}
|
||||
mLastEvent.set("Simulating TimeZoneProviderEvent, testCommand=" + testCommand);
|
||||
TimeZoneProviderEvent timeZoneProviderEvent =
|
||||
createTimeZoneProviderEventFromTestCommand(testCommand);
|
||||
handleTimeZoneProviderEvent(timeZoneProviderEvent);
|
||||
resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
String errorMsg = "Unknown test event type. testCommand=" + testCommand;
|
||||
mLastEvent.set(errorMsg);
|
||||
resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
|
||||
resultBundle.putString(TEST_COMMAND_RESULT_ERROR_KEY, errorMsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.sendResult(resultBundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onBindOnHandlerThread() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
mListener.onProviderBound();
|
||||
}
|
||||
}
|
||||
|
||||
private void onUnbindOnHandlerThread() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
mListener.onProviderUnbound();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
final void setRequest(@NonNull TimeZoneProviderRequest request) {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
Objects.requireNonNull(request);
|
||||
synchronized (mSharedLock) {
|
||||
mLastEvent.set("Request received: " + request);
|
||||
mRequest = request;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
|
||||
synchronized (mSharedLock) {
|
||||
ipw.println("{SimulatedLocationTimeZoneProviderProxy}");
|
||||
ipw.println("mRequest=" + mRequest);
|
||||
ipw.println("mLastEvent=" + mLastEvent);
|
||||
|
||||
ipw.increaseIndent();
|
||||
ipw.println("Last event history:");
|
||||
mLastEvent.dump(ipw);
|
||||
ipw.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the command line options that to create a {@link TestCommand} that can be passed to
|
||||
* {@link #createTimeZoneProviderEventFromTestCommand(TestCommand)}.
|
||||
*/
|
||||
static void printTestCommandShellHelp(@NonNull PrintWriter pw) {
|
||||
pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND);
|
||||
pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND);
|
||||
pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE);
|
||||
pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN);
|
||||
pw.printf("%s %s=string_array:<time zone id>[&<time zone id>]+\n",
|
||||
SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS,
|
||||
SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static TimeZoneProviderEvent createTimeZoneProviderEventFromTestCommand(
|
||||
@NonNull TestCommand testCommand) {
|
||||
String name = testCommand.getName();
|
||||
switch (name) {
|
||||
case SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE: {
|
||||
return TimeZoneProviderEvent.createPermanentFailureEvent("Simulated failure");
|
||||
}
|
||||
case SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN: {
|
||||
return TimeZoneProviderEvent.createUncertainEvent();
|
||||
}
|
||||
case SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS: {
|
||||
Bundle args = testCommand.getArgs();
|
||||
String[] timeZoneIds = args.getStringArray(
|
||||
SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ);
|
||||
if (timeZoneIds == null) {
|
||||
throw new IllegalArgumentException("No "
|
||||
+ SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ + " arg found");
|
||||
}
|
||||
TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
|
||||
.setTimeZoneIds(Arrays.asList(timeZoneIds))
|
||||
.setElapsedRealtimeMillis(SystemClock.elapsedRealtime())
|
||||
.build();
|
||||
return TimeZoneProviderEvent.createSuggestionEvent(suggestion);
|
||||
}
|
||||
default: {
|
||||
String msg = String.format("Error: Unknown command name %s", name);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* 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.timezonedetector.location;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.ShellCommand;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A command used to trigger behaviors in a component during tests. Routing to the correct
|
||||
* component is not handled by this class. The meaning of the {@code name} and {@code args}
|
||||
* properties are component-specific.
|
||||
*
|
||||
* <p>{@link TestCommand}s can be encoded as arguments in a shell command. See
|
||||
* {@link #createFromShellCommandArgs(ShellCommand)} and {@link
|
||||
* #printShellCommandEncodingHelp(PrintWriter)}.
|
||||
*/
|
||||
final class TestCommand {
|
||||
|
||||
private static final Pattern SHELL_ARG_PATTERN = Pattern.compile("([^=]+)=([^:]+):(.*)");
|
||||
private static final Pattern SHELL_ARG_VALUE_SPLIT_PATTERN = Pattern.compile("&");
|
||||
|
||||
@NonNull private final String mName;
|
||||
@NonNull private final Bundle mArgs;
|
||||
|
||||
/** Creates a {@link TestCommand} from components. */
|
||||
private TestCommand(@NonNull String type, @NonNull Bundle args) {
|
||||
mName = Objects.requireNonNull(type);
|
||||
mArgs = Objects.requireNonNull(args);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@NonNull
|
||||
public static TestCommand createForTests(@NonNull String type, @NonNull Bundle args) {
|
||||
return new TestCommand(type, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link TestCommand} from a {@link ShellCommand}'s remaining arguments.
|
||||
*
|
||||
* See {@link #printShellCommandEncodingHelp(PrintWriter)} for encoding details.
|
||||
*/
|
||||
@NonNull
|
||||
public static TestCommand createFromShellCommandArgs(@NonNull ShellCommand shellCommand) {
|
||||
String name = shellCommand.getNextArgRequired();
|
||||
Bundle args = new Bundle();
|
||||
String argKeyAndValue;
|
||||
while ((argKeyAndValue = shellCommand.getNextArg()) != null) {
|
||||
Matcher matcher = SHELL_ARG_PATTERN.matcher(argKeyAndValue);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(
|
||||
argKeyAndValue + " does not match " + SHELL_ARG_PATTERN);
|
||||
}
|
||||
String key = matcher.group(1);
|
||||
String type = matcher.group(2);
|
||||
String encodedValue = matcher.group(3);
|
||||
Object value = getTypedValue(type, encodedValue);
|
||||
args.putObject(key, value);
|
||||
}
|
||||
return new TestCommand(name, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command's name.
|
||||
*/
|
||||
@NonNull
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arg values. Returns an empty bundle if there are no args.
|
||||
*/
|
||||
@NonNull
|
||||
public Bundle getArgs() {
|
||||
return mArgs.deepCopy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestCommand{"
|
||||
+ "mName=" + mName
|
||||
+ ", mArgs=" + mArgs
|
||||
+ '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the text format that {@link #createFromShellCommandArgs(ShellCommand)} understands.
|
||||
*/
|
||||
public static void printShellCommandEncodingHelp(@NonNull PrintWriter pw) {
|
||||
pw.println("Test commands are encoded on the command line as: <name> <arg>*");
|
||||
pw.println();
|
||||
pw.println("The <name> is a string");
|
||||
pw.println("The <arg> encoding is: \"key=type:value\"");
|
||||
pw.println();
|
||||
pw.println("e.g. \"myKey=string:myValue\" represents an argument with the key \"myKey\""
|
||||
+ " and a string value of \"myValue\"");
|
||||
pw.println("Values are one or more URI-encoded strings separated by & characters. Only some"
|
||||
+ " types support multiple values, e.g. string arrays.");
|
||||
pw.println();
|
||||
pw.println("Recognized types are: string, boolean, double, long, string_array.");
|
||||
pw.println();
|
||||
pw.println("When passing test commands via adb shell, the & can be escaped by quoting the"
|
||||
+ " <arg> and escaping the & with \\");
|
||||
pw.println("For example:");
|
||||
pw.println(" $ adb shell ... my-command \"key1=string_array:value1\\&value2\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TestCommand that = (TestCommand) o;
|
||||
return mName.equals(that.mName)
|
||||
&& mArgs.kindofEquals(that.mArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mName, mArgs);
|
||||
}
|
||||
|
||||
|
||||
private static Object getTypedValue(String type, String encodedValue) {
|
||||
// The value is stored in a URL encoding. Multiple value types have values separated with
|
||||
// a & character.
|
||||
String[] values = SHELL_ARG_VALUE_SPLIT_PATTERN.split(encodedValue);
|
||||
|
||||
// URI decode the values.
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = Uri.decode(values[i]);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "boolean": {
|
||||
checkSingleValue(values);
|
||||
return Boolean.parseBoolean(values[0]);
|
||||
}
|
||||
case "double": {
|
||||
checkSingleValue(values);
|
||||
return Double.parseDouble(values[0]);
|
||||
}
|
||||
case "long": {
|
||||
checkSingleValue(values);
|
||||
return Long.parseLong(values[0]);
|
||||
}
|
||||
case "string": {
|
||||
checkSingleValue(values);
|
||||
return values[0];
|
||||
}
|
||||
case "string_array": {
|
||||
return values;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void checkSingleValue(String[] values) {
|
||||
if (values.length != 1) {
|
||||
throw new IllegalArgumentException("Expected a single value, but there were multiple: "
|
||||
+ Arrays.toString(values));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1006,24 +1006,25 @@ public class ControllerImplTest {
|
||||
|
||||
@Test
|
||||
public void stateRecording() {
|
||||
// The test provider enables state recording by default.
|
||||
ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
|
||||
mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
|
||||
TestEnvironment testEnvironment = new TestEnvironment(
|
||||
mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
|
||||
|
||||
// Initialize and check initial state.
|
||||
// Initialize and check initial states.
|
||||
controllerImpl.initialize(testEnvironment, mTestCallback);
|
||||
|
||||
{
|
||||
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
|
||||
assertNull(state.getLastSuggestion());
|
||||
assertTrue(state.getPrimaryProviderStates().isEmpty());
|
||||
assertTrue(state.getSecondaryProviderStates().isEmpty());
|
||||
assertProviderStates(state.getPrimaryProviderStates(),
|
||||
PROVIDER_STATE_STOPPED, PROVIDER_STATE_STARTED_INITIALIZING);
|
||||
assertProviderStates(state.getSecondaryProviderStates(), PROVIDER_STATE_STOPPED);
|
||||
}
|
||||
controllerImpl.clearRecordedProviderStates();
|
||||
|
||||
// State recording and simulate some provider behavior that will show up in the state
|
||||
// recording.
|
||||
controllerImpl.setProviderStateRecordingEnabled(true);
|
||||
// Simulate some provider behavior that will show up in the state recording.
|
||||
|
||||
// Simulate an uncertain event from the primary. This will start the secondary.
|
||||
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
|
||||
@@ -1032,19 +1033,14 @@ public class ControllerImplTest {
|
||||
{
|
||||
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);
|
||||
assertProviderStates(
|
||||
state.getPrimaryProviderStates(), PROVIDER_STATE_STARTED_UNCERTAIN);
|
||||
assertProviderStates(
|
||||
state.getSecondaryProviderStates(), PROVIDER_STATE_STARTED_INITIALIZING);
|
||||
}
|
||||
controllerImpl.clearRecordedProviderStates();
|
||||
|
||||
// Simulate an uncertain event from the primary. This will start the secondary.
|
||||
// Simulate a certain event from the secondary.
|
||||
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
|
||||
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
|
||||
|
||||
@@ -1052,23 +1048,27 @@ public class ControllerImplTest {
|
||||
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);
|
||||
assertProviderStates(state.getPrimaryProviderStates());
|
||||
assertProviderStates(
|
||||
state.getSecondaryProviderStates(), PROVIDER_STATE_STARTED_CERTAIN);
|
||||
}
|
||||
|
||||
controllerImpl.setProviderStateRecordingEnabled(false);
|
||||
controllerImpl.clearRecordedProviderStates();
|
||||
{
|
||||
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
|
||||
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
|
||||
state.getLastSuggestion().getZoneIds());
|
||||
assertTrue(state.getPrimaryProviderStates().isEmpty());
|
||||
assertTrue(state.getSecondaryProviderStates().isEmpty());
|
||||
assertProviderStates(state.getPrimaryProviderStates());
|
||||
assertProviderStates(state.getSecondaryProviderStates());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertProviderStates(
|
||||
List<LocationTimeZoneProvider.ProviderState> providerStates,
|
||||
int... expectedStates) {
|
||||
assertEquals(expectedStates.length, providerStates.size());
|
||||
for (int i = 0; i < expectedStates.length; i++) {
|
||||
assertEquals(expectedStates[i], providerStates.get(i).stateEnum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1228,7 +1228,7 @@ public class ControllerImplTest {
|
||||
TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger,
|
||||
ThreadingDomain threadingDomain, String providerName) {
|
||||
super(providerMetricsLogger, threadingDomain, providerName,
|
||||
new FakeTimeZoneProviderEventPreProcessor());
|
||||
new FakeTimeZoneProviderEventPreProcessor(), true /* recordStateChanges */);
|
||||
}
|
||||
|
||||
public void setFailDuringInitialization(boolean failInitialization) {
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
*/
|
||||
package com.android.server.timezonedetector.location;
|
||||
|
||||
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.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
|
||||
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
|
||||
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
|
||||
@@ -26,8 +23,6 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneProvi
|
||||
import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -36,8 +31,6 @@ import static java.util.Arrays.asList;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteCallback;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.service.timezone.TimeZoneProviderSuggestion;
|
||||
import android.util.IndentingPrintWriter;
|
||||
@@ -54,7 +47,6 @@ import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Tests for {@link LocationTimeZoneProvider}.
|
||||
@@ -168,27 +160,6 @@ public class LocationTimeZoneProviderTest {
|
||||
provider.assertOnDestroyCalled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHandleTestCommandImpl() {
|
||||
String providerName = "primary";
|
||||
StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
|
||||
TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
|
||||
providerMetricsLogger,
|
||||
mTestThreadingDomain,
|
||||
providerName,
|
||||
mTimeZoneProviderEventPreProcessor);
|
||||
|
||||
TestCommand testCommand = TestCommand.createForTests("test", new Bundle());
|
||||
AtomicReference<Bundle> resultReference = new AtomicReference<>();
|
||||
RemoteCallback callback = new RemoteCallback(resultReference::set);
|
||||
provider.handleTestCommand(testCommand, callback);
|
||||
|
||||
Bundle result = resultReference.get();
|
||||
assertNotNull(result);
|
||||
assertFalse(result.getBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY));
|
||||
assertNotNull(result.getString(TEST_COMMAND_RESULT_ERROR_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stateRecording() {
|
||||
String providerName = "primary";
|
||||
@@ -198,7 +169,6 @@ public class LocationTimeZoneProviderTest {
|
||||
mTestThreadingDomain,
|
||||
providerName,
|
||||
mTimeZoneProviderEventPreProcessor);
|
||||
provider.setStateChangeRecordingEnabled(true);
|
||||
|
||||
// initialize()
|
||||
provider.initialize(mProviderListener);
|
||||
@@ -244,7 +214,6 @@ public class LocationTimeZoneProviderTest {
|
||||
mTestThreadingDomain,
|
||||
providerName,
|
||||
mTimeZoneProviderEventPreProcessor);
|
||||
provider.setStateChangeRecordingEnabled(true);
|
||||
provider.initialize(mProviderListener);
|
||||
mTimeZoneProviderEventPreProcessor.enterUncertainMode();
|
||||
|
||||
@@ -315,8 +284,9 @@ public class LocationTimeZoneProviderTest {
|
||||
@NonNull ThreadingDomain threadingDomain,
|
||||
@NonNull String providerName,
|
||||
@NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor) {
|
||||
super(providerMetricsLogger,
|
||||
threadingDomain, providerName, timeZoneProviderEventPreProcessor);
|
||||
super(providerMetricsLogger, threadingDomain, providerName,
|
||||
timeZoneProviderEventPreProcessor,
|
||||
true /* recordStateChanges */);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user