Merge "Assorted location cleanup"
diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java
index cbf2a62..bbcfdc6 100644
--- a/services/core/java/com/android/server/GnssManagerService.java
+++ b/services/core/java/com/android/server/GnssManagerService.java
@@ -56,6 +56,7 @@
 import com.android.server.location.GnssMeasurementsProvider;
 import com.android.server.location.GnssNavigationMessageProvider;
 import com.android.server.location.GnssStatusListenerHelper;
+import com.android.server.location.LocationUsageLogger;
 import com.android.server.location.RemoteListenerHelper;
 
 import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 6300ab8..cad917b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -104,6 +104,7 @@
 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.LocationUsageLogger;
 import com.android.server.location.MockProvider;
 import com.android.server.location.PassiveProvider;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -158,21 +159,15 @@
         }
     }
 
-    private static final String TAG = "LocationManagerService";
+    public static final String TAG = "LocationManagerService";
     public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String WAKELOCK_KEY = "*location*";
 
-    // Location resolution level: no location data whatsoever
     private static final int RESOLUTION_LEVEL_NONE = 0;
-    // Location resolution level: coarse location data only
     private static final int RESOLUTION_LEVEL_COARSE = 1;
-    // Location resolution level: fine location data
     private static final int RESOLUTION_LEVEL_FINE = 2;
 
-    private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
-            android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
-
     private static final String NETWORK_LOCATION_SERVICE_ACTION =
             "com.android.location.service.v3.NetworkLocationProvider";
     private static final String FUSED_LOCATION_SERVICE_ACTION =
@@ -198,6 +193,8 @@
     private final Object mLock = new Object();
     private final Context mContext;
     private final Handler mHandler;
+    private final LocationSettingsStore mSettingsStore;
+    private final LocationUsageLogger mLocationUsageLogger;
 
     private AppOpsManager mAppOps;
     private PackageManager mPackageManager;
@@ -205,8 +202,6 @@
     private ActivityManager mActivityManager;
     private UserManager mUserManager;
 
-    private LocationSettingsStore mSettingsStore;
-
     private GeofenceManager mGeofenceManager;
     private LocationFudger mLocationFudger;
     private GeocoderProxy mGeocodeProvider;
@@ -219,12 +214,12 @@
 
     // list of currently active providers
     @GuardedBy("mLock")
-    private final ArrayList<LocationProvider> mProviders = new ArrayList<>();
+    private final ArrayList<LocationProviderManager> mProviders = new ArrayList<>();
 
     // list of non-mock providers, so that when mock providers replace real providers, they can be
     // later re-replaced
     @GuardedBy("mLock")
-    private final ArrayList<LocationProvider> mRealProviders = new ArrayList<>();
+    private final ArrayList<LocationProviderManager> mRealProviders = new ArrayList<>();
 
     @GuardedBy("mLock")
     private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
@@ -251,12 +246,10 @@
     @PowerManager.LocationPowerSaveMode
     private int mBatterySaverMode;
 
