Merge "Pull out Settings usage into utility"
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 39f039a..593faf1 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -43,7 +43,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
 import android.content.res.Resources;
-import android.database.ContentObserver;
 import android.hardware.location.ActivityRecognitionHardware;
 import android.location.Address;
 import android.location.Criteria;
@@ -79,7 +78,6 @@
 import android.provider.Settings;
 import android.stats.location.LocationStatsEnums;
 import android.text.TextUtils;
-import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
@@ -99,12 +97,12 @@
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
 import com.android.server.location.GeofenceProxy;
-import com.android.server.location.LocationBlacklist;
 import com.android.server.location.LocationFudger;
 import com.android.server.location.LocationProviderProxy;
 import com.android.server.location.LocationRequestStatistics;
 import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
 import com.android.server.location.LocationRequestStatistics.PackageStatistics;
+import com.android.server.location.LocationSettingsStore;
 import com.android.server.location.MockProvider;
 import com.android.server.location.PassiveProvider;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -183,13 +181,6 @@
     private static final int FOREGROUND_IMPORTANCE_CUTOFF
             = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
 
-    // default background throttling interval if not overriden in settings
-    private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
-
-    // Default value for maximum age of last location returned to applications with foreground-only
-    // location permissions.
-    private static final long DEFAULT_LAST_LOCATION_MAX_AGE_MS = 20 * 60 * 1000;
-
     // Location Providers may sometimes deliver location updates
     // slightly faster that requested - provide grace period so
     // we don't unnecessarily filter events that are otherwise on
@@ -208,13 +199,14 @@
     private ActivityManager mActivityManager;
     private UserManager mUserManager;
 
+    private LocationSettingsStore mSettingsStore;
+
     private GeofenceManager mGeofenceManager;
     private LocationFudger mLocationFudger;
     private GeocoderProxy mGeocodeProvider;
     @Nullable
     private GnssManagerService mGnssManagerService;
     private PassiveProvider mPassiveProvider; // track passive provider for special cases
-    private LocationBlacklist mBlacklist;
     @GuardedBy("mLock")
     private String mExtraLocationControllerPackage;
     private boolean mExtraLocationControllerPackageEnabled;
@@ -245,10 +237,6 @@
     private final HashMap<String, Location> mLastLocationCoarseInterval =
             new HashMap<>();
 
-    private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
-
-    private final ArraySet<String> mIgnoreSettingsPackageWhitelist = new ArraySet<>();
-
     // current active user on the device - other users are denied location data
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
     private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
@@ -287,16 +275,19 @@
 
     @GuardedBy("mLock")
     private void initializeLocked() {
-        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mPackageManager = mContext.getPackageManager();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+        mActivityManager = mContext.getSystemService(ActivityManager.class);
+        mUserManager = mContext.getSystemService(UserManager.class);
+
+        mSettingsStore = new LocationSettingsStore(mContext, mHandler);
 
         mLocationFudger = new LocationFudger(mContext, mHandler);
-        mBlacklist = new LocationBlacklist(mContext, mHandler);
-        mBlacklist.init();
-        mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+        mGeofenceManager = new GeofenceManager(mContext, mSettingsStore);
+
+        PowerManagerInternal localPowerManager =
+                LocalServices.getService(PowerManagerInternal.class);
 
         // prepare providers
         initializeProvidersLocked();
@@ -327,7 +318,6 @@
                         }
                     });
                 });
-
         mActivityManager.addOnUidImportanceListener(
                 (uid, importance) -> {
                     // listener invoked on ui thread, move to our thread to reduce risk of blocking
@@ -339,63 +329,7 @@
                     });
                 },
                 FOREGROUND_IMPORTANCE_CUTOFF);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE), true,
-                new ContentObserver(mHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            onLocationModeChangedLocked(true);
-                        }
-                    }
-                }, UserHandle.USER_ALL);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
-                new ContentObserver(mHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            onProviderAllowedChangedLocked();
-                        }
-                    }
-                }, UserHandle.USER_ALL);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
-                true,
-                new ContentObserver(mHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            onBackgroundThrottleIntervalChangedLocked();
-                        }
-                    }
-                }, UserHandle.USER_ALL);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(
-                        Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
-                true,
-                new ContentObserver(mHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            onBackgroundThrottleWhitelistChangedLocked();
-                        }
-                    }
-                }, UserHandle.USER_ALL);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(
-                        Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST),
-                true,
-                new ContentObserver(mHandler) {
-                    @Override
-                    public void onChange(boolean selfChange) {
-                        synchronized (mLock) {
-                            onIgnoreSettingsWhitelistChangedLocked();
-                        }
-                    }
-                }, UserHandle.USER_ALL);
-        PowerManagerInternal localPowerManager =
-                LocalServices.getService(PowerManagerInternal.class);
+
         localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION,
                 state -> {
                     // listener invoked on ui thread, move to our thread to reduce risk of blocking
@@ -406,6 +340,33 @@
                         }
                     });
                 });
