Revert "Move LocationManagerService to background thread"

This reverts commit b2fcddda1e0cfcaf98e4f2d58b8bc16501644e8a.

Reason for revert: May cause deadlock between bg and main thread.

Change-Id: I0cabbae7de81eed68981d2174240c7e1c746c121
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5cea56e..2346cfc 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -64,6 +64,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -86,7 +87,6 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
 import com.android.server.location.AbstractLocationProvider;
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.GeocoderProxy;
@@ -117,11 +117,6 @@
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -176,7 +171,10 @@
     private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
 
     private final Context mContext;
-    private AppOpsManager mAppOps;
+    private final AppOpsManager mAppOps;
+
+    // used internally for synchronization
+    private final Object mLock = new Object();
 
     // --- fields below are final after systemRunning() ---
     private LocationFudger mLocationFudger;
@@ -188,7 +186,7 @@
     private GeocoderProxy mGeocodeProvider;
     private GnssStatusListenerHelper mGnssStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
-    private final Handler mHandler;
+    private LocationWorkerHandler mLocationHandler;
     private PassiveProvider mPassiveProvider;  // track passive provider for special cases
     private LocationBlacklist mBlacklist;
     private GnssMeasurementsProvider mGnssMeasurementsProvider;
@@ -197,6 +195,8 @@
     private boolean mLocationControllerExtraPackageEnabled;
     private IGpsGeofenceHardware mGpsGeofenceProxy;
 
+    // --- fields below are protected by mLock ---
+
     // Mock (test) providers
     private final HashMap<String, MockProvider> mMockProviders =
             new HashMap<>();
@@ -205,8 +205,8 @@
     private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
 
     // currently installed providers (with mocks replacing real providers)
-    private final CopyOnWriteArrayList<LocationProvider> mProviders =
-            new CopyOnWriteArrayList<>();
+    private final ArrayList<LocationProvider> mProviders =
+            new ArrayList<>();
 
     // real providers, saved here when mocked out
     private final HashMap<String, LocationProvider> mRealProviders =
@@ -232,8 +232,8 @@
 
     // all providers that operate over proxy, for authorizing incoming location and whitelisting
     // throttling
-    private final CopyOnWriteArrayList<LocationProviderProxy> mProxyProviders =
-            new CopyOnWriteArrayList<>();
+    private final ArrayList<LocationProviderProxy> mProxyProviders =
+            new ArrayList<>();
 
     private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
 
@@ -246,6 +246,9 @@
     private int mCurrentUserId = UserHandle.USER_SYSTEM;
     private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
 
+    // Maximum age of last location returned to clients with foreground-only location permissions.
+    private long mLastLocationMaxAgeMs;
+
     private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
 
     private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -258,7 +261,7 @@
     public LocationManagerService(Context context) {
         super();
         mContext = context;
-        mHandler = BackgroundThread.getHandler();
+        mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
 
         // Let the package manager query which are the default location
         // providers as they get certain permissions granted by default.
@@ -268,92 +271,132 @@
                 userId -> mContext.getResources().getStringArray(
                         com.android.internal.R.array.config_locationProviderPackageNames));
 
+        if (D) Log.d(TAG, "Constructed");
+
         // most startup is deferred until systemRunning()
     }
 
     public void systemRunning() {
-        runInternal(this::initialize);
-    }
+        synchronized (mLock) {
+            if (D) Log.d(TAG, "systemRunning()");
 
-    private void initialize() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+            // fetch package manager
+            mPackageManager = mContext.getPackageManager();
 
-        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);
+            // fetch power manager
+            mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
 
-        // prepare mHandler's dependents
-        mLocationFudger = new LocationFudger(mContext, mHandler);
-        mBlacklist = new LocationBlacklist(mContext, mHandler);
-        mBlacklist.init();
-        mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+            // fetch activity manager
+            mActivityManager
+                    = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
-        mAppOps.startWatchingMode(
-                AppOpsManager.OP_COARSE_LOCATION,
-                null,
-                AppOpsManager.WATCH_FOREGROUND_CHANGES,
-                new AppOpsManager.OnOpChangedInternalListener() {
-                    public void onOpChanged(int op, String packageName) {
-                        mHandler.post(() -> onAppOpChanged());
-                    }
-                });
+            // prepare worker thread
+            mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
 
-        mPackageManager.addOnPermissionsChangeListener(
-                uid -> runInternal(this::onPermissionsChanged));
+            // prepare mLocationHandler's dependents
+            mLocationFudger = new LocationFudger(mContext, mLocationHandler);
+            mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
+            mBlacklist.init();
+            mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
 
-        mActivityManager.addOnUidImportanceListener(
-                (uid, importance) -> mHandler.post(
-                        () -> onUidImportanceChanged(uid, importance)),
-                FOREGROUND_IMPORTANCE_CUTOFF);
+            // Monitor for app ops mode changes.
+            AppOpsManager.OnOpChangedListener callback
+                    = new AppOpsManager.OnOpChangedInternalListener() {
+                public void onOpChanged(int op, String packageName) {
+                            mLocationHandler.post(() -> {
+                                synchronized (mLock) {
+                                    for (Receiver receiver : mReceivers.values()) {
+                                        receiver.updateMonitoring(true);
+                                    }
+                                    applyAllProviderRequirementsLocked();
+                                }
+                            });
+                }
+            };
+            mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
+                    AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
 
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        updateUserProfiles(mCurrentUserId);
+            PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
+                synchronized (mLock) {
+                    applyAllProviderRequirementsLocked();
+                }
+            };
+            mPackageManager.addOnPermissionsChangeListener(permissionListener);
 
-        updateBackgroundThrottlingWhitelist();
+            // listen for background/foreground changes
+            ActivityManager.OnUidImportanceListener uidImportanceListener =
+                    (uid, importance) -> mLocationHandler.post(
+                            () -> onUidImportanceChanged(uid, importance));
+            mActivityManager.addOnUidImportanceListener(uidImportanceListener,
+                    FOREGROUND_IMPORTANCE_CUTOFF);
 
-        // prepare providers
-        loadProvidersLocked();
-        updateProvidersSettings();
-        for (LocationProvider provider : mProviders) {
-            applyRequirements(provider.getName());
+            mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            updateUserProfiles(mCurrentUserId);
+
+            updateBackgroundThrottlingWhitelistLocked();
+            updateLastLocationMaxAgeLocked();
+
+            // prepare providers
+            loadProvidersLocked();
+            updateProvidersSettingsLocked();
+            for (LocationProvider provider : mProviders) {
+                applyRequirementsLocked(provider.getName());
+            }
         }
 
         // listen for settings changes
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
-                new ContentObserver(mHandler) {
+                new ContentObserver(mLocationHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        onProviderAllowedChanged();
+                        synchronized (mLock) {
+                            updateProvidersSettingsLocked();
+                        }
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
                 true,
-                new ContentObserver(mHandler) {
+                new ContentObserver(mLocationHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        onBackgroundThrottleIntervalChanged();
+                        synchronized (mLock) {
+                            for (LocationProvider provider : mProviders) {
+                                applyRequirementsLocked(provider.getName());
+                            }
+                        }
                     }
                 }, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
+                true,
+                new ContentObserver(mLocationHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        synchronized (mLock) {
+                            updateLastLocationMaxAgeLocked();
+                        }
+                    }
+                }
+        );
+        mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(
                         Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
                 true,
-                new ContentObserver(mHandler) {
+                new ContentObserver(mLocationHandler) {
                     @Override
                     public void onChange(boolean selfChange) {
-                        onBackgroundThrottleWhitelistChanged();
+                        synchronized (mLock) {
+                            updateBackgroundThrottlingWhitelistLocked();
+                            for (LocationProvider provider : mProviders) {
+                                applyRequirementsLocked(provider.getName());
+                            }
+                        }
                     }
                 }, UserHandle.USER_ALL);
 
-        new PackageMonitor() {
-            @Override
-            public void onPackageDisappeared(String packageName, int reason) {
-                LocationManagerService.this.onPackageDisappeared(packageName);
-            }
-        }.register(mContext, mHandler.getLooper(), true);
+        mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
 
         // listen for user change
         IntentFilter intentFilter = new IntentFilter();
@@ -372,170 +415,73 @@
                     updateUserProfiles(mCurrentUserId);
                 }
             }
