Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99bba2b9 authored by Neil Fuller's avatar Neil Fuller Committed by Android (Google) Code Review
Browse files

Merge "Initial version of LocationTimeZoneManagerService"

parents ca43ae65 95126bce
Loading
Loading
Loading
Loading
+214 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.location.timezone;

import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.timezone.LocationTimeZoneEvent;
import android.util.IndentingPrintWriter;
import android.util.Slog;

import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;

import java.util.Objects;

/**
 * The real, system-server side implementation of a binder call backed {@link
 * LocationTimeZoneProvider}. It handles keeping track of current state, timeouts and ensuring
 * events are passed to the {@link LocationTimeZoneProviderController} on the required thread.
 */
class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {

    private static final String TAG = LocationTimeZoneManagerService.TAG;

    @NonNull private final LocationTimeZoneProviderProxy mProxy;

    BinderLocationTimeZoneProvider(
            @NonNull ThreadingDomain threadingDomain,
            @NonNull String providerName,
            @NonNull LocationTimeZoneProviderProxy proxy) {
        super(threadingDomain, providerName);
        mProxy = Objects.requireNonNull(proxy);
    }

    @Override
    void onInitialize() {
        mProxy.setListener(new LocationTimeZoneProviderProxy.Listener() {
            @Override
            public void onReportLocationTimeZoneEvent(
                    @NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
                handleLocationTimeZoneEvent(locationTimeZoneEvent);
            }

            @Override
            public void onProviderBound() {
                handleOnProviderBound();
            }

            @Override
            public void onProviderUnbound() {
                handleProviderLost("onProviderUnbound()");
            }
        });
    }

    private void handleProviderLost(String reason) {
        mThreadingDomain.assertCurrentThread();

        synchronized (mSharedLock) {
            ProviderState currentState = mCurrentState.get();
            switch (currentState.stateEnum) {
                case PROVIDER_STATE_ENABLED: {
                    // Losing a remote provider is treated as becoming uncertain.
                    String msg = "handleProviderLost reason=" + reason
                            + ", mProviderName=" + mProviderName
                            + ", currentState=" + currentState;
                    debugLog(msg);
                    // This is an unusual PROVIDER_STATE_ENABLED state because event == null
                    ProviderState newState = currentState.newState(
                            PROVIDER_STATE_ENABLED, null, currentState.currentUserConfiguration,
                            msg);
                    setCurrentState(newState, true);
                    break;
                }
                case PROVIDER_STATE_DISABLED: {
                    debugLog("handleProviderLost reason=" + reason
                            + ", mProviderName=" + mProviderName
                            + ", currentState=" + currentState
                            + ": No state change required, provider is disabled.");
                    break;
                }
                case PROVIDER_STATE_PERM_FAILED: {
                    debugLog("handleProviderLost reason=" + reason
                            + ", mProviderName=" + mProviderName
                            + ", currentState=" + currentState
                            + ": No state change required, provider is perm failed.");
                    break;
                }
                default: {
                    throw new IllegalStateException("Unknown currentState=" + currentState);
                }
            }
        }
    }

    private void handleOnProviderBound() {
        mThreadingDomain.assertCurrentThread();

        synchronized (mSharedLock) {
            ProviderState currentState = mCurrentState.get();
            switch (currentState.stateEnum) {
                case PROVIDER_STATE_ENABLED: {
                    debugLog("handleOnProviderBound mProviderName=" + mProviderName
                            + ", currentState=" + currentState + ": Provider is enabled.");
                    break;
                }
                case PROVIDER_STATE_DISABLED: {
                    debugLog("handleOnProviderBound mProviderName=" + mProviderName
                            + ", currentState=" + currentState + ": Provider is disabled.");
                    break;
                }
                case PROVIDER_STATE_PERM_FAILED: {
                    debugLog("handleOnProviderBound"
                            + ", mProviderName=" + mProviderName
                            + ", currentState=" + currentState
                            + ": No state change required, provider is perm failed.");
                    break;
                }
                default: {
                    throw new IllegalStateException("Unknown currentState=" + currentState);
                }
            }
        }
    }

    @Override
    void onEnable() {
        // Set a request on the proxy - it will be sent immediately if the service is bound,
        // or will be sent as soon as the service becomes bound.
        // TODO(b/152744911): Decide whether to send a timeout so the provider knows how long
        //  it has to generate the first event before it could be bypassed.
        LocationTimeZoneProviderRequest request =
                new LocationTimeZoneProviderRequest.Builder()
                        .setReportLocationTimeZone(true)
                        .build();
        mProxy.setRequest(request);
    }

    @Override
    void onDisable() {
        LocationTimeZoneProviderRequest request =
                new LocationTimeZoneProviderRequest.Builder()
                        .setReportLocationTimeZone(false)
                        .build();
        mProxy.setRequest(request);
    }

