blob: 64bca78f294d1de36018bbd2bed0ac3d4f36553c [file] [log] [blame]
Soonil Nagarkar1575a042018-10-24 17:54:54 -07001/*
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
17package com.android.server.location;
18
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080019import android.annotation.Nullable;
Soonil Nagarkar0d77ea62019-01-31 14:36:56 -080020import android.content.Context;
Soonil Nagarkar1575a042018-10-24 17:54:54 -070021import android.location.Location;
Soonil Nagarkar747cbec2019-09-19 14:27:03 -070022import android.os.Binder;
Soonil Nagarkar1575a042018-10-24 17:54:54 -070023import android.os.Bundle;
Soonil Nagarkarb6375a42020-01-29 15:23:06 -080024import android.util.ArraySet;
Soonil Nagarkar1575a042018-10-24 17:54:54 -070025
26import com.android.internal.location.ProviderProperties;
27import com.android.internal.location.ProviderRequest;
28
29import java.io.FileDescriptor;
30import java.io.PrintWriter;
Soonil Nagarkar0d77ea62019-01-31 14:36:56 -080031import java.util.Collections;
Soonil Nagarkar1575a042018-10-24 17:54:54 -070032import java.util.List;
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080033import java.util.Objects;
34import java.util.Set;
35import java.util.concurrent.Executor;
36import java.util.concurrent.atomic.AtomicReference;
37import java.util.function.UnaryOperator;
Soonil Nagarkar1575a042018-10-24 17:54:54 -070038
39/**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080040 * Base class for all location providers.
Soonil Nagarkar1575a042018-10-24 17:54:54 -070041 *
42 * @hide
43 */
44public abstract class AbstractLocationProvider {
45
46 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080047 * Interface for listening to location providers.
Soonil Nagarkar1575a042018-10-24 17:54:54 -070048 */
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080049 public interface Listener {
Soonil Nagarkar1575a042018-10-24 17:54:54 -070050
51 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080052 * Called when a provider's state changes. May be invoked from any thread. Will be
53 * invoked with a cleared binder identity.
Soonil Nagarkar1575a042018-10-24 17:54:54 -070054 */
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080055 void onStateChanged(State oldState, State newState);
Soonil Nagarkar1575a042018-10-24 17:54:54 -070056
57 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080058 * Called when a provider has a new location available. May be invoked from any thread. Will
59 * be invoked with a cleared binder identity.
Soonil Nagarkar1575a042018-10-24 17:54:54 -070060 */
61 void onReportLocation(Location location);
62
63 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080064 * Called when a provider has a new location available. May be invoked from any thread. Will
65 * be invoked with a cleared binder identity.
Soonil Nagarkar1575a042018-10-24 17:54:54 -070066 */
67 void onReportLocation(List<Location> locations);
68 }
69
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080070 /**
71 * Holds a representation of the public state of a provider.
72 */
73 public static final class State {
Soonil Nagarkar1575a042018-10-24 17:54:54 -070074
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080075 /**
76 * Default state value for a location provider that is disabled with no properties and an
77 * empty provider package list.
78 */
79 public static final State EMPTY_STATE = new State(false, null,
80 Collections.emptySet());
81
82 /**
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -080083 * The provider's allowed state.
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080084 */
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -080085 public final boolean allowed;
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080086
87 /**
88 * The provider's properties.
89 */
90 @Nullable public final ProviderProperties properties;
91
92 /**
93 * The provider's package name list - provider packages may be afforded special privileges.
94 */
95 public final Set<String> providerPackageNames;
96
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -080097 private State(boolean allowed, ProviderProperties properties,
Soonil Nagarkar8df02f42020-01-08 13:23:26 -080098 Set<String> providerPackageNames) {
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -080099 this.allowed = allowed;
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800100 this.properties = properties;
101 this.providerPackageNames = Objects.requireNonNull(providerPackageNames);
102 }
103
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800104 private State withAllowed(boolean allowed) {
105 if (allowed == this.allowed) {
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800106 return this;
107 } else {
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800108 return new State(allowed, properties, providerPackageNames);
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800109 }
110 }
111
112 private State withProperties(ProviderProperties properties) {
113 if (properties.equals(this.properties)) {
114 return this;
115 } else {
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800116 return new State(allowed, properties, providerPackageNames);
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800117 }
118 }
119
120 private State withProviderPackageNames(Set<String> providerPackageNames) {
121 if (providerPackageNames.equals(this.providerPackageNames)) {
122 return this;
123 } else {
Soonil Nagarkarb6375a42020-01-29 15:23:06 -0800124 return new State(allowed, properties,
125 Collections.unmodifiableSet(new ArraySet<>(providerPackageNames)));
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800126 }
127 }
128
129 @Override
130 public boolean equals(Object o) {
131 if (this == o) {
132 return true;
133 }
134 if (!(o instanceof State)) {
135 return false;
136 }
137 State state = (State) o;
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800138 return allowed == state.allowed && properties == state.properties
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800139 && providerPackageNames.equals(state.providerPackageNames);
140 }
141
142 @Override
143 public int hashCode() {
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800144 return Objects.hash(allowed, properties, providerPackageNames);
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800145 }
146 }
147
148 // combines listener and state information so that they can be updated atomically with respect
149 // to each other and an ordering established.
150 private static class InternalState {
151 @Nullable public final Listener listener;
152 public final State state;
153
154 private InternalState(@Nullable Listener listener, State state) {
155 this.listener = listener;
156 this.state = state;
157 }
158
159 private InternalState withListener(Listener listener) {
160 if (listener == this.listener) {
161 return this;
162 } else {
163 return new InternalState(listener, state);
164 }
165 }
166
167 private InternalState withState(State state) {
168 if (state.equals(this.state)) {
169 return this;
170 } else {
171 return new InternalState(listener, state);
172 }
173 }
174
175 private InternalState withState(UnaryOperator<State> operator) {
176 return withState(operator.apply(state));
177 }
178 }
179
180 protected final Context mContext;
181 protected final Executor mExecutor;
182
183 // we use a lock-free implementation to update state to ensure atomicity between updating the
184 // provider state and setting the listener, so that the state updates a listener sees are
185 // consistent with when the listener was set (a listener should not see any updates that occur
186 // before it was set, and should not miss any updates that occur after it was set).
187 private final AtomicReference<InternalState> mInternalState;
188
189 protected AbstractLocationProvider(Context context, Executor executor) {
190 this(context, executor, Collections.singleton(context.getPackageName()));
191 }
192
193 protected AbstractLocationProvider(Context context, Executor executor,
194 Set<String> packageNames) {
Soonil Nagarkar0d77ea62019-01-31 14:36:56 -0800195 mContext = context;
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800196 mExecutor = executor;
197 mInternalState = new AtomicReference<>(
198 new InternalState(null, State.EMPTY_STATE.withProviderPackageNames(packageNames)));
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700199 }
200
201 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800202 * Sets the listener and returns the state at the moment the listener was set. The listener can
203 * expect to receive all state updates from after this point.
204 */
205 State setListener(@Nullable Listener listener) {
206 return mInternalState.updateAndGet(
207 internalState -> internalState.withListener(listener)).state;
208 }
209
210 /**
211 * Retrieves the state of the provider.
212 */
213 State getState() {
214 return mInternalState.get().state;
215 }
216
217 /**
218 * Sets the state of the provider to the new state.
219 */
220 void setState(State newState) {
221 InternalState oldInternalState = mInternalState.getAndUpdate(
222 internalState -> internalState.withState(newState));
223 if (newState.equals(oldInternalState.state)) {
224 return;
225 }
226
227 // we know that we only updated the state, so the listener for the old state is the same as
228 // the listener for the new state.
229 if (oldInternalState.listener != null) {
230 long identity = Binder.clearCallingIdentity();
231 try {
232 oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
233 } finally {
234 Binder.restoreCallingIdentity(identity);
235 }
236 }
237 }
238
239 private void setState(UnaryOperator<State> operator) {
240 InternalState oldInternalState = mInternalState.getAndUpdate(
241 internalState -> internalState.withState(operator));
242
243 // recreate the new state from our knowledge of the old state - unfortunately may result in
244 // an extra allocation, but oh well...
245 State newState = operator.apply(oldInternalState.state);
246
247 if (newState.equals(oldInternalState.state)) {
248 return;
249 }
250
251 // we know that we only updated the state, so the listener for the old state is the same as
252 // the listener for the new state.
253 if (oldInternalState.listener != null) {
254 long identity = Binder.clearCallingIdentity();
255 try {
256 oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
257 } finally {
258 Binder.restoreCallingIdentity(identity);
259 }
260 }
261 }
262
263 /**
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800264 * The current allowed state of this provider.
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800265 */
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800266 protected boolean isAllowed() {
267 return mInternalState.get().state.allowed;
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800268 }
269
270 /**
271 * The current provider properties of this provider.
272 */
273 @Nullable
274 protected ProviderProperties getProperties() {
275 return mInternalState.get().state.properties;
276 }
277
278 /**
279 * The current package set of this provider.
280 */
281 protected Set<String> getProviderPackages() {
282 return mInternalState.get().state.providerPackageNames;
283 }
284
285 /**
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800286 * Call this method to report a change in provider allowed status.
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700287 */
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800288 protected void setAllowed(boolean allowed) {
289 setState(state -> state.withAllowed(allowed));
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700290 }
291
292 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800293 * Call this method to report a change in provider properties.
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700294 */
295 protected void setProperties(ProviderProperties properties) {
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800296 setState(state -> state.withProperties(properties));
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700297 }
298
Soonil Nagarkard334f7b2019-07-08 16:16:19 -0700299 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800300 * Call this method to report a change in provider packages.
301 */
302 protected void setPackageNames(Set<String> packageNames) {
303 setState(state -> state.withProviderPackageNames(packageNames));
304 }
305
306 /**
307 * Call this method to report a new location.
Soonil Nagarkard334f7b2019-07-08 16:16:19 -0700308 */
309 protected void reportLocation(Location location) {
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800310 Listener listener = mInternalState.get().listener;
311 if (listener != null) {
312 long identity = Binder.clearCallingIdentity();
313 try {
314 listener.onReportLocation(location);
315 } finally {
316 Binder.restoreCallingIdentity(identity);
317 }
Soonil Nagarkar747cbec2019-09-19 14:27:03 -0700318 }
Soonil Nagarkard334f7b2019-07-08 16:16:19 -0700319 }
320
321 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800322 * Call this method to report a new location.
Soonil Nagarkard334f7b2019-07-08 16:16:19 -0700323 */
324 protected void reportLocation(List<Location> locations) {
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800325 Listener listener = mInternalState.get().listener;
326 if (listener != null) {
327 long identity = Binder.clearCallingIdentity();
328 try {
329 listener.onReportLocation(locations);
330 } finally {
331 Binder.restoreCallingIdentity(identity);
332 }
Soonil Nagarkar747cbec2019-09-19 14:27:03 -0700333 }
Soonil Nagarkard334f7b2019-07-08 16:16:19 -0700334 }
335
336 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800337 * Sets a new request and worksource for the provider.
Soonil Nagarkard334f7b2019-07-08 16:16:19 -0700338 */
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800339 public final void setRequest(ProviderRequest request) {
340 // all calls into the provider must be moved onto the provider thread to prevent deadlock
341 mExecutor.execute(() -> onSetRequest(request));
Soonil Nagarkar0d77ea62019-01-31 14:36:56 -0800342 }
343
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700344 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800345 * Always invoked on the provider executor.
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700346 */
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800347 protected abstract void onSetRequest(ProviderRequest request);
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700348
349 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800350 * Sends an extra command to the provider for it to interpret as it likes.
Soonil Nagarkard334f7b2019-07-08 16:16:19 -0700351 */
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800352 public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
353 // all calls into the provider must be moved onto the provider thread to prevent deadlock
354 mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras));
355 }
Soonil Nagarkard334f7b2019-07-08 16:16:19 -0700356
357 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800358 * Always invoked on the provider executor.
359 */
360 protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
361
362 /**
Soonil Nagarkar980ce6a2020-01-23 18:06:31 -0800363 * Requests a provider to enable itself for the given user id.
364 */
365 public final void requestSetAllowed(boolean allowed) {
366 // all calls into the provider must be moved onto the provider thread to prevent deadlock
367 mExecutor.execute(() -> onRequestSetAllowed(allowed));
368 }
369
370 /**
371 * Always invoked on the provider executor.
372 */
373 protected void onRequestSetAllowed(boolean allowed) {}
374
375 /**
Soonil Nagarkar8df02f42020-01-08 13:23:26 -0800376 * Dumps debug or log information. May be invoked from any thread.
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700377 */
378 public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
Soonil Nagarkar1575a042018-10-24 17:54:54 -0700379}