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 static android.Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE;
20 import static android.Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE;
21 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
22 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.content.Context;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.RemoteCallback;
31 import android.service.timezone.ITimeZoneProvider;
32 import android.service.timezone.ITimeZoneProviderManager;
33 import android.service.timezone.TimeZoneProviderSuggestion;
34 import android.util.IndentingPrintWriter;
35 
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.server.servicewatcher.CurrentUserServiceSupplier;
38 import com.android.server.servicewatcher.CurrentUserServiceSupplier.BoundServiceInfo;
39 import com.android.server.servicewatcher.ServiceWatcher;
40 import com.android.server.servicewatcher.ServiceWatcher.ServiceListener;
41 
42 import java.util.Objects;
43 
44 /**
45  * System server-side proxy for ITimeZoneProvider implementations, i.e. this provides the
46  * system server object used to communicate with a remote {@link
47  * android.service.timezone.TimeZoneProviderService} over Binder, which could be running in a
48  * different process. As "remote" providers are bound / unbound this proxy will rebind to the "best"
49  * available remote process.
50  */
51 class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy implements
52         ServiceListener<BoundServiceInfo> {
53 
54     @NonNull private final ServiceWatcher mServiceWatcher;
55 
56     @GuardedBy("mSharedLock")
57     @Nullable private ManagerProxy mManagerProxy;
58 
59     @GuardedBy("mSharedLock")
60     @NonNull private TimeZoneProviderRequest mRequest;
61 
RealLocationTimeZoneProviderProxy( @onNull Context context, @NonNull Handler handler, @NonNull ThreadingDomain threadingDomain, @NonNull String action, @NonNull String providerPackageName)62     RealLocationTimeZoneProviderProxy(
63             @NonNull Context context, @NonNull Handler handler,
64             @NonNull ThreadingDomain threadingDomain, @NonNull String action,
65             @NonNull String providerPackageName) {
66         super(context, threadingDomain);
67         mManagerProxy = null;
68         mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
69 
70         Objects.requireNonNull(providerPackageName);
71         mServiceWatcher = ServiceWatcher.create(context,
72                 handler,
73                 "RealLocationTimeZoneProviderProxy",
74                 new CurrentUserServiceSupplier(context, action,
75                         providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
76                         INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE),
77                 this);
78     }
79 
80     @Override
onInitialize()81     void onInitialize() {
82         if (!register()) {
83             throw new IllegalStateException("Unable to register binder proxy");
84         }
85     }
86 
87     @Override
onDestroy()88     void onDestroy() {
89         mServiceWatcher.unregister();
90     }
91 
register()92     private boolean register() {
93         boolean resolves = mServiceWatcher.checkServiceResolves();
94         if (resolves) {
95             mServiceWatcher.register();
96         }
97         return resolves;
98     }
99 
100     @Override
onBind(IBinder binder, BoundServiceInfo boundService)101     public void onBind(IBinder binder, BoundServiceInfo boundService) {
102         mThreadingDomain.assertCurrentThread();
103 
104         synchronized (mSharedLock) {
105             // When a new remote is first bound we create the ManagerProxy that will be passed to
106             // it. By creating a new one for each bind the ManagerProxy can check whether it is
107             // still the current proxy and if not it can ignore incoming calls.
108             mManagerProxy = new ManagerProxy();
109             mListener.onProviderBound();
110 
111             // Send the current request to the remote.
112             trySendCurrentRequest();
113         }
114     }
115 
116     @Override
onUnbind()117     public void onUnbind() {
118         mThreadingDomain.assertCurrentThread();
119 
120         synchronized (mSharedLock) {
121             // Clear the ManagerProxy used with the old remote so we will ignore calls from any old
122             // remotes that somehow hold a reference to it.
123             mManagerProxy = null;
124             mListener.onProviderUnbound();
125         }
126     }
127 
128     @Override
setRequest(@onNull TimeZoneProviderRequest request)129     final void setRequest(@NonNull TimeZoneProviderRequest request) {
130         mThreadingDomain.assertCurrentThread();
131 
132         Objects.requireNonNull(request);
133         synchronized (mSharedLock) {
134             mRequest = request;
135 
136             // Two possible outcomes here: Either we are already bound to a remote service, in
137             // which case trySendCurrentRequest() will communicate the request immediately, or we
138             // are not bound to the remote service yet, in which case it will be sent during
139             // onBindOnHandlerThread() instead.
140             trySendCurrentRequest();
141         }
142     }
143 
144     @GuardedBy("mSharedLock")
trySendCurrentRequest()145     private void trySendCurrentRequest() {
146         ManagerProxy managerProxy = mManagerProxy;
147         TimeZoneProviderRequest request = mRequest;
148         mServiceWatcher.runOnBinder(binder -> {
149             ITimeZoneProvider service = ITimeZoneProvider.Stub.asInterface(binder);
150             if (request.sendUpdates()) {
151                 service.startUpdates(managerProxy, request.getInitializationTimeout().toMillis());
152             } else {
153                 service.stopUpdates();
154             }
155         });
156     }
157 
158     /**
159      * A stubbed implementation.
160      */
161     @Override
handleTestCommand(@onNull TestCommand testCommand, @Nullable RemoteCallback callback)162     void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
163         mThreadingDomain.assertCurrentThread();
164 
165         if (callback != null) {
166             Bundle result = new Bundle();
167             result.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
168             result.putString(TEST_COMMAND_RESULT_ERROR_KEY, "Not implemented");
169             callback.sendResult(result);
170         }
171     }
172 
173     @Override
dump(@onNull IndentingPrintWriter ipw, @Nullable String[] args)174     public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
175         synchronized (mSharedLock) {
176             ipw.println("{RealLocationTimeZoneProviderProxy}");
177             ipw.println("mRequest=" + mRequest);
178             mServiceWatcher.dump(ipw);
179         }
180     }
181 
182     /**
183      * A system Server-side proxy for the ITimeZoneProviderManager, i.e. this is a local binder stub
184      * Each "remote" TimeZoneProvider is passed a binder instance that it then uses to communicate
185      * back with the system server, invoking the logic here.
186      */
187     private class ManagerProxy extends ITimeZoneProviderManager.Stub {
188 
189         // executed on binder thread
190         @Override
onTimeZoneProviderSuggestion(TimeZoneProviderSuggestion suggestion)191         public void onTimeZoneProviderSuggestion(TimeZoneProviderSuggestion suggestion) {
192             onTimeZoneProviderEvent(TimeZoneProviderEvent.createSuggestionEvent(suggestion));
193         }
194 
195         // executed on binder thread
196         @Override
onTimeZoneProviderUncertain()197         public void onTimeZoneProviderUncertain() {
198             onTimeZoneProviderEvent(TimeZoneProviderEvent.createUncertainEvent());
199 
200         }
201 
202         // executed on binder thread
203         @Override
onTimeZoneProviderPermanentFailure(String failureReason)204         public void onTimeZoneProviderPermanentFailure(String failureReason) {
205             onTimeZoneProviderEvent(
206                     TimeZoneProviderEvent.createPermanentFailureEvent(failureReason));
207         }
208 
onTimeZoneProviderEvent(TimeZoneProviderEvent event)209         private void onTimeZoneProviderEvent(TimeZoneProviderEvent event) {
210             synchronized (mSharedLock) {
211                 if (mManagerProxy != this) {
212                     // Ignore incoming calls if this instance is no longer the current
213                     // mManagerProxy.
214                     return;
215                 }
216             }
217             handleTimeZoneProviderEvent(event);
218         }
219     }
220 }
221