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