Make LocationManagerService aware of user profiles

LocationManagerService normally doesn't provide location to background users.
With managed profiles, there are more than one foreground users and they both
need to get location when in the foreground. So LMS will now compare the caller
against all foreground users and provide location if valid.

Bug: 14671472
Change-Id: If3baabfc5a3ab3f2f081d7fc28f35d840d5f1489
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 98fa522..5eee396 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.location.Address;
@@ -55,10 +56,12 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
+
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -144,6 +147,7 @@
     private GeofenceManager mGeofenceManager;
     private PackageManager mPackageManager;
     private PowerManager mPowerManager;
+    private UserManager mUserManager;
     private GeocoderProxy mGeocodeProvider;
     private IGpsStatusProvider mGpsStatusProvider;
     private INetInitiatedListener mNetInitiatedListener;
@@ -197,6 +201,7 @@
 
     // current active user on the device - other users are denied location data
     private int mCurrentUserId = UserHandle.USER_OWNER;
+    private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_OWNER };
 
     public LocationManagerService(Context context) {
         super();
@@ -241,6 +246,9 @@
             };
             mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, callback);
 
+            mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            updateUserProfiles(mCurrentUserId);
+
             // prepare providers
             loadProvidersLocked();
             updateProvidersLocked();
@@ -262,6 +270,8 @@
         // listen for user change
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+        intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+        intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
 
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
@@ -269,11 +279,46 @@
                 String action = intent.getAction();
                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                     switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
+                        || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+                    updateUserProfiles(mCurrentUserId);
                 }
             }
         }, UserHandle.ALL, intentFilter, null, mLocationHandler);
     }
 
+    /**
+     * 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.
+     */
+    void updateUserProfiles(int currentUserId) {
+        List<UserInfo> profiles = mUserManager.getProfiles(currentUserId);
+        synchronized (mLock) {
+            mCurrentUserProfiles = new int[profiles.size()];
+            for (int i = 0; i < mCurrentUserProfiles.length; i++) {
+                mCurrentUserProfiles[i] = profiles.get(i).id;
+            }
+        }
+    }
+
+    /**
+     * Checks if the specified userId matches any of the current foreground
+     * users stored in mCurrentUserProfiles.
+     */
+    private boolean isCurrentProfile(int userId) {
+        synchronized (mLock) {
+            for (int i = 0; i < mCurrentUserProfiles.length; i++) {
+                if (mCurrentUserProfiles[i] == userId) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
     private void ensureFallbackFusedProviderPresentLocked(ArrayList<String> pkgs) {
         PackageManager pm = mContext.getPackageManager();
         String systemPackageName = mContext.getPackageName();
@@ -492,9 +537,10 @@
             mLastLocation.clear();
             mLastLocationCoarseInterval.clear();
             for (LocationProviderInterface p : mProviders) {
-                updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
+                updateProviderListenersLocked(p.getName(), false);
             }
             mCurrentUserId = userId;
+            updateUserProfiles(userId);
             updateProvidersLocked();
         }
     }
@@ -894,7 +940,7 @@
      * @return
      */
     private boolean isAllowedByUserSettingsLocked(String provider, int uid) {
-        if (UserHandle.getUserId(uid) != mCurrentUserId && !isUidALocationProvider(uid)) {
+        if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
             return false;
         }
         return isAllowedByCurrentUserSettingsLocked(provider);
@@ -1181,7 +1227,7 @@
             String name = p.getName();
             boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
             if (isEnabled && !shouldBeEnabled) {
-                updateProviderListenersLocked(name, false, mCurrentUserId);
+                updateProviderListenersLocked(name, false);
                 // 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.
@@ -1189,7 +1235,7 @@
                 mLastLocationCoarseInterval.clear();
                 changesMade = true;
             } else if (!isEnabled && shouldBeEnabled) {
-                updateProviderListenersLocked(name, true, mCurrentUserId);
+                updateProviderListenersLocked(name, true);
                 changesMade = true;
             }
         }
@@ -1201,7 +1247,7 @@
         }
     }
 
-    private void updateProviderListenersLocked(String provider, boolean enabled, int userId) {
+    private void updateProviderListenersLocked(String provider, boolean enabled) {
         int listeners = 0;
 
         LocationProviderInterface p = mProvidersByName.get(provider);
@@ -1214,7 +1260,7 @@
             final int N = records.size();
             for (int i = 0; i < N; i++) {
                 UpdateRecord record = records.get(i);
-                if (UserHandle.getUserId(record.mReceiver.mUid) == userId) {
+                if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
                     // Sends a notification message to the receiver
                     if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
                         if (deadReceivers == null) {
@@ -1253,7 +1299,7 @@
 
         if (records != null) {
             for (UpdateRecord record : records) {
-                if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+                if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
                     if (checkLocationAccess(record.mReceiver.mUid, record.mReceiver.mPackageName,
                             record.mReceiver.mAllowedResolutionLevel)) {
                         LocationRequest locationRequest = record.mRequest;
@@ -1274,7 +1320,7 @@
                 // under that threshold.
                 long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
                 for (UpdateRecord record : records) {
-                    if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+                    if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
                         LocationRequest locationRequest = record.mRequest;
                         if (locationRequest.getInterval() <= thresholdInterval) {
                             if (record.mReceiver.mWorkSource != null
@@ -2018,7 +2064,7 @@
             boolean receiverDead = false;
 
             int receiverUserId = UserHandle.getUserId(receiver.mUid);
-            if (receiverUserId != mCurrentUserId && !isUidALocationProvider(receiver.mUid)) {
+            if (!isCurrentProfile(receiverUserId) && !isUidALocationProvider(receiver.mUid)) {
                 if (D) {
                     Log.d(TAG, "skipping loc update for background user " + receiverUserId +
                             " (current user: " + mCurrentUserId + ", app: " +