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 /** 105 * The action the wrapping service should have in its intent filter to implement the 106 * {@link android.location.LocationManager#GPS_PROVIDER}. 107 */ 108 public static final String ACTION_GNSS_PROVIDER = 109 "android.location.provider.action.GNSS_PROVIDER"; 110 111 final String mTag; 112 final @Nullable String mAttributionTag; 113 final IBinder mBinder; 114 115 // write locked on mBinder, read lock is optional depending on atomicity requirements 116 volatile @Nullable ILocationProviderManager mManager; 117 volatile ProviderProperties mProperties; 118 volatile boolean mAllowed; 119 LocationProviderBase(@onNull Context context, @NonNull String tag, @NonNull ProviderProperties properties)120 public LocationProviderBase(@NonNull Context context, @NonNull String tag, 121 @NonNull ProviderProperties properties) { 122 mTag = tag; 123 mAttributionTag = context.getAttributionTag(); 124 mBinder = new Service(); 125 126 mManager = null; 127 mProperties = Objects.requireNonNull(properties); 128 mAllowed = true; 129 } 130 131 /** 132 * Returns the IBinder instance that should be returned from the 133 * {@link android.app.Service#onBind(Intent)} method of the wrapping service. 134 */ getBinder()135 public final @Nullable IBinder getBinder() { 136 return mBinder; 137 } 138 139 /** 140 * Sets whether this provider is currently allowed or not. Note that this is specific to the 141 * provider only, and is unrelated to global location settings. This is a hint to the location 142 * manager that this provider will be unable to fulfill incoming requests. Setting a provider 143 * as not allowed will result in the provider being disabled. Setting a provider as allowed 144 * means that the provider may be in either the enabled or disabled state. 145 * 146 * <p>Some guidelines: providers should set their own allowed/disallowed status based only on 147 * state "owned" by that provider. For instance, providers should not take into account the 148 * state of the location master setting when setting themselves allowed or disallowed, as this 149 * state is not owned by a particular provider. If a provider requires some additional user 150 * consent that is particular to the provider, this should be use to set the allowed/disallowed 151 * state. If the provider proxies to another provider, the child provider's allowed/disallowed 152 * state should be taken into account in the parent's allowed state. For most providers, it is 153 * expected that they will be always allowed. 154 */ setAllowed(boolean allowed)155 public void setAllowed(boolean allowed) { 156 synchronized (mBinder) { 157 if (mAllowed == allowed) { 158 return; 159 } 160 161 mAllowed = allowed; 162 } 163 164 ILocationProviderManager manager = mManager; 165 if (manager != null) { 166 try { 167 manager.onSetAllowed(mAllowed); 168 } catch (RemoteException e) { 169 throw e.rethrowFromSystemServer(); 170 } catch (RuntimeException e) { 171 Log.w(mTag, e); 172 } 173 } 174 } 175 176 /** 177 * Returns true if this provider is allowed. Providers start as allowed on construction. 178 */ isAllowed()179 public boolean isAllowed() { 180 return mAllowed; 181 } 182 183 /** 184 * Sets the provider properties that may be queried by clients. Generally speaking, providers 185 * should try to avoid changing their properties after construction. 186 */ setProperties(@onNull ProviderProperties properties)187 public void setProperties(@NonNull ProviderProperties properties) { 188 synchronized (mBinder) { 189 mProperties = Objects.requireNonNull(properties); 190 } 191 192 ILocationProviderManager manager = mManager; 193 if (manager != null) { 194 try { 195 manager.onSetProperties(mProperties); 196 } catch (RemoteException e) { 197 throw e.rethrowFromSystemServer(); 198 } catch (RuntimeException e) { 199 Log.w(mTag, e); 200 } 201 } 202 } 203 204 /** 205 * Returns the currently set properties of the provider. 206 */ getProperties()207 public @NonNull ProviderProperties getProperties() { 208 return mProperties; 209 } 210 211 /** 212 * Reports a new location from this provider. 213 */ reportLocation(@onNull Location location)214 public void reportLocation(@NonNull Location location) { 215 ILocationProviderManager manager = mManager; 216 if (manager != null) { 217 try { 218 manager.onReportLocation(stripExtras(location)); 219 } catch (RemoteException e) { 220 throw e.rethrowFromSystemServer(); 221 } catch (RuntimeException e) { 222 Log.w(mTag, e); 223 } 224 } 225 } 226 227 /** 228 * Reports a new batch of locations from this provider. Locations must be ordered in the list 229 * from earliest first to latest last. 230 */ reportLocations(@onNull List<Location> locations)231 public void reportLocations(@NonNull List<Location> locations) { 232 ILocationProviderManager manager = mManager; 233 if (manager != null) { 234 235 236 try { 237 manager.onReportLocations(stripExtras(locations)); 238 } catch (RemoteException e) { 239 throw e.rethrowFromSystemServer(); 240 } catch (RuntimeException e) { 241 Log.w(mTag, e); 242 } 243 } 244 } 245 246 /** 247 * Set the current {@link ProviderRequest} for this provider. Each call to this method overrides 248 * any prior ProviderRequests. The provider should immediately attempt to provide locations (or 249 * not provide locations) according to the parameters of the provider request. 250 */ onSetRequest(@onNull ProviderRequest request)251 public abstract void onSetRequest(@NonNull ProviderRequest request); 252 253 /** 254 * Requests a flush of any pending batched locations. The callback must always be invoked once 255 * per invocation, and should be invoked after {@link #reportLocation(Location)} or 256 * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may 257 * be invoked immediately if no locations are flushed. 258 */ onFlush(@onNull OnFlushCompleteCallback callback)259 public abstract void onFlush(@NonNull OnFlushCompleteCallback callback); 260 261 /** 262 * Implements optional custom commands. 263 */ onSendExtraCommand(@onNull String command, @SuppressLint("NullableCollection") @Nullable Bundle extras)264 public abstract void onSendExtraCommand(@NonNull String command, 265 @SuppressLint("NullableCollection") 266 @Nullable Bundle extras); 267 stripExtras(Location location)268 private static Location stripExtras(Location location) { 269 Bundle extras = location.getExtras(); 270 if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION) 271 || extras.containsKey("indoorProbability") 272 || extras.containsKey("coarseLocation"))) { 273 location = new Location(location); 274 extras = location.getExtras(); 275 extras.remove(EXTRA_NO_GPS_LOCATION); 276 extras.remove("indoorProbability"); 277 extras.remove("coarseLocation"); 278 if (extras.isEmpty()) { 279 location.setExtras(null); 280 } 281 } 282 return location; 283 } 284 stripExtras(List<Location> locations)285 private static List<Location> stripExtras(List<Location> locations) { 286 List<Location> mapped = locations; 287 final int size = locations.size(); 288 int i = 0; 289 for (Location location : locations) { 290 Location newLocation = stripExtras(location); 291 if (mapped != locations) { 292 mapped.add(newLocation); 293 } else if (newLocation != location) { 294 mapped = new ArrayList<>(size); 295 int j = 0; 296 for (Location copiedLocation : locations) { 297 if (j >= i) { 298 break; 299 } 300 mapped.add(copiedLocation); 301 j++; 302 } 303 mapped.add(newLocation); 304 } 305 i++; 306 } 307 308 return mapped; 309 } 310 311 private final class Service extends ILocationProvider.Stub { 312 Service()313 Service() {} 314 315 @Override setLocationProviderManager(ILocationProviderManager manager)316 public void setLocationProviderManager(ILocationProviderManager manager) { 317 synchronized (mBinder) { 318 try { 319 manager.onInitialize(mAllowed, mProperties, mAttributionTag); 320 } catch (RemoteException | RuntimeException e) { 321 Log.w(mTag, e); 322 } 323 324 mManager = manager; 325 } 326 } 327 328 @Override setRequest(ProviderRequest request)329 public void setRequest(ProviderRequest request) { 330 try { 331 onSetRequest(request); 332 } catch (RuntimeException e) { 333 // exceptions on one-way binder threads are dropped - move to a different thread 334 Log.w(mTag, e); 335 new Handler(Looper.getMainLooper()).post(() -> { 336 throw new AssertionError(e); 337 }); 338 } 339 } 340 341 @Override flush()342 public void flush() { 343 try { 344 onFlush(this::onFlushComplete); 345 } catch (RuntimeException e) { 346 // exceptions on one-way binder threads are dropped - move to a different thread 347 Log.w(mTag, e); 348 new Handler(Looper.getMainLooper()).post(() -> { 349 throw new AssertionError(e); 350 }); 351 } 352 } 353 onFlushComplete()354 private void onFlushComplete() { 355 ILocationProviderManager manager = mManager; 356 if (manager != null) { 357 try { 358 manager.onFlushComplete(); 359 } catch (RemoteException | RuntimeException e) { 360 Log.w(mTag, e); 361 } 362 } 363 } 364 365 @Override sendExtraCommand(String command, Bundle extras)366 public void sendExtraCommand(String command, Bundle extras) { 367 try { 368 onSendExtraCommand(command, extras); 369 } catch (RuntimeException e) { 370 // exceptions on one-way binder threads are dropped - move to a different thread 371 Log.w(mTag, e); 372 new Handler(Looper.getMainLooper()).post(() -> { 373 throw new AssertionError(e); 374 }); 375 } 376 } 377 } 378 } 379