-        }, UserHandle.ALL, intentFilter, null, mHandler);
-    }
-
-    // will block until completion and propagate exceptions, and thus should be used from binder
-    // threads, particuarily binder threads from components that sit above LMS (ie, not location
-    // providers).
-    private void runFromBinderBlocking(Runnable runnable) throws RemoteException {
-        runFromBinderBlocking(Executors.callable(runnable));
-    }
-
-    // will block until completion and propagate exceptions, and thus should be used from binder
-    // threads, particuarily binder threads from components that sit above LMS (ie, not location
-    // providers).
-    private <T> T runFromBinderBlocking(Callable<T> callable) throws RemoteException {
-        FutureTask<T> task = new FutureTask<>(callable);
-        long identity = Binder.clearCallingIdentity();
-        try {
-            runInternal(task);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        try {
-            return task.get();
-        } catch (ExecutionException e) {
-            // binder calls can handle 3 types of exceptions, runtimeexception and error (which can
-            // be thrown any time), and remote exception. we transfer all of these exceptions from
-            // the execution thread to the binder thread so that they may propagate normally. note
-            // that we are loosing some context in doing so (losing the stack trace from the binder
-            // thread).
-            if (e.getCause() instanceof RemoteException) {
-                throw (RemoteException) e.getCause();
-            } else if (e.getCause() instanceof RuntimeException) {
-                throw (RuntimeException) e.getCause();
-            } else if (e.getCause() instanceof Error) {
-                throw (Error) e.getCause();
-            }
-
-            // callers should not throw checked exceptions
-            Log.wtf(TAG, "caller threw checked exception", e);
-            throw new UnsupportedOperationException(e);
-        } catch (InterruptedException e) {
-            throw new RemoteException("Binder call interrupted", e, true, true);
-        }
-    }
-
-    // will return immediately and will not propagate exceptions. should be used for non-binder work
-    // that needs to be shifted onto the location thread, primarily listeners that do not support
-    // running on arbitrary threads.
-    private void runInternal(Runnable runnable) {
-        // it would be a better use of resources to use locks to manage cross thread access to
-        // various pieces on information. however, the history of the location package has mostly
-        // shown that this is difficult to maintain in a multi-dev environment, and tends to always
-        // lead towards the use of uber-locks and deadlocks. using a single thread to run everything
-        // is more understandable for most devs, and seems less likely to result in future bugs
-        if (Looper.myLooper() == mHandler.getLooper()) {
-            runnable.run();
-        } else {
-            mHandler.post(runnable);
-        }
-    }
-
-    private void onAppOpChanged() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-        for (Receiver receiver : mReceivers.values()) {
-            receiver.updateMonitoring(true);
-        }
-        for (LocationProvider p : mProviders) {
-            applyRequirements(p.getName());
-        }
-    }
-
-    private void onPermissionsChanged() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-        for (LocationProvider p : mProviders) {
-            applyRequirements(p.getName());
-        }
-    }
-
-    private void onProviderAllowedChanged() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-        updateProvidersSettings();
-    }
-
-    private void onPackageDisappeared(String packageName) {
-        ArrayList<Receiver> deadReceivers = null;
-
-        for (Receiver receiver : mReceivers.values()) {
-            if (receiver.mIdentity.mPackageName.equals(packageName)) {
-                if (deadReceivers == null) {
-                    deadReceivers = new ArrayList<>();
-                }
-                deadReceivers.add(receiver);
-            }
-        }
-
-        // perform removal outside of mReceivers loop
-        if (deadReceivers != null) {
-            for (Receiver receiver : deadReceivers) {
-                removeUpdates(receiver);
-            }
-        }
+        }, UserHandle.ALL, intentFilter, null, mLocationHandler);
     }
 
     private void onUidImportanceChanged(int uid, int importance) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         boolean foreground = isImportanceForeground(importance);
         HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
-        for (Entry<String, ArrayList<UpdateRecord>> entry
-                : mRecordsByProvider.entrySet()) {
-            String provider = entry.getKey();
-            for (UpdateRecord record : entry.getValue()) {
-                if (record.mReceiver.mIdentity.mUid == uid
-                        && record.mIsForegroundUid != foreground) {
+        synchronized (mLock) {
+            for (Entry<String, ArrayList<UpdateRecord>> entry
+                    : mRecordsByProvider.entrySet()) {
+                String provider = entry.getKey();
+                for (UpdateRecord record : entry.getValue()) {
+                    if (record.mReceiver.mIdentity.mUid == uid
+                            && record.mIsForegroundUid != foreground) {
+                        if (D) {
+                            Log.d(TAG, "request from uid " + uid + " is now "
+                                    + (foreground ? "foreground" : "background)"));
+                        }
+                        record.updateForeground(foreground);
+
+                        if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+                            affectedProviders.add(provider);
+                        }
+                    }
+                }
+            }
+            for (String provider : affectedProviders) {
+                applyRequirementsLocked(provider);
+            }
+
+            for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
+                Identity callerIdentity = entry.getValue();
+                if (callerIdentity.mUid == uid) {
                     if (D) {
-                        Log.d(TAG, "request from uid " + uid + " is now "
+                        Log.d(TAG, "gnss measurements listener from uid " + uid
+                                + " is now " + (foreground ? "foreground" : "background)"));
+                    }
+                    if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+                        mGnssMeasurementsProvider.addListener(
+                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+                                callerIdentity.mUid, callerIdentity.mPackageName);
+                    } else {
+                        mGnssMeasurementsProvider.removeListener(
+                                IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+                    }
+                }
+            }
+
+            for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
+                Identity callerIdentity = entry.getValue();
+                if (callerIdentity.mUid == uid) {
+                    if (D) {
+                        Log.d(TAG, "gnss navigation message listener from uid "
+                                + uid + " is now "
                                 + (foreground ? "foreground" : "background)"));
                     }
-                    record.updateForeground(foreground);
-
-                    if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
-                        affectedProviders.add(provider);
+                    if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+                        mGnssNavigationMessageProvider.addListener(
+                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+                                callerIdentity.mUid, callerIdentity.mPackageName);
+                    } else {
+                        mGnssNavigationMessageProvider.removeListener(
+                                IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
                     }
                 }
             }
-        }
-        for (String provider : affectedProviders) {
-            applyRequirements(provider);
-        }
 
-        for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
-            Identity callerIdentity = entry.getValue();
-            if (callerIdentity.mUid == uid) {
-                if (D) {
-                    Log.d(TAG, "gnss measurements listener from uid " + uid
-                            + " is now " + (foreground ? "foreground" : "background)"));
-                }
-                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                    mGnssMeasurementsProvider.addListener(
-                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
-                            callerIdentity.mUid, callerIdentity.mPackageName);
-                } else {
-                    mGnssMeasurementsProvider.removeListener(
-                            IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
-                }
-            }
-        }
-
-        for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
-            Identity callerIdentity = entry.getValue();
-            if (callerIdentity.mUid == uid) {
-                if (D) {
-                    Log.d(TAG, "gnss navigation message listener from uid "
-                            + uid + " is now "
-                            + (foreground ? "foreground" : "background)"));
-                }
-                if (foreground || isThrottlingExemptLocked(entry.getValue())) {
-                    mGnssNavigationMessageProvider.addListener(
-                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
-                            callerIdentity.mUid, callerIdentity.mPackageName);
-                } else {
-                    mGnssNavigationMessageProvider.removeListener(
-                            IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
-                }
-            }
+            // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
         }
     }
 
@@ -543,33 +489,31 @@
         return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
     }
 
-    private void onBackgroundThrottleIntervalChanged() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-        for (LocationProvider provider : mProviders) {
-            applyRequirements(provider.getName());
-        }
-    }
-
-    private void onBackgroundThrottleWhitelistChanged() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-        updateBackgroundThrottlingWhitelist();
-        for (LocationProvider provider : mProviders) {
-            applyRequirements(provider.getName());
-        }
-    }
-
+    /**
+     * Makes a list of userids that are related to the current user. This is
+     * relevant when using managed profiles. Otherwise the list only contains
+     * the current user.
+     *
+     * @param currentUserId the current user, who might have an alter-ego.
+     */
     private void updateUserProfiles(int currentUserId) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-        mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(currentUserId);
+        int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
+        synchronized (mLock) {
+            mCurrentUserProfiles = profileIds;
+        }
     }
 
+    /**
+     * Checks if the specified userId matches any of the current foreground
+     * users stored in mCurrentUserProfiles.
+     */
     private boolean isCurrentProfile(int userId) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-        return ArrayUtils.contains(mCurrentUserProfiles, userId);
+        synchronized (mLock) {
+            return ArrayUtils.contains(mCurrentUserProfiles, userId);
+        }
     }
 
     private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         PackageManager pm = mContext.getPackageManager();
         String systemPackageName = mContext.getPackageName();
         ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
@@ -640,13 +584,12 @@
     }
 
     private void loadProvidersLocked() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         // create a passive location provider, which is always enabled
         LocationProvider passiveProviderManager = new LocationProvider(
                 LocationManager.PASSIVE_PROVIDER);
         PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
 
-        addProvider(passiveProviderManager);
+        addProviderLocked(passiveProviderManager);
         mPassiveProvider = passiveProvider;
 
         if (GnssLocationProvider.isSupported()) {
@@ -655,14 +598,14 @@
                     LocationManager.GPS_PROVIDER);
             GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
                     gnssProviderManager,
-                    mHandler.getLooper());
+                    mLocationHandler.getLooper());
 
             mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
             mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
             mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
             mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
             mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
-            addProvider(gnssProviderManager);
+            addProviderLocked(gnssProviderManager);
             mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
             mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
             mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
@@ -704,7 +647,7 @@
         if (networkProvider != null) {
             mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
             mProxyProviders.add(networkProvider);
-            addProvider(networkProviderManager);
+            addProviderLocked(networkProviderManager);
         } else {
             Slog.w(TAG, "no network location provider found");
         }
@@ -720,7 +663,7 @@
                 com.android.internal.R.string.config_fusedLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (fusedProvider != null) {
-            addProvider(fusedProviderManager);
+            addProviderLocked(fusedProviderManager);
             mProxyProviders.add(fusedProvider);
             mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
         } else {
@@ -785,22 +728,28 @@
                     Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
                     Integer.parseInt(fragments[8]) /* powerRequirement */,
                     Integer.parseInt(fragments[9]) /* accuracy */);