+        mBatterySaverMode = mPowerManager.getLocationPowerSaveMode();
+
+        mSettingsStore.addOnLocationEnabledChangedListener(() -> {
+            synchronized (mLock) {
+                onLocationModeChangedLocked(true);
+            }
+        });
+        mSettingsStore.addOnLocationProvidersAllowedChangedListener(() -> {
+            synchronized (mLock) {
+                onProviderAllowedChangedLocked();
+            }
+        });
+        mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> {
+            synchronized (mLock) {
+                onBackgroundThrottleIntervalChangedLocked();
+            }
+        });
+        mSettingsStore.addOnBackgroundThrottlePackageWhitelistChangedListener(() -> {
+            synchronized (mLock) {
+                onBackgroundThrottleWhitelistChangedLocked();
+            }
+        });
+        mSettingsStore.addOnIgnoreSettingsPackageWhitelistChangedListener(() -> {
+            synchronized (mLock) {
+                onIgnoreSettingsWhitelistChangedLocked();
+            }
+        });
 
         new PackageMonitor() {
             @Override
@@ -453,11 +414,6 @@
         // provider initialization, and propagates changes until a steady state is reached
         mCurrentUserId = UserHandle.USER_NULL;
         onUserChangedLocked(ActivityManager.getCurrentUser());
-
-        // initialize in-memory settings values
-        onBackgroundThrottleWhitelistChangedLocked();
-        onIgnoreSettingsWhitelistChangedLocked();
-        onBatterySaverModeChangedLocked(mPowerManager.getLocationPowerSaveMode());
     }
 
     @GuardedBy("mLock")
