Loading services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java +12 −4 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.location.timezone; import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; Loading Loading @@ -71,6 +72,11 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { }); } @Override void onDestroy() { mProxy.destroy(); } private void handleProviderLost(String reason) { mThreadingDomain.assertCurrentThread(); Loading Loading @@ -100,11 +106,12 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { + ": No state change required, provider is stopped."); break; } case PROVIDER_STATE_PERM_FAILED: { case PROVIDER_STATE_PERM_FAILED: case PROVIDER_STATE_DESTROYED: { debugLog("handleProviderLost reason=" + reason + ", mProviderName=" + mProviderName + ", currentState=" + currentState + ": No state change required, provider is perm failed."); + ": No state change required, provider is terminated."); break; } default: { Loading Loading @@ -132,11 +139,12 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { + ", currentState=" + currentState + ": Provider is stopped."); break; } case PROVIDER_STATE_PERM_FAILED: { case PROVIDER_STATE_PERM_FAILED: case PROVIDER_STATE_DESTROYED: { debugLog("handleOnProviderBound" + ", mProviderName=" + mProviderName + ", currentState=" + currentState + ": No state change required, provider is perm failed."); + ": No state change required, provider is terminated."); break; } default: { Loading services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java +10 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.location.timezone; import android.annotation.NonNull; import com.android.server.LocalServices; import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.TimeZoneDetectorInternal; Loading @@ -37,6 +38,7 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal; @NonNull private final LocationTimeZoneProviderController mController; @NonNull private final ConfigurationChangeListener mConfigurationChangeListener; ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain, @NonNull LocationTimeZoneProviderController controller) { Loading @@ -45,8 +47,14 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class); // Listen for configuration changes. mTimeZoneDetectorInternal.addConfigurationListener( () -> mThreadingDomain.post(mController::onConfigChanged)); mConfigurationChangeListener = () -> mThreadingDomain.post(mController::onConfigChanged); mTimeZoneDetectorInternal.addConfigurationListener(mConfigurationChangeListener); } @Override void destroy() { mTimeZoneDetectorInternal.removeConfigurationListener(mConfigurationChangeListener); } @Override Loading services/core/java/com/android/server/location/timezone/ControllerImpl.java +48 −25 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.location.timezone; import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; Loading Loading @@ -151,6 +152,23 @@ class ControllerImpl extends LocationTimeZoneProviderController { return mUncertaintyTimeoutQueue.getQueuedDelayMillis(); } @Override void destroy() { mThreadingDomain.assertCurrentThread(); synchronized (mSharedLock) { stopProviders(); mPrimaryProvider.destroy(); mSecondaryProvider.destroy(); // If the controller has made a "certain" suggestion, it should make an uncertain // suggestion to cancel it. if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) { makeSuggestion(createUncertainSuggestion("Controller is destroyed")); } } } @GuardedBy("mSharedLock") private void stopProviders() { stopProviderIfStarted(mPrimaryProvider); Loading Loading @@ -182,8 +200,9 @@ class ControllerImpl extends LocationTimeZoneProviderController { provider.stopUpdates(); break; } case PROVIDER_STATE_PERM_FAILED: { debugLog("Unable to stop " + provider + ": it is perm failed"); case PROVIDER_STATE_PERM_FAILED: case PROVIDER_STATE_DESTROYED: { debugLog("Unable to stop " + provider + ": it is terminated."); break; } default: { Loading Loading @@ -285,8 +304,9 @@ class ControllerImpl extends LocationTimeZoneProviderController { debugLog("No need to start " + provider + ": already started"); break; } case PROVIDER_STATE_PERM_FAILED: { debugLog("Unable to start " + provider + ": it is perm failed"); case PROVIDER_STATE_PERM_FAILED: case PROVIDER_STATE_DESTROYED: { debugLog("Unable to start " + provider + ": it is terminated"); break; } default: { Loading @@ -303,17 +323,20 @@ class ControllerImpl extends LocationTimeZoneProviderController { synchronized (mSharedLock) { switch (providerState.stateEnum) { case PROVIDER_STATE_STOPPED: { // This should never happen: entering stopped does not trigger a state change. warnLog("onProviderStateChange: Unexpected state change for stopped provider," case PROVIDER_STATE_STARTED_INITIALIZING: case PROVIDER_STATE_STOPPED: case PROVIDER_STATE_DESTROYED: { // This should never happen: entering initializing, stopped or destroyed are // triggered by the controller so and should not trigger a state change // callback. warnLog("onProviderStateChange: Unexpected state change for provider," + " provider=" + provider); break; } case PROVIDER_STATE_STARTED_INITIALIZING: case PROVIDER_STATE_STARTED_CERTAIN: case PROVIDER_STATE_STARTED_UNCERTAIN: { // Entering started does not trigger a state change, so this only happens if an // event is received while the provider is started. // These are valid and only happen if an event is received while the provider is // started. debugLog("onProviderStateChange: Received notification of a state change while" + " started, provider=" + provider); handleProviderStartedStateChange(providerState); Loading Loading @@ -349,17 +372,18 @@ class ControllerImpl extends LocationTimeZoneProviderController { // If a provider has failed, the other may need to be started. if (failedProvider == mPrimaryProvider) { if (secondaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) { // The primary must have failed. Try to start the secondary. This does nothing if // the provider is already started, and will leave the provider in // {started initializing} if the provider is stopped. if (!secondaryCurrentState.isTerminated()) { // Try to start the secondary. This does nothing if the provider is already // started, and will leave the provider in {started initializing} if the provider is // stopped. tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration); } } else if (failedProvider == mSecondaryProvider) { // No-op: The secondary will only be active if the primary is uncertain or is failed. // So, there the primary should not need to be started when the secondary fails. // No-op: The secondary will only be active if the primary is uncertain or is // terminated. So, there the primary should not need to be started when the secondary // fails. if (primaryCurrentState.stateEnum != PROVIDER_STATE_STARTED_UNCERTAIN && primaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) { && !primaryCurrentState.isTerminated()) { warnLog("Secondary provider unexpected reported a failure:" + " failed provider=" + failedProvider.getName() + ", primary provider=" + mPrimaryProvider Loading @@ -367,19 +391,18 @@ class ControllerImpl extends LocationTimeZoneProviderController { } } // If both providers are now failed, the controller needs to tell the next component in the // time zone detection process. if (primaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED && secondaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED) { // If both providers are now terminated, the controller needs to tell the next component in // the time zone detection process. if (primaryCurrentState.isTerminated() && secondaryCurrentState.isTerminated()) { // If both providers are newly failed then the controller is uncertain by definition // If both providers are newly terminated then the controller is uncertain by definition // and it will never recover so it can send a suggestion immediately. cancelUncertaintyTimeout(); // If both providers are now failed, then a suggestion must be sent informing the time // zone detector that there are no further updates coming in future. // If both providers are now terminated, then a suggestion must be sent informing the // time zone detector that there are no further updates coming in future. GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( "Both providers are permanently failed:" "Both providers are terminated:" + " primary=" + primaryCurrentState.provider + ", secondary=" + secondaryCurrentState.provider); makeSuggestion(suggestion); Loading services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java +36 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,10 @@ import android.annotation.NonNull; import android.os.Handler; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * The real implementation of {@link ThreadingDomain} that uses a {@link Handler}. Loading Loading @@ -57,6 +61,38 @@ final class HandlerThreadingDomain extends ThreadingDomain { getHandler().post(r); } @Override <V> V postAndWait(@NonNull Callable<V> callable, @DurationMillisLong long durationMillis) throws Exception { // Calling this on this domain's thread would lead to deadlock. assertNotCurrentThread(); AtomicReference<V> resultReference = new AtomicReference<>(); AtomicReference<Exception> exceptionReference = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); post(() -> { try { resultReference.set(callable.call()); } catch (Exception e) { exceptionReference.set(e); } finally { latch.countDown(); } }); try { if (!latch.await(durationMillis, TimeUnit.MILLISECONDS)) { throw new RuntimeException("Timed out"); } } catch (InterruptedException e) { throw new RuntimeException(e); } if (exceptionReference.get() != null) { throw exceptionReference.get(); } return resultReference.get(); } @Override void postDelayed(@NonNull Runnable r, @DurationMillisLong long delayMillis) { getHandler().postDelayed(r, delayMillis); Loading services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +73 −24 Original line number Diff line number Diff line Loading @@ -153,10 +153,14 @@ public class LocationTimeZoneManagerService extends Binder { /** The shared lock from {@link #mThreadingDomain}. */ @NonNull private final Object mSharedLock; // Lazily initialized. Non-null and effectively final after onSystemThirdPartyAppsCanStart(). // Lazily initialized. Can be null if the service has been stopped. @GuardedBy("mSharedLock") private ControllerImpl mLocationTimeZoneDetectorController; // Lazily initialized. Can be null if the service has been stopped. @GuardedBy("mSharedLock") private ControllerEnvironmentImpl mEnvironment; LocationTimeZoneManagerService(Context context) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); mHandler = FgThread.getHandler(); Loading @@ -178,31 +182,59 @@ public class LocationTimeZoneManagerService extends Binder { } void onSystemThirdPartyAppsCanStart() { // Called on an arbitrary thread during initialization. // Called on an arbitrary thread during initialization. We do not want to wait for // completion as it would delay boot. final boolean waitForCompletion = false; startInternal(waitForCompletion); } /** * Starts the service during server initialization or during tests after a call to * {@link #stop()}. */ void start() { enforceManageTimeZoneDetectorPermission(); final boolean waitForCompletion = true; startInternal(waitForCompletion); } /** * Starts the service during server initialization or during tests after a call to * {@link #stop()}. * * <p>To avoid tests needing to sleep, when {@code waitForCompletion} is {@code true}, this * method will not return until all the system server components have started. */ private void startInternal(boolean waitForCompletion) { Runnable runnable = () -> { synchronized (mSharedLock) { if (mLocationTimeZoneDetectorController == null) { LocationTimeZoneProvider primary = createPrimaryProvider(); LocationTimeZoneProvider secondary = createSecondaryProvider(); mLocationTimeZoneDetectorController = new ControllerImpl(mThreadingDomain, primary, secondary); ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain); ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl( ControllerCallbackImpl callback = new ControllerCallbackImpl( mThreadingDomain); mEnvironment = new ControllerEnvironmentImpl( mThreadingDomain, mLocationTimeZoneDetectorController); // Initialize the controller on the mThreadingDomain thread: this ensures that the // ThreadingDomain requirements for the controller / environment methods are honored. mThreadingDomain.post(() -> mLocationTimeZoneDetectorController.initialize(environment, callback)); mLocationTimeZoneDetectorController.initialize(mEnvironment, callback); } } private LocationTimeZoneProvider createPrimaryProvider() { if (isDisabled(PRIMARY_PROVIDER_NAME)) { return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME); }; if (waitForCompletion) { mThreadingDomain.postAndWait(runnable, BLOCKING_OP_WAIT_DURATION_MILLIS); } else { mThreadingDomain.post(runnable); } } private LocationTimeZoneProvider createPrimaryProvider() { LocationTimeZoneProviderProxy proxy; if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else if (isDisabled(PRIMARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( mContext, Loading @@ -217,13 +249,11 @@ public class LocationTimeZoneManagerService extends Binder { } private LocationTimeZoneProvider createSecondaryProvider() { if (isDisabled(SECONDARY_PROVIDER_NAME)) { return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME); } LocationTimeZoneProviderProxy proxy; if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else if (isDisabled(SECONDARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( mContext, Loading Loading @@ -274,6 +304,25 @@ public class LocationTimeZoneManagerService extends Binder { return Objects.equals(systemPropertyProviderMode, mode); } /** * Stops the service for tests. To avoid tests needing to sleep, this method will not return * until all the system server components have stopped. */ void stop() { enforceManageTimeZoneDetectorPermission(); mThreadingDomain.postAndWait(() -> { synchronized (mSharedLock) { if (mLocationTimeZoneDetectorController != null) { mLocationTimeZoneDetectorController.destroy(); mLocationTimeZoneDetectorController = null; mEnvironment.destroy(); mEnvironment = null; } } }, BLOCKING_OP_WAIT_DURATION_MILLIS); } @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, Loading Loading @@ -335,7 +384,7 @@ public class LocationTimeZoneManagerService extends Binder { ipw.println("LocationTimeZoneManagerService:"); ipw.increaseIndent(); if (mLocationTimeZoneDetectorController == null) { ipw.println("{Uninitialized}"); ipw.println("{Stopped}"); } else { mLocationTimeZoneDetectorController.dump(ipw, args); } Loading Loading
services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java +12 −4 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.location.timezone; import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; Loading Loading @@ -71,6 +72,11 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { }); } @Override void onDestroy() { mProxy.destroy(); } private void handleProviderLost(String reason) { mThreadingDomain.assertCurrentThread(); Loading Loading @@ -100,11 +106,12 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { + ": No state change required, provider is stopped."); break; } case PROVIDER_STATE_PERM_FAILED: { case PROVIDER_STATE_PERM_FAILED: case PROVIDER_STATE_DESTROYED: { debugLog("handleProviderLost reason=" + reason + ", mProviderName=" + mProviderName + ", currentState=" + currentState + ": No state change required, provider is perm failed."); + ": No state change required, provider is terminated."); break; } default: { Loading Loading @@ -132,11 +139,12 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { + ", currentState=" + currentState + ": Provider is stopped."); break; } case PROVIDER_STATE_PERM_FAILED: { case PROVIDER_STATE_PERM_FAILED: case PROVIDER_STATE_DESTROYED: { debugLog("handleOnProviderBound" + ", mProviderName=" + mProviderName + ", currentState=" + currentState + ": No state change required, provider is perm failed."); + ": No state change required, provider is terminated."); break; } default: { Loading
services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java +10 −2 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.location.timezone; import android.annotation.NonNull; import com.android.server.LocalServices; import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.TimeZoneDetectorInternal; Loading @@ -37,6 +38,7 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal; @NonNull private final LocationTimeZoneProviderController mController; @NonNull private final ConfigurationChangeListener mConfigurationChangeListener; ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain, @NonNull LocationTimeZoneProviderController controller) { Loading @@ -45,8 +47,14 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class); // Listen for configuration changes. mTimeZoneDetectorInternal.addConfigurationListener( () -> mThreadingDomain.post(mController::onConfigChanged)); mConfigurationChangeListener = () -> mThreadingDomain.post(mController::onConfigChanged); mTimeZoneDetectorInternal.addConfigurationListener(mConfigurationChangeListener); } @Override void destroy() { mTimeZoneDetectorInternal.removeConfigurationListener(mConfigurationChangeListener); } @Override Loading
services/core/java/com/android/server/location/timezone/ControllerImpl.java +48 −25 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.location.timezone; import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog; import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN; import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING; Loading Loading @@ -151,6 +152,23 @@ class ControllerImpl extends LocationTimeZoneProviderController { return mUncertaintyTimeoutQueue.getQueuedDelayMillis(); } @Override void destroy() { mThreadingDomain.assertCurrentThread(); synchronized (mSharedLock) { stopProviders(); mPrimaryProvider.destroy(); mSecondaryProvider.destroy(); // If the controller has made a "certain" suggestion, it should make an uncertain // suggestion to cancel it. if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) { makeSuggestion(createUncertainSuggestion("Controller is destroyed")); } } } @GuardedBy("mSharedLock") private void stopProviders() { stopProviderIfStarted(mPrimaryProvider); Loading Loading @@ -182,8 +200,9 @@ class ControllerImpl extends LocationTimeZoneProviderController { provider.stopUpdates(); break; } case PROVIDER_STATE_PERM_FAILED: { debugLog("Unable to stop " + provider + ": it is perm failed"); case PROVIDER_STATE_PERM_FAILED: case PROVIDER_STATE_DESTROYED: { debugLog("Unable to stop " + provider + ": it is terminated."); break; } default: { Loading Loading @@ -285,8 +304,9 @@ class ControllerImpl extends LocationTimeZoneProviderController { debugLog("No need to start " + provider + ": already started"); break; } case PROVIDER_STATE_PERM_FAILED: { debugLog("Unable to start " + provider + ": it is perm failed"); case PROVIDER_STATE_PERM_FAILED: case PROVIDER_STATE_DESTROYED: { debugLog("Unable to start " + provider + ": it is terminated"); break; } default: { Loading @@ -303,17 +323,20 @@ class ControllerImpl extends LocationTimeZoneProviderController { synchronized (mSharedLock) { switch (providerState.stateEnum) { case PROVIDER_STATE_STOPPED: { // This should never happen: entering stopped does not trigger a state change. warnLog("onProviderStateChange: Unexpected state change for stopped provider," case PROVIDER_STATE_STARTED_INITIALIZING: case PROVIDER_STATE_STOPPED: case PROVIDER_STATE_DESTROYED: { // This should never happen: entering initializing, stopped or destroyed are // triggered by the controller so and should not trigger a state change // callback. warnLog("onProviderStateChange: Unexpected state change for provider," + " provider=" + provider); break; } case PROVIDER_STATE_STARTED_INITIALIZING: case PROVIDER_STATE_STARTED_CERTAIN: case PROVIDER_STATE_STARTED_UNCERTAIN: { // Entering started does not trigger a state change, so this only happens if an // event is received while the provider is started. // These are valid and only happen if an event is received while the provider is // started. debugLog("onProviderStateChange: Received notification of a state change while" + " started, provider=" + provider); handleProviderStartedStateChange(providerState); Loading Loading @@ -349,17 +372,18 @@ class ControllerImpl extends LocationTimeZoneProviderController { // If a provider has failed, the other may need to be started. if (failedProvider == mPrimaryProvider) { if (secondaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) { // The primary must have failed. Try to start the secondary. This does nothing if // the provider is already started, and will leave the provider in // {started initializing} if the provider is stopped. if (!secondaryCurrentState.isTerminated()) { // Try to start the secondary. This does nothing if the provider is already // started, and will leave the provider in {started initializing} if the provider is // stopped. tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration); } } else if (failedProvider == mSecondaryProvider) { // No-op: The secondary will only be active if the primary is uncertain or is failed. // So, there the primary should not need to be started when the secondary fails. // No-op: The secondary will only be active if the primary is uncertain or is // terminated. So, there the primary should not need to be started when the secondary // fails. if (primaryCurrentState.stateEnum != PROVIDER_STATE_STARTED_UNCERTAIN && primaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) { && !primaryCurrentState.isTerminated()) { warnLog("Secondary provider unexpected reported a failure:" + " failed provider=" + failedProvider.getName() + ", primary provider=" + mPrimaryProvider Loading @@ -367,19 +391,18 @@ class ControllerImpl extends LocationTimeZoneProviderController { } } // If both providers are now failed, the controller needs to tell the next component in the // time zone detection process. if (primaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED && secondaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED) { // If both providers are now terminated, the controller needs to tell the next component in // the time zone detection process. if (primaryCurrentState.isTerminated() && secondaryCurrentState.isTerminated()) { // If both providers are newly failed then the controller is uncertain by definition // If both providers are newly terminated then the controller is uncertain by definition // and it will never recover so it can send a suggestion immediately. cancelUncertaintyTimeout(); // If both providers are now failed, then a suggestion must be sent informing the time // zone detector that there are no further updates coming in future. // If both providers are now terminated, then a suggestion must be sent informing the // time zone detector that there are no further updates coming in future. GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( "Both providers are permanently failed:" "Both providers are terminated:" + " primary=" + primaryCurrentState.provider + ", secondary=" + secondaryCurrentState.provider); makeSuggestion(suggestion); Loading
services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java +36 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,10 @@ import android.annotation.NonNull; import android.os.Handler; import java.util.Objects; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** * The real implementation of {@link ThreadingDomain} that uses a {@link Handler}. Loading Loading @@ -57,6 +61,38 @@ final class HandlerThreadingDomain extends ThreadingDomain { getHandler().post(r); } @Override <V> V postAndWait(@NonNull Callable<V> callable, @DurationMillisLong long durationMillis) throws Exception { // Calling this on this domain's thread would lead to deadlock. assertNotCurrentThread(); AtomicReference<V> resultReference = new AtomicReference<>(); AtomicReference<Exception> exceptionReference = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); post(() -> { try { resultReference.set(callable.call()); } catch (Exception e) { exceptionReference.set(e); } finally { latch.countDown(); } }); try { if (!latch.await(durationMillis, TimeUnit.MILLISECONDS)) { throw new RuntimeException("Timed out"); } } catch (InterruptedException e) { throw new RuntimeException(e); } if (exceptionReference.get() != null) { throw exceptionReference.get(); } return resultReference.get(); } @Override void postDelayed(@NonNull Runnable r, @DurationMillisLong long delayMillis) { getHandler().postDelayed(r, delayMillis); Loading
services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +73 −24 Original line number Diff line number Diff line Loading @@ -153,10 +153,14 @@ public class LocationTimeZoneManagerService extends Binder { /** The shared lock from {@link #mThreadingDomain}. */ @NonNull private final Object mSharedLock; // Lazily initialized. Non-null and effectively final after onSystemThirdPartyAppsCanStart(). // Lazily initialized. Can be null if the service has been stopped. @GuardedBy("mSharedLock") private ControllerImpl mLocationTimeZoneDetectorController; // Lazily initialized. Can be null if the service has been stopped. @GuardedBy("mSharedLock") private ControllerEnvironmentImpl mEnvironment; LocationTimeZoneManagerService(Context context) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); mHandler = FgThread.getHandler(); Loading @@ -178,31 +182,59 @@ public class LocationTimeZoneManagerService extends Binder { } void onSystemThirdPartyAppsCanStart() { // Called on an arbitrary thread during initialization. // Called on an arbitrary thread during initialization. We do not want to wait for // completion as it would delay boot. final boolean waitForCompletion = false; startInternal(waitForCompletion); } /** * Starts the service during server initialization or during tests after a call to * {@link #stop()}. */ void start() { enforceManageTimeZoneDetectorPermission(); final boolean waitForCompletion = true; startInternal(waitForCompletion); } /** * Starts the service during server initialization or during tests after a call to * {@link #stop()}. * * <p>To avoid tests needing to sleep, when {@code waitForCompletion} is {@code true}, this * method will not return until all the system server components have started. */ private void startInternal(boolean waitForCompletion) { Runnable runnable = () -> { synchronized (mSharedLock) { if (mLocationTimeZoneDetectorController == null) { LocationTimeZoneProvider primary = createPrimaryProvider(); LocationTimeZoneProvider secondary = createSecondaryProvider(); mLocationTimeZoneDetectorController = new ControllerImpl(mThreadingDomain, primary, secondary); ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain); ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl( ControllerCallbackImpl callback = new ControllerCallbackImpl( mThreadingDomain); mEnvironment = new ControllerEnvironmentImpl( mThreadingDomain, mLocationTimeZoneDetectorController); // Initialize the controller on the mThreadingDomain thread: this ensures that the // ThreadingDomain requirements for the controller / environment methods are honored. mThreadingDomain.post(() -> mLocationTimeZoneDetectorController.initialize(environment, callback)); mLocationTimeZoneDetectorController.initialize(mEnvironment, callback); } } private LocationTimeZoneProvider createPrimaryProvider() { if (isDisabled(PRIMARY_PROVIDER_NAME)) { return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME); }; if (waitForCompletion) { mThreadingDomain.postAndWait(runnable, BLOCKING_OP_WAIT_DURATION_MILLIS); } else { mThreadingDomain.post(runnable); } } private LocationTimeZoneProvider createPrimaryProvider() { LocationTimeZoneProviderProxy proxy; if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else if (isDisabled(PRIMARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( mContext, Loading @@ -217,13 +249,11 @@ public class LocationTimeZoneManagerService extends Binder { } private LocationTimeZoneProvider createSecondaryProvider() { if (isDisabled(SECONDARY_PROVIDER_NAME)) { return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME); } LocationTimeZoneProviderProxy proxy; if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else if (isDisabled(SECONDARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( mContext, Loading Loading @@ -274,6 +304,25 @@ public class LocationTimeZoneManagerService extends Binder { return Objects.equals(systemPropertyProviderMode, mode); } /** * Stops the service for tests. To avoid tests needing to sleep, this method will not return * until all the system server components have stopped. */ void stop() { enforceManageTimeZoneDetectorPermission(); mThreadingDomain.postAndWait(() -> { synchronized (mSharedLock) { if (mLocationTimeZoneDetectorController != null) { mLocationTimeZoneDetectorController.destroy(); mLocationTimeZoneDetectorController = null; mEnvironment.destroy(); mEnvironment = null; } } }, BLOCKING_OP_WAIT_DURATION_MILLIS); } @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, Loading Loading @@ -335,7 +384,7 @@ public class LocationTimeZoneManagerService extends Binder { ipw.println("LocationTimeZoneManagerService:"); ipw.increaseIndent(); if (mLocationTimeZoneDetectorController == null) { ipw.println("{Uninitialized}"); ipw.println("{Stopped}"); } else { mLocationTimeZoneDetectorController.dump(ipw, args); } Loading