blob: 43b1b5f017b216319f28d650bd15e60d7c505eb7 [file] [log] [blame]
/*
* 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.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
import android.os.RemoteCallback;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.server.timezonedetector.Dumpable;
import java.util.Objects;
/**
* System server-side proxy for ITimeZoneProvider implementations, i.e. this provides the system
* server object used to communicate with a remote TimeZoneProvider over Binder, which could be
* running in a different process. As TimeZoneProviders are bound / unbound this proxy will rebind
* to the "best" available remote process.
*
* <p>Threading guarantees provided / required by this interface:
* <ul>
* <li>All public methods defined by this class must be invoked using the {@link Handler} thread
* from the {@link ThreadingDomain} passed to the constructor, excluding
* {@link #dump(IndentingPrintWriter, String[])}</li>
* <li>Non-static public methods that make binder calls to remote processes (e.g.
* {@link #setRequest(TimeZoneProviderRequest)}) are executed asynchronously and will return
* immediately.</li>
* <li>Callbacks received via binder are delivered via {@link Listener} are delivered on the
* {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
* </ul>
*
* <p>This class exists to enable the introduction of test implementations of {@link
* LocationTimeZoneProviderProxy} that can be used when a device is in a test mode to inject test
* events / behavior that are otherwise difficult to simulate.
*/
abstract class LocationTimeZoneProviderProxy implements Dumpable {
@NonNull protected final Context mContext;
@NonNull protected final ThreadingDomain mThreadingDomain;
@NonNull protected final Object mSharedLock;
// Non-null and effectively final after setListener() is called.
@GuardedBy("mSharedLock")
@Nullable
protected Listener mListener;
LocationTimeZoneProviderProxy(
@NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
mContext = Objects.requireNonNull(context);
mThreadingDomain = Objects.requireNonNull(threadingDomain);
mSharedLock = threadingDomain.getLockObject();
}
/**
* Initializes the proxy. The supplied listener can expect to receive all events after this
* point. This method calls {@link #onInitialize()} for subclasses to handle their own
* initialization.
*/
void initialize(@NonNull Listener listener) {
Objects.requireNonNull(listener);
synchronized (mSharedLock) {
if (mListener != null) {
throw new IllegalStateException("listener already set");
}
this.mListener = listener;
onInitialize();
}
}
/**
* Implemented by subclasses to initializes the proxy. This is called after {@link #mListener}
* is set.
*/
@GuardedBy("mSharedLock")
abstract void onInitialize();
/**
* Destroys the proxy. This method calls {@link #onDestroy()} for subclasses to handle their own
* destruction.
*/
void destroy() {
synchronized (mSharedLock) {
onDestroy();
}
}
/**
* Implemented by subclasses to destroy the proxy.
*/
@GuardedBy("mSharedLock")
abstract void onDestroy();
/**
* Sets a new request for the provider.
*/
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.
*/
final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
// These calls are invoked on a binder thread. Move to the mThreadingDomain thread as
// required by the guarantees for this class.
mThreadingDomain.post(() -> mListener.onReportTimeZoneProviderEvent(timeZoneProviderEvent));
}
/**
* Interface for listening to location time zone providers. See {@link
* LocationTimeZoneProviderProxy} for threading guarantees.
*/
interface Listener {
/**
* Called when a provider receives a {@link TimeZoneProviderEvent}.
*/
void onReportTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent);
/**
* Called when a provider is (re)bound.
*/
void onProviderBound();
/** Called when a provider is unbound. */
void onProviderUnbound();
}
}