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 android.location.provider; 18 19 import static android.location.Location.EXTRA_NO_GPS_LOCATION; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SuppressLint; 24 import android.annotation.SystemApi; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.location.Location; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.Looper; 32 import android.os.RemoteException; 33 import android.util.Log; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Objects; 38 39 /** 40 * Base class for location providers outside the system server. 41 * 42 * Location providers should be wrapped in a non-exported service which returns the result of 43 * {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The 44 * service should not be exported so that components other than the system server cannot bind to it. 45 * Alternatively, the service may be guarded by a permission that only system server can obtain. The 46 * service may specify metadata on its capabilities: 47 * 48 * <ul> 49 * <li> 50 * "serviceVersion": An integer version code to help tie break if multiple services are 51 * capable of implementing the same location provider. All else equal, the service with the 52 * highest version code will be chosen. Assumed to be 0 if not specified. 53 * </li> 54 * <li> 55 * "serviceIsMultiuser": A boolean property, indicating if the service wishes to take 56 * responsibility for handling changes to the current user on the device. If true, the 57 * service will always be bound from the system user. If false, the service will always be 58 * bound from the current user. If the current user changes, the old binding will be 59 * released, and a new binding established under the new user. Assumed to be false if not 60 * specified. 61 * </li> 62 * </ul> 63 * 64 * <p>The service should have an intent filter in place for the location provider it wishes to 65 * implements. Defaults for some providers are specified as constants in this class. 66 * 67 * <p>Location providers are identified by their UID / package name / attribution tag. Based on this 68 * identity, location providers may be given some special privileges (such as making special 69 * requests to other location providers). 70 * 71 * @hide 72 */ 73 @SystemApi 74 public abstract class LocationProviderBase { 75 76 /** 77 * Callback to be invoked when a flush operation is complete and all flushed locations have been 78 * reported. 79 */ 80 public interface OnFlushCompleteCallback { 81 82 /** 83 * Should be invoked once the flush is complete. 84 */ onFlushComplete()85 void onFlushComplete(); 86 } 87 88 /** 89 * The action the wrapping service should have in its intent filter to implement the 90 * {@link android.location.LocationManager#NETWORK_PROVIDER}. 91 */ 92 @SuppressLint("ActionValue") 93 public static final String ACTION_NETWORK_PROVIDER = 94 "com.android.location.service.v3.NetworkLocationProvider"; 95 96 /** 97 * The action the wrapping service should have in its intent filter to implement the 98 * {@link android.location.LocationManager#FUSED_PROVIDER}. 99 */ 100 @SuppressLint("ActionValue") 101 public static final String ACTION_FUSED_PROVIDER = 102 "com.android.location.service.FusedLocationProvider"; 103 104 final String mTag; 105 final @Nullable String mAttributionTag; 106 final IBinder mBinder; 107 108 // write locked on mBinder, read lock is optional depending on atomicity requirements 109 volatile @Nullable ILocationProviderManager mManager; 110 volatile ProviderProperties mProperties; 111 volatile boolean mAllowed; 112 LocationProviderBase(@onNull Context context, @NonNull String tag, @NonNull ProviderProperties properties)113 public LocationProviderBase(@NonNull Context context, @NonNull String tag, 114 @NonNull ProviderProperties properties) { 115 mTag = tag; 116 mAttributionTag = context.getAttributionTag(); 117 mBinder = new Service(); 118 119 mManager = null; 120 mProperties = Objects.requireNonNull(properties); 121 mAllowed = true; 122 } 123 124 /** 125 * Returns the IBinder instance that should be returned from the 126 * {@link android.app.Service#onBind(Intent)} method of the wrapping service. 127 */ getBinder()128 public final @Nullable IBinder getBinder() { 129 return mBinder; 130 } 131 132 /** 133 * Sets whether this provider is currently allowed or not. Note that this is specific to the 134 * provider only, and is unrelated to global location settings. This is a hint to the location 135 * manager that this provider will be unable to fulfill incoming requests. Setting a provider 136 * as not allowed will result in the provider being disabled. Setting a provider as allowed 137 * means that the provider may be in either the enabled or disabled state. 138 * 139 * <p>Some guidelines: providers should set their own allowed/disallowed status based only on 140 * state "owned" by that provider. For instance, providers should not take into account the 141 * state of the location master setting when setting themselves allowed or disallowed, as this 142 * state is not owned by a particular provider. If a provider requires some additional user 143 * consent that is particular to the provider, this should be use to set the allowed/disallowed 144 * state. If the provider proxies to another provider, the child provider's allowed/disallowed 145 * state should be taken into account in the parent's allowed state. For most providers, it is 146 * expected that they will be always allowed. 147 */ setAllowed(boolean allowed)148 public void setAllowed(boolean allowed) { 149 synchronized (mBinder) { 150 if (mAllowed == allowed) { 151 return; 152 } 153 154 mAllowed = allowed; 155 } 156 157 ILocationProviderManager manager = mManager; 158 if (manager != null) { 159 try { 160 manager.onSetAllowed(mAllowed); 161 } catch (RemoteException e) { 162 throw e.rethrowFromSystemServer(); 163 } catch (RuntimeException e) { 164 Log.w(mTag, e); 165 } 166 } 167 } 168 169 /** 170 * Returns true if this provider is allowed. Providers start as allowed on construction. 171 */ isAllowed()172 public boolean isAllowed() { 173 return mAllowed; 174 } 175 176 /** 177 * Sets the provider properties that may be queried by clients. Generally speaking, providers 178 * should try to avoid changing their properties after construction. 179 */ setProperties(@onNull ProviderProperties properties)180 public void setProperties(@NonNull ProviderProperties properties) { 181 synchronized (mBinder) { 182 mProperties = Objects.requireNonNull(properties); 183 } 184 185 ILocationProviderManager manager = mManager; 186 if (manager != null) { 187 try { 188 manager.onSetProperties(mProperties); 189 } catch (RemoteException e) { 190 throw e.rethrowFromSystemServer(); 191 } catch (RuntimeException e) { 192 Log.w(mTag, e); 193 } 194 } 195 } 196 197 /** 198 * Returns the currently set properties of the provider. 199 */ getProperties()200 public @NonNull ProviderProperties getProperties() { 201 return mProperties; 202 } 203 204 /** 205 * Reports a new location from this provider. 206 */ reportLocation(@onNull Location location)207 public void reportLocation(@NonNull Location location) { 208 ILocationProviderManager manager = mManager; 209 if (manager != null) { 210 try { 211 manager.onReportLocation(stripExtras(location)); 212 } catch (RemoteException e) { 213 throw e.rethrowFromSystemServer(); 214 } catch (RuntimeException e) { 215 Log.w(mTag, e); 216 } 217 } 218 } 219 220 /** 221 * Reports a new batch of locations from this provider. Locations must be ordered in the list 222 * from earliest first to latest last. 223 */ reportLocations(@onNull List<Location> locations)224 public void reportLocations(@NonNull List<Location> locations) { 225 ILocationProviderManager manager = mManager; 226 if (manager != null) { 227 228 229 try { 230 manager.onReportLocations(stripExtras(locations)); 231 } catch (RemoteException e) { 232 throw e.rethrowFromSystemServer(); 233 } catch (RuntimeException e) { 234 Log.w(mTag, e); 235 } 236 } 237 } 238 239 /** 240 * Set the current {@link ProviderRequest} for this provider. Each call to this method overrides 241 * any prior ProviderRequests. The provider should immediately attempt to provide locations (or 242 * not provide locations) according to the parameters of the provider request. 243 */ onSetRequest(@onNull ProviderRequest request)244 public abstract void onSetRequest(@NonNull ProviderRequest request); 245 246 /** 247 * Requests a flush of any pending batched locations. The callback must always be invoked once 248 * per invocation, and should be invoked after {@link #reportLocation(Location)} or 249 * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may 250 * be invoked immediately if no locations are flushed. 251 */ onFlush(@onNull OnFlushCompleteCallback callback)252 public abstract void onFlush(@NonNull OnFlushCompleteCallback callback); 253 254 /** 255 * Implements optional custom commands. 256 */ onSendExtraCommand(@onNull String command, @SuppressLint("NullableCollection") @Nullable Bundle extras)257 public abstract void onSendExtraCommand(@NonNull String command, 258 @SuppressLint("NullableCollection") 259 @Nullable Bundle extras); 260 stripExtras(Location location)261 private static Location stripExtras(Location location) { 262 Bundle extras = location.getExtras(); 263 if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION) 264 || extras.containsKey("indoorProbability") 265 || extras.containsKey("coarseLocation"))) { 266 location = new Location(location); 267 extras = location.getExtras(); 268 extras.remove(EXTRA_NO_GPS_LOCATION); 269 extras.remove("indoorProbability"); 270 extras.remove("coarseLocation"); 271 if (extras.isEmpty()) { 272 location.setExtras(null); 273 } 274 } 275 return location; 276 } 277 stripExtras(List<Location> locations)278 private static List<Location> stripExtras(List<Location> locations) { 279 List<Location> mapped = locations; 280 final int size = locations.size(); 281 int i = 0; 282 for (Location location : locations) { 283 Location newLocation = stripExtras(location); 284 if (mapped != locations) { 285 mapped.add(newLocation); 286 } else if (newLocation != location) { 287 mapped = new ArrayList<>(size); 288 int j = 0; 289 for (Location copiedLocation : locations) { 290 if (j >= i) { 291 break; 292 } 293 mapped.add(copiedLocation); 294 j++; 295 } 296 mapped.add(newLocation); 297 } 298 i++; 299 } 300 301 return mapped; 302 } 303 304 private final class Service extends ILocationProvider.Stub { 305 Service()306 Service() {} 307 308 @Override setLocationProviderManager(ILocationProviderManager manager)309 public void setLocationProviderManager(ILocationProviderManager manager) { 310 synchronized (mBinder) { 311 try { 312 manager.onInitialize(mAllowed, mProperties, mAttributionTag); 313 } catch (RemoteException | RuntimeException e) { 314 Log.w(mTag, e); 315 } 316 317 mManager = manager; 318 } 319 } 320 321 @Override setRequest(ProviderRequest request)322 public void setRequest(ProviderRequest request) { 323 try { 324 onSetRequest(request); 325 } catch (RuntimeException e) { 326 // exceptions on one-way binder threads are dropped - move to a different thread 327 Log.w(mTag, e); 328 new Handler(Looper.getMainLooper()).post(() -> { 329 throw new AssertionError(e); 330 }); 331 } 332 } 333 334 @Override flush()335 public void flush() { 336 try { 337 onFlush(this::onFlushComplete); 338 } catch (RuntimeException e) { 339 // exceptions on one-way binder threads are dropped - move to a different thread 340 Log.w(mTag, e); 341 new Handler(Looper.getMainLooper()).post(() -> { 342 throw new AssertionError(e); 343 }); 344 } 345 } 346 onFlushComplete()347 private void onFlushComplete() { 348 ILocationProviderManager manager = mManager; 349 if (manager != null) { 350 try { 351 manager.onFlushComplete(); 352 } catch (RemoteException | RuntimeException e) { 353 Log.w(mTag, e); 354 } 355 } 356 } 357 358 @Override sendExtraCommand(String command, Bundle extras)359 public void sendExtraCommand(String command, Bundle extras) { 360 try { 361 onSendExtraCommand(command, extras); 362 } catch (RuntimeException e) { 363 // exceptions on one-way binder threads are dropped - move to a different thread 364 Log.w(mTag, e); 365 new Handler(Looper.getMainLooper()).post(() -> { 366 throw new AssertionError(e); 367 }); 368 } 369 } 370 } 371 } 372