-            addTestProvider(name, properties);
+            addTestProviderLocked(name, properties);
         }
     }
 
+    /**
+     * Called when the device's active user changes.
+     *
+     * @param userId the new active user's UserId
+     */
     private void switchUser(int userId) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (mCurrentUserId == userId) {
             return;
         }
         mBlacklist.switchUser(userId);
-        mHandler.removeMessages(MSG_LOCATION_CHANGED);
-        mLastLocation.clear();
-        mLastLocationCoarseInterval.clear();
-        updateUserProfiles(userId);
-        updateProvidersSettings();
-        mCurrentUserId = userId;
+        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
+        synchronized (mLock) {
+            mLastLocation.clear();
+            mLastLocationCoarseInterval.clear();
+            updateUserProfiles(userId);
+            updateProvidersSettingsLocked();
+            mCurrentUserId = userId;
+        }
     }
 
     private static final class Identity {
@@ -859,13 +808,10 @@
         }
 
         public void setRequest(ProviderRequest request, WorkSource workSource) {
-            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             mProvider.setRequest(request, workSource);
         }
 
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
             pw.println(mName + " provider:");
             pw.println(" setting=" + mSettingsEnabled);
             pw.println(" enabled=" + mEnabled);
@@ -874,38 +820,34 @@
         }
 
         public long getStatusUpdateTime() {
-            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             return mProvider.getStatusUpdateTime();
         }
 
         public int getStatus(Bundle extras) {
-            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             return mProvider.getStatus(extras);
         }
 
         public void sendExtraCommand(String command, Bundle extras) {
-            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
             mProvider.sendExtraCommand(command, extras);
         }
 
         // called from any thread
         @Override
         public void onReportLocation(Location location) {
-            // no security check necessary because this is coming from an internal-only interface
-            runInternal(() -> LocationManagerService.this.handleLocationChanged(location,
+            runOnHandler(() -> LocationManagerService.this.reportLocation(location,
                     mProvider == mPassiveProvider));
         }
 
         // called from any thread
         @Override
         public void onReportLocation(List<Location> locations) {
-            LocationManagerService.this.reportLocationBatch(locations);
+            runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
         }
 
         // called from any thread
         @Override
         public void onSetEnabled(boolean enabled) {
-            runInternal(() -> {
+            runOnHandler(() -> {
                 if (enabled == mEnabled) {
                     return;
                 }
@@ -930,15 +872,17 @@
                         Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
                         "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
 
-                if (!enabled) {
-                    // If any provider has been disabled, clear all last locations for all
-                    // providers. This is to be on the safe side in case a provider has location
-                    // derived from this disabled provider.
-                    mLastLocation.clear();
-                    mLastLocationCoarseInterval.clear();
-                }
+                synchronized (mLock) {
+                    if (!enabled) {
+                        // If any provider has been disabled, clear all last locations for all
+                        // providers. This is to be on the safe side in case a provider has location
+                        // derived from this disabled provider.
+                        mLastLocation.clear();
+                        mLastLocationCoarseInterval.clear();
+                    }
 
-                updateProviderListeners(mName);
+                    updateProviderListenersLocked(mName);
+                }
 
                 mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
                         UserHandle.ALL);
@@ -947,27 +891,34 @@
 
         @Override
         public void onSetProperties(ProviderProperties properties) {
-            runInternal(() -> {
-                mProperties = properties;
-            });
+            runOnHandler(() -> mProperties = properties);
         }
 
         private void setSettingsEnabled(boolean enabled) {
-            Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-            if (mSettingsEnabled == enabled) {
-                return;
-            }
+            synchronized (mLock) {
+                if (mSettingsEnabled == enabled) {
+                    return;
+                }
 
-            mSettingsEnabled = enabled;
-            if (!mSettingsEnabled) {
-                // if any provider has been disabled, clear all last locations for all
-                // providers. this is to be on the safe side in case a provider has location
-                // derived from this disabled provider.
-                mLastLocation.clear();
-                mLastLocationCoarseInterval.clear();
-                updateProviderListeners(mName);
-            } else if (mEnabled) {
-                updateProviderListeners(mName);
+                mSettingsEnabled = enabled;
+                if (!mSettingsEnabled) {
+                    // if any provider has been disabled, clear all last locations for all
+                    // providers. this is to be on the safe side in case a provider has location
+                    // derived from this disabled provider.
+                    mLastLocation.clear();
+                    mLastLocationCoarseInterval.clear();
+                    updateProviderListenersLocked(mName);
+                } else if (mEnabled) {
+                    updateProviderListenersLocked(mName);
+                }
+            }
+        }
+
+        private void runOnHandler(Runnable runnable) {
+            if (Looper.myLooper() == mLocationHandler.getLooper()) {
+                runnable.run();
+            } else {
+                mLocationHandler.post(runnable);
             }
         }
     }
@@ -1071,7 +1022,7 @@
                 // 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()) {
-                    if (isAllowedByUserSettingsForUser(updateRecord.mProvider,
+                    if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
                             mCurrentUserId)) {
                         requestingLocation = true;
                         LocationManagerService.LocationProvider locationProvider
@@ -1171,7 +1122,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
+                        mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1207,7 +1158,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
+                        mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1250,7 +1201,7 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
+                        mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
                                 getResolutionPermission(mAllowedResolutionLevel),
                                 PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                         // call this after broadcasting so we do not increment
@@ -1268,12 +1219,12 @@
         public void binderDied() {
             if (D) Log.d(TAG, "Location listener died");
 
-            runInternal(() -> {
-                removeUpdates(this);
-                synchronized (this) {
-                    clearPendingBroadcastsLocked();
-                }
-            });
+            synchronized (mLock) {
+                removeUpdatesLocked(this);
+            }
+            synchronized (this) {
+                clearPendingBroadcastsLocked();
+            }
         }
 
         @Override
@@ -1310,22 +1261,28 @@
     }
 
     @Override
-    public void locationCallbackFinished(ILocationListener listener) throws RemoteException {
+    public void locationCallbackFinished(ILocationListener listener) {
         //Do not use getReceiverLocked here as that will add the ILocationListener to
         //the receiver list if it is not found.  If it is not found then the
         //LocationListener was removed when it had a pending broadcast and should
         //not be added back.
-        runFromBinderBlocking(() -> {
+        synchronized (mLock) {
             IBinder binder = listener.asBinder();
             Receiver receiver = mReceivers.get(binder);
             if (receiver != null) {
                 synchronized (receiver) {
+                    // so wakelock calls will succeed
+                    long identity = Binder.clearCallingIdentity();
                     receiver.decrementPendingBroadcastsLocked();
+                    Binder.restoreCallingIdentity(identity);
                 }
             }
-        });
+        }
     }
 
+    /**
+     * Returns the year of the GNSS hardware.
+     */
     @Override
     public int getGnssYearOfHardware() {
         if (mGnssSystemInfoProvider != null) {
@@ -1335,6 +1292,10 @@
         }
     }
 
+
+    /**
+     * Returns the model name of the GNSS hardware.
+     */
     @Override
     @Nullable
     public String getGnssHardwareModelName() {
@@ -1345,6 +1306,10 @@
         }
     }
 
+    /**
+     * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly
+     * (try to) access GNSS information at this layer.
+     */
     private boolean hasGnssPermissions(String packageName) {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForProviderUse(
@@ -1364,6 +1329,9 @@
         return hasLocationAccess;
     }
 
+    /**
+     * Returns the GNSS batching size, if available.
+     */
     @Override
     public int getGnssBatchSize(String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1376,6 +1344,10 @@
         }
     }
 
+    /**
+     * Adds a callback for GNSS Batching events, if permissions allow, which are transported
+     * to potentially multiple listeners by the BatchedLocationCallbackTransport above this.
+     */
     @Override
     public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1419,6 +1391,9 @@
         }
     }
 
+    /**
+     * Removes callback for GNSS batching
+     */
     @Override
     public void removeGnssBatchingCallback() {
         try {
@@ -1433,6 +1408,10 @@
         mGnssBatchingDeathCallback = null;
     }
 
+
+    /**
+     * Starts GNSS batching, if available.
+     */
     @Override
     public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1453,6 +1432,9 @@
         return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
     }
 
+    /**
+     * Flushes a GNSS batch in progress
+     */
     @Override
     public void flushGnssBatch(String packageName) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1472,6 +1454,9 @@
         }
     }
 
+    /**
+     * Stops GNSS batching
+     */
     @Override
     public boolean stopGnssBatch() {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1490,7 +1475,7 @@
         checkCallerIsProvider();
 
         // Currently used only for GNSS locations - update permissions check if changed
-        if (isAllowedByUserSettingsForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
+        if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
             if (mGnssBatchingCallback == null) {
                 Slog.e(TAG, "reportLocationBatch() called without active Callback");
                 return;
@@ -1505,28 +1490,35 @@
         }
     }
 
-    private void addProvider(LocationProvider provider) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+    private void addProviderLocked(LocationProvider provider) {
         mProviders.add(provider);
         mProvidersByName.put(provider.getName(), provider);
     }
 
-    private void removeProvider(LocationProvider provider) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+    private void removeProviderLocked(LocationProvider provider) {
         mProviders.remove(provider);
         mProvidersByName.remove(provider.getName());
     }
 
-    private boolean isAllowedByUserSettingsForUser(String provider, int userId) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+    /**
+     * Returns "true" if access to the specified location provider is allowed by the specified
+     * user's settings. Access to all location providers is forbidden to non-location-provider
+     * processes belonging to background users.
+     *
+     * @param provider the name of the location provider
+     * @param userId   the user id to query
+     */
+    private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
         if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
-            return isLocationEnabledForUserInternal(userId);
+            return isLocationEnabledForUser(userId);
         }
         if (LocationManager.FUSED_PROVIDER.equals(provider)) {
-            return isLocationEnabledForUserInternal(userId);
+            return isLocationEnabledForUser(userId);
         }
-        if (mMockProviders.containsKey(provider)) {
-            return isLocationEnabledForUserInternal(userId);
+        synchronized (mLock) {
+            if (mMockProviders.containsKey(provider)) {
+                return isLocationEnabledForUser(userId);
+            }
         }
 
         long identity = Binder.clearCallingIdentity();
@@ -1541,14 +1533,29 @@
         }
     }
 
-    private boolean isAllowedByUserSettings(String provider, int uid, int userId) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+    /**
+     * Returns "true" if access to the specified location provider is allowed by the specified
+     * user's settings. Access to all location providers is forbidden to non-location-provider
+     * processes belonging to background users.
+     *
+     * @param provider the name of the location provider
+     * @param uid      the requestor's UID
+     * @param userId   the user id to query
+     */
+    private boolean isAllowedByUserSettingsLocked(String provider, int uid, int userId) {
         if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
             return false;
         }
-        return isAllowedByUserSettingsForUser(provider, userId);
+        return isAllowedByUserSettingsLockedForUser(provider, userId);
     }
 