@@ -479,6 +435,10 @@
 
     @GuardedBy("mLock")
     private void onBatterySaverModeChangedLocked(int newLocationMode) {
+        if (mBatterySaverMode == newLocationMode) {
+            return;
+        }
+
         if (D) {
             Slog.d(TAG,
                     "Battery Saver location mode changed from "
@@ -486,11 +446,8 @@
                             + locationPowerSaveModeToString(newLocationMode));
         }
 
-        if (mBatterySaverMode == newLocationMode) {
-            return;
-        }
-
         mBatterySaverMode = newLocationMode;
+
         for (LocationProvider p : mProviders) {
             applyRequirementsLocked(p);
         }
@@ -588,17 +545,6 @@
 
     @GuardedBy("mLock")
     private void onBackgroundThrottleWhitelistChangedLocked() {
-        mBackgroundThrottlePackageWhitelist.clear();
-        mBackgroundThrottlePackageWhitelist.addAll(
-                SystemConfig.getInstance().getAllowUnthrottledLocation());
-
-        String setting = Settings.Global.getString(
-                mContext.getContentResolver(),
-                Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
-        if (!TextUtils.isEmpty(setting)) {
-            mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(",")));
-        }
-
         for (LocationProvider p : mProviders) {
             applyRequirementsLocked(p);
         }
@@ -606,17 +552,6 @@
 
     @GuardedBy("lock")
     private void onIgnoreSettingsWhitelistChangedLocked() {
-        mIgnoreSettingsPackageWhitelist.clear();
-        mIgnoreSettingsPackageWhitelist.addAll(
-                SystemConfig.getInstance().getAllowIgnoreLocationSettings());
-
-        String setting = Settings.Global.getString(
-                mContext.getContentResolver(),
-                Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST);
-        if (!TextUtils.isEmpty(setting)) {
-            mIgnoreSettingsPackageWhitelist.addAll(Arrays.asList(setting.split(",")));
-        }
-
         for (LocationProvider p : mProviders) {
             applyRequirementsLocked(p);
         }
@@ -859,8 +794,6 @@
         mCurrentUserId = userId;
         onUserProfilesChangedLocked();
 
-        mBlacklist.switchUser(userId);
-
         // if the user changes, per-user settings may also have changed
         onLocationModeChangedLocked(false);
         onProviderAllowedChangedLocked();
@@ -1083,11 +1016,8 @@
         @GuardedBy("mLock")
         public void onAllowedChangedLocked() {
             if (mIsManagedBySettings) {
-                String allowedProviders = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        mCurrentUserId);
-                boolean allowed = TextUtils.delimitedStringContains(allowedProviders, ',', mName);
+                boolean allowed = mSettingsStore.getLocationProvidersAllowed(
+                        mCurrentUserId).contains(mName);
 
                 if (allowed == mAllowed) {
                     return;
@@ -1909,10 +1839,7 @@
 
             long identity = Binder.clearCallingIdentity();
             try {
-                backgroundThrottleInterval = Settings.Global.getLong(
-                        mContext.getContentResolver(),
-                        Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
-                        DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+                backgroundThrottleInterval = mSettingsStore.getBackgroundThrottleIntervalMs();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2032,16 +1959,12 @@
 
     @Override
     public String[] getBackgroundThrottlingWhitelist() {
-        synchronized (mLock) {
-            return mBackgroundThrottlePackageWhitelist.toArray(new String[0]);
-        }
+        return mSettingsStore.getBackgroundThrottlePackageWhitelist().toArray(new String[0]);
     }
 
     @Override
     public String[] getIgnoreSettingsWhitelist() {
-        synchronized (mLock) {
-            return mIgnoreSettingsPackageWhitelist.toArray(new String[0]);
-        }
+        return mSettingsStore.getIgnoreSettingsPackageWhitelist().toArray(new String[0]);
     }
 
     @GuardedBy("mLock")
@@ -2050,7 +1973,8 @@
             return true;
         }
 
-        if (mBackgroundThrottlePackageWhitelist.contains(callerIdentity.mPackageName)) {
+        if (mSettingsStore.getBackgroundThrottlePackageWhitelist().contains(
+                callerIdentity.mPackageName)) {
             return true;
         }
 
@@ -2064,7 +1988,7 @@
             return false;
         }
 
-        if (mIgnoreSettingsPackageWhitelist.contains(
+        if (mSettingsStore.getIgnoreSettingsPackageWhitelist().contains(
                 record.mReceiver.mCallerIdentity.mPackageName)) {
             return true;
         }
@@ -2472,7 +2396,8 @@
             final int uid = Binder.getCallingUid();
             final long identity = Binder.clearCallingIdentity();
             try {
-                if (mBlacklist.isBlacklisted(packageName)) {
+                if (mSettingsStore.isLocationPackageBlacklisted(UserHandle.getUserId(uid),
+                        packageName)) {
                     if (D) {
                         Log.d(TAG, "not returning last loc for blacklisted app: "
                                 + packageName);
@@ -2513,10 +2438,7 @@
                 String op = resolutionLevelToOpStr(allowedResolutionLevel);
                 long locationAgeMs = TimeUnit.NANOSECONDS.toMillis(
                         SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos());
-                if ((locationAgeMs > Settings.Global.getLong(
-                        mContext.getContentResolver(),
-                        Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
-                        DEFAULT_LAST_LOCATION_MAX_AGE_MS))
+                if (locationAgeMs > mSettingsStore.getMaxLastLocationAgeMs()
                         && (mAppOps.unsafeCheckOp(op, uid, packageName)
                         == AppOpsManager.MODE_FOREGROUND)) {
                     return null;
@@ -2575,11 +2497,7 @@
                 boolean foreground = LocationManagerServiceUtils.isImportanceForeground(
                         mActivityManager.getPackageImportance(packageName));
                 if (!foreground) {
-                    long backgroundThrottleIntervalMs = Settings.Global.getLong(
-                            mContext.getContentResolver(),
-                            Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
-                            DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
-                    if (locationAgeMs < backgroundThrottleIntervalMs) {
+                    if (locationAgeMs < mSettingsStore.getBackgroundThrottleIntervalMs()) {
                         // not allowed to request new locations, so we can't return anything
                         return false;
                     }
@@ -2910,11 +2828,7 @@
 
         long identity = Binder.clearCallingIdentity();
         try {
-            return Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.LOCATION_MODE,
-                    Settings.Secure.LOCATION_MODE_OFF,
-                    userId) != Settings.Secure.LOCATION_MODE_OFF;
+            return mSettingsStore.isLocationEnabled(userId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -3050,7 +2964,8 @@
                 continue;
             }
 
-            if (mBlacklist.isBlacklisted(receiver.mCallerIdentity.mPackageName)) {
+            if (mSettingsStore.isLocationPackageBlacklisted(receiverUserId,
+                    receiver.mCallerIdentity.mPackageName)) {
                 if (D) {
                     Log.d(TAG, "skipping loc update for blacklisted app: " +
                             receiver.mCallerIdentity.mPackageName);
@@ -3392,33 +3307,11 @@
                 ipw.decreaseIndent();
             }
 
-            if (mBlacklist != null) {
-                mBlacklist.dump(ipw);
-            }
-
             if (mExtraLocationControllerPackage != null) {
                 ipw.println("Location Controller Extra Package: " + mExtraLocationControllerPackage
                         + (mExtraLocationControllerPackageEnabled ? " [enabled]" : "[disabled]"));
             }
 
-            if (!mBackgroundThrottlePackageWhitelist.isEmpty()) {
-                ipw.println("Throttling Whitelisted Packages:");
-                ipw.increaseIndent();
-                for (String packageName : mBackgroundThrottlePackageWhitelist) {
-                    ipw.println(packageName);
-                }
-                ipw.decreaseIndent();
-            }
-
-            if (!mIgnoreSettingsPackageWhitelist.isEmpty()) {
-                ipw.println("Bypass Whitelisted Packages:");
-                ipw.increaseIndent();
-                for (String packageName : mIgnoreSettingsPackageWhitelist) {
-                    ipw.println(packageName);
-                }
-                ipw.decreaseIndent();
-            }
-
             if (mLocationFudger != null) {
                 ipw.println("Location Fudger:");
                 ipw.increaseIndent();
@@ -3426,6 +3319,8 @@
                 ipw.decreaseIndent();
             }
 
+            mSettingsStore.dump(fd, pw, args);
+
             ipw.println("Location Providers:");
             ipw.increaseIndent();
             for (LocationProvider provider : mProviders) {
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index 17a2169..c970788 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -18,12 +18,11 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.database.ContentObserver;
 import android.location.Geofence;
 import android.location.Location;
 import android.location.LocationListener;
@@ -31,13 +30,14 @@
 import android.location.LocationRequest;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.util.Log;
 import android.util.Slog;
 
+import com.android.server.FgThread;
 import com.android.server.LocationManagerService;
 import com.android.server.PendingIntentUtils;
 
@@ -48,7 +48,7 @@
 
 public class GeofenceManager implements LocationListener, PendingIntent.OnFinished {
     private static final String TAG = "GeofenceManager";
-    private static final boolean D = LocationManagerService.D;
+    private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int MSG_UPDATE_FENCES = 1;
 
@@ -64,10 +64,6 @@
      */
     private static final long MAX_AGE_NANOS = 5 * 60 * 1000000000L; // five minutes
 
-    /**
-     * The default value of most frequent update interval allowed.
-     */
-    private static final long DEFAULT_MIN_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
 
     /**
      * Least frequent update interval allowed.
@@ -75,11 +71,13 @@
     private static final long MAX_INTERVAL_MS = 2 * 60 * 60 * 1000; // two hours
 
     private final Context mContext;
+    private final GeofenceHandler mHandler;
+
     private final LocationManager mLocationManager;
     private final AppOpsManager mAppOps;
     private final PowerManager.WakeLock mWakeLock;
-    private final GeofenceHandler mHandler;
-    private final LocationBlacklist mBlacklist;
+
+    private final LocationSettingsStore mSettingsStore;
 
     private final Object mLock = new Object();
 
@@ -113,43 +111,17 @@
      */
     private boolean mPendingUpdate;
 
-    /**
-     * The actual value of most frequent update interval allowed.
-     */
-    private long mEffectiveMinIntervalMs;
-    private ContentResolver mResolver;
-
-    public GeofenceManager(Context context, LocationBlacklist blacklist) {
+    public GeofenceManager(Context context, LocationSettingsStore settingsStore) {
         mContext = context;
-        mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
-        mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
-        PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        mHandler = new GeofenceHandler();
-        mBlacklist = blacklist;
-        mResolver = mContext.getContentResolver();
-        updateMinInterval();
-        mResolver.registerContentObserver(
-            Settings.Global.getUriFor(
-                    Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS),
-            true,
-            new ContentObserver(mHandler) {
-                @Override
-                public void onChange(boolean selfChange) {
-                    synchronized (mLock) {
-                        updateMinInterval();
-                    }
-                }
-            }, UserHandle.USER_ALL);
-    }
+        mHandler = new GeofenceHandler(FgThread.getHandler().getLooper());
 
-    /**
-     * Updates the minimal location request frequency.
-     */
-    private void updateMinInterval() {
-        mEffectiveMinIntervalMs = Settings.Global.getLong(mResolver,
-                Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
-                DEFAULT_MIN_INTERVAL_MS);
+        mLocationManager = mContext.getSystemService(LocationManager.class);
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
+
+        mWakeLock = mContext.getSystemService(PowerManager.class).newWakeLock(
+                PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+        mSettingsStore = settingsStore;
     }
 
     public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
@@ -294,7 +266,8 @@
             double minFenceDistance = Double.MAX_VALUE;
             boolean needUpdates = false;
             for (GeofenceState state : mFences) {
-                if (mBlacklist.isBlacklisted(state.mPackageName)) {
+                if (mSettingsStore.isLocationPackageBlacklisted(ActivityManager.getCurrentUser(),
+                        state.mPackageName)) {
                     if (D) {
                         Slog.d(TAG, "skipping geofence processing for blacklisted app: "
                                 + state.mPackageName);
@@ -340,10 +313,11 @@
                 // Compute a location update interval based on the distance to the nearest fence.
                 long intervalMs;
                 if (location != null && Double.compare(minFenceDistance, Double.MAX_VALUE) != 0) {
-                    intervalMs = (long)Math.min(MAX_INTERVAL_MS, Math.max(mEffectiveMinIntervalMs,
+                    intervalMs = (long) Math.min(MAX_INTERVAL_MS, Math.max(
+                            mSettingsStore.getBackgroundThrottleProximityAlertIntervalMs(),
                             minFenceDistance * 1000 / MAX_SPEED_M_S));
                 } else {
-                    intervalMs = mEffectiveMinIntervalMs;
+                    intervalMs = mSettingsStore.getBackgroundThrottleProximityAlertIntervalMs();
                 }
                 if (!mReceivingLocationUpdates || mLocationUpdateInterval != intervalMs) {
                     mReceivingLocationUpdates = true;
@@ -436,13 +410,16 @@
     }
 
     @Override
-    public void onStatusChanged(String provider, int status, Bundle extras) { }
+    public void onStatusChanged(String provider, int status, Bundle extras) {
+    }
 
     @Override
-    public void onProviderEnabled(String provider) { }
+    public void onProviderEnabled(String provider) {
+    }
 
     @Override
-    public void onProviderDisabled(String provider) { }
+    public void onProviderDisabled(String provider) {
+    }
 
     @Override
     public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
@@ -457,17 +434,14 @@
     }
 
     private final class GeofenceHandler extends Handler {
-        public GeofenceHandler() {
-            super(true /*async*/);
+        private GeofenceHandler(Looper looper) {
+            super(looper);
         }
 
         @Override
         public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_UPDATE_FENCES: {
-                    updateFences();
-                    break;
-                }
+            if (msg.what == MSG_UPDATE_FENCES) {
+                updateFences();
             }
         }
     }
diff --git a/services/core/java/com/android/server/location/LocationBlacklist.java b/services/core/java/com/android/server/location/LocationBlacklist.java
deleted file mode 100644
index 3f3f828..0000000
--- a/services/core/java/com/android/server/location/LocationBlacklist.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.server.location;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.Slog;
-
-import com.android.server.LocationManagerService;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Allows applications to be blacklisted from location updates at run-time.
- *
- * This is a silent blacklist. Applications can still call Location Manager
- * API's, but they just won't receive any locations.
- */
-public final class LocationBlacklist extends ContentObserver {
-    private static final String TAG = "LocationBlacklist";
-    private static final boolean D = LocationManagerService.D;
-    private static final String BLACKLIST_CONFIG_NAME = "locationPackagePrefixBlacklist";
-    private static final String WHITELIST_CONFIG_NAME = "locationPackagePrefixWhitelist";
-
-    private final Context mContext;
-    private final Object mLock = new Object();
-
-    // all fields below synchronized on mLock
-    private String[] mWhitelist = new String[0];
-    private String[] mBlacklist = new String[0];
-
-    private int mCurrentUserId = UserHandle.USER_SYSTEM;
-    
-    public LocationBlacklist(Context context, Handler handler) {
-        super(handler);
-        mContext = context;
-    }
-
-    public void init() {
-        mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
-                BLACKLIST_CONFIG_NAME), false, this, UserHandle.USER_ALL);
-//        mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
-//                WHITELIST_CONFIG_NAME), false, this, UserHandle.USER_ALL);
-        reloadBlacklist();
-    }
-
-    private void reloadBlacklistLocked() {
-        mWhitelist = getStringArrayLocked(WHITELIST_CONFIG_NAME);
-        if (D) Slog.d(TAG, "whitelist: " + Arrays.toString(mWhitelist));
-        mBlacklist = getStringArrayLocked(BLACKLIST_CONFIG_NAME);
-        if (D) Slog.d(TAG, "blacklist: " + Arrays.toString(mBlacklist));
-    }
-
-    private void reloadBlacklist() {
-        synchronized (mLock) {
-            reloadBlacklistLocked();
-        }
-    }
-
-    /**
-     * Return true if in blacklist
-     * (package name matches blacklist, and does not match whitelist)
-     */
-    public boolean isBlacklisted(String packageName) {
-        synchronized (mLock) {
-            for (String black : mBlacklist) {
-                if (packageName.startsWith(black)) {
-                    if (inWhitelist(packageName)) {
-                        continue;
-                    } else {
-                        if (D) Log.d(TAG, "dropping location (blacklisted): "
-                                + packageName + " matches " + black);
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Return true if any of packages are in whitelist
-     */
-    private boolean inWhitelist(String pkg) {
-        synchronized (mLock) {
-            for (String white : mWhitelist) {
-                if (pkg.startsWith(white)) return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void onChange(boolean selfChange) {
-        reloadBlacklist();
-    }
-
-    public void switchUser(int userId) {
-        synchronized(mLock) {
-            mCurrentUserId = userId;
-            reloadBlacklistLocked();
-        }
-    }
-
-    private String[] getStringArrayLocked(String key) {
-        String flatString;
-        synchronized(mLock) {
-            flatString = Settings.Secure.getStringForUser(mContext.getContentResolver(), key,
-                    mCurrentUserId);
-        }
-        if (flatString == null) {
-            return new String[0];
-        }
-        String[] splitStrings = flatString.split(",");
-        ArrayList<String> result = new ArrayList<String>();
-        for (String pkg : splitStrings) {
-            pkg = pkg.trim();
-            if (pkg.isEmpty()) {
-                continue;
-            }
-            result.add(pkg);
-        }
-        return result.toArray(new String[result.size()]);
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println("mWhitelist=" + Arrays.toString(mWhitelist) + " mBlacklist=" +
-                Arrays.toString(mBlacklist));
-    }
-}
diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/LocationSettingsStore.java
new file mode 100644
index 0000000..a4b6d97
--- /dev/null
+++ b/services/core/java/com/android/server/location/LocationSettingsStore.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS;
+import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST;
+import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS;
+import static android.provider.Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST;
+import static android.provider.Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS;
+import static android.provider.Settings.Secure.LOCATION_MODE;
+import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
+import static android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for all location related settings.
+ */
+public class LocationSettingsStore {
+
+    private static final String LOCATION_PACKAGE_BLACKLIST = "locationPackagePrefixBlacklist";
+    private static final String LOCATION_PACKAGE_WHITELIST = "locationPackagePrefixWhitelist";
+
+    private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
+    private static final long DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS =
+            30 * 60 * 1000;
+    private static final long DEFAULT_MAX_LAST_LOCATION_AGE_MS = 20 * 60 * 1000;
+
+    private final Context mContext;
+
+    private final IntegerSecureSetting mLocationMode;
+    private final StringListCachedSecureSetting mLocationProvidersAllowed;
+    private final LongGlobalSetting mBackgroundThrottleIntervalMs;
+    private final StringListCachedSecureSetting mLocationPackageBlacklist;
+    private final StringListCachedSecureSetting mLocationPackageWhitelist;
+    private final StringListCachedGlobalSetting mBackgroundThrottlePackageWhitelist;
+    private final StringListCachedGlobalSetting mIgnoreSettingsPackageWhitelist;
+
+    public LocationSettingsStore(Context context, Handler handler) {
+        mContext = context;
+
+        mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler);
+        mLocationProvidersAllowed = new StringListCachedSecureSetting(context,
+                LOCATION_PROVIDERS_ALLOWED, handler);
+        mBackgroundThrottleIntervalMs = new LongGlobalSetting(context,
+                LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, handler);
+        mLocationPackageBlacklist = new StringListCachedSecureSetting(context,
+                LOCATION_PACKAGE_BLACKLIST, handler);
+        mLocationPackageWhitelist = new StringListCachedSecureSetting(context,
+                LOCATION_PACKAGE_WHITELIST, handler);
+        mBackgroundThrottlePackageWhitelist = new StringListCachedGlobalSetting(context,
+                LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, handler);
+        mIgnoreSettingsPackageWhitelist = new StringListCachedGlobalSetting(context,
+                LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, handler);
+    }
+
+    /**
+     * Retrieve if location is enabled or not.
+     */
+    public boolean isLocationEnabled(int userId) {
+        return mLocationMode.getValueForUser(LOCATION_MODE_OFF, userId) != LOCATION_MODE_OFF;
+    }
+
+    /**
+     * Add a listener for changes to the location enabled setting.
+     */
+    public void addOnLocationEnabledChangedListener(Runnable listener) {
+        mLocationMode.addListener(listener);
+    }
+
+    /**
+     * Remove a listener for changes to the location enabled setting.
+     */
+    public void removeOnLocationEnabledChangedListener(Runnable listener) {
+        mLocationMode.addListener(listener);
+    }
+
+    /**
+     * Retrieve the currently allowed location providers.
+     */
+    public List<String> getLocationProvidersAllowed(int userId) {
+        return mLocationProvidersAllowed.getValueForUser(userId);
+    }
+
+    /**
+     * Add a listener for changes to the currently allowed location providers.
+     */
+    public void addOnLocationProvidersAllowedChangedListener(Runnable runnable) {
+        mLocationProvidersAllowed.addListener(runnable);
+    }
+
+    /**
+     * Remove a listener for changes to the currently allowed location providers.
+     */
+    public void removeOnLocationProvidersAllowedChangedListener(Runnable runnable) {
+        mLocationProvidersAllowed.removeListener(runnable);
+    }
+
+    /**
+     * Retrieve the background throttle interval.
+     */
+    public long getBackgroundThrottleIntervalMs() {
+        return mBackgroundThrottleIntervalMs.getValue(DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+    }
+
+    /**
+     * Add a listener for changes to the background throttle interval.
+     */
+    public void addOnBackgroundThrottleIntervalChangedListener(Runnable listener) {
+        mBackgroundThrottleIntervalMs.addListener(listener);
+    }
+
+    /**
+     * Remove a listener for changes to the background throttle interval.
+     */
+    public void removeOnBackgroundThrottleIntervalChangedListener(Runnable listener) {
+        mBackgroundThrottleIntervalMs.removeListener(listener);
+    }
+
+    /**
+     * Check if the given package is blacklisted for location access.
+     */
+    public boolean isLocationPackageBlacklisted(int userId, String packageName) {
+        List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
+        if (locationPackageBlacklist.isEmpty()) {
+            return false;
+        }
+
+        List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(userId);
+        for (String locationWhitelistPackage : locationPackageWhitelist) {
+            if (packageName.startsWith(locationWhitelistPackage)) {
+                return false;
+            }
+        }
+
+        for (String locationBlacklistPackage : locationPackageBlacklist) {
+            if (packageName.startsWith(locationBlacklistPackage)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Retrieve the background throttle package whitelist.
+     */
+    public List<String> getBackgroundThrottlePackageWhitelist() {
+        return mBackgroundThrottlePackageWhitelist.getValue();
+    }
+
+    /**
+     * Add a listener for changes to the background throttle package whitelist.
+     */
+    public void addOnBackgroundThrottlePackageWhitelistChangedListener(Runnable listener) {
+        mBackgroundThrottlePackageWhitelist.addListener(listener);
+    }
+
+    /**
+     * Remove a listener for changes to the background throttle package whitelist.
+     */
+    public void removeOnBackgroundThrottlePackageWhitelistChangedListener(Runnable listener) {
+        mBackgroundThrottlePackageWhitelist.removeListener(listener);
+    }
+
+    /**
+     * Retrieve the ignore settings package whitelist.
+     */
+    public List<String> getIgnoreSettingsPackageWhitelist() {
+        return mIgnoreSettingsPackageWhitelist.getValue();
+    }
+
+    /**
+     * Add a listener for changes to the ignore settings package whitelist.
+     */
+    public void addOnIgnoreSettingsPackageWhitelistChangedListener(Runnable listener) {
+        mIgnoreSettingsPackageWhitelist.addListener(listener);
+    }
+
+    /**
+     * Remove a listener for changes to the ignore settings package whitelist.
+     */
+    public void removeOnIgnoreSettingsPackageWhitelistChangedListener(Runnable listener) {
+        mIgnoreSettingsPackageWhitelist.removeListener(listener);
+    }
+
+    /**
+     * Retrieve the background throttling proximity alert interval.
+     */
+    public long getBackgroundThrottleProximityAlertIntervalMs() {
+        return Settings.Global.getLong(mContext.getContentResolver(),
+                LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
+                DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
+    }
+
+    public long getMaxLastLocationAgeMs() {
+        return Settings.Global.getLong(
+                mContext.getContentResolver(),
+                LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+                DEFAULT_MAX_LAST_LOCATION_AGE_MS);
+    }
+
+    /**
+     * Dump info for debugging.
+     */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        int userId = ActivityManager.getCurrentUser();
+
+        ipw.println("--Location Settings--");
+        ipw.increaseIndent();
+
+        ipw.print("Location Enabled: ");
+        ipw.println(isLocationEnabled(userId));
+
+        ipw.print("Location Providers Allowed: ");
+        ipw.println(getLocationProvidersAllowed(userId));
+
+        List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
+        if (!locationPackageBlacklist.isEmpty()) {
+            ipw.println("Location Blacklisted Packages:");
+            ipw.increaseIndent();
+            for (String packageName : locationPackageBlacklist) {
+                ipw.println(packageName);
+            }
+            ipw.decreaseIndent();
+
+            List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(
+                    userId);
+            if (!locationPackageWhitelist.isEmpty()) {
+                ipw.println("Location Whitelisted Packages:");
+                ipw.increaseIndent();
+                for (String packageName : locationPackageWhitelist) {
+                    ipw.println(packageName);
+                }
+                ipw.decreaseIndent();
+            }
+        }
+
+        List<String> backgroundThrottlePackageWhitelist =
+                mBackgroundThrottlePackageWhitelist.getValue();
+        if (!backgroundThrottlePackageWhitelist.isEmpty()) {
+            ipw.println("Throttling Whitelisted Packages:");
+            ipw.increaseIndent();
+            for (String packageName : backgroundThrottlePackageWhitelist) {
+                ipw.println(packageName);
+            }
+            ipw.decreaseIndent();
+        }
+
+        List<String> ignoreSettingsPackageWhitelist = mIgnoreSettingsPackageWhitelist.getValue();
+        if (!ignoreSettingsPackageWhitelist.isEmpty()) {
+            ipw.println("Bypass Whitelisted Packages:");
+            ipw.increaseIndent();
+            for (String packageName : ignoreSettingsPackageWhitelist) {
+                ipw.println(packageName);
+            }
+            ipw.decreaseIndent();
+        }
+    }
+
+    private abstract static class ObservingSetting extends ContentObserver {
+
+        private final CopyOnWriteArrayList<Runnable> mListeners;
+
+        private ObservingSetting(Context context, String settingName, Handler handler) {
+            super(handler);
+            mListeners = new CopyOnWriteArrayList<>();
+
+            context.getContentResolver().registerContentObserver(
+                    getUriFor(settingName), false, this, UserHandle.USER_ALL);
+        }
+
+        public void addListener(Runnable listener) {
+            mListeners.add(listener);
+        }
+
+        public void removeListener(Runnable listener) {
+            mListeners.remove(listener);
+        }
+
+        protected abstract Uri getUriFor(String settingName);
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            for (Runnable listener : mListeners) {
+                listener.run();
+            }
+        }
+    }
+
+    private static class IntegerSecureSetting extends ObservingSetting {
+
+        private final Context mContext;
+        private final String mSettingName;
+
+        private IntegerSecureSetting(Context context, String settingName, Handler handler) {
+            super(context, settingName, handler);
+            mContext = context;
+            mSettingName = settingName;
+        }
+
+        public int getValueForUser(int defaultValue, int userId) {
+            return Settings.Secure.getIntForUser(mContext.getContentResolver(), mSettingName,
+                    defaultValue, userId);
+        }
+
+        @Override
+        protected Uri getUriFor(String settingName) {
+            return Settings.Secure.getUriFor(settingName);
+        }
+    }
+
+    private static class StringListCachedSecureSetting extends ObservingSetting {
+
+        private final Context mContext;
+        private final String mSettingName;
+
+        private int mCachedUserId = UserHandle.USER_NULL;
+        private List<String> mCachedValue;
+
+        private StringListCachedSecureSetting(Context context, String settingName,
+                Handler handler) {
+            super(context, settingName, handler);
+            mContext = context;
+            mSettingName = settingName;
+        }
+
+        public synchronized List<String> getValueForUser(int userId) {
+            if (userId != mCachedUserId) {
+                String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                        mSettingName, userId);
+                if (TextUtils.isEmpty(setting)) {
+                    mCachedValue = Collections.emptyList();
+                } else {
+                    mCachedValue = Arrays.asList(setting.split(","));
+                }
+                mCachedUserId = userId;
+            }
+
+            return mCachedValue;
+        }
+
+        public synchronized void invalidateForUser(int userId) {
+            if (mCachedUserId == userId) {
+                mCachedUserId = UserHandle.USER_NULL;
+                mCachedValue = null;
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            invalidateForUser(userId);
+            super.onChange(selfChange, uri, userId);
+        }
+
+        @Override
+        protected Uri getUriFor(String settingName) {
+            return Settings.Secure.getUriFor(settingName);
+        }
+    }
+
+    private static class LongGlobalSetting extends ObservingSetting {
+
+        private final Context mContext;
+        private final String mSettingName;
+
+        private LongGlobalSetting(Context context, String settingName, Handler handler) {
+            super(context, settingName, handler);
+            mContext = context;
+            mSettingName = settingName;
+        }
+
+        public long getValue(long defaultValue) {
+            return Settings.Global.getLong(mContext.getContentResolver(), mSettingName,
+                    defaultValue);
+        }
+
+        @Override
+        protected Uri getUriFor(String settingName) {
+            return Settings.Global.getUriFor(settingName);
+        }
+    }
+
+    private static class StringListCachedGlobalSetting extends ObservingSetting {
+
+        private final Context mContext;
+        private final String mSettingName;
+
+        private boolean mValid;
+        private List<String> mCachedValue;
+
+        private StringListCachedGlobalSetting(Context context, String settingName,
+                Handler handler) {
+            super(context, settingName, handler);
+            mContext = context;
+            mSettingName = settingName;
+        }
+
+        public synchronized List<String> getValue() {
+            if (!mValid) {
+                String setting = Settings.Global.getString(mContext.getContentResolver(),
+                        mSettingName);
+                if (TextUtils.isEmpty(setting)) {
+                    mCachedValue = Collections.emptyList();
+                } else {
+                    mCachedValue = Arrays.asList(setting.split(","));
+                }
+                mValid = true;
+            }
+
+            return mCachedValue;
+        }
+
+        public synchronized void invalidate() {
+            mValid = false;
+            mCachedValue = null;
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            invalidate();
+            super.onChange(selfChange, uri, userId);
+        }
+
+        @Override
+        protected Uri getUriFor(String settingName) {
+            return Settings.Global.getUriFor(settingName);
+        }
+    }
+}