Loading core/java/android/service/timezone/ITimeZoneProvider.aidl +1 −2 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.service.timezone.ITimeZoneProviderManager; * @hide */ oneway interface ITimeZoneProvider { void setTimeZoneProviderManager(in @nullable ITimeZoneProviderManager manager); void startUpdates(in long initializationTimeoutMillis); void startUpdates(in ITimeZoneProviderManager manager, in long initializationTimeoutMillis); void stopUpdates(); } core/java/android/service/timezone/TimeZoneProviderService.java +29 −9 Original line number Diff line number Diff line Loading @@ -112,7 +112,18 @@ public abstract class TimeZoneProviderService extends Service { private static final String TAG = "TimeZoneProviderService"; private final Handler mHandler = BackgroundThread.getHandler(); /** * The test command result key indicating whether a command succeeded. Value type: boolean * @hide */ public static final String TEST_COMMAND_RESULT_SUCCESS_KEY = "SUCCESS"; /** * The test command result key for the error message present when {@link * #TEST_COMMAND_RESULT_SUCCESS_KEY} is false. Value type: string * @hide */ public static final String TEST_COMMAND_RESULT_ERROR_KEY = "ERROR"; /** * The Intent action that the primary location-derived time zone provider service must respond Loading @@ -132,6 +143,8 @@ public abstract class TimeZoneProviderService extends Service { private final TimeZoneProviderServiceWrapper mWrapper = new TimeZoneProviderServiceWrapper(); private final Handler mHandler = BackgroundThread.getHandler(); /** Set by {@link #mHandler} thread. */ @Nullable private ITimeZoneProviderManager mManager; Loading Loading @@ -198,11 +211,22 @@ public abstract class TimeZoneProviderService extends Service { }); } private void onStartUpdatesInternal(@NonNull ITimeZoneProviderManager manager, @DurationMillisLong long initializationTimeoutMillis) { mManager = manager; onStartUpdates(initializationTimeoutMillis); } /** * Starts the provider sending updates. */ public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis); private void onStopUpdatesInternal() { onStopUpdates(); mManager = null; } /** * Stops the provider sending updates. */ Loading @@ -210,18 +234,14 @@ public abstract class TimeZoneProviderService extends Service { private class TimeZoneProviderServiceWrapper extends ITimeZoneProvider.Stub { @Override public void setTimeZoneProviderManager(ITimeZoneProviderManager manager) { public void startUpdates(@NonNull ITimeZoneProviderManager manager, @DurationMillisLong long initializationTimeoutMillis) { Objects.requireNonNull(manager); mHandler.post(() -> TimeZoneProviderService.this.mManager = manager); } public void startUpdates(@DurationMillisLong long initializationTimeoutMillis) { mHandler.post(() -> onStartUpdates(initializationTimeoutMillis)); mHandler.post(() -> onStartUpdatesInternal(manager, initializationTimeoutMillis)); } public void stopUpdates() { mHandler.post(TimeZoneProviderService.this::onStopUpdates); mHandler.post(TimeZoneProviderService.this::onStopUpdatesInternal); } } } services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java +11 −16 Original line number Diff line number Diff line Loading @@ -25,8 +25,8 @@ import static com.android.server.location.timezone.LocationTimeZoneProvider.Prov import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteCallback; import android.util.IndentingPrintWriter; import android.util.Slog; import java.time.Duration; import java.util.Objects; Loading Loading @@ -161,6 +161,16 @@ 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) { Loading Loading @@ -191,19 +201,4 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { + '}'; } } /** * Passes the supplied simulation / testing event to the current proxy iff the proxy is a * {@link SimulatedLocationTimeZoneProviderProxy}. If not, the event is logged but discarded. */ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) { mThreadingDomain.assertCurrentThread(); if (!(mProxy instanceof SimulatedLocationTimeZoneProviderProxy)) { Slog.w(TAG, mProxy + " is not a " + SimulatedLocationTimeZoneProviderProxy.class + ", event=" + event); return; } ((SimulatedLocationTimeZoneProviderProxy) mProxy).simulate(event); } } services/core/java/com/android/server/location/timezone/ControllerImpl.java +32 −18 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_T import android.annotation.DurationMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteCallback; import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -117,7 +118,7 @@ class ControllerImpl extends LocationTimeZoneProviderController { mThreadingDomain.assertCurrentThread(); synchronized (mSharedLock) { debugLog("onEnvironmentConfigChanged()"); debugLog("onConfigChanged()"); ConfigurationInternal oldConfig = mCurrentUserConfiguration; ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal(); Loading Loading @@ -553,6 +554,7 @@ class ControllerImpl extends LocationTimeZoneProviderController { } } @NonNull private static GeolocationTimeZoneSuggestion createUncertainSuggestion(@NonNull String reason) { GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(null); suggestion.addDebugInfo(reason); Loading @@ -560,30 +562,42 @@ class ControllerImpl extends LocationTimeZoneProviderController { } /** * Passes a {@link SimulatedBinderProviderEvent] to the appropriate provider. * If the provider name does not match a known provider, then the event is logged and discarded. * 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. */ void simulateBinderProviderEvent(@NonNull SimulatedBinderProviderEvent event) { void handleProviderTestCommand( @NonNull String providerName, @NonNull TestCommand testCommand, @Nullable RemoteCallback callback) { mThreadingDomain.assertCurrentThread(); String targetProviderName = event.getProviderName(); LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerName); if (targetProvider == null) { warnLog("Unable to process test command:" + " providerName=" + providerName + ", testCommand=" + testCommand); return; } synchronized (mSharedLock) { try { targetProvider.handleTestCommand(testCommand, callback); } catch (Exception e) { warnLog("Unable to process test command:" + " providerName=" + providerName + ", testCommand=" + testCommand, e); } } } @Nullable private LocationTimeZoneProvider getLocationTimeZoneProvider(@NonNull String providerName) { LocationTimeZoneProvider targetProvider; if (Objects.equals(mPrimaryProvider.getName(), targetProviderName)) { if (Objects.equals(mPrimaryProvider.getName(), providerName)) { targetProvider = mPrimaryProvider; } else if (Objects.equals(mSecondaryProvider.getName(), targetProviderName)) { } else if (Objects.equals(mSecondaryProvider.getName(), providerName)) { targetProvider = mSecondaryProvider; } else { warnLog("Unable to process simulated binder provider event," + " unknown providerName in event=" + event); return; } if (!(targetProvider instanceof BinderLocationTimeZoneProvider)) { warnLog("Unable to process simulated binder provider event," + " provider=" + targetProvider + " is not a " + BinderLocationTimeZoneProvider.class + ", event=" + event); return; warnLog("Bad providerName=" + providerName); targetProvider = null; } ((BinderLocationTimeZoneProvider) targetProvider).simulateBinderProviderEvent(event); return targetProvider; } } services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +91 −20 Original line number Diff line number Diff line Loading @@ -21,7 +21,9 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; 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.os.SystemProperties; Loading @@ -40,7 +42,11 @@ import com.android.server.timezonedetector.TimeZoneDetectorService; 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}, Loading Loading @@ -116,8 +122,11 @@ public class LocationTimeZoneManagerService extends Binder { static final String PRIMARY_PROVIDER_NAME = "primary"; static final String SECONDARY_PROVIDER_NAME = "secondary"; private static final String SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX = "persist.sys.location_tz_simulation_mode."; static final String PROVIDER_MODE_OVERRIDE_SYSTEM_PROPERTY_PREFIX = "persist.sys.geotz."; static final String PROVIDER_MODE_SIMULATED = "simulated"; static final String PROVIDER_MODE_DISABLED = "disabled"; private static final long BLOCKING_OP_WAIT_DURATION_MILLIS = Duration.ofSeconds(20).toMillis(); private static final String ATTRIBUTION_TAG = "LocationTimeZoneService"; Loading Loading @@ -187,8 +196,7 @@ public class LocationTimeZoneManagerService extends Binder { } private LocationTimeZoneProvider createPrimaryProvider() { Resources resources = mContext.getResources(); if (!resources.getBoolean(R.bool.config_enablePrimaryLocationTimeZoneProvider)) { if (isDisabled(PRIMARY_PROVIDER_NAME)) { return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME); } Loading @@ -209,8 +217,7 @@ public class LocationTimeZoneManagerService extends Binder { } private LocationTimeZoneProvider createSecondaryProvider() { Resources resources = mContext.getResources(); if (!resources.getBoolean(R.bool.config_enableSecondaryLocationTimeZoneProvider)) { if (isDisabled(SECONDARY_PROVIDER_NAME)) { return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME); } Loading @@ -230,9 +237,41 @@ public class LocationTimeZoneManagerService extends Binder { return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy); } private boolean isInSimulationMode(String providerName) { return SystemProperties.getBoolean( SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false); /** Used for bug triage and in tests to simulate provider events. */ private static boolean isInSimulationMode(String providerName) { return isProviderModeSetInSystemProperties(providerName, PROVIDER_MODE_SIMULATED); } /** Used for bug triage, tests and experiments to remove a provider. */ private boolean isDisabled(String providerName) { return !isProviderEnabledInConfig(providerName) || isProviderModeSetInSystemProperties(providerName, PROVIDER_MODE_DISABLED); } private boolean isProviderEnabledInConfig(String providerName) { int providerEnabledConfigId; switch (providerName) { case PRIMARY_PROVIDER_NAME: { providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider; break; } case SECONDARY_PROVIDER_NAME: { providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider; break; } default: { throw new IllegalArgumentException(providerName); } } Resources resources = mContext.getResources(); return resources.getBoolean(providerEnabledConfigId); } private static boolean isProviderModeSetInSystemProperties( @NonNull String providerName, @NonNull String mode) { String systemPropertyProviderMode = SystemProperties.get( PROVIDER_MODE_OVERRIDE_SYSTEM_PROPERTY_PREFIX + providerName, null); return Objects.equals(systemPropertyProviderMode, mode); } @Override Loading @@ -244,19 +283,45 @@ public class LocationTimeZoneManagerService extends Binder { } /** * Asynchronously passes a {@link SimulatedBinderProviderEvent] to the appropriate provider. * The device must be in simulation mode, otherwise an {@link IllegalStateException} will be * thrown. * Passes a {@link TestCommand} to the specified provider and waits for the response. */ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) throws IllegalStateException { if (!isInSimulationMode(event.getProviderName())) { throw new IllegalStateException("Use \"setprop " + SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + event.getProviderName() + " 1\" and reboot before injecting simulated binder events."); @NonNull Bundle handleProviderTestCommand( @NonNull String providerName, @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; } mThreadingDomain.post( () -> mLocationTimeZoneDetectorController.simulateBinderProviderEvent(event)); mLocationTimeZoneDetectorController.handleProviderTestCommand( providerName, 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 Loading Loading @@ -293,4 +358,10 @@ public class LocationTimeZoneManagerService extends Binder { Slog.w(TAG, msg, t); } } private void enforceManageTimeZoneDetectorPermission() { mContext.enforceCallingPermission( android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION, "manage time and time zone detection"); } } Loading
core/java/android/service/timezone/ITimeZoneProvider.aidl +1 −2 Original line number Diff line number Diff line Loading @@ -22,7 +22,6 @@ import android.service.timezone.ITimeZoneProviderManager; * @hide */ oneway interface ITimeZoneProvider { void setTimeZoneProviderManager(in @nullable ITimeZoneProviderManager manager); void startUpdates(in long initializationTimeoutMillis); void startUpdates(in ITimeZoneProviderManager manager, in long initializationTimeoutMillis); void stopUpdates(); }
core/java/android/service/timezone/TimeZoneProviderService.java +29 −9 Original line number Diff line number Diff line Loading @@ -112,7 +112,18 @@ public abstract class TimeZoneProviderService extends Service { private static final String TAG = "TimeZoneProviderService"; private final Handler mHandler = BackgroundThread.getHandler(); /** * The test command result key indicating whether a command succeeded. Value type: boolean * @hide */ public static final String TEST_COMMAND_RESULT_SUCCESS_KEY = "SUCCESS"; /** * The test command result key for the error message present when {@link * #TEST_COMMAND_RESULT_SUCCESS_KEY} is false. Value type: string * @hide */ public static final String TEST_COMMAND_RESULT_ERROR_KEY = "ERROR"; /** * The Intent action that the primary location-derived time zone provider service must respond Loading @@ -132,6 +143,8 @@ public abstract class TimeZoneProviderService extends Service { private final TimeZoneProviderServiceWrapper mWrapper = new TimeZoneProviderServiceWrapper(); private final Handler mHandler = BackgroundThread.getHandler(); /** Set by {@link #mHandler} thread. */ @Nullable private ITimeZoneProviderManager mManager; Loading Loading @@ -198,11 +211,22 @@ public abstract class TimeZoneProviderService extends Service { }); } private void onStartUpdatesInternal(@NonNull ITimeZoneProviderManager manager, @DurationMillisLong long initializationTimeoutMillis) { mManager = manager; onStartUpdates(initializationTimeoutMillis); } /** * Starts the provider sending updates. */ public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis); private void onStopUpdatesInternal() { onStopUpdates(); mManager = null; } /** * Stops the provider sending updates. */ Loading @@ -210,18 +234,14 @@ public abstract class TimeZoneProviderService extends Service { private class TimeZoneProviderServiceWrapper extends ITimeZoneProvider.Stub { @Override public void setTimeZoneProviderManager(ITimeZoneProviderManager manager) { public void startUpdates(@NonNull ITimeZoneProviderManager manager, @DurationMillisLong long initializationTimeoutMillis) { Objects.requireNonNull(manager); mHandler.post(() -> TimeZoneProviderService.this.mManager = manager); } public void startUpdates(@DurationMillisLong long initializationTimeoutMillis) { mHandler.post(() -> onStartUpdates(initializationTimeoutMillis)); mHandler.post(() -> onStartUpdatesInternal(manager, initializationTimeoutMillis)); } public void stopUpdates() { mHandler.post(TimeZoneProviderService.this::onStopUpdates); mHandler.post(TimeZoneProviderService.this::onStopUpdatesInternal); } } }
services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java +11 −16 Original line number Diff line number Diff line Loading @@ -25,8 +25,8 @@ import static com.android.server.location.timezone.LocationTimeZoneProvider.Prov import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteCallback; import android.util.IndentingPrintWriter; import android.util.Slog; import java.time.Duration; import java.util.Objects; Loading Loading @@ -161,6 +161,16 @@ 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) { Loading Loading @@ -191,19 +201,4 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { + '}'; } } /** * Passes the supplied simulation / testing event to the current proxy iff the proxy is a * {@link SimulatedLocationTimeZoneProviderProxy}. If not, the event is logged but discarded. */ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) { mThreadingDomain.assertCurrentThread(); if (!(mProxy instanceof SimulatedLocationTimeZoneProviderProxy)) { Slog.w(TAG, mProxy + " is not a " + SimulatedLocationTimeZoneProviderProxy.class + ", event=" + event); return; } ((SimulatedLocationTimeZoneProviderProxy) mProxy).simulate(event); } }
services/core/java/com/android/server/location/timezone/ControllerImpl.java +32 −18 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_T import android.annotation.DurationMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteCallback; import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -117,7 +118,7 @@ class ControllerImpl extends LocationTimeZoneProviderController { mThreadingDomain.assertCurrentThread(); synchronized (mSharedLock) { debugLog("onEnvironmentConfigChanged()"); debugLog("onConfigChanged()"); ConfigurationInternal oldConfig = mCurrentUserConfiguration; ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal(); Loading Loading @@ -553,6 +554,7 @@ class ControllerImpl extends LocationTimeZoneProviderController { } } @NonNull private static GeolocationTimeZoneSuggestion createUncertainSuggestion(@NonNull String reason) { GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(null); suggestion.addDebugInfo(reason); Loading @@ -560,30 +562,42 @@ class ControllerImpl extends LocationTimeZoneProviderController { } /** * Passes a {@link SimulatedBinderProviderEvent] to the appropriate provider. * If the provider name does not match a known provider, then the event is logged and discarded. * 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. */ void simulateBinderProviderEvent(@NonNull SimulatedBinderProviderEvent event) { void handleProviderTestCommand( @NonNull String providerName, @NonNull TestCommand testCommand, @Nullable RemoteCallback callback) { mThreadingDomain.assertCurrentThread(); String targetProviderName = event.getProviderName(); LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerName); if (targetProvider == null) { warnLog("Unable to process test command:" + " providerName=" + providerName + ", testCommand=" + testCommand); return; } synchronized (mSharedLock) { try { targetProvider.handleTestCommand(testCommand, callback); } catch (Exception e) { warnLog("Unable to process test command:" + " providerName=" + providerName + ", testCommand=" + testCommand, e); } } } @Nullable private LocationTimeZoneProvider getLocationTimeZoneProvider(@NonNull String providerName) { LocationTimeZoneProvider targetProvider; if (Objects.equals(mPrimaryProvider.getName(), targetProviderName)) { if (Objects.equals(mPrimaryProvider.getName(), providerName)) { targetProvider = mPrimaryProvider; } else if (Objects.equals(mSecondaryProvider.getName(), targetProviderName)) { } else if (Objects.equals(mSecondaryProvider.getName(), providerName)) { targetProvider = mSecondaryProvider; } else { warnLog("Unable to process simulated binder provider event," + " unknown providerName in event=" + event); return; } if (!(targetProvider instanceof BinderLocationTimeZoneProvider)) { warnLog("Unable to process simulated binder provider event," + " provider=" + targetProvider + " is not a " + BinderLocationTimeZoneProvider.class + ", event=" + event); return; warnLog("Bad providerName=" + providerName); targetProvider = null; } ((BinderLocationTimeZoneProvider) targetProvider).simulateBinderProviderEvent(event); return targetProvider; } }
services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +91 −20 Original line number Diff line number Diff line Loading @@ -21,7 +21,9 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; 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.os.SystemProperties; Loading @@ -40,7 +42,11 @@ import com.android.server.timezonedetector.TimeZoneDetectorService; 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}, Loading Loading @@ -116,8 +122,11 @@ public class LocationTimeZoneManagerService extends Binder { static final String PRIMARY_PROVIDER_NAME = "primary"; static final String SECONDARY_PROVIDER_NAME = "secondary"; private static final String SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX = "persist.sys.location_tz_simulation_mode."; static final String PROVIDER_MODE_OVERRIDE_SYSTEM_PROPERTY_PREFIX = "persist.sys.geotz."; static final String PROVIDER_MODE_SIMULATED = "simulated"; static final String PROVIDER_MODE_DISABLED = "disabled"; private static final long BLOCKING_OP_WAIT_DURATION_MILLIS = Duration.ofSeconds(20).toMillis(); private static final String ATTRIBUTION_TAG = "LocationTimeZoneService"; Loading Loading @@ -187,8 +196,7 @@ public class LocationTimeZoneManagerService extends Binder { } private LocationTimeZoneProvider createPrimaryProvider() { Resources resources = mContext.getResources(); if (!resources.getBoolean(R.bool.config_enablePrimaryLocationTimeZoneProvider)) { if (isDisabled(PRIMARY_PROVIDER_NAME)) { return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME); } Loading @@ -209,8 +217,7 @@ public class LocationTimeZoneManagerService extends Binder { } private LocationTimeZoneProvider createSecondaryProvider() { Resources resources = mContext.getResources(); if (!resources.getBoolean(R.bool.config_enableSecondaryLocationTimeZoneProvider)) { if (isDisabled(SECONDARY_PROVIDER_NAME)) { return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME); } Loading @@ -230,9 +237,41 @@ public class LocationTimeZoneManagerService extends Binder { return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy); } private boolean isInSimulationMode(String providerName) { return SystemProperties.getBoolean( SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false); /** Used for bug triage and in tests to simulate provider events. */ private static boolean isInSimulationMode(String providerName) { return isProviderModeSetInSystemProperties(providerName, PROVIDER_MODE_SIMULATED); } /** Used for bug triage, tests and experiments to remove a provider. */ private boolean isDisabled(String providerName) { return !isProviderEnabledInConfig(providerName) || isProviderModeSetInSystemProperties(providerName, PROVIDER_MODE_DISABLED); } private boolean isProviderEnabledInConfig(String providerName) { int providerEnabledConfigId; switch (providerName) { case PRIMARY_PROVIDER_NAME: { providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider; break; } case SECONDARY_PROVIDER_NAME: { providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider; break; } default: { throw new IllegalArgumentException(providerName); } } Resources resources = mContext.getResources(); return resources.getBoolean(providerEnabledConfigId); } private static boolean isProviderModeSetInSystemProperties( @NonNull String providerName, @NonNull String mode) { String systemPropertyProviderMode = SystemProperties.get( PROVIDER_MODE_OVERRIDE_SYSTEM_PROPERTY_PREFIX + providerName, null); return Objects.equals(systemPropertyProviderMode, mode); } @Override Loading @@ -244,19 +283,45 @@ public class LocationTimeZoneManagerService extends Binder { } /** * Asynchronously passes a {@link SimulatedBinderProviderEvent] to the appropriate provider. * The device must be in simulation mode, otherwise an {@link IllegalStateException} will be * thrown. * Passes a {@link TestCommand} to the specified provider and waits for the response. */ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) throws IllegalStateException { if (!isInSimulationMode(event.getProviderName())) { throw new IllegalStateException("Use \"setprop " + SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + event.getProviderName() + " 1\" and reboot before injecting simulated binder events."); @NonNull Bundle handleProviderTestCommand( @NonNull String providerName, @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; } mThreadingDomain.post( () -> mLocationTimeZoneDetectorController.simulateBinderProviderEvent(event)); mLocationTimeZoneDetectorController.handleProviderTestCommand( providerName, 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 Loading Loading @@ -293,4 +358,10 @@ public class LocationTimeZoneManagerService extends Binder { Slog.w(TAG, msg, t); } } private void enforceManageTimeZoneDetectorPermission() { mContext.enforceCallingPermission( android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION, "manage time and time zone detection"); } }