-    @GuardedBy("mLock")
-    private final LocationUsageLogger mLocationUsageLogger;
-
     private LocationManagerService(Context context) {
         mContext = context;
         mHandler = FgThread.getHandler();
+        mSettingsStore = new LocationSettingsStore(mContext, mHandler);
         mLocationUsageLogger = new LocationUsageLogger();
 
         // Let the package manager query which are the default location
@@ -274,6 +267,8 @@
     }
 
     private void onSystemReady() {
+        mSettingsStore.onSystemReady();
+
         synchronized (mLock) {
             mPackageManager = mContext.getPackageManager();
             mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -281,7 +276,6 @@
             mActivityManager = mContext.getSystemService(ActivityManager.class);
             mUserManager = mContext.getSystemService(UserManager.class);
 
-            mSettingsStore = new LocationSettingsStore(mContext, mHandler);
             mLocationFudger = new LocationFudger(mContext, mHandler);
             mGeofenceManager = new GeofenceManager(mContext, mSettingsStore);
 
@@ -421,14 +415,14 @@
         for (Receiver receiver : mReceivers.values()) {
             receiver.updateMonitoring(true);
         }
-        for (LocationProvider p : mProviders) {
+        for (LocationProviderManager p : mProviders) {
             applyRequirementsLocked(p);
         }
     }
 
     @GuardedBy("mLock")
     private void onPermissionsChangedLocked() {
-        for (LocationProvider p : mProviders) {
+        for (LocationProviderManager p : mProviders) {
             applyRequirementsLocked(p);
         }
     }
@@ -448,7 +442,7 @@
 
         mBatterySaverMode = newLocationMode;
 
-        for (LocationProvider p : mProviders) {
+        for (LocationProviderManager p : mProviders) {
             applyRequirementsLocked(p);
         }
     }
@@ -456,7 +450,7 @@
     @GuardedBy("mLock")
     private void onScreenStateChangedLocked() {
         if (mBatterySaverMode == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) {
-            for (LocationProvider p : mProviders) {
+            for (LocationProviderManager p : mProviders) {
                 applyRequirementsLocked(p);
             }
         }
@@ -472,7 +466,7 @@
         intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId));
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
 
-        for (LocationProvider p : mProviders) {
+        for (LocationProviderManager p : mProviders) {
             p.onUseableChangedLocked(userId);
         }
     }
@@ -527,21 +521,21 @@
 
     @GuardedBy("mLock")
     private void onBackgroundThrottleIntervalChangedLocked() {
-        for (LocationProvider provider : mProviders) {
+        for (LocationProviderManager provider : mProviders) {
             applyRequirementsLocked(provider);
         }
     }
 
     @GuardedBy("mLock")
     private void onBackgroundThrottleWhitelistChangedLocked() {
-        for (LocationProvider p : mProviders) {
+        for (LocationProviderManager p : mProviders) {
             applyRequirementsLocked(p);
         }
     }
 
     @GuardedBy("lock")
     private void onIgnoreSettingsWhitelistChangedLocked() {
-        for (LocationProvider p : mProviders) {
+        for (LocationProviderManager p : mProviders) {
             applyRequirementsLocked(p);
         }
     }
@@ -630,14 +624,15 @@
     @GuardedBy("mLock")
     private void initializeProvidersLocked() {
         // create a passive location provider, which is always enabled
-        LocationProvider passiveProviderManager = new LocationProvider(PASSIVE_PROVIDER);
+        LocationProviderManager passiveProviderManager = new LocationProviderManager(
+                PASSIVE_PROVIDER);
         addProviderLocked(passiveProviderManager);
         mPassiveProvider = new PassiveProvider(mContext, passiveProviderManager);
         passiveProviderManager.attachLocked(mPassiveProvider);
 
         if (GnssManagerService.isGnssSupported()) {
             // Create a gps location provider manager
-            LocationProvider gnssProviderManager = new LocationProvider(GPS_PROVIDER);
+            LocationProviderManager gnssProviderManager = new LocationProviderManager(GPS_PROVIDER);
             mRealProviders.add(gnssProviderManager);
             addProviderLocked(gnssProviderManager);
 
@@ -668,7 +663,8 @@
         ensureFallbackFusedProviderPresentLocked(pkgs);
 
         // bind to network provider
-        LocationProvider networkProviderManager = new LocationProvider(NETWORK_PROVIDER);
+        LocationProviderManager networkProviderManager = new LocationProviderManager(
+                NETWORK_PROVIDER);
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
                 mContext,
                 networkProviderManager,
@@ -685,7 +681,7 @@
         }
 
         // bind to fused provider
-        LocationProvider fusedProviderManager = new LocationProvider(FUSED_PROVIDER);
+        LocationProviderManager fusedProviderManager = new LocationProviderManager(FUSED_PROVIDER);
         LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
                 mContext,
                 fusedProviderManager,
@@ -758,7 +754,7 @@
                     Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
                     Integer.parseInt(fragments[8]) /* powerRequirement */,
                     Integer.parseInt(fragments[9]) /* accuracy */);
-            LocationProvider testProviderManager = new LocationProvider(name);
+            LocationProviderManager testProviderManager = new LocationProviderManager(name);
             addProviderLocked(testProviderManager);
             testProviderManager.attachLocked(
                     new MockProvider(mContext, testProviderManager, properties));
@@ -780,7 +776,7 @@
         onUserProfilesChangedLocked();
 
         // let providers know the current user has changed
-        for (LocationProvider p : mProviders) {
+        for (LocationProviderManager p : mProviders) {
             p.onUseableChangedLocked(oldUserId);
             p.onUseableChangedLocked(mCurrentUserId);
         }
@@ -789,7 +785,7 @@
     /**
      * Location provider manager, manages a LocationProvider.
      */
-    class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
+    class LocationProviderManager implements AbstractLocationProvider.LocationProviderManager {
 
         private final String mName;
 
@@ -807,7 +803,7 @@
         @Nullable
         private ProviderProperties mProperties;
 
-        private LocationProvider(String name) {
+        private LocationProviderManager(String name) {
             mName = name;
 
             mProvider = null;
@@ -944,7 +940,7 @@
                 return;
             }
             synchronized (mLock) {
-                LocationProvider gpsProvider = getLocationProviderLocked(GPS_PROVIDER);
+                LocationProviderManager gpsProvider = getLocationProviderLocked(GPS_PROVIDER);
                 if (gpsProvider == null || !gpsProvider.isUseableLocked()) {
                     Slog.w(TAG, "reportLocationBatch() called without user permission");
                     return;
@@ -1035,7 +1031,7 @@
         }
     }
 
-    private class MockLocationProvider extends LocationProvider {
+    private class MockLocationProvider extends LocationProviderManager {
 
         private ProviderRequest mCurrentRequest;
 
@@ -1185,7 +1181,8 @@
                 // See if receiver has any enabled update records.  Also note if any update records
                 // are high power (has a high power provider with an interval under a threshold).
                 for (UpdateRecord updateRecord : mUpdateRecords.values()) {
-                    LocationProvider provider = getLocationProviderLocked(updateRecord.mProvider);
+                    LocationProviderManager provider = getLocationProviderLocked(
+                            updateRecord.mProvider);
                     if (provider == null) {
                         continue;
                     }
@@ -1461,7 +1458,7 @@
     }
 
     @GuardedBy("mLock")
-    private void addProviderLocked(LocationProvider provider) {
+    private void addProviderLocked(LocationProviderManager provider) {
         Preconditions.checkState(getLocationProviderLocked(provider.getName()) == null);
 
         mProviders.add(provider);
@@ -1472,7 +1469,7 @@
     }
 
     @GuardedBy("mLock")
-    private void removeProviderLocked(LocationProvider provider) {
+    private void removeProviderLocked(LocationProviderManager provider) {
         if (mProviders.remove(provider)) {
             // it would be more correct to call this for all users, but we know this can only
             // affect the current user since providers are disabled for non-current users
@@ -1482,8 +1479,8 @@
 
     @GuardedBy("mLock")
     @Nullable
-    private LocationProvider getLocationProviderLocked(String providerName) {
-        for (LocationProvider provider : mProviders) {
+    private LocationProviderManager getLocationProviderLocked(String providerName) {
+        for (LocationProviderManager provider : mProviders) {
             if (providerName.equals(provider.getName())) {
                 return provider;
             }
@@ -1534,7 +1531,7 @@
             // network and fused providers are ok with COARSE or FINE
             return RESOLUTION_LEVEL_COARSE;
         } else {
-            for (LocationProvider lp : mProviders) {
+            for (LocationProviderManager lp : mProviders) {
                 if (!lp.getName().equals(provider)) {
                     continue;
                 }
@@ -1634,7 +1631,7 @@
     public List<String> getAllProviders() {
         synchronized (mLock) {
             ArrayList<String> providers = new ArrayList<>(mProviders.size());
-            for (LocationProvider provider : mProviders) {
+            for (LocationProviderManager provider : mProviders) {
                 String name = provider.getName();
                 if (FUSED_PROVIDER.equals(name)) {
                     continue;
@@ -1655,7 +1652,7 @@
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         synchronized (mLock) {
             ArrayList<String> providers = new ArrayList<>(mProviders.size());
-            for (LocationProvider provider : mProviders) {
+            for (LocationProviderManager provider : mProviders) {
                 String name = provider.getName();
                 if (FUSED_PROVIDER.equals(name)) {
                     continue;
@@ -1705,7 +1702,7 @@
     }
 
     @GuardedBy("mLock")
-    private void updateProviderUseableLocked(LocationProvider provider) {
+    private void updateProviderUseableLocked(LocationProviderManager provider) {
         boolean useable = provider.isUseableLocked();
 
         ArrayList<Receiver> deadReceivers = null;
@@ -1744,14 +1741,14 @@
 
     @GuardedBy("mLock")
     private void applyRequirementsLocked(String providerName) {
-        LocationProvider provider = getLocationProviderLocked(providerName);
+        LocationProviderManager provider = getLocationProviderLocked(providerName);
         if (provider != null) {
             applyRequirementsLocked(provider);
         }
     }
 
     @GuardedBy("mLock")
-    private void applyRequirementsLocked(LocationProvider provider) {
+    private void applyRequirementsLocked(LocationProviderManager provider) {
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
         WorkSource worksource = new WorkSource();
         ProviderRequest providerRequest = new ProviderRequest();
@@ -2201,7 +2198,7 @@
             throw new IllegalArgumentException("provider name must not be null");
         }
 
-        LocationProvider provider = getLocationProviderLocked(name);
+        LocationProviderManager provider = getLocationProviderLocked(name);
         if (provider == null) {
             throw new IllegalArgumentException("provider doesn't exist: " + name);
         }
@@ -2323,7 +2320,7 @@
                 // or use the fused provider
                 String name = request.getProvider();
                 if (name == null) name = LocationManager.FUSED_PROVIDER;
-                LocationProvider provider = getLocationProviderLocked(name);
+                LocationProviderManager provider = getLocationProviderLocked(name);
                 if (provider == null) return null;
 
                 // only the current user or location providers may get location this way
@@ -2453,7 +2450,7 @@
                 "Access Fine Location permission not granted to inject Location");
 
         synchronized (mLock) {
-            LocationProvider provider = getLocationProviderLocked(location.getProvider());
+            LocationProviderManager provider = getLocationProviderLocked(location.getProvider());
             if (provider == null || !provider.isUseableLocked()) {
                 return false;
             }
@@ -2624,6 +2621,10 @@
             // throw NullPointerException to remain compatible with previous implementation
             throw new NullPointerException();
         }
+
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
+
         synchronized (mLock) {
             checkResolutionLevelIsSufficientForProviderUseLocked(getCallerAllowedResolutionLevel(),
                     providerName);
@@ -2633,13 +2634,7 @@
                     LocationStatsEnums.API_SEND_EXTRA_COMMAND,
                     providerName);
 
-            // and check for ACCESS_LOCATION_EXTRA_COMMANDS
-            if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
-                    != PERMISSION_GRANTED)) {
-                throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
-            }
-
-            LocationProvider provider = getLocationProviderLocked(providerName);
+            LocationProviderManager provider = getLocationProviderLocked(providerName);
             if (provider != null) {
                 provider.sendExtraCommand(command, extras);
             }
@@ -2662,7 +2657,7 @@
     @Override
     public ProviderProperties getProviderProperties(String providerName) {
         synchronized (mLock) {
-            LocationProvider provider = getLocationProviderLocked(providerName);
+            LocationProviderManager provider = getLocationProviderLocked(providerName);
             if (provider == null) {
                 return null;
             }
@@ -2675,7 +2670,7 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
                 Manifest.permission.READ_DEVICE_CONFIG + " permission required");
         synchronized (mLock) {
-            for (LocationProvider provider : mProviders) {
+            for (LocationProviderManager provider : mProviders) {
                 if (provider.getPackagesLocked().contains(packageName)) {
                     return true;
                 }
@@ -2689,7 +2684,7 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
                 Manifest.permission.READ_DEVICE_CONFIG + " permission required");
         synchronized (mLock) {
-            LocationProvider provider = getLocationProviderLocked(providerName);
+            LocationProviderManager provider = getLocationProviderLocked(providerName);
             return provider == null ? Collections.emptyList() : provider.getPackagesLocked();
         }
     }
@@ -2758,7 +2753,7 @@
         if (FUSED_PROVIDER.equals(providerName)) return false;
 
         synchronized (mLock) {
-            LocationProvider provider = getLocationProviderLocked(providerName);
+            LocationProviderManager provider = getLocationProviderLocked(providerName);
             return provider != null && provider.isUseableLocked(userId);
         }
     }
@@ -2797,7 +2792,7 @@
     }
 
     @GuardedBy("mLock")
-    private void handleLocationChangedLocked(Location location, LocationProvider provider) {
+    private void handleLocationChangedLocked(Location location, LocationProviderManager provider) {
         if (!mProviders.contains(provider)) {
             return;
         }
@@ -3029,7 +3024,7 @@
         synchronized (mLock) {
             long identity = Binder.clearCallingIdentity();
             try {
-                LocationProvider oldProvider = getLocationProviderLocked(name);
+                LocationProviderManager oldProvider = getLocationProviderLocked(name);
                 if (oldProvider != null) {
                     removeProviderLocked(oldProvider);
                 }
@@ -3053,7 +3048,7 @@
         synchronized (mLock) {
             long identity = Binder.clearCallingIdentity();
             try {
-                LocationProvider testProvider = getLocationProviderLocked(name);
+                LocationProviderManager testProvider = getLocationProviderLocked(name);
                 if (testProvider == null || !testProvider.isMock()) {
                     return;
                 }
@@ -3061,8 +3056,8 @@
                 removeProviderLocked(testProvider);
 
                 // reinstate real provider if available
-                LocationProvider realProvider = null;
-                for (LocationProvider provider : mRealProviders) {
+                LocationProviderManager realProvider = null;
+                for (LocationProviderManager provider : mRealProviders) {
                     if (name.equals(provider.getName())) {
                         realProvider = provider;
                         break;
@@ -3086,7 +3081,7 @@
         }
 
         synchronized (mLock) {
-            LocationProvider testProvider = getLocationProviderLocked(providerName);
+            LocationProviderManager testProvider = getLocationProviderLocked(providerName);
             if (testProvider == null || !testProvider.isMock()) {
                 throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
             }
@@ -3110,7 +3105,7 @@
         }
 
         synchronized (mLock) {
-            LocationProvider testProvider = getLocationProviderLocked(providerName);
+            LocationProviderManager testProvider = getLocationProviderLocked(providerName);
             if (testProvider == null || !testProvider.isMock()) {
                 throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
             }
@@ -3128,7 +3123,7 @@
         }
 
         synchronized (mLock) {
-            LocationProvider testProvider = getLocationProviderLocked(providerName);
+            LocationProviderManager testProvider = getLocationProviderLocked(providerName);
             if (testProvider == null || !testProvider.isMock()) {
                 throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
             }
@@ -3237,7 +3232,7 @@
 
             ipw.println("Location Providers:");
             ipw.increaseIndent();
-            for (LocationProvider provider : mProviders) {
+            for (LocationProviderManager provider : mProviders) {
                 provider.dumpLocked(fd, ipw, args);
             }
             ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/LocationSettingsStore.java
index 3d18d4a..f625452 100644
--- a/services/core/java/com/android/server/location/LocationSettingsStore.java
+++ b/services/core/java/com/android/server/location/LocationSettingsStore.java
@@ -34,6 +34,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemConfig;
@@ -113,6 +114,16 @@
                 () -> SystemConfig.getInstance().getAllowIgnoreLocationSettings(), handler);
     }
 
+    /** Called when system is ready. */
+    public synchronized void onSystemReady() {
+        mLocationMode.register();
+        mBackgroundThrottleIntervalMs.register();
+        mLocationPackageBlacklist.register();
+        mLocationPackageWhitelist.register();
+        mBackgroundThrottlePackageWhitelist.register();
+        mIgnoreSettingsPackageWhitelist.register();
+    }
+
     /**
      * Retrieve if location is enabled or not.
      */
@@ -300,13 +311,25 @@
     private abstract static class ObservingSetting extends ContentObserver {
 
         private final CopyOnWriteArrayList<UserSettingChangedListener> mListeners;
+        private boolean mRegistered;
 
-        private ObservingSetting(Context context, String settingName, Handler handler) {
+        private ObservingSetting(Handler handler) {
             super(handler);
             mListeners = new CopyOnWriteArrayList<>();
+        }
+
+        protected boolean isRegistered() {
+            return mRegistered;
+        }
+
+        protected void register(Context context, Uri uri) {
+            if (mRegistered) {
+                return;
+            }
 
             context.getContentResolver().registerContentObserver(
-                    getUriFor(settingName), false, this, UserHandle.USER_ALL);
+                    uri, false, this, UserHandle.USER_ALL);
+            mRegistered = true;
         }
 
         public void addListener(UserSettingChangedListener listener) {
@@ -317,8 +340,6 @@
             mListeners.remove(listener);
         }
 
-        protected abstract Uri getUriFor(String settingName);
-
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
             for (UserSettingChangedListener listener : mListeners) {
@@ -333,20 +354,19 @@
         private final String mSettingName;
 
         private IntegerSecureSetting(Context context, String settingName, Handler handler) {
-            super(context, settingName, handler);
+            super(handler);
             mContext = context;
             mSettingName = settingName;
         }
 
+        private void register() {
+            register(mContext, Settings.Secure.getUriFor(mSettingName));
+        }
+
         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 {
@@ -354,31 +374,44 @@
         private final Context mContext;
         private final String mSettingName;
 
-        private int mCachedUserId = UserHandle.USER_NULL;
+        @GuardedBy("this")
+        private int mCachedUserId;
+        @GuardedBy("this")
         private List<String> mCachedValue;
 
         private StringListCachedSecureSetting(Context context, String settingName,
                 Handler handler) {
-            super(context, settingName, handler);
+            super(handler);
             mContext = context;
             mSettingName = settingName;
+
+            mCachedUserId = UserHandle.USER_NULL;
+        }
+
+        public synchronized void register() {
+            register(mContext, Settings.Secure.getUriFor(mSettingName));
         }
 
         public synchronized List<String> getValueForUser(int userId) {
             Preconditions.checkArgument(userId != UserHandle.USER_NULL);
 
+            List<String> value = mCachedValue;
             if (userId != mCachedUserId) {
                 String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                         mSettingName, userId);
                 if (TextUtils.isEmpty(setting)) {
-                    mCachedValue = Collections.emptyList();
+                    value = Collections.emptyList();
                 } else {
-                    mCachedValue = Arrays.asList(setting.split(","));
+                    value = Arrays.asList(setting.split(","));
                 }
-                mCachedUserId = userId;
+
+                if (isRegistered()) {
+                    mCachedUserId = userId;
+                    mCachedValue = value;
+                }
             }
 
-            return mCachedValue;
+            return value;
         }
 
         public synchronized void invalidateForUser(int userId) {
@@ -393,11 +426,6 @@
             invalidateForUser(userId);
             super.onChange(selfChange, uri, userId);
         }
-
-        @Override
-        protected Uri getUriFor(String settingName) {
-            return Settings.Secure.getUriFor(settingName);
-        }
     }
 
     private static class LongGlobalSetting extends ObservingSetting {
@@ -406,20 +434,19 @@
         private final String mSettingName;
 
         private LongGlobalSetting(Context context, String settingName, Handler handler) {
-            super(context, settingName, handler);
+            super(handler);
             mContext = context;
             mSettingName = settingName;
         }
 
+        public void register() {
+            register(mContext, Settings.Global.getUriFor(mSettingName));
+        }
+
         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 StringSetCachedGlobalSetting extends ObservingSetting {
@@ -428,29 +455,42 @@
         private final String mSettingName;
         private final Supplier<ArraySet<String>> mBaseValuesSupplier;
 
+        @GuardedBy("this")
         private boolean mValid;
+        @GuardedBy("this")
         private ArraySet<String> mCachedValue;
 
         private StringSetCachedGlobalSetting(Context context, String settingName,
                 Supplier<ArraySet<String>> baseValuesSupplier, Handler handler) {
-            super(context, settingName, handler);
+            super(handler);
             mContext = context;
             mSettingName = settingName;
             mBaseValuesSupplier = baseValuesSupplier;
+
+            mValid = false;
+        }
+
+        public synchronized void register() {
+            register(mContext, Settings.Global.getUriFor(mSettingName));
         }
 
         public synchronized Set<String> getValue() {
+            ArraySet<String> value = mCachedValue;
             if (!mValid) {
-                mCachedValue = new ArraySet<>(mBaseValuesSupplier.get());
+                value = new ArraySet<>(mBaseValuesSupplier.get());
                 String setting = Settings.Global.getString(mContext.getContentResolver(),
                         mSettingName);
                 if (!TextUtils.isEmpty(setting)) {
-                    mCachedValue.addAll(Arrays.asList(setting.split(",")));
+                    value.addAll(Arrays.asList(setting.split(",")));
                 }
-                mValid = true;
+
+                if (isRegistered()) {
+                    mValid = true;
+                    mCachedValue = value;
+                }
             }
 
-            return mCachedValue;
+            return value;
         }
 
         public synchronized void invalidate() {
@@ -463,10 +503,5 @@
             invalidate();
             super.onChange(selfChange, uri, userId);
         }
-
-        @Override
-        protected Uri getUriFor(String settingName) {
-            return Settings.Global.getUriFor(settingName);
-        }
     }
 }
diff --git a/services/core/java/com/android/server/LocationUsageLogger.java b/services/core/java/com/android/server/location/LocationUsageLogger.java
similarity index 72%
rename from services/core/java/com/android/server/LocationUsageLogger.java
rename to services/core/java/com/android/server/location/LocationUsageLogger.java
index a8a3cc4..755438b 100644
--- a/services/core/java/com/android/server/LocationUsageLogger.java
+++ b/services/core/java/com/android/server/location/LocationUsageLogger.java
@@ -14,37 +14,113 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.location;
+
+import static com.android.server.LocationManagerService.TAG;
 
 import android.app.ActivityManager;
 import android.location.Geofence;
 import android.location.LocationManager;
 import android.location.LocationRequest;
-import android.os.SystemClock;
 import android.stats.location.LocationStatsEnums;
 import android.util.Log;
 import android.util.StatsLog;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.time.Instant;
 
 /**
  * Logger for Location API usage logging.
  */
 public class LocationUsageLogger {
-    private static final String TAG = "LocationUsageLogger";
-    private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int ONE_SEC_IN_MILLIS = 1000;
     private static final int ONE_MINUTE_IN_MILLIS = 60000;
     private static final int ONE_HOUR_IN_MILLIS = 3600000;
 
-    private long mLastApiUsageLogHour = 0;
-
-    private int mApiUsageLogHourlyCount = 0;
-
     private static final int API_USAGE_LOG_HOURLY_CAP = 60;
 
-    private static int providerNameToStatsdEnum(String provider) {
+    @GuardedBy("this")
+    private long mLastApiUsageLogHour = 0;
+    @GuardedBy("this")
+    private int mApiUsageLogHourlyCount = 0;
+
+    /**
+     * Log a location API usage event.
+     */
+    public void logLocationApiUsage(int usageType, int apiInUse,
+            String packageName, LocationRequest locationRequest,
+            boolean hasListener, boolean hasIntent,
+            Geofence geofence, int activityImportance) {
+        try {
+            if (hitApiUsageLogCap()) {
+                return;
+            }
+
+            boolean isLocationRequestNull = locationRequest == null;
+            boolean isGeofenceNull = geofence == null;
+
+            StatsLog.write(StatsLog.LOCATION_MANAGER_API_USAGE_REPORTED, usageType,
+                    apiInUse, packageName,
+                    isLocationRequestNull
+                        ? LocationStatsEnums.PROVIDER_UNKNOWN
+                        : bucketizeProvider(locationRequest.getProvider()),
+                    isLocationRequestNull
+                        ? LocationStatsEnums.QUALITY_UNKNOWN
+                        : locationRequest.getQuality(),
+                    isLocationRequestNull
+                        ? LocationStatsEnums.INTERVAL_UNKNOWN
+                        : bucketizeInterval(locationRequest.getInterval()),
+                    isLocationRequestNull
+                        ? LocationStatsEnums.DISTANCE_UNKNOWN
+                        : bucketizeDistance(
+                                locationRequest.getSmallestDisplacement()),
+                    isLocationRequestNull ? 0 : locationRequest.getNumUpdates(),
+                    // only log expireIn for USAGE_STARTED
+                    isLocationRequestNull || usageType == LocationStatsEnums.USAGE_ENDED
+                        ? LocationStatsEnums.EXPIRATION_UNKNOWN
+                        : bucketizeExpireIn(locationRequest.getExpireIn()),
+                    getCallbackType(apiInUse, hasListener, hasIntent),
+                    isGeofenceNull
+                        ? LocationStatsEnums.RADIUS_UNKNOWN
+                        : bucketizeRadius(geofence.getRadius()),
+                    categorizeActivityImportance(activityImportance));
+        } catch (Exception e) {
+            // Swallow exceptions to avoid crashing LMS.
+            Log.w(TAG, "Failed to log API usage to statsd.", e);
+        }
+    }
+
+    /**
+     * Log a location API usage event.
+     */
+    public void logLocationApiUsage(int usageType, int apiInUse, String providerName) {
+        try {
+            if (hitApiUsageLogCap()) {
+                return;
+            }
+
+            StatsLog.write(StatsLog.LOCATION_MANAGER_API_USAGE_REPORTED, usageType, apiInUse,
+                    /* package_name= */ null,
+                    bucketizeProvider(providerName),
+                    LocationStatsEnums.QUALITY_UNKNOWN,
+                    LocationStatsEnums.INTERVAL_UNKNOWN,
+                    LocationStatsEnums.DISTANCE_UNKNOWN,
+                    /* numUpdates= */ 0,
+                    LocationStatsEnums.EXPIRATION_UNKNOWN,
+                    getCallbackType(
+                            apiInUse,
+                            /* isListenerNull= */ true,
+                            /* isIntentNull= */ true),
+                    /* bucketizedRadius= */ 0,
+                    LocationStatsEnums.IMPORTANCE_UNKNOWN);
+        } catch (Exception e) {
+            Log.w(TAG, "Failed to log API usage to statsd.", e);
+        }
+    }
+
+    private static int bucketizeProvider(String provider) {
         if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
             return LocationStatsEnums.PROVIDER_NETWORK;
         } else if (LocationManager.GPS_PROVIDER.equals(provider)) {
@@ -58,8 +134,7 @@
         }
     }
 
-    private static int bucketizeIntervalToStatsdEnum(long interval) {
-        // LocationManager already converts negative values to 0.
+    private static int bucketizeInterval(long interval) {
         if (interval < ONE_SEC_IN_MILLIS) {
             return LocationStatsEnums.INTERVAL_BETWEEN_0_SEC_AND_1_SEC;
         } else if (interval < ONE_SEC_IN_MILLIS * 5) {
@@ -75,9 +150,8 @@
         }
     }
 
-    private static int bucketizeSmallestDisplacementToStatsdEnum(float smallestDisplacement) {
-        // LocationManager already converts negative values to 0.
-        if (smallestDisplacement == 0) {
+    private static int bucketizeDistance(float smallestDisplacement) {
+        if (smallestDisplacement <= 0) {
             return LocationStatsEnums.DISTANCE_ZERO;
         } else if (smallestDisplacement > 0 && smallestDisplacement <= 100) {
             return LocationStatsEnums.DISTANCE_BETWEEN_0_AND_100;
@@ -86,7 +160,7 @@
         }
     }
 
-    private static int bucketizeRadiusToStatsdEnum(float radius) {
+    private static int bucketizeRadius(float radius) {
         if (radius < 0) {
             return LocationStatsEnums.RADIUS_NEGATIVE;
         } else if (radius < 100) {
@@ -104,14 +178,11 @@
         }
     }
 
-    private static int getBucketizedExpireIn(long expireAt) {
-        if (expireAt == Long.MAX_VALUE) {
+    private static int bucketizeExpireIn(long expireIn) {
+        if (expireIn == Long.MAX_VALUE) {
             return LocationStatsEnums.EXPIRATION_NO_EXPIRY;
         }
 
-        long elapsedRealtime = SystemClock.elapsedRealtime();
-        long expireIn = Math.max(0, expireAt - elapsedRealtime);
-
         if (expireIn < 20 * ONE_SEC_IN_MILLIS) {
             return LocationStatsEnums.EXPIRATION_BETWEEN_0_AND_20_SEC;
         } else if (expireIn < ONE_MINUTE_IN_MILLIS) {
@@ -129,8 +200,8 @@
         if (importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
             return LocationStatsEnums.IMPORTANCE_TOP;
         } else if (importance == ActivityManager
-                                    .RunningAppProcessInfo
-                                    .IMPORTANCE_FOREGROUND_SERVICE) {
+                .RunningAppProcessInfo
+                .IMPORTANCE_FOREGROUND_SERVICE) {
             return LocationStatsEnums.IMPORTANCE_FORGROUND_SERVICE;
         } else {
             return LocationStatsEnums.IMPORTANCE_BACKGROUND;
@@ -154,117 +225,16 @@
         }
     }
 
-    // Update the hourly count of APIUsage log event.
-    // Returns false if hit the hourly log cap.
-    private boolean checkApiUsageLogCap() {
-        if (D) {
-            Log.d(TAG, "checking APIUsage log cap.");
-        }
-
+    private synchronized boolean hitApiUsageLogCap() {
         long currentHour = Instant.now().toEpochMilli() / ONE_HOUR_IN_MILLIS;
         if (currentHour > mLastApiUsageLogHour) {
             mLastApiUsageLogHour = currentHour;
             mApiUsageLogHourlyCount = 0;
-            return true;
+            return false;
         } else {
             mApiUsageLogHourlyCount = Math.min(
-                mApiUsageLogHourlyCount + 1, API_USAGE_LOG_HOURLY_CAP);
-            return mApiUsageLogHourlyCount < API_USAGE_LOG_HOURLY_CAP;
-        }
-    }
-
-    /**
-     * Log a Location API usage event to Statsd.
-     * Logging event is capped at 60 per hour. Usage events exceeding
-     * the cap will be dropped by LocationUsageLogger.
-     */
-    public void logLocationApiUsage(int usageType, int apiInUse,
-            String packageName, LocationRequest locationRequest,
-            boolean hasListener, boolean hasIntent,
-            Geofence geofence, int activityImportance) {
-        try {
-            if (!checkApiUsageLogCap()) {
-                return;
-            }
-
-            boolean isLocationRequestNull = locationRequest == null;
-            boolean isGeofenceNull = geofence == null;
-            if (D) {
-                Log.d(TAG, "log API Usage to statsd. usageType: " + usageType + ", apiInUse: "
-                        + apiInUse + ", packageName: " + (packageName == null ? "" : packageName)
-                        + ", locationRequest: "
-                        + (isLocationRequestNull ? "" : locationRequest.toString())
-                        + ", hasListener: " + hasListener
-                        + ", hasIntent: " + hasIntent
-                        + ", geofence: "
-                        + (isGeofenceNull ? "" : geofence.toString())
-                        + ", importance: " + activityImportance);
-            }
-
-            StatsLog.write(StatsLog.LOCATION_MANAGER_API_USAGE_REPORTED, usageType,
-                    apiInUse, packageName,
-                    isLocationRequestNull
-                        ? LocationStatsEnums.PROVIDER_UNKNOWN
-                        : providerNameToStatsdEnum(locationRequest.getProvider()),
-                    isLocationRequestNull
-                        ? LocationStatsEnums.QUALITY_UNKNOWN
-                        : locationRequest.getQuality(),
-                    isLocationRequestNull
-                        ? LocationStatsEnums.INTERVAL_UNKNOWN
-                        : bucketizeIntervalToStatsdEnum(locationRequest.getInterval()),
-                    isLocationRequestNull
-                        ? LocationStatsEnums.DISTANCE_UNKNOWN
-                        : bucketizeSmallestDisplacementToStatsdEnum(
-                                locationRequest.getSmallestDisplacement()),
-                    isLocationRequestNull ? 0 : locationRequest.getNumUpdates(),
-                    // only log expireIn for USAGE_STARTED
-                    isLocationRequestNull || usageType == LocationStatsEnums.USAGE_ENDED
-                        ? LocationStatsEnums.EXPIRATION_UNKNOWN
-                        : getBucketizedExpireIn(locationRequest.getExpireAt()),
-                    getCallbackType(apiInUse, hasListener, hasIntent),
-                    isGeofenceNull
-                        ? LocationStatsEnums.RADIUS_UNKNOWN
-                        : bucketizeRadiusToStatsdEnum(geofence.getRadius()),
-                    categorizeActivityImportance(activityImportance));
-        } catch (Exception e) {
-            // Swallow exceptions to avoid crashing LMS.
-            Log.w(TAG, "Failed to log API usage to statsd.", e);
-        }
-    }
-
-    /**
-     * Log a Location API usage event to Statsd.
-     * Logging event is capped at 60 per hour. Usage events exceeding
-     * the cap will be dropped by LocationUsageLogger.
-     */
-    public void logLocationApiUsage(int usageType, int apiInUse, String providerName) {
-        try {
-            if (!checkApiUsageLogCap()) {
-                return;
-            }
-
-            if (D) {
-                Log.d(TAG, "log API Usage to statsd. usageType: " + usageType + ", apiInUse: "
-                        + apiInUse + ", providerName: " + providerName);
-            }
-
-            StatsLog.write(StatsLog.LOCATION_MANAGER_API_USAGE_REPORTED, usageType, apiInUse,
-                    /* package_name= */ null,
-                    providerNameToStatsdEnum(providerName),
-                    LocationStatsEnums.QUALITY_UNKNOWN,
-                    LocationStatsEnums.INTERVAL_UNKNOWN,
-                    LocationStatsEnums.DISTANCE_UNKNOWN,
-                    /* numUpdates= */ 0,
-                    LocationStatsEnums.EXPIRATION_UNKNOWN,
-                    getCallbackType(
-                            apiInUse,
-                            /* isListenerNull= */ true,
-                            /* isIntentNull= */ true),
-                    /* bucketizedRadius= */ 0,
-                    LocationStatsEnums.IMPORTANCE_UNKNOWN);
-        } catch (Exception e) {
-            // Swallow exceptions to avoid crashing LMS.
-            Log.w(TAG, "Failed to log API usage to statsd.", e);
+                    mApiUsageLogHourlyCount + 1, API_USAGE_LOG_HOURLY_CAP);
+            return mApiUsageLogHourlyCount >= API_USAGE_LOG_HOURLY_CAP;
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java
index 9692c25..8b5444c 100644
--- a/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java
@@ -61,6 +61,7 @@
 import com.android.server.location.GnssNavigationMessageProvider;
 import com.android.server.location.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative;
 import com.android.server.location.GnssStatusListenerHelper;
+import com.android.server.location.LocationUsageLogger;
 
 import org.junit.Before;
 import org.junit.Test;