Multiuser love for LocationManager

LocationManagerService now keeps track of the current user ID and
denies location requests made by all but the foreground user.

Additionally, location settings are now user-specific, rather than
global to the device. Location provider services now run as specific
users, and when the device's foreground user changes, we rebind to
appropriately-owned providers.

Bug: 6926385
Bug: 7247203
Change-Id: I346074959e96e52bcc77eeb188dffe322b690879
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3bbdf36..91c6db5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4052,7 +4052,20 @@
          * @return true if the provider is enabled
          */
         public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
-            String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED);
+            return isLocationProviderEnabledForUser(cr, provider, UserHandle.myUserId());
+        }
+
+        /**
+         * Helper method for determining if a location provider is enabled.
+         * @param cr the content resolver to use
+         * @param provider the location provider to query
+         * @param userId the userId to query
+         * @return true if the provider is enabled
+         * @hide
+         */
+        public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) {
+            String allowedProviders = Settings.Secure.getStringForUser(cr,
+                    LOCATION_PROVIDERS_ALLOWED, userId);
             return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
         }
 
@@ -4064,6 +4077,19 @@
          */
         public static final void setLocationProviderEnabled(ContentResolver cr,
                 String provider, boolean enabled) {
+            setLocationProviderEnabledForUser(cr, provider, enabled, UserHandle.myUserId());
+        }
+
+        /**
+         * Thread-safe method for enabling or disabling a single location provider.
+         * @param cr the content resolver to use
+         * @param provider the location provider to enable or disable
+         * @param enabled true if the provider should be enabled
+         * @param userId the userId for which to enable/disable providers
+         * @hide
+         */
+        public static final void setLocationProviderEnabledForUser(ContentResolver cr,
+                String provider, boolean enabled, int userId) {
             // 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.
@@ -4072,7 +4098,8 @@
             } else {
                 provider = "-" + provider;
             }
-            putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider);
+            putStringForUser(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider,
+                    userId);
         }
     }
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 0087b57..c855418 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -226,7 +226,7 @@
                    updateProvidersLocked();
                }
             }
-        });
+        }, UserHandle.USER_ALL);
         mPackageMonitor.register(mContext, Looper.myLooper(), true);
 
         // listen for user change
@@ -289,7 +289,7 @@
                 mContext,
                 LocationManager.NETWORK_PROVIDER,
                 NETWORK_LOCATION_SERVICE_ACTION,
-                providerPackageNames, mLocationHandler);
+                providerPackageNames, mLocationHandler, mCurrentUserId);
         if (networkProvider != null) {
             mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
             mProxyProviders.add(networkProvider);
@@ -303,7 +303,7 @@
                 mContext,
                 LocationManager.FUSED_PROVIDER,
                 FUSED_LOCATION_SERVICE_ACTION,
-                providerPackageNames, mLocationHandler);
+                providerPackageNames, mLocationHandler, mCurrentUserId);
         if (fusedLocationProvider != null) {
             addProviderLocked(fusedLocationProvider);
             mProxyProviders.add(fusedLocationProvider);
@@ -314,7 +314,8 @@
         }
 
         // bind to geocoder provider
-        mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
+        mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames,
+                mCurrentUserId);
         if (mGeocodeProvider == null) {
             Slog.e(TAG,  "no geocoder provider found");
         }
@@ -326,11 +327,14 @@
      */
     private void switchUser(int userId) {
         mBlacklist.switchUser(userId);
-        //Log.d("LocationManagerService", "switchUser(" + mCurrentUserId + " -> " + userId + ")"); // TODO: remove this
         synchronized (mLock) {
-            // TODO: inform previous user's Receivers that they will no longer receive updates
+            mLastLocation.clear();
+            for (LocationProviderInterface p : mProviders) {
+                updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
+                p.switchUser(userId);
+            }
             mCurrentUserId = userId;
-            // TODO: inform new user's Receivers that they are back on the update train
+            updateProvidersLocked();
         }
     }
 
@@ -587,7 +591,10 @@
     }
 
 