    @Override
    void logWarn(String msg) {
        Slog.w(TAG, msg);
    }

    @Override
    public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
        synchronized (mSharedLock) {
            ipw.println("{BinderLocationTimeZoneProvider}");
            ipw.println("mProviderName=" + mProviderName);
            ipw.println("mCurrentState=" + mCurrentState);
            ipw.println("mProxy=" + mProxy);

            ipw.println("State history:");
            ipw.increaseIndent();
            mCurrentState.dump(ipw);
            ipw.decreaseIndent();

            ipw.println("Proxy details:");
            ipw.increaseIndent();
            mProxy.dump(ipw, args);
            ipw.decreaseIndent();
        }
    }

    @Override
    public String toString() {
        synchronized (mSharedLock) {
            return "BinderLocationTimeZoneProvider{"
                    + "mProviderName=" + mProviderName
                    + "mCurrentState=" + mCurrentState
                    + "mProxy=" + mProxy
                    + '}';
        }
    }

    /**
     * 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) {
        if (!(mProxy instanceof SimulatedLocationTimeZoneProviderProxy)) {
            Slog.w(TAG, mProxy + " is not a " + SimulatedLocationTimeZoneProviderProxy.class
                    + ", event=" + event);
            return;
        }
        ((SimulatedLocationTimeZoneProviderProxy) mProxy).simulate(event);
    }
}
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.location.timezone;

import android.annotation.NonNull;

import com.android.server.LocalServices;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;

/**
 * The real implementation of {@link LocationTimeZoneProviderController.Callback} used by
 * {@link ControllerImpl} to interact with other server components.
 */
class ControllerCallbackImpl extends LocationTimeZoneProviderController.Callback {

    ControllerCallbackImpl(@NonNull ThreadingDomain threadingDomain) {
        super(threadingDomain);
    }

    @Override
    void suggest(@NonNull GeolocationTimeZoneSuggestion suggestion) {
        mThreadingDomain.assertCurrentThread();

        TimeZoneDetectorInternal timeZoneDetector =
                LocalServices.getService(TimeZoneDetectorInternal.class);
        timeZoneDetector.suggestGeolocationTimeZone(suggestion);
    }
}
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.location.timezone;

import android.annotation.NonNull;

import com.android.server.LocalServices;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;

import java.util.Objects;

/**
 * The real implementation of {@link LocationTimeZoneProviderController.Environment} used by
 * {@link ControllerImpl} to interact with other server components.
 */
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {

    @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
    @NonNull private final LocationTimeZoneProviderController mController;

    ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
            @NonNull LocationTimeZoneProviderController controller) {
        super(threadingDomain);
        mController = Objects.requireNonNull(controller);
        mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);

        // Listen for configuration changes.
        mTimeZoneDetectorInternal.addConfigurationListener(
                () -> mThreadingDomain.post(mController::onConfigChanged));
    }

    @Override
    ConfigurationInternal getCurrentUserConfigurationInternal() {
        return mTimeZoneDetectorInternal.getCurrentUserConfigurationInternal();
    }
}
+420 −0

File added.

Preview size limit exceeded, changes collapsed.

+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.location.timezone;

import android.annotation.NonNull;
import android.os.Handler;

import java.util.Objects;

/**
 * The real implementation of {@link ThreadingDomain} that uses a {@link Handler}.
 */
final class HandlerThreadingDomain extends ThreadingDomain {

    @NonNull private final Handler mHandler;

    HandlerThreadingDomain(Handler handler) {
        mHandler = Objects.requireNonNull(handler);
    }

    /**
     * Returns the {@link Handler} associated with this threading domain. The same {@link Handler}
     * may be associated with multiple threading domains, e.g. multiple threading domains could
     * choose to use the {@link com.android.server.FgThread} handler.
     *
     * <p>If you find yourself making this public because you need a {@link Handler}, then it may
     * cause problems with testability. Try to avoid using this method and use methods like {@link
     * #post(Runnable)} instead.
     */
    @NonNull
    Handler getHandler() {
        return mHandler;
    }

    @NonNull
    Thread getThread() {
        return getHandler().getLooper().getThread();
    }

    @Override
    void post(@NonNull Runnable r) {
        getHandler().post(r);
    }

    @Override
    void postDelayed(@NonNull Runnable r, long delayMillis) {
        getHandler().postDelayed(r, delayMillis);
    }

    @Override
    void postDelayed(Runnable r, Object token, long delayMillis) {
        getHandler().postDelayed(r, token, delayMillis);
    }

    @Override
    void removeQueuedRunnables(Object token) {
        getHandler().removeCallbacksAndMessages(token);
    }
}
Loading