1 /* 2 * Copyright (C) 2019 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; 18 19 import static android.location.LocationManager.FUSED_PROVIDER; 20 import static android.location.LocationManager.PASSIVE_PROVIDER; 21 import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS; 22 import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST; 23 import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS; 24 import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST; 25 import static android.provider.Settings.Secure.LOCATION_COARSE_ACCURACY_M; 26 import static android.provider.Settings.Secure.LOCATION_MODE; 27 import static android.provider.Settings.Secure.LOCATION_MODE_OFF; 28 29 import static com.android.server.location.LocationManagerService.D; 30 import static com.android.server.location.LocationManagerService.TAG; 31 32 import android.app.ActivityManager; 33 import android.content.Context; 34 import android.database.ContentObserver; 35 import android.net.Uri; 36 import android.os.Binder; 37 import android.os.Handler; 38 import android.os.UserHandle; 39 import android.provider.Settings; 40 import android.text.TextUtils; 41 import android.util.ArraySet; 42 import android.util.Log; 43 44 import com.android.internal.annotations.GuardedBy; 45 import com.android.internal.util.IndentingPrintWriter; 46 import com.android.internal.util.Preconditions; 47 import com.android.server.SystemConfig; 48 49 import java.io.FileDescriptor; 50 import java.io.PrintWriter; 51 import java.util.Arrays; 52 import java.util.Collections; 53 import java.util.List; 54 import java.util.Set; 55 import java.util.concurrent.CopyOnWriteArrayList; 56 import java.util.function.Supplier; 57 58 /** 59 * Provides accessors and listeners for all location related settings. 60 */ 61 public class SettingsHelper { 62 63 /** 64 * Listener for user-specific settings changes. 65 */ 66 public interface UserSettingChangedListener { 67 /** 68 * Called when setting changes. 69 */ onSettingChanged(int userId)70 void onSettingChanged(int userId); 71 } 72 73 /** 74 * Listener for global settings changes. 75 */ 76 public interface GlobalSettingChangedListener extends UserSettingChangedListener { 77 /** 78 * Called when setting changes. 79 */ onSettingChanged()80 void onSettingChanged(); 81 82 @Override onSettingChanged(int userId)83 default void onSettingChanged(int userId) { 84 onSettingChanged(); 85 } 86 } 87 88 private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist"; 89 private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist"; 90 91 private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000; 92 private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS = 93 30 * 60 * 1000; 94 private static final float DEFAULT_COARSE_LOCATION_ACCURACY_M = 2000.0f; 95 96 private final Context mContext; 97 98 private final IntegerSecureSetting mLocationMode; 99 private final LongGlobalSetting mBackgroundThrottleIntervalMs; 100 private final StringListCachedSecureSetting mLocationPackageBlacklist; 101 private final StringListCachedSecureSetting mLocationPackageWhitelist; 102 private final StringSetCachedGlobalSetting mBackgroundThrottlePackageWhitelist; 103 private final StringSetCachedGlobalSetting mIgnoreSettingsPackageWhitelist; 104 105 // TODO: get rid of handler SettingsHelper(Context context, Handler handler)106 public SettingsHelper(Context context, Handler handler) { 107 mContext = context; 108 109 mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler); 110 mBackgroundThrottleIntervalMs = new LongGlobalSetting(context, 111 LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, handler); 112 mLocationPackageBlacklist = new StringListCachedSecureSetting(context, 113 LOCATION_PACKAGE_BLACKLIST, handler); 114 mLocationPackageWhitelist = new StringListCachedSecureSetting(context, 115 LOCATION_PACKAGE_WHITELIST, handler); 116 mBackgroundThrottlePackageWhitelist = new StringSetCachedGlobalSetting(context, 117 LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, 118 () -> SystemConfig.getInstance().getAllowUnthrottledLocation(), handler); 119 mIgnoreSettingsPackageWhitelist = new StringSetCachedGlobalSetting(context, 120 LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, 121 () -> SystemConfig.getInstance().getAllowIgnoreLocationSettings(), handler); 122 } 123 124 /** Called when system is ready. */ onSystemReady()125 public void onSystemReady() { 126 mLocationMode.register(); 127 mBackgroundThrottleIntervalMs.register(); 128 mLocationPackageBlacklist.register(); 129 mLocationPackageWhitelist.register(); 130 mBackgroundThrottlePackageWhitelist.register(); 131 mIgnoreSettingsPackageWhitelist.register(); 132 } 133 134 /** 135 * Retrieve if location is enabled or not. 136 */ isLocationEnabled(int userId)137 public boolean isLocationEnabled(int userId) { 138 return mLocationMode.getValueForUser(LOCATION_MODE_OFF, userId) != LOCATION_MODE_OFF; 139 } 140 141 /** 142 * Set location enabled for a user. 143 */ setLocationEnabled(boolean enabled, int userId)144 public void setLocationEnabled(boolean enabled, int userId) { 145 long identity = Binder.clearCallingIdentity(); 146 try { 147 Settings.Secure.putIntForUser( 148 mContext.getContentResolver(), 149 Settings.Secure.LOCATION_MODE, 150 enabled 151 ? Settings.Secure.LOCATION_MODE_ON 152 : Settings.Secure.LOCATION_MODE_OFF, 153 userId); 154 } finally { 155 Binder.restoreCallingIdentity(identity); 156 } 157 } 158 159 /** 160 * Add a listener for changes to the location enabled setting. Callbacks occur on an unspecified 161 * thread. 162 */ addOnLocationEnabledChangedListener(UserSettingChangedListener listener)163 public void addOnLocationEnabledChangedListener(UserSettingChangedListener listener) { 164 mLocationMode.addListener(listener); 165 } 166 167 /** 168 * Remove a listener for changes to the location enabled setting. 169 */ removeOnLocationEnabledChangedListener(UserSettingChangedListener listener)170 public void removeOnLocationEnabledChangedListener(UserSettingChangedListener listener) { 171 mLocationMode.removeListener(listener); 172 } 173 174 /** 175 * Retrieve the background throttle interval. 176 */ getBackgroundThrottleIntervalMs()177 public long getBackgroundThrottleIntervalMs() { 178 return mBackgroundThrottleIntervalMs.getValue(DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS); 179 } 180 181 /** 182 * Add a listener for changes to the background throttle interval. Callbacks occur on an 183 * unspecified thread. 184 */ addOnBackgroundThrottleIntervalChangedListener( GlobalSettingChangedListener listener)185 public void addOnBackgroundThrottleIntervalChangedListener( 186 GlobalSettingChangedListener listener) { 187 mBackgroundThrottleIntervalMs.addListener(listener); 188 } 189 190 /** 191 * Remove a listener for changes to the background throttle interval. 192 */ removeOnBackgroundThrottleIntervalChangedListener( GlobalSettingChangedListener listener)193 public void removeOnBackgroundThrottleIntervalChangedListener( 194 GlobalSettingChangedListener listener) { 195 mBackgroundThrottleIntervalMs.removeListener(listener); 196 } 197 198 /** 199 * Check if the given package is blacklisted for location access. 200 */ isLocationPackageBlacklisted(int userId, String packageName)201 public boolean isLocationPackageBlacklisted(int userId, String packageName) { 202 List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId); 203 if (locationPackageBlacklist.isEmpty()) { 204 return false; 205 } 206 207 List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(userId); 208 for (String locationWhitelistPackage : locationPackageWhitelist) { 209 if (packageName.startsWith(locationWhitelistPackage)) { 210 return false; 211 } 212 } 213 214 for (String locationBlacklistPackage : locationPackageBlacklist) { 215 if (packageName.startsWith(locationBlacklistPackage)) { 216 return true; 217 } 218 } 219 220 return false; 221 } 222 223 /** 224 * Retrieve the background throttle package whitelist. 225 */ getBackgroundThrottlePackageWhitelist()226 public Set<String> getBackgroundThrottlePackageWhitelist() { 227 return mBackgroundThrottlePackageWhitelist.getValue(); 228 } 229 230 /** 231 * Add a listener for changes to the background throttle package whitelist. Callbacks occur on 232 * an unspecified thread. 233 */ addOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener)234 public void addOnBackgroundThrottlePackageWhitelistChangedListener( 235 GlobalSettingChangedListener listener) { 236 mBackgroundThrottlePackageWhitelist.addListener(listener); 237 } 238 239 /** 240 * Remove a listener for changes to the background throttle package whitelist. 241 */ removeOnBackgroundThrottlePackageWhitelistChangedListener( GlobalSettingChangedListener listener)242 public void removeOnBackgroundThrottlePackageWhitelistChangedListener( 243 GlobalSettingChangedListener listener) { 244 mBackgroundThrottlePackageWhitelist.removeListener(listener); 245 } 246 247 /** 248 * Retrieve the ignore settings package whitelist. 249 */ getIgnoreSettingsPackageWhitelist()250 public Set<String> getIgnoreSettingsPackageWhitelist() { 251 return mIgnoreSettingsPackageWhitelist.getValue(); 252 } 253 254 /** 255 * Add a listener for changes to the ignore settings package whitelist. Callbacks occur on an 256 * unspecified thread. 257 */ addOnIgnoreSettingsPackageWhitelistChangedListener( GlobalSettingChangedListener listener)258 public void addOnIgnoreSettingsPackageWhitelistChangedListener( 259 GlobalSettingChangedListener listener) { 260 mIgnoreSettingsPackageWhitelist.addListener(listener); 261 } 262 263 /** 264 * Remove a listener for changes to the ignore settings package whitelist. 265 */ removeOnIgnoreSettingsPackageWhitelistChangedListener( GlobalSettingChangedListener listener)266 public void removeOnIgnoreSettingsPackageWhitelistChangedListener( 267 GlobalSettingChangedListener listener) { 268 mIgnoreSettingsPackageWhitelist.removeListener(listener); 269 } 270 271 /** 272 * Retrieve the background throttling proximity alert interval. 273 */ getBackgroundThrottleProximityAlertIntervalMs()274 public long getBackgroundThrottleProximityAlertIntervalMs() { 275 long identity = Binder.clearCallingIdentity(); 276 try { 277 return Settings.Global.getLong(mContext.getContentResolver(), 278 LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, 279 DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS); 280 } finally { 281 Binder.restoreCallingIdentity(identity); 282 } 283 } 284 285 /** 286 * Retrieve the accuracy for coarsening location, ie, the grid size used for snap-to-grid 287 * coarsening. 288 */ getCoarseLocationAccuracyM()289 public float getCoarseLocationAccuracyM() { 290 long identity = Binder.clearCallingIdentity(); 291 try { 292 return Settings.Secure.getFloat( 293 mContext.getContentResolver(), 294 LOCATION_COARSE_ACCURACY_M, 295 DEFAULT_COARSE_LOCATION_ACCURACY_M); 296 } finally { 297 Binder.restoreCallingIdentity(identity); 298 } 299 } 300 301 /** 302 * Set a value for the deprecated LOCATION_PROVIDERS_ALLOWED setting. This is used purely for 303 * backwards compatibility for old clients, and may be removed in the future. 304 */ setLocationProviderAllowed(String provider, boolean enabled, int userId)305 public void setLocationProviderAllowed(String provider, boolean enabled, int userId) { 306 // fused and passive provider never get public updates for legacy reasons 307 if (FUSED_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) { 308 return; 309 } 310 311 long identity = Binder.clearCallingIdentity(); 312 try { 313 // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility 314 Settings.Secure.putStringForUser( 315 mContext.getContentResolver(), 316 Settings.Secure.LOCATION_PROVIDERS_ALLOWED, 317 (enabled ? "+" : "-") + provider, 318 userId); 319 } finally { 320 Binder.restoreCallingIdentity(identity); 321 } 322 } 323 324 /** 325 * Dump info for debugging. 326 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)327 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 328 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 329 int userId = ActivityManager.getCurrentUser(); 330 331 ipw.print("Location Enabled: "); 332 ipw.println(isLocationEnabled(userId)); 333 334 List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId); 335 if (!locationPackageBlacklist.isEmpty()) { 336 ipw.println("Location Blacklisted Packages:"); 337 ipw.increaseIndent(); 338 for (String packageName : locationPackageBlacklist) { 339 ipw.println(packageName); 340 } 341 ipw.decreaseIndent(); 342 343 List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser( 344 userId); 345 if (!locationPackageWhitelist.isEmpty()) { 346 ipw.println("Location Whitelisted Packages:"); 347 ipw.increaseIndent(); 348 for (String packageName : locationPackageWhitelist) { 349 ipw.println(packageName); 350 } 351 ipw.decreaseIndent(); 352 } 353 } 354 355 Set<String> backgroundThrottlePackageWhitelist = 356 mBackgroundThrottlePackageWhitelist.getValue(); 357 if (!backgroundThrottlePackageWhitelist.isEmpty()) { 358 ipw.println("Throttling Whitelisted Packages:"); 359 ipw.increaseIndent(); 360 for (String packageName : backgroundThrottlePackageWhitelist) { 361 ipw.println(packageName); 362 } 363 ipw.decreaseIndent(); 364 } 365 366 Set<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue(); 367 if (!ignoreSettingsPackageWhitelist.isEmpty()) { 368 ipw.println("Bypass Whitelisted Packages:"); 369 ipw.increaseIndent(); 370 for (String packageName : ignoreSettingsPackageWhitelist) { 371 ipw.println(packageName); 372 } 373 ipw.decreaseIndent(); 374 } 375 } 376 377 private abstract static class ObservingSetting extends ContentObserver { 378 379 private final CopyOnWriteArrayList<UserSettingChangedListener> mListeners; 380 381 @GuardedBy("this") 382 private boolean mRegistered; 383 ObservingSetting(Handler handler)384 private ObservingSetting(Handler handler) { 385 super(handler); 386 mListeners = new CopyOnWriteArrayList<>(); 387 } 388 isRegistered()389 protected synchronized boolean isRegistered() { 390 return mRegistered; 391 } 392 register(Context context, Uri uri)393 protected synchronized void register(Context context, Uri uri) { 394 if (mRegistered) { 395 return; 396 } 397 398 context.getContentResolver().registerContentObserver( 399 uri, false, this, UserHandle.USER_ALL); 400 mRegistered = true; 401 } 402 addListener(UserSettingChangedListener listener)403 public void addListener(UserSettingChangedListener listener) { 404 mListeners.add(listener); 405 } 406 removeListener(UserSettingChangedListener listener)407 public void removeListener(UserSettingChangedListener listener) { 408 mListeners.remove(listener); 409 } 410 411 @Override onChange(boolean selfChange, Uri uri, int userId)412 public void onChange(boolean selfChange, Uri uri, int userId) { 413 if (D) { 414 Log.d(TAG, "location setting changed [u" + userId + "]: " + uri); 415 } 416 417 for (UserSettingChangedListener listener : mListeners) { 418 listener.onSettingChanged(userId); 419 } 420 } 421 } 422 423 private static class IntegerSecureSetting extends ObservingSetting { 424 425 private final Context mContext; 426 private final String mSettingName; 427 IntegerSecureSetting(Context context, String settingName, Handler handler)428 private IntegerSecureSetting(Context context, String settingName, Handler handler) { 429 super(handler); 430 mContext = context; 431 mSettingName = settingName; 432 } 433 register()434 private void register() { 435 register(mContext, Settings.Secure.getUriFor(mSettingName)); 436 } 437 getValueForUser(int defaultValue, int userId)438 public int getValueForUser(int defaultValue, int userId) { 439 long identity = Binder.clearCallingIdentity(); 440 try { 441 return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName, 442 defaultValue, userId); 443 } finally { 444 Binder.restoreCallingIdentity(identity); 445 } 446 } 447 } 448 449 private static class StringListCachedSecureSetting extends ObservingSetting { 450 451 private final Context mContext; 452 private final String mSettingName; 453 454 @GuardedBy("this") 455 private int mCachedUserId; 456 @GuardedBy("this") 457 private List<String> mCachedValue; 458 StringListCachedSecureSetting(Context context, String settingName, Handler handler)459 private StringListCachedSecureSetting(Context context, String settingName, 460 Handler handler) { 461 super(handler); 462 mContext = context; 463 mSettingName = settingName; 464 465 mCachedUserId = UserHandle.USER_NULL; 466 } 467 register()468 public void register() { 469 register(mContext, Settings.Secure.getUriFor(mSettingName)); 470 } 471 getValueForUser(int userId)472 public synchronized List<String> getValueForUser(int userId) { 473 Preconditions.checkArgument(userId != UserHandle.USER_NULL); 474 475 List<String> value = mCachedValue; 476 if (userId != mCachedUserId) { 477 long identity = Binder.clearCallingIdentity(); 478 try { 479 String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(), 480 mSettingName, userId); 481 if (TextUtils.isEmpty(setting)) { 482 value = Collections.emptyList(); 483 } else { 484 value = Arrays.asList(setting.split(",")); 485 } 486 } finally { 487 Binder.restoreCallingIdentity(identity); 488 } 489 490 if (isRegistered()) { 491 mCachedUserId = userId; 492 mCachedValue = value; 493 } 494 } 495 496 return value; 497 } 498 invalidateForUser(int userId)499 public synchronized void invalidateForUser(int userId) { 500 if (mCachedUserId == userId) { 501 mCachedUserId = UserHandle.USER_NULL; 502 mCachedValue = null; 503 } 504 } 505 506 @Override onChange(boolean selfChange, Uri uri, int userId)507 public void onChange(boolean selfChange, Uri uri, int userId) { 508 invalidateForUser(userId); 509 super.onChange(selfChange, uri, userId); 510 } 511 } 512 513 private static class LongGlobalSetting extends ObservingSetting { 514 515 private final Context mContext; 516 private final String mSettingName; 517 LongGlobalSetting(Context context, String settingName, Handler handler)518 private LongGlobalSetting(Context context, String settingName, Handler handler) { 519 super(handler); 520 mContext = context; 521 mSettingName = settingName; 522 } 523 register()524 public void register() { 525 register(mContext, Settings.Global.getUriFor(mSettingName)); 526 } 527 getValue(long defaultValue)528 public long getValue(long defaultValue) { 529 long identity = Binder.clearCallingIdentity(); 530 try { 531 return Settings.Global.getLong(mContext.getContentResolver(), mSettingName, 532 defaultValue); 533 } finally { 534 Binder.restoreCallingIdentity(identity); 535 } 536 } 537 } 538 539 private static class StringSetCachedGlobalSetting extends ObservingSetting { 540 541 private final Context mContext; 542 private final String mSettingName; 543 private final Supplier<ArraySet<String>> mBaseValuesSupplier; 544 545 @GuardedBy("this") 546 private boolean mValid; 547 @GuardedBy("this") 548 private ArraySet<String> mCachedValue; 549 StringSetCachedGlobalSetting(Context context, String settingName, Supplier<ArraySet<String>> baseValuesSupplier, Handler handler)550 private StringSetCachedGlobalSetting(Context context, String settingName, 551 Supplier<ArraySet<String>> baseValuesSupplier, Handler handler) { 552 super(handler); 553 mContext = context; 554 mSettingName = settingName; 555 mBaseValuesSupplier = baseValuesSupplier; 556 557 mValid = false; 558 } 559 register()560 public void register() { 561 register(mContext, Settings.Global.getUriFor(mSettingName)); 562 } 563 getValue()564 public synchronized Set<String> getValue() { 565 ArraySet<String> value = mCachedValue; 566 if (!mValid) { 567 long identity = Binder.clearCallingIdentity(); 568 try { 569 value = new ArraySet<>(mBaseValuesSupplier.get()); 570 String setting = Settings.Global.getString(mContext.getContentResolver(), 571 mSettingName); 572 if (!TextUtils.isEmpty(setting)) { 573 value.addAll(Arrays.asList(setting.split(","))); 574 } 575 } finally { 576 Binder.restoreCallingIdentity(identity); 577 } 578 579 if (isRegistered()) { 580 mValid = true; 581 mCachedValue = value; 582 } 583 } 584 585 return value; 586 } 587 invalidate()588 public synchronized void invalidate() { 589 mValid = false; 590 mCachedValue = null; 591 } 592 593 @Override onChange(boolean selfChange, Uri uri, int userId)594 public void onChange(boolean selfChange, Uri uri, int userId) { 595 invalidate(); 596 super.onChange(selfChange, uri, userId); 597 } 598 } 599 } 600