-    private boolean isAllowedBySettingsLocked(String provider) {
+    private boolean isAllowedBySettingsLocked(String provider, int userId) {
+        if (userId != mCurrentUserId) {
+            return false;
+        }
         if (mEnabledProviders.contains(provider)) {
             return true;
         }
@@ -597,7 +604,7 @@
         // Use system settings
         ContentResolver resolver = mContext.getContentResolver();
 
-        return Settings.Secure.isLocationProviderEnabled(resolver, provider);
+        return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId);
     }
 
     /**
@@ -695,24 +702,30 @@
     @Override
     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
         ArrayList<String> out;
-        synchronized (mLock) {
-            out = new ArrayList<String>(mProviders.size());
-            for (LocationProviderInterface provider : mProviders) {
-                String name = provider.getName();
-                if (LocationManager.FUSED_PROVIDER.equals(name)) {
-                    continue;
-                }
-                if (isAllowedProviderSafe(name)) {
-                    if (enabledOnly && !isAllowedBySettingsLocked(name)) {
+        int callingUserId = UserHandle.getCallingUserId();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                out = new ArrayList<String>(mProviders.size());
+                for (LocationProviderInterface provider : mProviders) {
+                    String name = provider.getName();
+                    if (LocationManager.FUSED_PROVIDER.equals(name)) {
                         continue;
                     }
-                    if (criteria != null && !LocationProvider.propertiesMeetCriteria(
-                            name, provider.getProperties(), criteria)) {
-                        continue;
+                    if (isAllowedProviderSafe(name)) {
+                        if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
+                            continue;
+                        }
+                        if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+                                name, provider.getProperties(), criteria)) {
+                            continue;
+                        }
+                        out.add(name);
                     }
-                    out.add(name);
                 }
             }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
 
         if (D) Log.d(TAG, "getProviders()=" + out);
@@ -778,12 +791,12 @@
             LocationProviderInterface p = mProviders.get(i);
             boolean isEnabled = p.isEnabled();
             String name = p.getName();
-            boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
+            boolean shouldBeEnabled = isAllowedBySettingsLocked(name, mCurrentUserId);
             if (isEnabled && !shouldBeEnabled) {
-                updateProviderListenersLocked(name, false);
+                updateProviderListenersLocked(name, false, mCurrentUserId);
                 changesMade = true;
             } else if (!isEnabled && shouldBeEnabled) {
-                updateProviderListenersLocked(name, true);
+                updateProviderListenersLocked(name, true, mCurrentUserId);
                 changesMade = true;
             }
         }
@@ -793,7 +806,7 @@
         }
     }
 
-    private void updateProviderListenersLocked(String provider, boolean enabled) {
+    private void updateProviderListenersLocked(String provider, boolean enabled, int userId) {
         int listeners = 0;
 
         LocationProviderInterface p = mProvidersByName.get(provider);
@@ -806,14 +819,16 @@
             final int N = records.size();
             for (int i = 0; i < N; i++) {
                 UpdateRecord record = records.get(i);
-                // Sends a notification message to the receiver
-                if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
-                    if (deadReceivers == null) {
-                        deadReceivers = new ArrayList<Receiver>();
+                if (UserHandle.getUserId(record.mReceiver.mUid) == userId) {
+                    // Sends a notification message to the receiver
+                    if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
+                        if (deadReceivers == null) {
+                            deadReceivers = new ArrayList<Receiver>();
+                        }
+                        deadReceivers.add(record.mReceiver);
                     }
-                    deadReceivers.add(record.mReceiver);
+                    listeners++;
                 }
-                listeners++;
             }
         }
 
@@ -843,12 +858,13 @@
 
         if (records != null) {
             for (UpdateRecord record : records) {
-                LocationRequest locationRequest = record.mRequest;
-
-                providerRequest.locationRequests.add(locationRequest);
-                if (locationRequest.getInterval() < providerRequest.interval) {
-                    providerRequest.reportLocation = true;
-                    providerRequest.interval = locationRequest.getInterval();
+                if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+                    LocationRequest locationRequest = record.mRequest;
+                    providerRequest.locationRequests.add(locationRequest);
+                    if (locationRequest.getInterval() < providerRequest.interval) {
+                        providerRequest.reportLocation = true;
+                        providerRequest.interval = locationRequest.getInterval();
+                    }
                 }
             }
 
@@ -860,9 +876,11 @@
                 // under that threshold.
                 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
                 for (UpdateRecord record : records) {
-                    LocationRequest locationRequest = record.mRequest;
-                    if (locationRequest.getInterval() <= thresholdInterval) {
-                        worksource.add(record.mReceiver.mUid);
+                    if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+                        LocationRequest locationRequest = record.mRequest;
+                        if (locationRequest.getInterval() <= thresholdInterval) {
+                            worksource.add(record.mReceiver.mUid);
+                        }
                     }
                 }
             }
@@ -1084,7 +1102,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        boolean isProviderEnabled = isAllowedBySettingsLocked(name);
+        boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid));
         if (isProviderEnabled) {
             applyRequirementsLocked(name);
         } else {
@@ -1141,7 +1159,7 @@
         // update provider
         for (String provider : providers) {
             // If provider is already disabled, don't need to do anything
-            if (!isAllowedBySettingsLocked(provider)) {
+            if (!isAllowedBySettingsLocked(provider, mCurrentUserId)) {
                 continue;
             }
 
@@ -1156,36 +1174,41 @@
         String perm = checkPermissionAndRequest(request);
         checkPackageName(packageName);
 
-        if (mBlacklist.isBlacklisted(packageName)) {
-            if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
-                    packageName);
-            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;
-            LocationProviderInterface provider = mProvidersByName.get(name);
-            if (provider == null) return null;
-
-            if (!isAllowedBySettingsLocked(name)) return null;
-
-            Location location = mLastLocation.get(name);
-            if (location == null) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            if (mBlacklist.isBlacklisted(packageName)) {
+                if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
+                        packageName);
                 return null;
             }
-            if (ACCESS_FINE_LOCATION.equals(perm)) {
-                return location;
-            } else {
-                Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
-                if (noGPSLocation != null) {
-                    return mLocationFudger.getOrCreate(noGPSLocation);
+
+            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;
+                LocationProviderInterface provider = mProvidersByName.get(name);
+                if (provider == null) return null;
+
+                if (!isAllowedBySettingsLocked(name, mCurrentUserId)) return null;
+
+                Location location = mLastLocation.get(name);
+                if (location == null) {
+                    return null;
+                }
+                if (ACCESS_FINE_LOCATION.equals(perm)) {
+                    return location;
+                } else {
+                    Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
+                    if (noGPSLocation != null) {
+                        return mLocationFudger.getOrCreate(noGPSLocation);
+                    }
                 }
             }
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        return null;
     }
 
     @Override
@@ -1321,11 +1344,16 @@
                     "\" provider requires ACCESS_FINE_LOCATION permission");
         }
 
-        synchronized (mLock) {
-            LocationProviderInterface p = mProvidersByName.get(provider);
-            if (p == null) return false;
+        long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                LocationProviderInterface p = mProvidersByName.get(provider);
+                if (p == null) return false;
 
-            return isAllowedBySettingsLocked(provider);
+                return isAllowedBySettingsLocked(provider, mCurrentUserId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -1458,6 +1486,16 @@
             Receiver receiver = r.mReceiver;
             boolean receiverDead = false;
 
+            int receiverUserId = UserHandle.getUserId(receiver.mUid);
+            if (receiverUserId != mCurrentUserId) {
+                if (D) {
+                    Log.d(TAG, "skipping loc update for background user " + receiverUserId +
+                            " (current user: " + mCurrentUserId + ", app: " +
+                            receiver.mPackageName + ")");
+                }
+                continue;
+            }
+
             if (mBlacklist.isBlacklisted(receiver.mPackageName)) {
                 if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
                         receiver.mPackageName);
@@ -1548,7 +1586,7 @@
         }
 
         synchronized (mLock) {
-            if (isAllowedBySettingsLocked(provider)) {
+            if (isAllowedBySettingsLocked(provider, mCurrentUserId)) {
                 handleLocationChangedLocked(location, passive);
             }
         }
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
index e99949b..5598b0a 100644
--- a/services/java/com/android/server/ServiceWatcher.java
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -27,6 +27,7 @@
 import android.content.pm.Signature;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.content.PackageMonitor;
@@ -58,15 +59,17 @@
     private IBinder mBinder;   // connected service
     private String mPackageName;  // current best package
     private int mVersion;  // current best version
+    private int mCurrentUserId;
 
     public ServiceWatcher(Context context, String logTag, String action,
-            List<String> initialPackageNames, Runnable newServiceWork, Handler handler) {
+            List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) {
         mContext = context;
         mTag = logTag;
         mAction = action;
         mPm = mContext.getPackageManager();
         mNewServiceWork = newServiceWork;
         mHandler = handler;
+        mCurrentUserId = userId;
 
         mSignatureSets = new ArrayList<HashSet<Signature>>();
         for (int i=0; i < initialPackageNames.size(); i++) {
@@ -85,9 +88,11 @@
     }
 
     public boolean start() {
-        if (!bindBestPackage(null)) return false;
+        synchronized (mLock) {
+            if (!bindBestPackageLocked(null)) return false;
+        }
 
-        mPackageMonitor.register(mContext, null, true);
+        mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
         return true;
     }
 
@@ -98,13 +103,13 @@
      * is null.
      * Return true if a new package was found to bind to.
      */