+    /**
+     * Returns the permission string associated with the specified resolution level.
+     *
+     * @param resolutionLevel the resolution level
+     * @return the permission string
+     */
     private String getResolutionPermission(int resolutionLevel) {
         switch (resolutionLevel) {
             case RESOLUTION_LEVEL_FINE:
@@ -1560,6 +1567,13 @@
         }
     }
 
+    /**
+     * Returns the resolution level allowed to the given PID/UID pair.
+     *
+     * @param pid the PID
+     * @param uid the UID
+     * @return resolution level allowed to the pid/uid pair
+     */
     private int getAllowedResolutionLevel(int pid, int uid) {
         if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
                 pid, uid) == PERMISSION_GRANTED) {
@@ -1572,16 +1586,32 @@
         }
     }
 
+    /**
+     * Returns the resolution level allowed to the caller
+     *
+     * @return resolution level allowed to caller
+     */
     private int getCallerAllowedResolutionLevel() {
         return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
     }
 
+    /**
+     * Throw SecurityException if specified resolution level is insufficient to use geofences.
+     *
+     * @param allowedResolutionLevel resolution level allowed to caller
+     */
     private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
         if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
             throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
         }
     }
 
+    /**
+     * Return the minimum resolution level required to use the specified location provider.
+     *
+     * @param provider the name of the location provider
+     * @return minimum resolution level required for provider
+     */
     private int getMinimumResolutionLevelForProviderUse(String provider) {
         if (LocationManager.GPS_PROVIDER.equals(provider) ||
                 LocationManager.PASSIVE_PROVIDER.equals(provider)) {
@@ -1613,6 +1643,13 @@
         return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
     }
 
+    /**
+     * Throw SecurityException if specified resolution level is insufficient to use the named
+     * location provider.
+     *
+     * @param allowedResolutionLevel resolution level allowed to caller
+     * @param providerName           the name of the location provider
+     */
     private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
             String providerName) {
         int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
@@ -1631,6 +1668,20 @@
         }
     }
 
+    /**
+     * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages
+     * for battery).
+     */
+    private void checkDeviceStatsAllowed() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_DEVICE_STATS, null);
+    }
+
+    private void checkUpdateAppOpsAllowed() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_APP_OPS_STATS, null);
+    }
+
     public static int resolutionLevelToOp(int allowedResolutionLevel) {
         if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
             if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
@@ -1688,15 +1739,19 @@
      */
     @Override
     public List<String> getAllProviders() {
-        ArrayList<String> providers = new ArrayList<>(mProviders.size());
-        for (LocationProvider provider : mProviders) {
-            String name = provider.getName();
-            if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                continue;
+        ArrayList<String> out;
+        synchronized (mLock) {
+            out = new ArrayList<>(mProviders.size());
+            for (LocationProvider provider : mProviders) {
+                String name = provider.getName();
+                if (LocationManager.FUSED_PROVIDER.equals(name)) {
+                    continue;
+                }
+                out.add(name);
             }
-            providers.add(name);
         }
-        return providers;
+        if (D) Log.d(TAG, "getAllProviders()=" + out);
+        return out;
     }
 
     /**
@@ -1705,33 +1760,39 @@
      * Can return passive provider, but never returns fused provider.
      */
     @Override
-    public List<String> getProviders(Criteria criteria, boolean enabledOnly)
-            throws RemoteException {
+    public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+        ArrayList<String> out;
         int uid = Binder.getCallingUid();
-        return runFromBinderBlocking(() -> {
-            ArrayList<String> providers = new ArrayList<>(mProviders.size());
-            for (LocationProvider provider : mProviders) {
-                String name = provider.getName();
-                if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                    continue;
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                out = new ArrayList<>(mProviders.size());
+                for (LocationProvider provider : mProviders) {
+                    String name = provider.getName();
+                    if (LocationManager.FUSED_PROVIDER.equals(name)) {
+                        continue;
+                    }
+                    if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
+                        if (enabledOnly
+                                && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
+                            continue;
+                        }
+                        if (criteria != null
+                                && !android.location.LocationProvider.propertiesMeetCriteria(
+                                name, provider.getProperties(), criteria)) {
+                            continue;
+                        }
+                        out.add(name);
+                    }
                 }
-                if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUse(name)) {
-                    continue;
-                }
-                if (enabledOnly
-                        && !isAllowedByUserSettings(name, uid, mCurrentUserId)) {
-                    continue;
-                }
-                if (criteria != null
-                        && !android.location.LocationProvider.propertiesMeetCriteria(
-                        name, provider.getProperties(), criteria)) {
-                    continue;
-                }
-                providers.add(name);
             }
-            return providers;
-        });
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        if (D) Log.d(TAG, "getProviders()=" + out);
+        return out;
     }
 
     /**
@@ -1742,51 +1803,59 @@
      * some simplified logic.
      */
     @Override
-    public String getBestProvider(Criteria criteria, boolean enabledOnly) throws RemoteException {
+    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+        String result;
+
         List<String> providers = getProviders(criteria, enabledOnly);
-        if (providers.isEmpty()) {
-            providers = getProviders(null, enabledOnly);
-        }
-
         if (!providers.isEmpty()) {
-            if (providers.contains(LocationManager.GPS_PROVIDER)) {
-                return LocationManager.GPS_PROVIDER;
-            } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
-                return LocationManager.NETWORK_PROVIDER;
-            } else {
-                return providers.get(0);
-            }
+            result = pickBest(providers);
+            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+            return result;
+        }
+        providers = getProviders(null, enabledOnly);
+        if (!providers.isEmpty()) {
+            result = pickBest(providers);
+            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+            return result;
         }
 
+        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
         return null;
     }
 
-    @Override
-    public boolean providerMeetsCriteria(String provider, Criteria criteria)
-            throws RemoteException {
-        return runFromBinderBlocking(() -> {
-            LocationProvider p = mProvidersByName.get(provider);
-            if (p == null) {
-                throw new IllegalArgumentException("provider=" + provider);
-            }
-
-            return android.location.LocationProvider.propertiesMeetCriteria(
-                    p.getName(), p.getProperties(), criteria);
-        });
+    private String pickBest(List<String> providers) {
+        if (providers.contains(LocationManager.GPS_PROVIDER)) {
+            return LocationManager.GPS_PROVIDER;
+        } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+            return LocationManager.NETWORK_PROVIDER;
+        } else {
+            return providers.get(0);
+        }
     }
 
-    private void updateProvidersSettings() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+    @Override
+    public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+        LocationProvider p = mProvidersByName.get(provider);
+        if (p == null) {
+            throw new IllegalArgumentException("provider=" + provider);
+        }
+
+        boolean result = android.location.LocationProvider.propertiesMeetCriteria(
+                p.getName(), p.getProperties(), criteria);
+        if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
+        return result;
+    }
+
+    private void updateProvidersSettingsLocked() {
         for (LocationProvider p : mProviders) {
-            p.setSettingsEnabled(isAllowedByUserSettingsForUser(p.getName(), mCurrentUserId));
+            p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
         }
 
         mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
                 UserHandle.ALL);
     }
 
-    private void updateProviderListeners(String provider) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+    private void updateProviderListenersLocked(String provider) {
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
@@ -1811,15 +1880,14 @@
 
         if (deadReceivers != null) {
             for (int i = deadReceivers.size() - 1; i >= 0; i--) {
-                removeUpdates(deadReceivers.get(i));
+                removeUpdatesLocked(deadReceivers.get(i));
             }
         }
 
-        applyRequirements(provider);
+        applyRequirementsLocked(provider);
     }
 
-    private void applyRequirements(String provider) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+    private void applyRequirementsLocked(String provider) {
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
@@ -1925,13 +1993,14 @@
     }
 
     @Override
