• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.provider;
18 
19 import android.annotation.Nullable;
20 import android.location.LocationResult;
21 import android.location.provider.ProviderProperties;
22 import android.location.provider.ProviderRequest;
23 import android.location.util.identity.CallerIdentity;
24 import android.os.Binder;
25 import android.os.Bundle;
26 
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.Objects;
33 import java.util.Set;
34 import java.util.concurrent.Executor;
35 import java.util.concurrent.atomic.AtomicReference;
36 import java.util.function.UnaryOperator;
37 
38 /**
39  * Base class for all location providers.
40  *
41  * @hide
42  */
43 public abstract class AbstractLocationProvider {
44 
45     /**
46      * Interface for listening to location providers.
47      */
48     public interface Listener {
49 
50         /**
51          * Called when a provider's state changes. May be invoked from any thread. Will be
52          * invoked with a cleared binder identity.
53          */
onStateChanged(State oldState, State newState)54         void onStateChanged(State oldState, State newState);
55 
56         /**
57          * Called when a provider has a new location available. May be invoked from any thread. Will
58          * be invoked with a cleared binder identity.
59          */
onReportLocation(LocationResult locationResult)60         void onReportLocation(LocationResult locationResult);
61     }
62 
63     /**
64      * Holds a representation of the public state of a provider.
65      */
66     public static final class State {
67 
68         /**
69          * Default state value for a location provider that is disabled with no properties and an
70          * empty extra attribution tag set.
71          */
72         public static final State EMPTY_STATE = new State(false, null, null,
73                 Collections.emptySet());
74 
75         /**
76          * The provider's allowed state.
77          */
78         public final boolean allowed;
79 
80         /**
81          * The provider's properties.
82          */
83         @Nullable public final ProviderProperties properties;
84 
85         /**
86          * The provider's identity - providers may be afforded special privileges.
87          */
88         @Nullable public final CallerIdentity identity;
89 
90         /**
91          * A set of attribution tags also associated with this provider - these attribution tags may
92          * be afforded special privileges.
93          */
94         public final Set<String> extraAttributionTags;
95 
State(boolean allowed, ProviderProperties properties, CallerIdentity identity, Set<String> extraAttributionTags)96         private State(boolean allowed, ProviderProperties properties, CallerIdentity identity,
97                 Set<String> extraAttributionTags) {
98             this.allowed = allowed;
99             this.properties = properties;
100             this.identity = identity;
101             this.extraAttributionTags = Objects.requireNonNull(extraAttributionTags);
102         }
103 
104         /**
105          * Returns a state the same as the current but with allowed set as specified.
106          */
withAllowed(boolean allowed)107         public State withAllowed(boolean allowed) {
108             if (allowed == this.allowed) {
109                 return this;
110             } else {
111                 return new State(allowed, properties, identity, extraAttributionTags);
112             }
113         }
114 
115         /**
116          * Returns a state the same as the current but with properties set as specified.
117          */
withProperties(@ullable ProviderProperties properties)118         public State withProperties(@Nullable ProviderProperties properties) {
119             if (Objects.equals(properties, this.properties)) {
120                 return this;
121             } else {
122                 return new State(allowed, properties, identity, extraAttributionTags);
123             }
124         }
125 
126         /**
127          * Returns a state the same as the current but with an identity set as specified.
128          */
withIdentity(@ullable CallerIdentity identity)129         public State withIdentity(@Nullable CallerIdentity identity) {
130             if (Objects.equals(identity, this.identity)) {
131                 return this;
132             } else {
133                 return new State(allowed, properties, identity, extraAttributionTags);
134             }
135         }
136 
137         /**
138          * Returns a state the same as the current but with extra attribution tags set as specified.
139          */
withExtraAttributionTags(Set<String> extraAttributionTags)140         public State withExtraAttributionTags(Set<String> extraAttributionTags) {
141             if (extraAttributionTags.equals(this.extraAttributionTags)) {
142                 return this;
143             } else {
144                 return new State(allowed, properties, identity, extraAttributionTags);
145             }
146         }
147 
148 
149         @Override
equals(Object o)150         public boolean equals(Object o) {
151             if (this == o) {
152                 return true;
153             }
154             if (!(o instanceof State)) {
155                 return false;
156             }
157             State state = (State) o;
158             return allowed == state.allowed && properties == state.properties
159                     && Objects.equals(identity, state.identity)
160                     && extraAttributionTags.equals(state.extraAttributionTags);
161         }
162 
163         @Override
hashCode()164         public int hashCode() {
165             return Objects.hash(allowed, properties, identity, extraAttributionTags);
166         }
167     }
168 
169     // combines listener and state information so that they can be updated atomically with respect
170     // to each other and an ordering established.
171     private static class InternalState {
172         @Nullable public final Listener listener;
173         public final State state;
174 
InternalState(@ullable Listener listener, State state)175         InternalState(@Nullable Listener listener, State state) {
176             this.listener = listener;
177             this.state = state;
178         }
179 
withListener(Listener listener)180         InternalState withListener(Listener listener) {
181             if (listener == this.listener) {
182                 return this;
183             } else {
184                 return new InternalState(listener, state);
185             }
186         }
187 
withState(State state)188         InternalState withState(State state) {
189             if (state.equals(this.state)) {
190                 return this;
191             } else {
192                 return new InternalState(listener, state);
193             }
194         }
195 
withState(UnaryOperator<State> operator)196         InternalState withState(UnaryOperator<State> operator) {
197             return withState(operator.apply(state));
198         }
199     }
200 
201     protected final Executor mExecutor;
202 
203     // we use a lock-free implementation to update state to ensure atomicity between updating the
204     // provider state and setting the listener, so that the state updates a listener sees are
205     // consistent with when the listener was set (a listener should not see any updates that occur
206     // before it was set, and should not miss any updates that occur after it was set).
207     private final AtomicReference<InternalState> mInternalState;
208 
209     private final LocationProviderController mController;
210 
211     /**
212      * Creates a new location provider.
213      *
214      * All callback methods will be invoked on the given executor. A direct executor may be provided
215      * only if the provider can guarantee that all callback methods will never synchronously invoke
216      * any {@link LocationProviderController} methods. If this invariant is not held, use a normal
217      * executor or risk deadlock.
218      *
219      * An optional identity and properties may be provided to initialize the location provider.
220      */
AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity, @Nullable ProviderProperties properties, Set<String> extraAttributionTags)221     protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity,
222             @Nullable ProviderProperties properties, Set<String> extraAttributionTags) {
223         Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
224         mExecutor = Objects.requireNonNull(executor);
225         mInternalState = new AtomicReference<>(new InternalState(null,
226                 State.EMPTY_STATE
227                         .withIdentity(identity)
228                         .withProperties(properties)
229                         .withExtraAttributionTags(extraAttributionTags)));
230         mController = new Controller();
231     }
232 
233     /**
234      * Retrieves the controller for this location provider. Should never be invoked by subclasses,
235      * as a location provider should not be controlling itself. Using this method from subclasses
236      * could also result in deadlock.
237      */
getController()238     LocationProviderController getController() {
239         return mController;
240     }
241 
setState(UnaryOperator<State> operator)242     protected void setState(UnaryOperator<State> operator) {
243         AtomicReference<State> oldStateRef = new AtomicReference<>();
244         InternalState newInternalState = mInternalState.updateAndGet(
245                 internalState -> {
246                     oldStateRef.set(internalState.state);
247                     return internalState.withState(operator);
248                 });
249         State oldState = oldStateRef.get();
250 
251         if (oldState.equals(newInternalState.state)) {
252             return;
253         }
254 
255         if (newInternalState.listener != null) {
256             final long identity = Binder.clearCallingIdentity();
257             try {
258                 newInternalState.listener.onStateChanged(oldState, newInternalState.state);
259             } finally {
260                 Binder.restoreCallingIdentity(identity);
261             }
262         }
263     }
264 
265     /**
266      * The current state of the provider.
267      */
getState()268     public final State getState() {
269         return mInternalState.get().state;
270     }
271 
272     /**
273      * Call this method to report a change in provider allowed status.
274      */
setAllowed(boolean allowed)275     protected void setAllowed(boolean allowed) {
276         setState(state -> state.withAllowed(allowed));
277     }
278 
279     /**
280      * Call this method to report a change in provider properties.
281      */
setProperties(@ullable ProviderProperties properties)282     protected void setProperties(@Nullable ProviderProperties properties) {
283         setState(state -> state.withProperties(properties));
284     }
285 
286     /**
287      * Call this method to report a change in the provider's identity.
288      */
setIdentity(@ullable CallerIdentity identity)289     protected void setIdentity(@Nullable CallerIdentity identity) {
290         Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
291         setState(state -> state.withIdentity(identity));
292     }
293 
getExtraAttributionTags()294     public final Set<String> getExtraAttributionTags() {
295         return mInternalState.get().state.extraAttributionTags;
296     }
297 
298     /**
299      * Call this method to report a change in the provider's extra attribution tags.
300      */
setExtraAttributionTags(Set<String> extraAttributionTags)301     protected void setExtraAttributionTags(Set<String> extraAttributionTags) {
302         setState(state -> state.withExtraAttributionTags(extraAttributionTags));
303     }
304 
305     /**
306      * Call this method to report a new location.
307      */
reportLocation(LocationResult locationResult)308     protected void reportLocation(LocationResult locationResult) {
309         Listener listener = mInternalState.get().listener;
310         if (listener != null) {
311             final long identity = Binder.clearCallingIdentity();
312             try {
313                 listener.onReportLocation(Objects.requireNonNull(locationResult));
314             } finally {
315                 Binder.restoreCallingIdentity(identity);
316             }
317         }
318     }
319 
320     /**
321      * Callback invoked when the provider is started, and signals that other callback invocations
322      * can now be expected. Always implies that the provider request is set to the empty request.
323      * Always invoked on the provider executor.
324      */
onStart()325     protected void onStart() { }
326 
327     /**
328      * Callback invoked when the provider is stopped, and signals that no further callback
329      * invocations will occur (until a further call to {@link #onStart()}. Always invoked on the
330      * provider executor.
331      */
onStop()332     protected void onStop() { }
333 
334     /**
335      * Callback invoked to inform the provider of a new provider request which replaces any prior
336      * provider request. Always invoked on the provider executor.
337      */
onSetRequest(ProviderRequest request)338     protected abstract void onSetRequest(ProviderRequest request);
339 
340     /**
341      * Callback invoked to request any batched locations to be flushed. The argument callback must
342      * always be invoked exactly once for every invocation of this method, and should only be
343      * invoked only after {@link #reportLocation(LocationResult)} has been called for all flushed
344      * locations. If no locations are flushed, the argument callback may be invoked immediately.
345      * Always invoked on the provider executor.
346      */
onFlush(Runnable callback)347     protected abstract void onFlush(Runnable callback);
348 
349     /**
350      * Callback invoked to informs the provider of an extra command it may choose to respond to.
351      * Always invoked on the provider executor.
352      */
onExtraCommand(int uid, int pid, String command, Bundle extras)353     protected abstract void onExtraCommand(int uid, int pid, String command, Bundle extras);
354 
355     /**
356      * Dumps debug or log information. May be invoked from any thread.
357      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)358     protected abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
359 
360     private class Controller implements LocationProviderController {
361 
362         private boolean mStarted = false;
363 
Controller()364         Controller() {}
365 
366         @Override
setListener(@ullable Listener listener)367         public State setListener(@Nullable Listener listener) {
368             InternalState oldInternalState = mInternalState.getAndUpdate(
369                     internalState -> internalState.withListener(listener));
370             Preconditions.checkState(listener == null || oldInternalState.listener == null);
371             return oldInternalState.state;
372         }
373 
374         @Override
isStarted()375         public boolean isStarted() {
376             return mStarted;
377         }
378 
379         @Override
start()380         public void start() {
381             Preconditions.checkState(!mStarted);
382             mStarted = true;
383             mExecutor.execute(AbstractLocationProvider.this::onStart);
384         }
385 
386         @Override
stop()387         public void stop() {
388             Preconditions.checkState(mStarted);
389             mStarted = false;
390             mExecutor.execute(AbstractLocationProvider.this::onStop);
391         }
392 
393         @Override
setRequest(ProviderRequest request)394         public void setRequest(ProviderRequest request) {
395             Preconditions.checkState(mStarted);
396             mExecutor.execute(() -> onSetRequest(request));
397         }
398 
399         @Override
flush(Runnable listener)400         public void flush(Runnable listener) {
401             Preconditions.checkState(mStarted);
402             mExecutor.execute(() -> onFlush(listener));
403         }
404 
405         @Override
sendExtraCommand(int uid, int pid, String command, Bundle extras)406         public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
407             Preconditions.checkState(mStarted);
408             mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras));
409         }
410     }
411 }
412