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