-    public String[] getBackgroundThrottlingWhitelist() throws RemoteException {
-        return runFromBinderBlocking(
-                () -> mBackgroundThrottlePackageWhitelist.toArray(new String[0]));
+    public String[] getBackgroundThrottlingWhitelist() {
+        synchronized (mLock) {
+            return mBackgroundThrottlePackageWhitelist.toArray(
+                    new String[0]);
+        }
     }
 
-    private void updateBackgroundThrottlingWhitelist() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+    private void updateBackgroundThrottlingWhitelistLocked() {
         String setting = Settings.Global.getString(
                 mContext.getContentResolver(),
                 Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
@@ -1946,8 +2015,15 @@
                 Arrays.asList(setting.split(",")));
     }
 
+    private void updateLastLocationMaxAgeLocked() {
+        mLastLocationMaxAgeMs =
+                Settings.Global.getLong(
+                        mContext.getContentResolver(),
+                        Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+                        DEFAULT_LAST_LOCATION_MAX_AGE_MS);
+    }
+
     private boolean isThrottlingExemptLocked(Identity identity) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         if (identity.mUid == Process.SYSTEM_UID) {
             return true;
         }
@@ -2029,7 +2105,7 @@
 
             // and also remove the Receiver if it has no more update records
             if (receiverRecords.size() == 0) {
-                removeUpdates(mReceiver);
+                removeUpdatesLocked(mReceiver);
             }
         }
 
@@ -2043,9 +2119,8 @@
         }
     }
 
-    private Receiver getReceiver(ILocationListener listener, int pid, int uid,
+    private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
             String packageName, WorkSource workSource, boolean hideFromAppOps) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         IBinder binder = listener.asBinder();
         Receiver receiver = mReceivers.get(binder);
         if (receiver == null) {
@@ -2062,9 +2137,8 @@
         return receiver;
     }
 
-    private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName,
+    private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
             WorkSource workSource, boolean hideFromAppOps) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         Receiver receiver = mReceivers.get(intent);
         if (receiver == null) {
             receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
@@ -2128,9 +2202,29 @@
         throw new SecurityException("invalid package name: " + packageName);
     }
 
+    private void checkPendingIntent(PendingIntent intent) {
+        if (intent == null) {
+            throw new IllegalArgumentException("invalid pending intent: " + null);
+        }
+    }
+
+    private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent,
+            int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) {
+        if (intent == null && listener == null) {
+            throw new IllegalArgumentException("need either listener or intent");
+        } else if (intent != null && listener != null) {
+            throw new IllegalArgumentException("cannot register both listener and intent");
+        } else if (intent != null) {
+            checkPendingIntent(intent);
+            return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps);
+        } else {
+            return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps);
+        }
+    }
+
     @Override
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
-            PendingIntent intent, String packageName) throws RemoteException {
+            PendingIntent intent, String packageName) {
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         checkPackageName(packageName);
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2138,13 +2232,11 @@
                 request.getProvider());
         WorkSource workSource = request.getWorkSource();
         if (workSource != null && !workSource.isEmpty()) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.UPDATE_DEVICE_STATS, null);
+            checkDeviceStatsAllowed();
         }
         boolean hideFromAppOps = request.getHideFromAppOps();
         if (hideFromAppOps) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.UPDATE_APP_OPS_STATS, null);
+            checkUpdateAppOpsAllowed();
         }
         boolean callerHasLocationHardwarePermission =
                 mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -2154,33 +2246,25 @@
 
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
+        // providers may use public location API's, need to clear identity
+        long identity = Binder.clearCallingIdentity();
+        try {
+            // We don't check for MODE_IGNORED here; we will do that when we go to deliver
+            // a location.
+            checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
 
-        // We don't check for MODE_IGNORED here; we will do that when we go to deliver
-        // a location.
-        checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
-
-        if (intent == null && listener == null) {
-            throw new IllegalArgumentException("need either listener or intent");
-        } else if (intent != null && listener != null) {
-            throw new IllegalArgumentException("cannot register both listener and intent");
-        }
-
-        runFromBinderBlocking(() -> {
-            Receiver receiver;
-            if (intent != null) {
-                receiver = getReceiver(intent, pid, uid, packageName, workSource,
-                        hideFromAppOps);
-            } else {
-                receiver = getReceiver(listener, pid, uid, packageName, workSource,
-                        hideFromAppOps);
+            synchronized (mLock) {
+                Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
+                        packageName, workSource, hideFromAppOps);
+                requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
             }
-            requestLocationUpdates(sanitizedRequest, receiver, uid, packageName);
-        });
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
-    private void requestLocationUpdates(LocationRequest request, Receiver receiver,
+    private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
             int uid, String packageName) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
         // Figure out the provider. Either its explicitly request (legacy use cases), or
         // use the fused provider
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2209,7 +2293,7 @@
         }
 
         if (provider.isEnabled()) {
-            applyRequirements(name);
+            applyRequirementsLocked(name);
         } else {
             // Notify the listener that updates are currently disabled
             receiver.callProviderEnabledLocked(name, false);
@@ -2221,31 +2305,27 @@
 
     @Override
     public void removeUpdates(ILocationListener listener, PendingIntent intent,
-            String packageName) throws RemoteException {
+            String packageName) {
         checkPackageName(packageName);
 
-        int pid = Binder.getCallingPid();
-        int uid = Binder.getCallingUid();
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
 
-        if (intent == null && listener == null) {
-            throw new IllegalArgumentException("need either listener or intent");
-        } else if (intent != null && listener != null) {
-            throw new IllegalArgumentException("cannot register both listener and intent");
-        }
+        synchronized (mLock) {
+            Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
+                    packageName, null, false);
 
-        runFromBinderBlocking(() -> {
-            Receiver receiver;
-            if (intent != null) {
-                receiver = getReceiver(intent, pid, uid, packageName, null, false);
-            } else {
-                receiver = getReceiver(listener, pid, uid, packageName, null, false);
+            // providers may use public location API's, need to clear identity
+            long identity = Binder.clearCallingIdentity();
+            try {
+                removeUpdatesLocked(receiver);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            removeUpdates(receiver);
-        });
+        }
     }
 
-    private void removeUpdates(Receiver receiver) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+    private void removeUpdatesLocked(Receiver receiver) {
         if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
 
         if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
@@ -2272,14 +2352,20 @@
 
         // update provider
         for (String provider : providers) {
-            applyRequirements(provider);
+            applyRequirementsLocked(provider);
+        }
+    }
+
+    private void applyAllProviderRequirementsLocked() {
+        for (LocationProvider p : mProviders) {
+            applyRequirementsLocked(p.getName());
         }
     }
 
     @Override
-    public Location getLastLocation(LocationRequest r, String packageName) throws RemoteException {
-        if (D) Log.d(TAG, "getLastLocation: " + r);
-        LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
+    public Location getLastLocation(LocationRequest request, String packageName) {
+        if (D) Log.d(TAG, "getLastLocation: " + request);
+        if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkPackageName(packageName);
         checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
@@ -2305,60 +2391,68 @@
                 }
                 return null;
             }
+
+            synchronized (mLock) {
+                // Figure out the provider. Either its explicitly request (deprecated API's),
+                // or use the fused provider
+                String name = request.getProvider();
+                if (name == null) name = LocationManager.FUSED_PROVIDER;
+                LocationProvider provider = mProvidersByName.get(name);
+                if (provider == null) return null;
+
+                if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
+
+                Location location;
+                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+                    // Make sure that an app with coarse permissions can't get frequent location
+                    // updates by calling LocationManager.getLastKnownLocation repeatedly.
+                    location = mLastLocationCoarseInterval.get(name);
+                } else {
+                    location = mLastLocation.get(name);
+                }
+                if (location == null) {
+                    return null;
+                }
+
+                // Don't return stale location to apps with foreground-only location permission.
+                String op = resolutionLevelToOpStr(allowedResolutionLevel);
+                long locationAgeMs = SystemClock.elapsedRealtime() -
+                        location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+                if ((locationAgeMs > mLastLocationMaxAgeMs)
+                        && (mAppOps.unsafeCheckOp(op, uid, packageName)
+                        == AppOpsManager.MODE_FOREGROUND)) {
+                    return null;
+                }
+
+                if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+                    Location noGPSLocation = location.getExtraLocation(
+                            Location.EXTRA_NO_GPS_LOCATION);
+                    if (noGPSLocation != null) {
+                        return new Location(mLocationFudger.getOrCreate(noGPSLocation));
+                    }
+                } else {
+                    return new Location(location);
+                }
+            }
+            return null;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
-
-        return runFromBinderBlocking(() -> {
-            // Figure out the provider. Either its explicitly request (deprecated API's),
-            // or use the fused provider
-            String name = request.getProvider();
-            if (name == null) name = LocationManager.FUSED_PROVIDER;
-            LocationProvider provider = mProvidersByName.get(name);
-            if (provider == null) return null;
-
-            if (!isAllowedByUserSettings(name, uid, mCurrentUserId)) return null;
-
-            Location location;
-            if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-                // Make sure that an app with coarse permissions can't get frequent location
-                // updates by calling LocationManager.getLastKnownLocation repeatedly.
-                location = mLastLocationCoarseInterval.get(name);
-            } else {
-                location = mLastLocation.get(name);
-            }
-            if (location == null) {
-                return null;
-            }
-
-            // Don't return stale location to apps with foreground-only location permission.
-            String op = resolutionLevelToOpStr(allowedResolutionLevel);
-            long locationAgeMs = SystemClock.elapsedRealtime()
-                    - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
-            if ((locationAgeMs > Settings.Global.getLong(
-                    mContext.getContentResolver(),
-                    Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
-                    DEFAULT_LAST_LOCATION_MAX_AGE_MS))
-                    && (mAppOps.unsafeCheckOp(op, uid, packageName)
-                    == AppOpsManager.MODE_FOREGROUND)) {
-                return null;
-            }
-
-            if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
-                Location noGPSLocation = location.getExtraLocation(
-                        Location.EXTRA_NO_GPS_LOCATION);
-                if (noGPSLocation != null) {
-                    return new Location(mLocationFudger.getOrCreate(noGPSLocation));
-                }
-            } else {
-                return new Location(location);
-            }
-            return null;
-        });
     }
 
