• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.location;
18 
19 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
20 
21 import android.annotation.Nullable;
22 import android.location.Location;
23 import android.os.Bundle;
24 
25 import com.android.internal.annotations.GuardedBy;
26 import com.android.internal.location.ProviderRequest;
27 import com.android.internal.util.Preconditions;
28 
29 import java.io.FileDescriptor;
30 import java.io.PrintWriter;
31 import java.util.Collections;
32 import java.util.List;
33 
34 /**
35  * Represents a location provider that may switch between a mock implementation and a real
36  * implementation. Requires owners to provide a lock object that will be used internally and held
37  * for the duration of all listener callbacks. Owners are reponsible for ensuring this cannot lead
38  * to deadlock.
39  *
40  * In order to ensure deadlock does not occur, the owner must validate that the ONLY lock which can
41  * be held BOTH when calling into this class AND when receiving a callback from this class is the
42  * lock given to this class via the constructor. Holding any other lock is ok as long as there is no
43  * possibility that it can be obtained within both codepaths.
44  *
45  * Holding the given lock guarantees atomicity of any operations on this class for the duration.
46  *
47  * @hide
48  */
49 public class MockableLocationProvider extends AbstractLocationProvider {
50 
51     private final Object mOwnerLock;
52 
53     @GuardedBy("mOwnerLock")
54     @Nullable private AbstractLocationProvider mProvider;
55     @GuardedBy("mOwnerLock")
56     @Nullable private AbstractLocationProvider mRealProvider;
57     @GuardedBy("mOwnerLock")
58     @Nullable private MockProvider mMockProvider;
59 
60     @GuardedBy("mOwnerLock")
61     private ProviderRequest mRequest;
62 
63     /**
64      * The given lock object will be held any time the listener is invoked, and may also be acquired
65      * and released during the course of invoking any public methods. Holding the given lock ensures
66      * that provider state cannot change except as result of an explicit call by the owner of the
67      * lock into this class. The client is reponsible for ensuring this cannot cause deadlock.
68      *
69      * The client should expect that it may being to receive callbacks as soon as this constructor
70      * is invoked.
71      */
MockableLocationProvider(Object ownerLock, Listener listener)72     public MockableLocationProvider(Object ownerLock, Listener listener) {
73         // using a direct executor is acceptable because all inbound calls are delegated to the
74         // actual provider implementations which will use their own executors
75         super(DIRECT_EXECUTOR, Collections.emptySet());
76         mOwnerLock = ownerLock;
77         mRequest = ProviderRequest.EMPTY_REQUEST;
78 
79         setListener(listener);
80     }
81 
82     /**
83      * Returns the current provider implementation. May be null if there is no current
84      * implementation.
85      */
86     @Nullable
getProvider()87     public AbstractLocationProvider getProvider() {
88         synchronized (mOwnerLock) {
89             return mProvider;
90         }
91     }
92 
93     /**
94      * Sets the real provider implementation, replacing any previous real provider implementation.
95      * May cause an inline invocation of {@link Listener#onStateChanged(State, State)} if this
96      * results in a state change.
97      */
setRealProvider(@ullable AbstractLocationProvider provider)98     public void setRealProvider(@Nullable AbstractLocationProvider provider) {
99         synchronized (mOwnerLock) {
100             if (mRealProvider == provider) {
101                 return;
102             }
103 
104             mRealProvider = provider;
105             if (!isMock()) {
106                 setProviderLocked(mRealProvider);
107             }
108         }
109     }
110 
111     /**
112      * Sets the mock provider implementation, replacing any previous mock provider implementation.
113      * Mock implementations are always used instead of real implementations if set. May cause an
114      * inline invocation of {@link Listener#onStateChanged(State, State)} if this results in a
115      * state change.
116      */
setMockProvider(@ullable MockProvider provider)117     public void setMockProvider(@Nullable MockProvider provider) {
118         synchronized (mOwnerLock) {
119             if (mMockProvider == provider) {
120                 return;
121             }
122 
123             mMockProvider = provider;
124             if (mMockProvider != null) {
125                 setProviderLocked(mMockProvider);
126             } else {
127                 setProviderLocked(mRealProvider);
128             }
129         }
130     }
131 
132     @GuardedBy("mOwnerLock")
setProviderLocked(@ullable AbstractLocationProvider provider)133     private void setProviderLocked(@Nullable AbstractLocationProvider provider) {
134         if (mProvider == provider) {
135             return;
136         }
137 
138         AbstractLocationProvider oldProvider = mProvider;
139         mProvider = provider;
140 
141         if (oldProvider != null) {
142             // do this after switching the provider - so even if the old provider is using a direct
143             // executor, if it re-enters this class within setRequest(), it will be ignored
144             oldProvider.setListener(null);
145             oldProvider.setRequest(ProviderRequest.EMPTY_REQUEST);
146         }
147 
148         State newState;
149         if (mProvider != null) {
150             newState = mProvider.setListener(new ListenerWrapper(mProvider));
151         } else {
152             newState = State.EMPTY_STATE;
153         }
154 
155         ProviderRequest oldRequest = mRequest;
156         setState(newState);
157 
158         if (mProvider != null && oldRequest == mRequest) {
159             mProvider.setRequest(mRequest);
160         }
161     }
162 
163     /**
164      * Returns true if the current active provider implementation is the mock implementation, and
165      * false otherwise.
166      */
isMock()167     public boolean isMock() {
168         synchronized (mOwnerLock) {
169             return mMockProvider != null && mProvider == mMockProvider;
170         }
171     }
172 
173     /**
174      * Sets the mock provider implementation's allowed state. Will throw an exception if the mock
175      * provider is not currently the active implementation.
176      */
setMockProviderAllowed(boolean allowed)177     public void setMockProviderAllowed(boolean allowed) {
178         synchronized (mOwnerLock) {
179             Preconditions.checkState(isMock());
180             mMockProvider.setProviderAllowed(allowed);
181         }
182     }
183     /**
184      * Sets the mock provider implementation's location. Will throw an exception if the mock
185      * provider is not currently the active implementation.
186      */
setMockProviderLocation(Location location)187     public void setMockProviderLocation(Location location) {
188         synchronized (mOwnerLock) {
189             Preconditions.checkState(isMock());
190             mMockProvider.setProviderLocation(location);
191         }
192     }
193 
194     /**
195      * Returns the current location request.
196      */
getCurrentRequest()197     public ProviderRequest getCurrentRequest() {
198         synchronized (mOwnerLock) {
199             return mRequest;
200         }
201     }
202 
203     @Override
onSetRequest(ProviderRequest request)204     protected void onSetRequest(ProviderRequest request) {
205         synchronized (mOwnerLock) {
206             if (request == mRequest) {
207                 return;
208             }
209 
210             mRequest = request;
211 
212             if (mProvider != null) {
213                 mProvider.setRequest(request);
214             }
215         }
216     }
217 
218     @Override
onExtraCommand(int uid, int pid, String command, Bundle extras)219     protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {
220         synchronized (mOwnerLock) {
221             if (mProvider != null) {
222                 mProvider.sendExtraCommand(uid, pid, command, extras);
223             }
224         }
225     }
226 
227     /**
228      * Dumps the current provider implementation.
229      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)230     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
231         // holding the owner lock outside this could lead to deadlock since we don't run dump on the
232         // executor specified by the provider, we run it directly
233         Preconditions.checkState(!Thread.holdsLock(mOwnerLock));
234 
235         AbstractLocationProvider provider;
236         synchronized (mOwnerLock) {
237             provider = mProvider;
238             pw.println("allowed=" + getState().allowed);
239             pw.println("properties=" + getState().properties);
240             pw.println("packages=" + getState().providerPackageNames);
241             pw.println("request=" + mRequest);
242         }
243 
244         if (provider != null) {
245             // dump outside the lock in case the provider wants to acquire its own locks, and since
246             // the default provider dump behavior does not move things onto the provider thread...
247             provider.dump(fd, pw, args);
248         }
249     }
250 
251     // ensures that callbacks from the incorrect provider are never visible to clients - this
252     // requires holding the owner's lock for the duration of the callback
253     private class ListenerWrapper implements Listener {
254 
255         private final AbstractLocationProvider mListenerProvider;
256 
ListenerWrapper(AbstractLocationProvider listenerProvider)257         private ListenerWrapper(AbstractLocationProvider listenerProvider) {
258             mListenerProvider = listenerProvider;
259         }
260 
261         @Override
onStateChanged(State oldState, State newState)262         public final void onStateChanged(State oldState, State newState) {
263             synchronized (mOwnerLock) {
264                 if (mListenerProvider != mProvider) {
265                     return;
266                 }
267 
268                 setState(newState);
269             }
270         }
271 
272         @Override
onReportLocation(Location location)273         public final void onReportLocation(Location location) {
274             synchronized (mOwnerLock) {
275                 if (mListenerProvider != mProvider) {
276                     return;
277                 }
278 
279                 reportLocation(location);
280             }
281         }
282 
283         @Override
onReportLocation(List<Location> locations)284         public final void onReportLocation(List<Location> locations) {
285             synchronized (mOwnerLock) {
286                 if (mListenerProvider != mProvider) {
287                     return;
288                 }
289 
290                 reportLocation(locations);
291             }
292         }
293     }
294 }
295