1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.timezonedetector.location;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.os.Handler;
23 import android.os.RemoteCallback;
24 import android.util.IndentingPrintWriter;
25 
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.server.timezonedetector.Dumpable;
28 
29 import java.util.Objects;
30 
31 /**
32  * System server-side proxy for ITimeZoneProvider implementations, i.e. this provides the system
33  * server object used to communicate with a remote TimeZoneProvider over Binder, which could be
34  * running in a different process. As TimeZoneProviders are bound / unbound this proxy will rebind
35  * to the "best" available remote process.
36  *
37  * <p>Threading guarantees provided / required by this interface:
38  * <ul>
39  *     <li>All public methods defined by this class must be invoked using the {@link Handler} thread
40  *     from the {@link ThreadingDomain} passed to the constructor, excluding
41  *     {@link #dump(IndentingPrintWriter, String[])}</li>
42  *     <li>Non-static public methods that make binder calls to remote processes (e.g.
43  *     {@link #setRequest(TimeZoneProviderRequest)}) are executed asynchronously and will return
44  *     immediately.</li>
45  *     <li>Callbacks received via binder are delivered via {@link Listener} are delivered on the
46  *     {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
47  * </ul>
48  *
49  * <p>This class exists to enable the introduction of test implementations of {@link
50  * LocationTimeZoneProviderProxy} that can be used when a device is in a test mode to inject test
51  * events / behavior that are otherwise difficult to simulate.
52  */
53 abstract class LocationTimeZoneProviderProxy implements Dumpable {
54 
55     @NonNull protected final Context mContext;
56     @NonNull protected final ThreadingDomain mThreadingDomain;
57     @NonNull protected final Object mSharedLock;
58 
59     // Non-null and effectively final after setListener() is called.
60     @GuardedBy("mSharedLock")
61     @Nullable
62     protected Listener mListener;
63 
LocationTimeZoneProviderProxy( @onNull Context context, @NonNull ThreadingDomain threadingDomain)64     LocationTimeZoneProviderProxy(
65             @NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
66         mContext = Objects.requireNonNull(context);
67         mThreadingDomain = Objects.requireNonNull(threadingDomain);
68         mSharedLock = threadingDomain.getLockObject();
69     }
70 
71     /**
72      * Initializes the proxy. The supplied listener can expect to receive all events after this
73      * point. This method calls {@link #onInitialize()} for subclasses to handle their own
74      * initialization.
75      */
initialize(@onNull Listener listener)76     void initialize(@NonNull Listener listener) {
77         Objects.requireNonNull(listener);
78         synchronized (mSharedLock) {
79             if (mListener != null) {
80                 throw new IllegalStateException("listener already set");
81             }
82             this.mListener = listener;
83             onInitialize();
84         }
85     }
86 
87     /**
88      * Implemented by subclasses to initializes the proxy. This is called after {@link #mListener}
89      * is set.
90      */
91     @GuardedBy("mSharedLock")
onInitialize()92     abstract void onInitialize();
93 
94     /**
95      * Destroys the proxy. This method calls {@link #onDestroy()} for subclasses to handle their own
96      * destruction.
97      */
destroy()98     void destroy() {
99         synchronized (mSharedLock) {
100             onDestroy();
101         }
102     }
103 
104     /**
105      * Implemented by subclasses to destroy the proxy.
106      */
107     @GuardedBy("mSharedLock")
onDestroy()108     abstract void onDestroy();
109 
110     /**
111      * Sets a new request for the provider.
112      */
setRequest(@onNull TimeZoneProviderRequest request)113     abstract void setRequest(@NonNull TimeZoneProviderRequest request);
114 
115     /**
116      * Processes the supplied test command. An optional callback can be supplied to listen for a
117      * response.
118      */
handleTestCommand(@onNull TestCommand testCommand, @Nullable RemoteCallback callback)119     abstract void handleTestCommand(@NonNull TestCommand testCommand,
120             @Nullable RemoteCallback callback);
121 
122     /**
123      * Handles a {@link TimeZoneProviderEvent} from a remote process.
124      */
handleTimeZoneProviderEvent(@onNull TimeZoneProviderEvent timeZoneProviderEvent)125     final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
126         // These calls are invoked on a binder thread. Move to the mThreadingDomain thread as
127         // required by the guarantees for this class.
128         mThreadingDomain.post(() -> mListener.onReportTimeZoneProviderEvent(timeZoneProviderEvent));
129     }
130 
131     /**
132      * Interface for listening to location time zone providers. See {@link
133      * LocationTimeZoneProviderProxy} for threading guarantees.
134      */
135     interface Listener {
136 
137         /**
138          * Called when a provider receives a {@link TimeZoneProviderEvent}.
139          */
onReportTimeZoneProviderEvent(@onNull TimeZoneProviderEvent timeZoneProviderEvent)140         void onReportTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent);
141 
142         /**
143          * Called when a provider is (re)bound.
144          */
onProviderBound()145         void onProviderBound();
146 
147         /** Called when a provider is unbound. */
onProviderUnbound()148         void onProviderUnbound();
149     }
150 }
151