+    /**
+     * Provides an interface to inject and set the last location if location is not available
+     * currently.
+     *
+     * This helps in cases where the product (Cars for example) has saved the last known location
+     * before powering off.  This interface lets the client inject the saved location while the GPS
+     * chipset is getting its first fix, there by improving user experience.
+     *
+     * @param location - Location object to inject
+     * @return true if update was successful, false if not
+     */
     @Override
-    public boolean injectLocation(Location location) throws RemoteException {
+    public boolean injectLocation(Location location) {
         mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
                 "Location Hardware permission not granted to inject location");
         mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
@@ -2370,31 +2464,29 @@
             }
             return false;
         }
-
-        return runFromBinderBlocking(() -> {
-            LocationProvider p = null;
-            String provider = location.getProvider();
-            if (provider != null) {
-                p = mProvidersByName.get(provider);
+        LocationProvider p = null;
+        String provider = location.getProvider();
+        if (provider != null) {
+            p = mProvidersByName.get(provider);
+        }
+        if (p == null) {
+            if (D) {
+                Log.d(TAG, "injectLocation(): unknown provider");
             }
-            if (p == null) {
+            return false;
+        }
+        synchronized (mLock) {
+            if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
                 if (D) {
-                    Log.d(TAG, "injectLocation(): unknown provider");
-                }
-                return false;
-            }
-            if (!isAllowedByUserSettingsForUser(provider, mCurrentUserId)) {
-                if (D) {
-                    Log.d(TAG, "Location disabled in Settings for current user:"
-                            + mCurrentUserId);
+                    Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
                 }
                 return false;
             } else {
                 // NOTE: If last location is already available, location is not injected.  If
-                // provider's normal source (like a GPS chipset) have already provided an output
+                // provider's normal source (like a GPS chipset) have already provided an output,
                 // there is no need to inject this location.
                 if (mLastLocation.get(provider) == null) {
-                    updateLastLocation(location, provider);
+                    updateLastLocationLocked(location, provider);
                 } else {
                     if (D) {
                         Log.d(TAG, "injectLocation(): Location exists. Not updating");
@@ -2402,8 +2494,8 @@
                     return false;
                 }
             }
-            return true;
-        });
+        }
+        return true;
     }
 
     @Override
@@ -2412,9 +2504,7 @@
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
-        if (intent == null) {
-            throw new IllegalArgumentException("invalid pending intent: " + null);
-        }
+        checkPendingIntent(intent);
         checkPackageName(packageName);
         checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
                 request.getProvider());
@@ -2425,10 +2515,7 @@
         LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
                 callerHasLocationHardwarePermission);
 
-        if (D) {
-            Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " "
-                    + intent);
-        }
+        if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
 
         // geo-fence manager uses the public location API, need to clear identity
         int uid = Binder.getCallingUid();
@@ -2449,9 +2536,7 @@
 
     @Override
     public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
-        if (intent == null) {
-            throw new IllegalArgumentException("invalid pending intent: " + null);
-        }
+        checkPendingIntent(intent);
         checkPackageName(packageName);
 
         if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
@@ -2483,14 +2568,14 @@
 
     @Override
     public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName) throws RemoteException {
+            IGnssMeasurementsListener listener, String packageName) {
         if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
             return false;
         }
 
-        Identity callerIdentity =
-                new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
-        return runFromBinderBlocking(() -> {
+        synchronized (mLock) {
+            Identity callerIdentity
+                    = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
             // TODO(b/120481270): Register for client death notification and update map.
             mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
@@ -2506,7 +2591,7 @@
             }
 
             return true;
-        });
+        }
     }
 
     @Override
@@ -2534,30 +2619,28 @@
     }
 
     @Override
-    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener)
-            throws RemoteException {
+    public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
         if (mGnssMeasurementsProvider == null) {
             return;
         }
 
-        runFromBinderBlocking(() -> {
+        synchronized (mLock) {
             mGnssMeasurementsListeners.remove(listener.asBinder());
             mGnssMeasurementsProvider.removeListener(listener);
-        });
+        }
     }
 
     @Override
     public boolean addGnssNavigationMessageListener(
             IGnssNavigationMessageListener listener,
-            String packageName) throws RemoteException {
+            String packageName) {
         if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
             return false;
         }
 
-        Identity callerIdentity =
-                new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
-
-        return runFromBinderBlocking(() -> {
+        synchronized (mLock) {
+            Identity callerIdentity
+                    = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
             // TODO(b/120481270): Register for client death notification and update map.
             mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
             long identity = Binder.clearCallingIdentity();
@@ -2573,23 +2656,21 @@
             }
 
             return true;
-        });
-    }
-
-    @Override
-    public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener)
-            throws RemoteException {
-        if (mGnssNavigationMessageProvider != null) {
-            runFromBinderBlocking(() -> {
-                mGnssNavigationMessageListeners.remove(listener.asBinder());
-                mGnssNavigationMessageProvider.removeListener(listener);
-            });
         }
     }
 
     @Override
-    public boolean sendExtraCommand(String provider, String command, Bundle extras)
-            throws RemoteException {
+    public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
+        if (mGnssNavigationMessageProvider != null) {
+            synchronized (mLock) {
+                mGnssNavigationMessageListeners.remove(listener.asBinder());
+                mGnssNavigationMessageProvider.removeListener(listener);
+            }
+        }
+    }
+
+    @Override
+    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
         if (provider == null) {
             // throw NullPointerException to remain compatible with previous implementation
             throw new NullPointerException();
@@ -2603,13 +2684,13 @@
             throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
         }
 
-        return runFromBinderBlocking(() -> {
+        synchronized (mLock) {
             LocationProvider p = mProvidersByName.get(provider);
             if (p == null) return false;
 
             p.sendExtraCommand(command, extras);
             return true;
-        });
+        }
     }
 
     @Override
@@ -2626,181 +2707,251 @@
         }
     }
 
+    /**
+     * @return null if the provider does not exist
+     * @throws SecurityException if the provider is not allowed to be
+     *                           accessed by the caller
+     */
     @Override
-    public ProviderProperties getProviderProperties(String provider) throws RemoteException {
+    public ProviderProperties getProviderProperties(String provider) {
         checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
                 provider);
 
-        return runFromBinderBlocking(() -> {
-            LocationProvider p = mProvidersByName.get(provider);
-            if (p == null) return null;
-            return p.getProperties();
-        });
+        LocationProvider p;
+        synchronized (mLock) {
+            p = mProvidersByName.get(provider);
+        }
+
+        if (p == null) return null;
+        return p.getProperties();
     }
 
+    /**
+     * @return null if the provider does not exist
+     * @throws SecurityException if the provider is not allowed to be
+     *                           accessed by the caller
+     */
     @Override