-    private boolean bindBestPackage(String justCheckThisPackage) {
+    private boolean bindBestPackageLocked(String justCheckThisPackage) {
         Intent intent = new Intent(mAction);
         if (justCheckThisPackage != null) {
             intent.setPackage(justCheckThisPackage);
         }
-        List<ResolveInfo> rInfos = mPm.queryIntentServices(new Intent(mAction),
-                PackageManager.GET_META_DATA);
+        List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction),
+                PackageManager.GET_META_DATA, mCurrentUserId);
         int bestVersion = Integer.MIN_VALUE;
         String bestPackage = null;
         for (ResolveInfo rInfo : rInfos) {
@@ -141,36 +146,32 @@
                 (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
 
         if (bestPackage != null) {
-            bindToPackage(bestPackage, bestVersion);
+            bindToPackageLocked(bestPackage, bestVersion);
             return true;
         }
         return false;
     }
 
-    private void unbind() {
+    private void unbindLocked() {
         String pkg;
-        synchronized (mLock) {
-            pkg = mPackageName;
-            mPackageName = null;
-            mVersion = Integer.MIN_VALUE;
-        }
+        pkg = mPackageName;
+        mPackageName = null;
+        mVersion = Integer.MIN_VALUE;
         if (pkg != null) {
             if (D) Log.d(mTag, "unbinding " + pkg);
             mContext.unbindService(this);
         }
     }
 
-    private void bindToPackage(String packageName, int version) {
-        unbind();
+    private void bindToPackageLocked(String packageName, int version) {
+        unbindLocked();
         Intent intent = new Intent(mAction);
         intent.setPackage(packageName);
-        synchronized (mLock) {
-            mPackageName = packageName;
-            mVersion = version;
-        }
+        mPackageName = packageName;
+        mVersion = version;
         if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
         mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
-                | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE);
+                | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId);
     }
 
     private boolean isSignatureMatch(Signature[] signatures) {
@@ -197,31 +198,37 @@
          */
         @Override
         public void onPackageUpdateFinished(String packageName, int uid) {
-            if (packageName.equals(mPackageName)) {
-                // package updated, make sure to rebind
-                unbind();
+            synchronized (mLock) {
+                if (packageName.equals(mPackageName)) {
+                    // package updated, make sure to rebind
+                    unbindLocked();
+                }
+                // check the updated package in case it is better
+                bindBestPackageLocked(packageName);
             }
-            // check the updated package in case it is better
-            bindBestPackage(packageName);
         }
 
         @Override
         public void onPackageAdded(String packageName, int uid) {
-            if (packageName.equals(mPackageName)) {
-                // package updated, make sure to rebind
-                unbind();
+            synchronized (mLock) {
+                if (packageName.equals(mPackageName)) {
+                    // package updated, make sure to rebind
+                    unbindLocked();
+                }
+                // check the new package is case it is better
+                bindBestPackageLocked(packageName);
             }
-            // check the new package is case it is better
-            bindBestPackage(packageName);
         }
 
         @Override
         public void onPackageRemoved(String packageName, int uid) {
-            if (packageName.equals(mPackageName)) {
-                unbind();
-                // the currently bound package was removed,
-                // need to search for a new package
-                bindBestPackage(null);
+            synchronized (mLock) {
+                if (packageName.equals(mPackageName)) {
+                    unbindLocked();
+                    // the currently bound package was removed,
+                    // need to search for a new package
+                    bindBestPackageLocked(null);
+                }
             }
         }
     };
@@ -271,4 +278,12 @@
             return mBinder;
         }
     }
+
+    public void switchUser(int userId) {
+        synchronized (mLock) {
+            unbindLocked();
+            mCurrentUserId = userId;
+            bindBestPackageLocked(null);
+        }
+    }
 }
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index 7d030e9..f5cc59f 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -21,6 +21,7 @@
 import android.location.GeocoderParams;
 import android.location.IGeocodeProvider;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.server.ServiceWatcher;
@@ -38,8 +39,8 @@
     private final ServiceWatcher mServiceWatcher;
 
     public static GeocoderProxy createAndBind(Context context,
-            List<String> initialPackageNames) {
-        GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames);
+            List<String> initialPackageNames, int userId) {
+        GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames, userId);
         if (proxy.bind()) {
             return proxy;
         } else {
@@ -47,11 +48,11 @@
         }
     }
 
