Merge "Multiuser love for LocationManager" into jb-mr1-dev

This commit is contained in:
Victoria Lease
2012-10-09 12:22:02 -07:00
committed by Android (Google) Code Review
9 changed files with 222 additions and 118 deletions

View File

@ -4052,7 +4052,20 @@ public final class Settings {
* @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 final class Settings {
*/
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 @@ public final class Settings {
} else {
provider = "-" + provider;
}
putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider);
putStringForUser(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider,
userId);
}
}

View File

@ -226,7 +226,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
updateProvidersLocked();
}
}
});
}, UserHandle.USER_ALL);
mPackageMonitor.register(mContext, Looper.myLooper(), true);
// listen for user change
@ -289,7 +289,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
// 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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
*/
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Use system settings
ContentResolver resolver = mContext.getContentResolver();
return Settings.Secure.isLocationProviderEnabled(resolver, provider);
return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId);
}
/**
@ -695,24 +702,30 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// 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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
oldRecord.disposeLocked(false);
}
boolean isProviderEnabled = isAllowedBySettingsLocked(name);
boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid));
if (isProviderEnabled) {
applyRequirementsLocked(name);
} else {
@ -1141,7 +1159,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// 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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
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 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
"\" 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);
}
}
@ -1461,6 +1489,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
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);
@ -1551,7 +1589,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
synchronized (mLock) {
if (isAllowedBySettingsLocked(provider)) {
if (isAllowedBySettingsLocked(provider, mCurrentUserId)) {
handleLocationChangedLocked(location, passive);
}
}

View File

@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo;
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 @@ public class ServiceWatcher implements ServiceConnection {
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 class ServiceWatcher implements ServiceConnection {
}
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 @@ public class ServiceWatcher implements ServiceConnection {
* 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 @@ public class ServiceWatcher implements ServiceConnection {
(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 @@ public class ServiceWatcher implements ServiceConnection {
*/
@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 @@ public class ServiceWatcher implements ServiceConnection {
return mBinder;
}
}
public void switchUser(int userId) {
synchronized (mLock) {
unbindLocked();
mCurrentUserId = userId;
bindBestPackageLocked(null);
}
}
}

View File

@ -21,6 +21,7 @@ import android.location.Address;
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 @@ public class GeocoderProxy {
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 class GeocoderProxy {
}
}
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 () {

View File

@ -783,6 +783,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
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);

View File

@ -38,6 +38,8 @@ public interface LocationProviderInterface {
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) ---

View File

@ -25,6 +25,7 @@ import android.location.LocationProvider;
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 @@ public class LocationProviderProxy implements LocationProviderInterface {
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 @@ public class LocationProviderProxy implements LocationProviderInterface {
}
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 () {
@ -210,6 +211,11 @@ public class LocationProviderProxy implements LocationProviderInterface {
}
}
@Override
public void switchUser(int userId) {
mServiceWatcher.switchUser(userId);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.append("REMOTE SERVICE");

View File

@ -155,6 +155,11 @@ public class MockProvider implements LocationProviderInterface {
@Override
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;

View File

@ -96,6 +96,11 @@ public class PassiveProvider implements LocationProviderInterface {
mReportLocation = request.reportLocation;
}
@Override
public void switchUser(int userId) {
// nothing to do here
}
public void updateLocation(Location location) {
if (mReportLocation) {
try {