-    public String getNetworkProviderPackage() throws RemoteException {
-        return runFromBinderBlocking(() -> {
-            LocationProvider p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
+    public String getNetworkProviderPackage() {
+        LocationProvider p;
+        synchronized (mLock) {
+            p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
+        }
 
-            if (p == null) {
-                return null;
-            }
-            if (p.mProvider instanceof LocationProviderProxy) {
-                return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
-            }
+        if (p == null) {
             return null;
-        });
+        }
+        if (p.mProvider instanceof LocationProviderProxy) {
+            return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
+        }
+        return null;
     }
 
     @Override
-    public void setLocationControllerExtraPackage(String packageName) throws RemoteException {
+    public void setLocationControllerExtraPackage(String packageName) {
         mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                 Manifest.permission.LOCATION_HARDWARE + " permission required");
-
-        runFromBinderBlocking(() -> mLocationControllerExtraPackage = packageName);
+        synchronized (mLock) {
+            mLocationControllerExtraPackage = packageName;
+        }
     }
 
     @Override
-    public String getLocationControllerExtraPackage() throws RemoteException {
-        return runFromBinderBlocking(() -> mLocationControllerExtraPackage);
+    public String getLocationControllerExtraPackage() {
+        synchronized (mLock) {
+            return mLocationControllerExtraPackage;
+        }
     }
 
     @Override
-    public void setLocationControllerExtraPackageEnabled(boolean enabled) throws RemoteException {
+    public void setLocationControllerExtraPackageEnabled(boolean enabled) {
         mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
                 Manifest.permission.LOCATION_HARDWARE + " permission required");
-        runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled = enabled);
+        synchronized (mLock) {
+            mLocationControllerExtraPackageEnabled = enabled;
+        }
     }
 
     @Override
-    public boolean isLocationControllerExtraPackageEnabled() throws RemoteException {
-        return runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled
-                && (mLocationControllerExtraPackage != null));
+    public boolean isLocationControllerExtraPackageEnabled() {
+        synchronized (mLock) {
+            return mLocationControllerExtraPackageEnabled
+                    && (mLocationControllerExtraPackage != null);
+        }
     }
 
+    /**
+     * Returns the current location enabled/disabled status for a user
+     *
+     * @param userId the id of the user
+     * @return true if location is enabled
+     */
     @Override
-    public boolean isLocationEnabledForUser(int userId) throws RemoteException {
+    public boolean isLocationEnabledForUser(int userId) {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS,
-                    "Requires INTERACT_ACROSS_USERS permission");
-        }
+        checkInteractAcrossUsersPermission(userId);
 
-        return runFromBinderBlocking(() -> isLocationEnabledForUserInternal(userId));
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                final String allowedProviders = Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        userId);
+                if (allowedProviders == null) {
+                    return false;
+                }
+                final List<String> providerList = Arrays.asList(allowedProviders.split(","));
+                for (String provider : mRealProviders.keySet()) {
+                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
+                        continue;
+                    }
+                    if (providerList.contains(provider)) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
-    private boolean isLocationEnabledForUserInternal(int userId) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
-        final String allowedProviders = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                userId);
-        if (allowedProviders == null) {
-            return false;
-        }
-        final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-
-        for (String provider : mRealProviders.keySet()) {
-            if (provider.equals(LocationManager.PASSIVE_PROVIDER)
-                    || provider.equals(LocationManager.FUSED_PROVIDER)) {
-                continue;
-            }
-            if (providerList.contains(provider)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
+    /**
+     * Enable or disable location for a user
+     *
+     * @param enabled true to enable location, false to disable location
+     * @param userId  the id of the user
+     */
     @Override
-    public void setLocationEnabledForUser(boolean enabled, int userId) throws RemoteException {
+    public void setLocationEnabledForUser(boolean enabled, int userId) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS,
                 "Requires WRITE_SECURE_SETTINGS permission");
 
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS,
-                    "Requires INTERACT_ACROSS_USERS permission");
-        }
+        checkInteractAcrossUsersPermission(userId);
 
-        runFromBinderBlocking(() -> {
-            final Set<String> allRealProviders = mRealProviders.keySet();
-            // Update all providers on device plus gps and network provider when disabling
-            // location
-            Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
-            allProvidersSet.addAll(allRealProviders);
-            // When disabling location, disable gps and network provider that could have been
-            // enabled by location mode api.
-            if (!enabled) {
-                allProvidersSet.add(LocationManager.GPS_PROVIDER);
-                allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
-            }
-            if (allProvidersSet.isEmpty()) {
-                return;
-            }
-            // to ensure thread safety, we write the provider name with a '+' or '-'
-            // and let the SettingsProvider handle it rather than reading and modifying
-            // the list of enabled providers.
-            final String prefix = enabled ? "+" : "-";
-            StringBuilder locationProvidersAllowed = new StringBuilder();
-            for (String provider : allProvidersSet) {
-                if (provider.equals(LocationManager.PASSIVE_PROVIDER)
-                        || provider.equals(LocationManager.FUSED_PROVIDER)) {
-                    continue;
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                final Set<String> allRealProviders = mRealProviders.keySet();
+                // Update all providers on device plus gps and network provider when disabling
+                // location
+                Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
+                allProvidersSet.addAll(allRealProviders);
+                // When disabling location, disable gps and network provider that could have been
+                // enabled by location mode api.
+                if (!enabled) {
+                    allProvidersSet.add(LocationManager.GPS_PROVIDER);
+                    allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
                 }
-                locationProvidersAllowed.append(prefix);
-                locationProvidersAllowed.append(provider);
-                locationProvidersAllowed.append(",");
+                if (allProvidersSet.isEmpty()) {
+                    return;
+                }
+                // to ensure thread safety, we write the provider name with a '+' or '-'
+                // and let the SettingsProvider handle it rather than reading and modifying
+                // the list of enabled providers.
+                final String prefix = enabled ? "+" : "-";
+                StringBuilder locationProvidersAllowed = new StringBuilder();
+                for (String provider : allProvidersSet) {
+                    if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+                            || provider.equals(LocationManager.FUSED_PROVIDER)) {
+                        continue;
+                    }
+                    locationProvidersAllowed.append(prefix);
+                    locationProvidersAllowed.append(provider);
+                    locationProvidersAllowed.append(",");
+                }
+                // Remove the trailing comma
+                locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
+                Settings.Secure.putStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        locationProvidersAllowed.toString(),
+                        userId);
             }
-            // Remove the trailing comma
-            locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
-            Settings.Secure.putStringForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                    locationProvidersAllowed.toString(),
-                    userId);
-        });
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
+    /**
+     * Returns the current enabled/disabled status of a location provider and user
+     *
+     * @param providerName name of the provider
+     * @param userId       the id of the user
+     * @return true if the provider exists and is enabled
+     */
     @Override
-    public boolean isProviderEnabledForUser(String providerName, int userId)
-            throws RemoteException {
+    public boolean isProviderEnabledForUser(String providerName, int userId) {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS,
-                    "Requires INTERACT_ACROSS_USERS permission");
+        checkInteractAcrossUsersPermission(userId);
+
+        if (!isLocationEnabledForUser(userId)) {
+            return false;
         }
 
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
         if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
 
-        if (!isLocationEnabledForUser(userId)) {
-            return false;
-        }
-
-        return runFromBinderBlocking(() -> {
-            LocationProvider provider = mProvidersByName.get(providerName);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            LocationProvider provider;
+            synchronized (mLock) {
+                provider = mProvidersByName.get(providerName);
+            }
             return provider != null && provider.isEnabled();
-        });
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
+    /**
+     * Enable or disable a single location provider.
+     *
+     * @param provider name of the provider
+     * @param enabled  true to enable the provider. False to disable the provider
+     * @param userId   the id of the user to set
+     * @return true if the value was set, false on errors
+     */
     @Override
     public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
         return false;
     }
 
+    /**
+     * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
+     * current user id
+     *
+     * @param userId the user id to get or set value
+     */
+    private void checkInteractAcrossUsersPermission(int userId) {
+        int uid = Binder.getCallingUid();
+        if (UserHandle.getUserId(uid) != userId) {
+            if (ActivityManager.checkComponentPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
+                    != PERMISSION_GRANTED) {
+                throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
+            }
+        }
+    }
+
+    /**
+     * Returns "true" if the UID belongs to a bound location provider.
+     *
+     * @param uid the uid
+     * @return true if uid belongs to a bound location provider
+     */
     private boolean isUidALocationProvider(int uid) {
         if (uid == Process.SYSTEM_UID) {
             return true;
         }
-
+        if (mGeocodeProvider != null) {
+            if (doesUidHavePackage(uid, mGeocodeProvider.getConnectedPackageName())) return true;
+        }
         for (LocationProviderProxy proxy : mProxyProviders) {
             if (doesUidHavePackage(uid, proxy.getConnectedPackageName())) return true;
         }
@@ -2819,6 +2970,7 @@
         // providers installed oustide the system image. So
         // also allow providers with a UID matching the
         // currently bound package name
+
         if (isUidALocationProvider(Binder.getCallingUid())) {
             return;
         }
@@ -2827,6 +2979,9 @@
                 "or UID of a currently bound location provider");
     }
 
+    /**
+     * Returns true if the given package belongs to the given uid.
+     */
     private boolean doesUidHavePackage(int uid, String packageName) {
         if (packageName == null) {
             return false;
@@ -2843,12 +2998,22 @@
         return false;
     }
 
-    // TODO: will be removed in future
     @Override
     public void reportLocation(Location location, boolean passive) {
-        throw new IllegalStateException("operation unsupported");
+        checkCallerIsProvider();
+
+        if (!location.isComplete()) {
+            Log.w(TAG, "Dropping incomplete location: " + location);
+            return;
+        }
+
+        mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
+        Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
+        m.arg1 = (passive ? 1 : 0);
+        mLocationHandler.sendMessageAtFrontOfQueue(m);
     }
 
+
     private static boolean shouldBroadcastSafe(
             Location loc, Location lastLoc, UpdateRecord record, long now) {
         // Always broadcast the first update
@@ -2881,33 +3046,14 @@
         return record.mRealRequest.getExpireAt() >= now;
     }
 
-    private void handleLocationChanged(Location location, boolean passive) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
-        // create a working copy of the incoming Location so that the service can modify it without
-        // disturbing the caller's copy
-        Location myLocation = new Location(location);
-        String pr = myLocation.getProvider();
-
-        // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
-        // bit if location did not come from a mock provider because passive/fused providers can
-        // forward locations from mock providers, and should not grant them legitimacy in doing so.
-        if (!myLocation.isFromMockProvider() && isMockProvider(pr)) {
-            myLocation.setIsFromMockProvider(true);
-        }
-
-        if (!passive) {
-            // notify passive provider of the new location
-            mPassiveProvider.updateLocation(myLocation);
-        }
-
-        if (D) Log.d(TAG, "incoming location: " + myLocation);
+    private void handleLocationChangedLocked(Location location, boolean passive) {
+        if (D) Log.d(TAG, "incoming location: " + location);
         long now = SystemClock.elapsedRealtime();
-        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : myLocation.getProvider());
+        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
         // Skip if the provider is unknown.
         LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
-        updateLastLocation(myLocation, provider);
+        updateLastLocationLocked(location, provider);
         // mLastLocation should have been updated from the updateLastLocationLocked call above.
         Location lastLocation = mLastLocation.get(provider);
         if (lastLocation == null) {
@@ -2918,13 +3064,13 @@
         // Update last known coarse interval location if enough time has passed.
         Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
         if (lastLocationCoarseInterval == null) {
-            lastLocationCoarseInterval = new Location(myLocation);
+            lastLocationCoarseInterval = new Location(location);
             mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
         }
-        long timeDiffNanos = myLocation.getElapsedRealtimeNanos()
+        long timeDiffNanos = location.getElapsedRealtimeNanos()
                 - lastLocationCoarseInterval.getElapsedRealtimeNanos();
         if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
-            lastLocationCoarseInterval.set(myLocation);
+            lastLocationCoarseInterval.set(location);
         }
         // Don't ever return a coarse location that is more recent than the allowed update
         // interval (i.e. don't allow an app to keep registering and unregistering for
@@ -3048,20 +3194,24 @@
         // remove dead records and receivers outside the loop
         if (deadReceivers != null) {
             for (Receiver receiver : deadReceivers) {
-                removeUpdates(receiver);
+                removeUpdatesLocked(receiver);
             }
         }
         if (deadUpdateRecords != null) {
             for (UpdateRecord r : deadUpdateRecords) {
                 r.disposeLocked(true);
             }
-            applyRequirements(provider);
+            applyRequirementsLocked(provider);
         }
     }
 