-    public GeocoderProxy(Context context, List<String> initialPackageNames) {
+    public GeocoderProxy(Context context, List<String> initialPackageNames, int userId) {
         mContext = context;
 
         mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames,
-                null, null);
+                null, null, userId);
     }
 
     private boolean bind () {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index a254d74..c272da4 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -783,6 +783,11 @@
         sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
     }
 
+    @Override
+    public void switchUser(int userId) {
+        // nothing to do here
+    }
+
     private void handleSetRequest(ProviderRequest request, WorkSource source) {
         if (DEBUG) Log.d(TAG, "setRequest " + request);
 
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 6f09232..80e71f1 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -38,6 +38,8 @@
     public boolean isEnabled();
     public void setRequest(ProviderRequest request, WorkSource source);
 
+    public void switchUser(int userId);
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
 
     // --- deprecated (but still supported) ---
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 7faf72c..dd2e71c 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.WorkSource;
 import android.util.Log;
 
@@ -54,9 +55,9 @@
     private WorkSource mWorksource = new WorkSource();
 
     public static LocationProviderProxy createAndBind(Context context, String name, String action,
-            List<String> initialPackageNames, Handler handler) {
+            List<String> initialPackageNames, Handler handler, int userId) {
         LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
-                initialPackageNames, handler);
+                initialPackageNames, handler, userId);
         if (proxy.bind()) {
             return proxy;
         } else {
@@ -65,11 +66,11 @@
     }
 
     private LocationProviderProxy(Context context, String name, String action,
-            List<String> initialPackageNames, Handler handler) {
+            List<String> initialPackageNames, Handler handler, int userId) {
         mContext = context;
         mName = name;
         mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
-                mNewServiceWork, handler);
+                mNewServiceWork, handler, userId);
     }
 
     private boolean bind () {
@@ -211,6 +212,11 @@
     }
 
     @Override
+    public void switchUser(int userId) {
+        mServiceWatcher.switchUser(userId);
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.append("REMOTE SERVICE");
         pw.append(" name=").append(mName);
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 36c43ff..1194cbc 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -156,6 +156,11 @@
     public void setRequest(ProviderRequest request, WorkSource source) { }
 
     @Override
+    public void switchUser(int userId) {
+        // nothing to do here
+    }
+
+    @Override
     public boolean sendExtraCommand(String command, Bundle extras) {
         return false;
     }
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index 71bae07..734c572 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -96,6 +96,11 @@
         mReportLocation = request.reportLocation;
     }
 
+    @Override
+    public void switchUser(int userId) {
+        // nothing to do here
+    }
+
     public void updateLocation(Location location) {
         if (mReportLocation) {
             try {