-    private void updateLastLocation(Location location, String provider) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
+    /**
+     * Updates last location with the given location
+     *
+     * @param location new location to update
+     * @param provider Location provider to update for
+     */
+    private void updateLastLocationLocked(Location location, String provider) {
         Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
         Location lastNoGPSLocation;
         Location lastLocation = mLastLocation.get(provider);
@@ -3079,11 +3229,75 @@
         lastLocation.set(location);
     }
 
-    private boolean isMockProvider(String provider) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-        return mMockProviders.containsKey(provider);
+    private class LocationWorkerHandler extends Handler {
+        public LocationWorkerHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_LOCATION_CHANGED:
+                    handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
+                    break;
+            }
+        }
     }
 
+    private boolean isMockProvider(String provider) {
+        synchronized (mLock) {
+            return mMockProviders.containsKey(provider);
+        }
+    }
+
+    private void handleLocationChanged(Location location, boolean passive) {
+        // create a working copy of the incoming Location so that the service can modify it without
+        // disturbing the caller's copy
+        Location myLocation = new Location(location);
+        String provider = myLocation.getProvider();
+
+        // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
+        // bit if location did not come from a mock provider because passive/fused providers can
+        // forward locations from mock providers, and should not grant them legitimacy in doing so.
+        if (!myLocation.isFromMockProvider() && isMockProvider(provider)) {
+            myLocation.setIsFromMockProvider(true);
+        }
+
+        synchronized (mLock) {
+            if (!passive) {
+                // notify passive provider of the new location
+                mPassiveProvider.updateLocation(myLocation);
+            }
+            handleLocationChangedLocked(myLocation, passive);
+        }
+    }
+
+    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+        @Override
+        public void onPackageDisappeared(String packageName, int reason) {
+            // remove all receivers associated with this package name
+            synchronized (mLock) {
+                ArrayList<Receiver> deadReceivers = null;
+
+                for (Receiver receiver : mReceivers.values()) {
+                    if (receiver.mIdentity.mPackageName.equals(packageName)) {
+                        if (deadReceivers == null) {
+                            deadReceivers = new ArrayList<>();
+                        }
+                        deadReceivers.add(receiver);
+                    }
+                }
+
+                // perform removal outside of mReceivers loop
+                if (deadReceivers != null) {
+                    for (Receiver receiver : deadReceivers) {
+                        removeUpdatesLocked(receiver);
+                    }
+                }
+            }
+        }
+    };
+
     // Geocoder
 
     @Override
@@ -3124,8 +3338,7 @@
     }
 
     @Override
-    public void addTestProvider(String name, ProviderProperties properties, String opPackageName)
-            throws RemoteException {
+    public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
@@ -3134,24 +3347,23 @@
             throw new IllegalArgumentException("Cannot mock the passive location provider");
         }
 
-        runFromBinderBlocking(() -> {
+        long identity = Binder.clearCallingIdentity();
+        synchronized (mLock) {
             // remove the real provider if we are replacing GPS or network provider
             if (LocationManager.GPS_PROVIDER.equals(name)
                     || LocationManager.NETWORK_PROVIDER.equals(name)
                     || LocationManager.FUSED_PROVIDER.equals(name)) {
                 LocationProvider p = mProvidersByName.get(name);
                 if (p != null) {
-                    removeProvider(p);
+                    removeProviderLocked(p);
                 }
             }
-            addTestProvider(name, properties);
-            return null;
-        });
+            addTestProviderLocked(name, properties);
+        }
+        Binder.restoreCallingIdentity(identity);
     }
 
-    private void addTestProvider(String name, ProviderProperties properties) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
+    private void addTestProviderLocked(String name, ProviderProperties properties) {
         if (mProvidersByName.get(name) != null) {
             throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
         }
@@ -3159,103 +3371,122 @@
         LocationProvider provider = new LocationProvider(name);
         MockProvider mockProvider = new MockProvider(provider, properties);
 
-        addProvider(provider);
+        addProviderLocked(provider);
         mMockProviders.put(name, mockProvider);
         mLastLocation.put(name, null);
         mLastLocationCoarseInterval.put(name, null);
     }
 
     @Override
-    public void removeTestProvider(String provider, String opPackageName) throws RemoteException {
+    public void removeTestProvider(String provider, String opPackageName) {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        runFromBinderBlocking(() -> {
+        synchronized (mLock) {
             MockProvider mockProvider = mMockProviders.remove(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
 
-            removeProvider(mProvidersByName.get(provider));
+            long identity = Binder.clearCallingIdentity();
+            try {
+                removeProviderLocked(mProvidersByName.get(provider));
 
-            // reinstate real provider if available
-            LocationProvider realProvider = mRealProviders.get(provider);
-            if (realProvider != null) {
-                addProvider(realProvider);
+                // reinstate real provider if available
+                LocationProvider realProvider = mRealProviders.get(provider);
+                if (realProvider != null) {
+                    addProviderLocked(realProvider);
+                }
+                mLastLocation.put(provider, null);
+                mLastLocationCoarseInterval.put(provider, null);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            mLastLocation.put(provider, null);
-            mLastLocationCoarseInterval.put(provider, null);
-        });
+        }
     }
 
     @Override
-    public void setTestProviderLocation(String provider, Location loc, String opPackageName)
-            throws RemoteException {
+    public void setTestProviderLocation(String provider, Location loc, String opPackageName) {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        runFromBinderBlocking(() -> {
+        synchronized (mLock) {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
 
-            // Ensure that the location is marked as being mock. There's some logic to do this
-            // in handleLocationChanged(), but it fails if loc has the wrong provider
-            // (b/33091107).
+            // Ensure that the location is marked as being mock. There's some logic to do this in
+            // handleLocationChanged(), but it fails if loc has the wrong provider (bug 33091107).
             Location mock = new Location(loc);
             mock.setIsFromMockProvider(true);
 
             if (!TextUtils.isEmpty(loc.getProvider()) && !provider.equals(loc.getProvider())) {
-                // The location has an explicit provider that is different from the mock
-                // provider name. The caller may be trying to fool us via bug 33091107.
+                // The location has an explicit provider that is different from the mock provider
+                // name. The caller may be trying to fool us via bug 33091107.
                 EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
                         provider + "!=" + loc.getProvider());
             }
 
-            mockProvider.setLocation(mock);
-        });
+            // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mockProvider.setLocation(mock);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
     }
 
     @Override
-    public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName)
-            throws RemoteException {
+    public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        runFromBinderBlocking(() -> {
+        synchronized (mLock) {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            mockProvider.setEnabled(enabled);
-        });
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mockProvider.setEnabled(enabled);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
     }
 
     @Override
     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime,
-            String opPackageName) throws RemoteException {
+            String opPackageName) {
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
 
-        runFromBinderBlocking(() -> {
+        synchronized (mLock) {
             MockProvider mockProvider = mMockProviders.get(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
             mockProvider.setStatus(status, extras, updateTime);
-        });
+        }
+    }
+
+    private void log(String log) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Slog.d(TAG, log);
+        }
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        Runnable dump = () -> {
+        synchronized (mLock) {
             if (args.length > 0 && args[0].equals("--gnssmetrics")) {
                 if (mGnssMetricsProvider != null) {
                     pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
@@ -3348,14 +3579,6 @@
             if (mGnssBatchingInProgress) {
                 pw.println("  GNSS batching in progress");
             }
-        };
-
-        FutureTask<Void> task = new FutureTask<>(dump, null);
-        mHandler.postAtFrontOfQueue(task);
-        try {
-            task.get();
-        } catch (InterruptedException | ExecutionException e) {
-            pw.println("error dumping: " + e);
         }
     }
 }